prisma
This commit is contained in:
parent
16bd673c7d
commit
fb09daf983
10
.env
10
.env
@ -29,3 +29,13 @@ OPENAI_API_KEY=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
|
|||||||
# List models available from Groq
|
# List models available from Groq
|
||||||
# aider --models groq/
|
# aider --models groq/
|
||||||
SUBSCRIPTION_ID='2217755'
|
SUBSCRIPTION_ID='2217755'
|
||||||
|
|
||||||
|
|
||||||
|
# This was inserted by `prisma init`:
|
||||||
|
# Environment variables declared in this file are automatically made available to Prisma.
|
||||||
|
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
|
||||||
|
|
||||||
|
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
|
||||||
|
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
|
||||||
|
|
||||||
|
DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
|
@ -2,213 +2,161 @@ import sys
|
|||||||
import os
|
import os
|
||||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
import aiosqlite
|
|
||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import prisma
|
||||||
|
from prisma import Prisma
|
||||||
|
|
||||||
DATABASE_FILE = "./app_data.db"
|
# Initialize the Prisma client
|
||||||
|
prisma_client = Prisma()
|
||||||
|
|
||||||
async def init_db():
|
async def init_db():
|
||||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
await prisma_client.connect()
|
||||||
await db.executescript("""
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
username TEXT UNIQUE NOT NULL,
|
|
||||||
password_hash TEXT NOT NULL,
|
|
||||||
email TEXT UNIQUE NOT NULL,
|
|
||||||
api_key TEXT UNIQUE,
|
|
||||||
plan TEXT DEFAULT 'free'
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS wallets (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
user_id INTEGER,
|
|
||||||
address TEXT NOT NULL,
|
|
||||||
secret TEXT NOT NULL,
|
|
||||||
name TEXT,
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS transactions (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
wallet_id INTEGER,
|
|
||||||
timestamp TEXT,
|
|
||||||
type TEXT,
|
|
||||||
sell_currency TEXT,
|
|
||||||
sell_amount REAL,
|
|
||||||
sell_value REAL,
|
|
||||||
buy_currency TEXT,
|
|
||||||
buy_amount REAL,
|
|
||||||
buy_value REAL,
|
|
||||||
details TEXT,
|
|
||||||
solana_signature TEXT UNIQUE,
|
|
||||||
FOREIGN KEY (wallet_id) REFERENCES wallets(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS holdings (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
wallet_id INTEGER,
|
|
||||||
currency TEXT,
|
|
||||||
amount REAL,
|
|
||||||
last_updated TEXT,
|
|
||||||
is_base BOOLEAN DEFAULT 0,
|
|
||||||
FOREIGN KEY (wallet_id) REFERENCES wallets(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS price_alerts (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
user_id INTEGER,
|
|
||||||
currency TEXT,
|
|
||||||
target_price REAL,
|
|
||||||
alert_type TEXT,
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS followed_accounts (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
user_id INTEGER,
|
|
||||||
address TEXT,
|
|
||||||
followed_address TEXT,
|
|
||||||
name TEXT,
|
|
||||||
FOREIGN KEY (address) REFERENCES wallets(address),
|
|
||||||
FOREIGN KEY (followed_address) REFERENCES wallets(address),
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
||||||
);
|
|
||||||
""")
|
|
||||||
await db.commit()
|
|
||||||
|
|
||||||
async def store_transaction(wallet_id, transaction_type, sell_currency, sell_amount, sell_value, buy_currency, buy_amount, buy_value, solana_signature, details=None):
|
async def store_transaction(wallet_id, transaction_type, sell_currency, sell_amount, sell_value, buy_currency, buy_amount, buy_value, solana_signature, details=None):
|
||||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
await prisma_client.transaction.create(
|
||||||
await db.execute("""
|
data={
|
||||||
INSERT INTO transactions (wallet_id, timestamp, type, sell_currency, sell_amount, sell_value, buy_currency, buy_amount, buy_value, solana_signature, details)
|
'wallet_id': wallet_id,
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
'timestamp': datetime.now().isoformat(),
|
||||||
""", (wallet_id, datetime.now().isoformat(), transaction_type, sell_currency, sell_amount, sell_value, buy_currency, buy_amount, buy_value, solana_signature, json.dumps(details or {})))
|
'type': transaction_type,
|
||||||
await db.commit()
|
'sell_currency': sell_currency,
|
||||||
|
'sell_amount': sell_amount,
|
||||||
|
'sell_value': sell_value,
|
||||||
|
'buy_currency': buy_currency,
|
||||||
|
'buy_amount': buy_amount,
|
||||||
|
'buy_value': buy_value,
|
||||||
|
'solana_signature': solana_signature,
|
||||||
|
'details': json.dumps(details or {})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
async def update_holdings(wallet_id, currency, amount_change):
|
async def update_holdings(wallet_id, currency, amount_change):
|
||||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
holding = await prisma_client.holding.find_first(
|
||||||
cursor = await db.execute("SELECT amount FROM holdings WHERE wallet_id = ? AND currency = ?", (wallet_id, currency))
|
where={
|
||||||
result = await cursor.fetchone()
|
'wallet_id': wallet_id,
|
||||||
if result:
|
'currency': currency
|
||||||
new_amount = result[0] + amount_change
|
}
|
||||||
await db.execute("UPDATE holdings SET amount = ?, last_updated = ? WHERE wallet_id = ? AND currency = ?",
|
)
|
||||||
(new_amount, datetime.now().isoformat(), wallet_id, currency))
|
if holding:
|
||||||
|
new_amount = holding.amount + amount_change
|
||||||
|
await prisma_client.holding.update(
|
||||||
|
where={'id': holding.id},
|
||||||
|
data={
|
||||||
|
'amount': new_amount,
|
||||||
|
'last_updated': datetime.now().isoformat()
|
||||||
|
}
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
await db.execute("INSERT INTO holdings (wallet_id, currency, amount, last_updated) VALUES (?, ?, ?, ?)",
|
await prisma_client.holding.create(
|
||||||
(wallet_id, currency, amount_change, datetime.now().isoformat()))
|
data={
|
||||||
await db.commit()
|
'wallet_id': wallet_id,
|
||||||
|
'currency': currency,
|
||||||
|
'amount': amount_change,
|
||||||
|
'last_updated': datetime.now().isoformat()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
async def get_wallet_holdings(wallet_id):
|
async def get_wallet_holdings(wallet_id):
|
||||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
return await prisma_client.holding.find_many(
|
||||||
cursor = await db.execute("SELECT currency, amount FROM holdings WHERE wallet_id = ?", (wallet_id,))
|
where={'wallet_id': wallet_id},
|
||||||
return await cursor.fetchall()
|
select={'currency': True, 'amount': True}
|
||||||
|
)
|
||||||
|
|
||||||
async def get_transaction_history(wallet_id, start_date=None, end_date=None, include_closed=False):
|
async def get_transaction_history(wallet_id, start_date=None, end_date=None, include_closed=False):
|
||||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
filters = {'wallet_id': wallet_id}
|
||||||
query = "SELECT * FROM transactions WHERE wallet_id = ?"
|
|
||||||
params = [wallet_id]
|
|
||||||
if not include_closed:
|
if not include_closed:
|
||||||
query += " AND closed = 0"
|
filters['closed'] = False
|
||||||
if start_date:
|
if start_date:
|
||||||
query += " AND timestamp >= ?"
|
filters['timestamp'] = {'gte': start_date}
|
||||||
params.append(start_date)
|
|
||||||
if end_date:
|
if end_date:
|
||||||
query += " AND timestamp <= ?"
|
filters['timestamp'] = {'lte': end_date}
|
||||||
params.append(end_date)
|
|
||||||
query += " ORDER BY timestamp DESC"
|
|
||||||
cursor = await db.execute(query, params)
|
|
||||||
return await cursor.fetchall()
|
|
||||||
|
|
||||||
# New utility functions
|
return await prisma_client.transaction.find_many(
|
||||||
|
where=filters,
|
||||||
|
order={'timestamp': 'desc'}
|
||||||
|
)
|
||||||
|
|
||||||
async def close_transaction(transaction_id):
|
async def close_transaction(transaction_id):
|
||||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
await prisma_client.transaction.update(
|
||||||
await db.execute("UPDATE transactions SET closed = 1 WHERE id = ?", (transaction_id,))
|
where={'id': transaction_id},
|
||||||
await db.commit()
|
data={'closed': True}
|
||||||
|
)
|
||||||
|
|
||||||
async def get_open_transactions(wallet_id, currency):
|
async def get_open_transactions(wallet_id, currency):
|
||||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
return await prisma_client.transaction.find_many(
|
||||||
cursor = await db.execute("""
|
where={
|
||||||
SELECT * FROM transactions
|
'wallet_id': wallet_id,
|
||||||
WHERE wallet_id = ? AND buy_currency = ? AND closed = 0
|
'buy_currency': currency,
|
||||||
ORDER BY timestamp ASC
|
'closed': False
|
||||||
""", (wallet_id, currency))
|
},
|
||||||
return await cursor.fetchall()
|
order={'timestamp': 'asc'}
|
||||||
|
)
|
||||||
|
|
||||||
async def calculate_current_holdings(wallet_id):
|
async def calculate_current_holdings(wallet_id):
|
||||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
transactions = await prisma_client.transaction.group_by(
|
||||||
cursor = await db.execute("""
|
by=['buy_currency'],
|
||||||
SELECT
|
where={'wallet_id': wallet_id, 'closed': False},
|
||||||
buy_currency AS currency,
|
_sum={'buy_amount': True, 'sell_amount': True}
|
||||||
SUM(buy_amount) - COALESCE(
|
)
|
||||||
(SELECT SUM(sell_amount)
|
return [
|
||||||
FROM transactions t2
|
{
|
||||||
WHERE t2.wallet_id = t1.wallet_id
|
'currency': t.buy_currency,
|
||||||
AND t2.sell_currency = t1.buy_currency
|
'amount': t._sum.buy_amount - (t._sum.sell_amount or 0)
|
||||||
AND t2.closed = 0),
|
}
|
||||||
0
|
for t in transactions if t._sum.buy_amount > (t._sum.sell_amount or 0)
|
||||||
) AS amount
|
]
|
||||||
FROM transactions t1
|
|
||||||
WHERE wallet_id = ? AND closed = 0
|
|
||||||
GROUP BY buy_currency
|
|
||||||
HAVING amount > 0
|
|
||||||
""", (wallet_id,))
|
|
||||||
return await cursor.fetchall()
|
|
||||||
|
|
||||||
STABLECOINS = ['USDC', 'USDT', 'SOL']
|
STABLECOINS = ['USDC', 'USDT', 'SOL']
|
||||||
|
|
||||||
async def is_transaction_closed(wallet_id, transaction_id):
|
async def is_transaction_closed(wallet_id, transaction_id):
|
||||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
transaction = await prisma_client.transaction.find_unique(
|
||||||
cursor = await db.execute("""
|
where={'id': transaction_id}
|
||||||
SELECT t1.buy_currency, t1.buy_amount,
|
)
|
||||||
(SELECT SUM(sell_amount)
|
if transaction:
|
||||||
FROM transactions t2
|
sold_amount = await prisma_client.transaction.aggregate(
|
||||||
WHERE t2.wallet_id = t1.wallet_id
|
_sum={'sell_amount': True},
|
||||||
AND t2.sell_currency = t1.buy_currency
|
where={
|
||||||
AND t2.timestamp > t1.timestamp) AS sold_amount
|
'wallet_id': wallet_id,
|
||||||
FROM transactions t1
|
'sell_currency': transaction.buy_currency,
|
||||||
WHERE t1.id = ? AND t1.wallet_id = ?
|
'timestamp': {'gt': transaction.timestamp}
|
||||||
""", (transaction_id, wallet_id))
|
}
|
||||||
result = await cursor.fetchone()
|
)
|
||||||
|
return sold_amount._sum.sell_amount >= transaction.buy_amount
|
||||||
if result:
|
|
||||||
buy_currency, buy_amount, sold_amount = result
|
|
||||||
return sold_amount is not None and sold_amount >= buy_amount
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def close_completed_transactions(wallet_id):
|
async def close_completed_transactions(wallet_id):
|
||||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
transactions = await prisma_client.transaction.find_many(
|
||||||
cursor = await db.execute("""
|
where={
|
||||||
SELECT id FROM transactions
|
'wallet_id': wallet_id,
|
||||||
WHERE wallet_id = ? AND closed = 0 AND buy_currency NOT IN (?)
|
'closed': False,
|
||||||
""", (wallet_id, ','.join(STABLECOINS)))
|
'buy_currency': {'notIn': STABLECOINS}
|
||||||
transactions = await cursor.fetchall()
|
}
|
||||||
|
)
|
||||||
for (transaction_id,) in transactions:
|
for transaction in transactions:
|
||||||
if await is_transaction_closed(wallet_id, transaction_id):
|
if await is_transaction_closed(wallet_id, transaction.id):
|
||||||
await close_transaction(transaction_id)
|
await close_transaction(transaction.id)
|
||||||
|
|
||||||
async def get_profit_loss(wallet_id, currency, start_date=None, end_date=None):
|
async def get_profit_loss(wallet_id, currency, start_date=None, end_date=None):
|
||||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
filters = {
|
||||||
query = """
|
'wallet_id': wallet_id,
|
||||||
SELECT
|
'OR': [
|
||||||
SUM(CASE WHEN sell_currency = ? THEN sell_value ELSE -buy_value END) as profit_loss
|
{'sell_currency': currency},
|
||||||
FROM transactions
|
{'buy_currency': currency}
|
||||||
WHERE wallet_id = ? AND (sell_currency = ? OR buy_currency = ?)
|
]
|
||||||
"""
|
}
|
||||||
params = [currency, wallet_id, currency, currency]
|
|
||||||
|
|
||||||
if start_date:
|
if start_date:
|
||||||
query += " AND timestamp >= ?"
|
filters['timestamp'] = {'gte': start_date}
|
||||||
params.append(start_date)
|
|
||||||
if end_date:
|
if end_date:
|
||||||
query += " AND timestamp <= ?"
|
filters['timestamp'] = {'lte': end_date}
|
||||||
params.append(end_date)
|
|
||||||
|
|
||||||
cursor = await db.execute(query, params)
|
result = await prisma_client.transaction.aggregate(
|
||||||
result = await cursor.fetchone()
|
_sum={
|
||||||
return result[0] if result else 0
|
'sell_value': True,
|
||||||
|
'buy_value': True
|
||||||
|
},
|
||||||
|
where=filters
|
||||||
|
)
|
||||||
|
return (result._sum.sell_value or 0) - (result._sum.buy_value or 0)
|
||||||
|
|
||||||
# # # # # # USERS
|
# # # # # # USERS
|
||||||
|
|
||||||
|
81
package-lock.json
generated
81
package-lock.json
generated
@ -20,6 +20,9 @@
|
|||||||
"openai": "^4.50.0",
|
"openai": "^4.50.0",
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
"ws": "^8.12.1"
|
"ws": "^8.12.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prisma": "^5.22.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@prisma/client": {
|
"node_modules/@prisma/client": {
|
||||||
@ -39,6 +42,51 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@prisma/debug": {
|
||||||
|
"version": "5.22.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz",
|
||||||
|
"integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==",
|
||||||
|
"devOptional": true
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/engines": {
|
||||||
|
"version": "5.22.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz",
|
||||||
|
"integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "5.22.0",
|
||||||
|
"@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2",
|
||||||
|
"@prisma/fetch-engine": "5.22.0",
|
||||||
|
"@prisma/get-platform": "5.22.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/engines-version": {
|
||||||
|
"version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz",
|
||||||
|
"integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==",
|
||||||
|
"devOptional": true
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/fetch-engine": {
|
||||||
|
"version": "5.22.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz",
|
||||||
|
"integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "5.22.0",
|
||||||
|
"@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2",
|
||||||
|
"@prisma/get-platform": "5.22.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/get-platform": {
|
||||||
|
"version": "5.22.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz",
|
||||||
|
"integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==",
|
||||||
|
"devOptional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "5.22.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "18.19.34",
|
"version": "18.19.34",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.34.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.34.tgz",
|
||||||
@ -595,6 +643,20 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fsevents": {
|
||||||
|
"version": "2.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
|
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
@ -992,6 +1054,25 @@
|
|||||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||||
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
|
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
|
||||||
},
|
},
|
||||||
|
"node_modules/prisma": {
|
||||||
|
"version": "5.22.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz",
|
||||||
|
"integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==",
|
||||||
|
"devOptional": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/engines": "5.22.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"prisma": "build/index.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.13"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "2.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/proxy-addr": {
|
"node_modules/proxy-addr": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
|
@ -24,5 +24,8 @@
|
|||||||
"openai": "^4.50.0",
|
"openai": "^4.50.0",
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
"ws": "^8.12.1"
|
"ws": "^8.12.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prisma": "^5.22.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
88
prisma/schema.prisma
Normal file
88
prisma/schema.prisma
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
username String @unique
|
||||||
|
password String
|
||||||
|
ownedWallets Wallet[] @relation("UserWallets")
|
||||||
|
followedWallets WalletFollow[] // Intermediate table relation
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
model Wallet {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
publicKey String
|
||||||
|
privateKey String
|
||||||
|
userId Int
|
||||||
|
owner User @relation("UserWallets", fields: [userId], references: [id])
|
||||||
|
followers WalletFollow[] // Intermediate table relation
|
||||||
|
holdings Holding[]
|
||||||
|
|
||||||
|
@@index([userId])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intermediate table for many-to-many follow relationship
|
||||||
|
model WalletFollow {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
userId Int
|
||||||
|
walletId Int
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
wallet Wallet @relation(fields: [walletId], references: [id])
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
|
@@unique([userId, walletId]) // Prevent duplicate follows
|
||||||
|
@@index([userId])
|
||||||
|
@@index([walletId])
|
||||||
|
}
|
||||||
|
|
||||||
|
model Holding {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
tokenMint String @unique
|
||||||
|
tokenName String
|
||||||
|
ammount Float
|
||||||
|
totalAcquisitionValueUSD Float
|
||||||
|
totalSellValueUSD Float
|
||||||
|
walletId Int
|
||||||
|
wallet Wallet @relation(fields: [walletId], references: [id])
|
||||||
|
transactions Transaction[]
|
||||||
|
pnl Float @default(0.0)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TransactionStatus {
|
||||||
|
ORIGINAL
|
||||||
|
ORIGINAL_FOLLOWED
|
||||||
|
PENDING
|
||||||
|
FAILED
|
||||||
|
SENT
|
||||||
|
CONFIRMED
|
||||||
|
}
|
||||||
|
|
||||||
|
model Transaction {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
transactionId String
|
||||||
|
amount Float
|
||||||
|
date DateTime
|
||||||
|
amountUSD Float
|
||||||
|
holdingId Int
|
||||||
|
holding Holding @relation(fields: [holdingId], references: [id])
|
||||||
|
isClosed Boolean @default(false)
|
||||||
|
status TransactionStatus
|
||||||
|
originalId Int?
|
||||||
|
original Transaction? @relation("OriginalTransaction", fields: [originalId], references: [id])
|
||||||
|
relatedTo Transaction[] @relation("OriginalTransaction")
|
||||||
|
timestamp DateTime @default(now())
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "mysql"
|
||||||
|
url = "mysql://root:Zelen0ku4e@192.168.0.10:3306/trader"
|
||||||
|
}
|
||||||
|
|
||||||
|
model Timeseries {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
timestamp DateTime @default(now())
|
||||||
|
data String
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user