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.
 
 
 
 
 

254 lines
5.9 KiB

import { PrismaClient } from '@prisma/client';
import { createHmac } from 'node:crypto';
const prisma = new PrismaClient();
const DEMO_USER_ID = 'demo0000-0000-0000-0000-000000000000';
const DEMO_ACCOUNT_ID = 'demo-acc0-0000-0000-000000000000';
function createAccessToken(password: string, salt: string): string {
const hash = createHmac('sha512', salt);
hash.update(password);
return hash.digest('hex');
}
async function main() {
// Create default tags
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
});
// Create demo user for evaluators (if not already exists)
const accessTokenSalt = process.env.ACCESS_TOKEN_SALT;
if (!accessTokenSalt) {
console.log('ACCESS_TOKEN_SALT not set, skipping demo user creation');
return;
}
const existingDemoUser = await prisma.user.findUnique({
where: { id: DEMO_USER_ID }
});
if (existingDemoUser) {
console.log('Demo user already exists, skipping creation');
return;
}
console.log('Creating demo user with portfolio data...');
// Generate access token for demo user
const rawAccessToken = 'demo-access-token-for-ghostfolio-ai-eval';
const hashedAccessToken = createAccessToken(rawAccessToken, accessTokenSalt);
// Create demo user with DEMO role
await prisma.user.create({
data: {
id: DEMO_USER_ID,
accessToken: hashedAccessToken,
role: 'DEMO',
provider: 'ANONYMOUS',
settings: {
create: {
settings: {
baseCurrency: 'USD',
locale: 'en-US',
language: 'en',
viewMode: 'DEFAULT'
}
}
}
}
});
// Create demo account
await prisma.account.create({
data: {
id: DEMO_ACCOUNT_ID,
name: 'Demo Portfolio',
currency: 'USD',
userId: DEMO_USER_ID
}
});
// Create symbol profiles for demo holdings (if not exists)
const symbols = [
{
symbol: 'AAPL',
name: 'Apple Inc.',
currency: 'USD',
dataSource: 'YAHOO',
assetClass: 'EQUITY',
assetSubClass: 'STOCK'
},
{
symbol: 'MSFT',
name: 'Microsoft Corporation',
currency: 'USD',
dataSource: 'YAHOO',
assetClass: 'EQUITY',
assetSubClass: 'STOCK'
},
{
symbol: 'VTI',
name: 'Vanguard Total Stock Market ETF',
currency: 'USD',
dataSource: 'YAHOO',
assetClass: 'EQUITY',
assetSubClass: 'ETF'
},
{
symbol: 'GOOGL',
name: 'Alphabet Inc.',
currency: 'USD',
dataSource: 'YAHOO',
assetClass: 'EQUITY',
assetSubClass: 'STOCK'
},
{
symbol: 'AMZN',
name: 'Amazon.com Inc.',
currency: 'USD',
dataSource: 'YAHOO',
assetClass: 'EQUITY',
assetSubClass: 'STOCK'
}
];
for (const s of symbols) {
await prisma.symbolProfile.upsert({
where: {
dataSource_symbol: {
dataSource: s.dataSource as any,
symbol: s.symbol
}
},
create: {
symbol: s.symbol,
name: s.name,
currency: s.currency,
dataSource: s.dataSource as any,
assetClass: s.assetClass as any,
assetSubClass: s.assetSubClass as any
},
update: {}
});
}
// Create demo activities (BUY orders)
const activities = [
{
symbol: 'AAPL',
dataSource: 'YAHOO',
quantity: 15,
unitPrice: 150,
date: new Date('2024-01-15')
},
{
symbol: 'MSFT',
dataSource: 'YAHOO',
quantity: 10,
unitPrice: 380,
date: new Date('2024-02-01')
},
{
symbol: 'VTI',
dataSource: 'YAHOO',
quantity: 25,
unitPrice: 230,
date: new Date('2024-03-10')
},
{
symbol: 'GOOGL',
dataSource: 'YAHOO',
quantity: 8,
unitPrice: 140,
date: new Date('2024-04-05')
},
{
symbol: 'AMZN',
dataSource: 'YAHOO',
quantity: 12,
unitPrice: 178,
date: new Date('2024-05-20')
},
{
symbol: 'AAPL',
dataSource: 'YAHOO',
quantity: 0,
unitPrice: 0.82,
date: new Date('2024-08-15'),
type: 'DIVIDEND'
},
{
symbol: 'MSFT',
dataSource: 'YAHOO',
quantity: 0,
unitPrice: 0.75,
date: new Date('2024-09-12'),
type: 'DIVIDEND'
}
];
for (const a of activities) {
const profile = await prisma.symbolProfile.findFirst({
where: {
symbol: a.symbol,
dataSource: a.dataSource as any
}
});
if (!profile) continue;
await prisma.order.create({
data: {
accountId: DEMO_ACCOUNT_ID,
accountUserId: DEMO_USER_ID,
currency: 'USD',
date: a.date,
fee: 0,
quantity: a.type === 'DIVIDEND' ? 0 : a.quantity,
symbolProfileId: profile.id,
type: a.type === 'DIVIDEND' ? 'DIVIDEND' : 'BUY',
unitPrice: a.unitPrice,
userId: DEMO_USER_ID
}
});
}
// Set DEMO_USER_ID and DEMO_ACCOUNT_ID in Property table
await prisma.property.upsert({
where: { key: 'DEMO_USER_ID' },
create: { key: 'DEMO_USER_ID', value: `"${DEMO_USER_ID}"` },
update: { value: `"${DEMO_USER_ID}"` }
});
await prisma.property.upsert({
where: { key: 'DEMO_ACCOUNT_ID' },
create: { key: 'DEMO_ACCOUNT_ID', value: `"${DEMO_ACCOUNT_ID}"` },
update: { value: `"${DEMO_ACCOUNT_ID}"` }
});
console.log('Demo user created successfully!');
console.log(` User ID: ${DEMO_USER_ID}`);
console.log(` Account ID: ${DEMO_ACCOUNT_ID}`);
console.log(' Evaluators can visit /demo to auto-login, then /ai-chat to use AI.');
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});