mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
264 lines
7.2 KiB
264 lines
7.2 KiB
import { PrismaClient } from '@prisma/client';
|
|
import { createHmac } from 'node:crypto';
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
const ACCESS_TOKEN_SALT =
|
|
process.env.ACCESS_TOKEN_SALT ?? 'agentforge-dev-salt-2026';
|
|
|
|
function hashToken(plain: string): string {
|
|
return createHmac('sha512', ACCESS_TOKEN_SALT).update(plain).digest('hex');
|
|
}
|
|
|
|
async function main() {
|
|
// Tags — always seeded (upstream default)
|
|
await prisma.tag.createMany({
|
|
data: [
|
|
{ id: '4452656d-9fa4-4bd0-ba38-70492e31d180', name: 'EMERGENCY_FUND' },
|
|
{
|
|
id: 'f2e868af-8333-459f-b161-cbc6544c24bd',
|
|
name: 'EXCLUDE_FROM_ANALYSIS'
|
|
}
|
|
],
|
|
skipDuplicates: true
|
|
});
|
|
|
|
// Demo portfolio data — opt-in via SEED_DEMO_DATA=true
|
|
if (process.env.SEED_DEMO_DATA === 'true') {
|
|
const DEMO_USER_ID = '403deeb8-edd5-4e64-99a9-3752782ad0a2';
|
|
const DEMO_ACCOUNT_ID = 'c2a0c0f6-366b-4992-97ac-1adfa81d6cbc';
|
|
const DEMO_ACCESS_TOKEN_PLAIN = 'demo-token-2026';
|
|
|
|
// Demo user
|
|
await prisma.user.upsert({
|
|
where: { id: DEMO_USER_ID },
|
|
update: {},
|
|
create: {
|
|
id: DEMO_USER_ID,
|
|
accessToken: hashToken(DEMO_ACCESS_TOKEN_PLAIN),
|
|
provider: 'ANONYMOUS',
|
|
role: 'ADMIN'
|
|
}
|
|
});
|
|
|
|
// Demo account
|
|
await prisma.account.upsert({
|
|
where: {
|
|
id_userId: { id: DEMO_ACCOUNT_ID, userId: DEMO_USER_ID }
|
|
},
|
|
update: {},
|
|
create: {
|
|
id: DEMO_ACCOUNT_ID,
|
|
userId: DEMO_USER_ID,
|
|
name: 'Main Brokerage',
|
|
balance: 5000,
|
|
currency: 'USD',
|
|
isExcluded: false
|
|
}
|
|
});
|
|
|
|
// Symbol profiles
|
|
const symbols = [
|
|
{
|
|
id: 'd8f0b8c3-212c-48ef-a837-fff75ef98176',
|
|
symbol: 'AAPL',
|
|
name: 'Apple Inc.',
|
|
currency: 'USD',
|
|
dataSource: 'YAHOO' as const,
|
|
assetClass: 'EQUITY' as const,
|
|
assetSubClass: 'STOCK' as const
|
|
},
|
|
{
|
|
id: '5bb696ab-aaf3-4924-a0e4-79c69bfcd81b',
|
|
symbol: 'MSFT',
|
|
name: 'Microsoft Corporation',
|
|
currency: 'USD',
|
|
dataSource: 'YAHOO' as const,
|
|
assetClass: 'EQUITY' as const,
|
|
assetSubClass: 'STOCK' as const
|
|
},
|
|
{
|
|
id: '7df6544c-c592-459c-af69-aafe65db60c9',
|
|
symbol: 'VOO',
|
|
name: 'Vanguard S&P 500 ETF',
|
|
currency: 'USD',
|
|
dataSource: 'YAHOO' as const,
|
|
assetClass: 'EQUITY' as const,
|
|
assetSubClass: 'ETF' as const
|
|
},
|
|
{
|
|
id: 'ba75d50e-34f6-4c9e-bbb7-71b43b7cbfc0',
|
|
symbol: 'GOOGL',
|
|
name: 'Alphabet Inc.',
|
|
currency: 'USD',
|
|
dataSource: 'YAHOO' as const,
|
|
assetClass: 'EQUITY' as const,
|
|
assetSubClass: 'STOCK' as const
|
|
},
|
|
{
|
|
id: '8b846370-2e16-4594-9785-a94da15d60a1',
|
|
symbol: 'bitcoin',
|
|
name: 'Bitcoin',
|
|
currency: 'USD',
|
|
dataSource: 'COINGECKO' as const,
|
|
assetClass: 'ALTERNATIVE_INVESTMENT' as const,
|
|
assetSubClass: 'CRYPTOCURRENCY' as const
|
|
}
|
|
];
|
|
|
|
for (const sp of symbols) {
|
|
await prisma.symbolProfile.upsert({
|
|
where: {
|
|
dataSource_symbol: { dataSource: sp.dataSource, symbol: sp.symbol }
|
|
},
|
|
update: {},
|
|
create: {
|
|
id: sp.id,
|
|
symbol: sp.symbol,
|
|
name: sp.name,
|
|
currency: sp.currency,
|
|
dataSource: sp.dataSource,
|
|
assetClass: sp.assetClass,
|
|
assetSubClass: sp.assetSubClass
|
|
}
|
|
});
|
|
}
|
|
|
|
// Resolve actual symbolProfile IDs (may differ if profiles pre-existed)
|
|
const profileLookup = new Map<string, string>();
|
|
for (const sp of symbols) {
|
|
const found = await prisma.symbolProfile.findUnique({
|
|
where: {
|
|
dataSource_symbol: { dataSource: sp.dataSource, symbol: sp.symbol }
|
|
},
|
|
select: { id: true }
|
|
});
|
|
if (found) {
|
|
profileLookup.set(sp.id, found.id);
|
|
}
|
|
}
|
|
|
|
// Orders
|
|
const orders = [
|
|
{
|
|
id: '49f6cba4-7dd2-47e1-918e-4d8538e3818f',
|
|
seedProfileId: 'd8f0b8c3-212c-48ef-a837-fff75ef98176',
|
|
type: 'BUY' as const,
|
|
quantity: 15,
|
|
unitPrice: 178.5,
|
|
fee: 0,
|
|
date: new Date('2024-03-15'),
|
|
currency: 'USD'
|
|
},
|
|
{
|
|
id: '7090411c-a046-4363-a89e-d61d28417820',
|
|
seedProfileId: '5bb696ab-aaf3-4924-a0e4-79c69bfcd81b',
|
|
type: 'BUY' as const,
|
|
quantity: 10,
|
|
unitPrice: 420.0,
|
|
fee: 0,
|
|
date: new Date('2024-04-01'),
|
|
currency: 'USD'
|
|
},
|
|
{
|
|
id: '06cd0784-c5f4-42a0-b799-eb48b61b7afb',
|
|
seedProfileId: '7df6544c-c592-459c-af69-aafe65db60c9',
|
|
type: 'BUY' as const,
|
|
quantity: 20,
|
|
unitPrice: 480.0,
|
|
fee: 0,
|
|
date: new Date('2024-01-10'),
|
|
currency: 'USD'
|
|
},
|
|
{
|
|
id: '151b2a27-f82f-4393-85b2-a57660fc2d25',
|
|
seedProfileId: 'ba75d50e-34f6-4c9e-bbb7-71b43b7cbfc0',
|
|
type: 'BUY' as const,
|
|
quantity: 8,
|
|
unitPrice: 155.0,
|
|
fee: 0,
|
|
date: new Date('2024-06-20'),
|
|
currency: 'USD'
|
|
},
|
|
{
|
|
id: 'abc07d0a-3be2-4185-9ae4-d2b0fa4a5d48',
|
|
seedProfileId: '8b846370-2e16-4594-9785-a94da15d60a1',
|
|
type: 'BUY' as const,
|
|
quantity: 0.5,
|
|
unitPrice: 43000.0,
|
|
fee: 0,
|
|
date: new Date('2024-02-01'),
|
|
currency: 'USD'
|
|
},
|
|
{
|
|
id: 'e3a1f7c2-8d94-4b61-a5e3-9c72d1f08e34',
|
|
seedProfileId: 'd8f0b8c3-212c-48ef-a837-fff75ef98176',
|
|
type: 'BUY' as const,
|
|
quantity: 5,
|
|
unitPrice: 195.0,
|
|
fee: 0,
|
|
date: new Date('2024-09-15'),
|
|
currency: 'USD'
|
|
},
|
|
{
|
|
id: 'f7b2c8d1-6e45-4a93-b812-d3e9f0a17c56',
|
|
seedProfileId: '7df6544c-c592-459c-af69-aafe65db60c9',
|
|
type: 'DIVIDEND' as const,
|
|
quantity: 0,
|
|
unitPrice: 1.78,
|
|
fee: 0,
|
|
date: new Date('2024-12-20'),
|
|
currency: 'USD'
|
|
},
|
|
{
|
|
id: 'a1c3e5f7-2b4d-4869-9a0c-e6f8d2b4a7c1',
|
|
seedProfileId: '5bb696ab-aaf3-4924-a0e4-79c69bfcd81b',
|
|
type: 'SELL' as const,
|
|
quantity: 3,
|
|
unitPrice: 450.0,
|
|
fee: 0,
|
|
date: new Date('2025-01-10'),
|
|
currency: 'USD'
|
|
}
|
|
];
|
|
|
|
for (const ord of orders) {
|
|
const symbolProfileId = profileLookup.get(ord.seedProfileId);
|
|
if (!symbolProfileId) {
|
|
console.warn(
|
|
`Skipping order ${ord.id}: no symbolProfile found for seed ID ${ord.seedProfileId}`
|
|
);
|
|
continue;
|
|
}
|
|
await prisma.order.upsert({
|
|
where: { id: ord.id },
|
|
update: { symbolProfileId },
|
|
create: {
|
|
id: ord.id,
|
|
userId: DEMO_USER_ID,
|
|
accountId: DEMO_ACCOUNT_ID,
|
|
accountUserId: DEMO_USER_ID,
|
|
symbolProfileId,
|
|
type: ord.type,
|
|
quantity: ord.quantity,
|
|
unitPrice: ord.unitPrice,
|
|
fee: ord.fee,
|
|
date: ord.date,
|
|
currency: ord.currency
|
|
}
|
|
});
|
|
}
|
|
|
|
console.log('Seeded demo user, account, symbols, and orders.');
|
|
console.log(` Login token: ${DEMO_ACCESS_TOKEN_PLAIN}`);
|
|
}
|
|
}
|
|
|
|
main()
|
|
.catch((e) => {
|
|
console.error(e);
|
|
process.exit(1);
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect();
|
|
});
|
|
|