dorfteich/apps/api/prisma/seed.ts
Claude Fable 5 bef6d8e4dc
All checks were successful
CD / Build and push images (push) Successful in 1m49s
CI / Lint, typecheck, test (push) Successful in 1m15s
CI / Auth e2e pack (push) Successful in 1m41s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 7s
CD / Smoke tests against Test (push) Successful in 1m5s
CD / Promote to Int (push) Successful in 9s
Make stage mail + base URL configurable; seed password overrides
- compose: pass APP_BASE_URL and SMTP_* through to the api container so
  stages can use a real relay (defaults still match the dev Mailpit
  overlay); document the new keys in .env.example and stages.md
- seed: FIXTURE_ADMIN_PASSWORD / FIXTURE_USER_PASSWORD env overrides so
  shared stages get non-public fixture passwords; credential is re-hashed
  on every run so re-seeding applies a changed password

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UpQz6ypHJsLfMf4S6fyQEB
2026-07-05 10:42:32 +02:00

99 lines
3.1 KiB
TypeScript

/**
* Development/Test fixture seeding. Idempotent: running it twice must not
* duplicate anything. Never run against production data.
*
* Fixture matrix (documented in apps/web/e2e/README.md):
* fixture-admin active, Site Admin
* fixture-user active, regular account
* fixture-pending registered but e-mail not verified
*
* All fixture accounts share the password below — they exist only on
* dev machines and disposable CI/Test databases. On shared stages
* (test/int), set FIXTURE_ADMIN_PASSWORD / FIXTURE_USER_PASSWORD to give
* those two accounts non-public passwords.
*/
import { PrismaClient, UserStatus } from '@prisma/client';
import { hashPassword } from '../src/users/password';
export const FIXTURE_PASSWORD = 'fixture passwort 123';
const PASSWORD_OVERRIDES: Record<string, string | undefined> = {
'fixture-admin': process.env.FIXTURE_ADMIN_PASSWORD,
'fixture-user': process.env.FIXTURE_USER_PASSWORD,
};
const prisma = new PrismaClient();
interface FixtureUser {
username: string;
displayName: string;
status: UserStatus;
isSiteAdmin: boolean;
}
const FIXTURES: FixtureUser[] = [
{ username: 'fixture-admin', displayName: 'Fixture Admin', status: 'ACTIVE', isSiteAdmin: true },
{ username: 'fixture-user', displayName: 'Fixture User', status: 'ACTIVE', isSiteAdmin: false },
{
username: 'fixture-pending',
displayName: 'Fixture Pending',
status: 'PENDING_VERIFICATION',
isSiteAdmin: false,
},
];
async function upsertFixtureUser(fixture: FixtureUser): Promise<void> {
const email = `${fixture.username}@dorfteich.test`;
const user = await prisma.user.upsert({
where: { username: fixture.username },
create: {
username: fixture.username,
email,
displayName: fixture.displayName,
locale: 'de',
status: fixture.status,
isSiteAdmin: fixture.isSiteAdmin,
emailVerifiedAt: fixture.status === 'ACTIVE' ? new Date() : null,
},
update: {
status: fixture.status,
isSiteAdmin: fixture.isSiteAdmin,
},
});
// Re-hash on every run so a changed override takes effect on re-seed.
const credential = await hashPassword(PASSWORD_OVERRIDES[fixture.username] ?? FIXTURE_PASSWORD);
await prisma.userIdentity.upsert({
where: { provider_subject: { provider: 'password', subject: user.id } },
create: {
userId: user.id,
provider: 'password',
subject: user.id,
credential,
},
update: { credential },
});
}
async function main(): Promise<void> {
// Fresh rate-limit budget for e2e runs — seed targets are always
// disposable dev/CI databases, never production.
await prisma.rateLimit.deleteMany({});
for (const fixture of FIXTURES) {
await upsertFixtureUser(fixture);
}
await prisma.instanceSetting.upsert({
where: { key: 'seed.marker' },
create: { key: 'seed.marker', value: { seededAt: new Date().toISOString() } },
update: { value: { seededAt: new Date().toISOString() } },
});
console.log(`seed: done (${FIXTURES.length} fixture users)`);
}
main()
.catch((error) => {
console.error(error);
process.exitCode = 1;
})
.finally(() => prisma.$disconnect());