Added demo access

This commit is contained in:
blaisadmin
2026-03-26 09:59:40 -04:00
parent 45c6d03f8d
commit 8b7763cf13
5 changed files with 116 additions and 1 deletions
+65 -1
View File
@@ -100,6 +100,9 @@ const databaseUrl =
const frontendUrl = process.env.FRONTEND_URL || 'http://localhost:3000';
const apiBaseUrl = process.env.API_BASE_URL || 'http://localhost:5000/api';
const allowRegistration = (process.env.ALLOW_REGISTRATION ?? 'true').toLowerCase() !== 'false';
const allowDemoAccount = (process.env.ALLOW_DEMO_ACCOUNT ?? 'false').toLowerCase() === 'true';
const demoAccountPassword = process.env.DEMO_ACCOUNT_PASSWORD ?? 'demo1234';
const demoAccountName = process.env.DEMO_ACCOUNT_NAME ?? 'Demo User';
const { Pool } = pg;
const pool = new Pool({ connectionString: databaseUrl });
@@ -152,6 +155,7 @@ const getNumber = (value: unknown, fieldName: string): number => {
};
const normalizeEmail = (email: string) => email.trim().toLowerCase();
const demoAccountEmail = normalizeEmail(process.env.DEMO_ACCOUNT_EMAIL ?? 'demo@arsenaliq.local');
const hashToken = (token: string) => crypto.createHash('sha256').update(token).digest('hex');
const createSessionToken = () => crypto.randomBytes(32).toString('hex');
const isUuid = (value: string) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
@@ -414,6 +418,35 @@ const ensureDefaultProfile = async (userId: string, userName: string) => {
return created.rows[0];
};
const ensureDemoAccount = async () => {
if (!allowDemoAccount) {
return null;
}
const passwordHash = await bcrypt.hash(demoAccountPassword, 10);
const existing = await pool.query<UserRow>('SELECT id, email, name FROM users WHERE email = $1', [demoAccountEmail]);
let user: UserRow;
if ((existing.rowCount ?? 0) > 0) {
user = existing.rows[0];
await pool.query('UPDATE users SET name = $2, password_hash = $3 WHERE id = $1', [
user.id,
demoAccountName,
passwordHash,
]);
} else {
const created = await pool.query<UserRow>(
'INSERT INTO users (email, password_hash, name) VALUES ($1, $2, $3) RETURNING id, email, name',
[demoAccountEmail, passwordHash, demoAccountName],
);
user = created.rows[0];
}
const profile = await ensureDefaultProfile(user.id, demoAccountName);
return { user, profile };
};
const createSession = async (userId: string, activeProfileId: string) => {
const token = createSessionToken();
const tokenHash = hashToken(token);
@@ -628,6 +661,7 @@ app.get('/api', (_req, res) => {
name: 'Arsenal IQ API',
version: '3.0.0',
allowRegistration,
allowDemoAccount,
resources: [
'/api/auth/login',
'/api/auth/register',
@@ -639,6 +673,35 @@ app.get('/api', (_req, res) => {
});
});
app.post('/api/auth/demo', async (_req, res, next) => {
try {
if (!allowDemoAccount) {
res.status(403).json({ error: 'Demo account is disabled' });
return;
}
const demoAccount = await ensureDemoAccount();
if (!demoAccount) {
res.status(500).json({ error: 'Demo account is unavailable' });
return;
}
const { user, profile } = demoAccount;
const { token, session } = await createSession(user.id, profile.id);
const profiles = await getUserProfiles(user.id);
res.json({
token,
user,
profiles,
activeProfileId: session.active_profile_id,
});
} catch (error) {
next(error);
}
});
app.get('/api/auth/providers', async (_req, res, next) => {
try {
const result = await pool.query<ProviderConfigRow>(
@@ -1268,7 +1331,8 @@ app.use((error: Error, _req: express.Request, res: express.Response, _next: expr
});
void ensureSchema()
.then(() => {
.then(async () => {
await ensureDemoAccount();
app.listen(port, () => {
console.log(`Arsenal IQ API listening on http://localhost:${port}`);
});