feat : add profile page

This commit is contained in:
2026-08-06 22:46:47 +07:00 Unverified
parent e5a6ad3e04
commit c1c015ef78
26 changed files with 1169 additions and 310 deletions
-65
View File
@@ -1,65 +0,0 @@
import { readFile } from "fs/promises";
import postgres from "postgres";
const migrationTag = "0011_unique_submission_user";
const migrationPath = "drizzle/0011_unique_submission_user.sql";
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error("DATABASE_URL is not set");
}
const sql = postgres(databaseUrl, { max: 1 });
try {
const duplicates = await sql<{ form_id: string; user_discord_id: string; count: string }[]>`
select form_id, user_discord_id, count(*)::text as count
from form.submission
where user_discord_id is not null
group by form_id, user_discord_id
having count(*) > 1
limit 20
`;
if (duplicates.length > 0) {
console.error("Duplicate submissions would violate the unique index:");
for (const duplicate of duplicates) {
console.error(
`form_id=${duplicate.form_id} user_discord_id=${duplicate.user_discord_id} count=${duplicate.count}`
);
}
process.exit(1);
}
await sql`
create unique index if not exists "submission_form_user_discord_id_unique"
on "form"."submission" using btree ("form_id", "user_discord_id")
`;
const migrationTable = await sql<{ exists: boolean }[]>`
select to_regclass('drizzle.__drizzle_migrations') is not null as exists
`;
if (migrationTable[0]?.exists) {
const existing = await sql<{ count: string }[]>`
select count(*)::text as count
from drizzle.__drizzle_migrations
where hash = ${migrationTag}
`;
if (existing[0]?.count === "0") {
const migrationSql = await readFile(migrationPath, "utf8");
await sql`
insert into drizzle.__drizzle_migrations (hash, created_at)
values (${migrationTag}, ${Date.now()})
`;
console.log(`Applied ${migrationTag}: ${migrationSql.trim()}`);
} else {
console.log(`${migrationTag} was already recorded.`);
}
} else {
console.log("Applied index. Drizzle migration table was not found, so no migration row was recorded.");
}
} finally {
await sql.end();
}
-155
View File
@@ -1,155 +0,0 @@
import postgres from "postgres";
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error("DATABASE_URL is not set");
}
const expectedMigrations = [
{
hash: "418d5afb86b4866e697a0abfc992b913da1533e8df41cc0b618a9e23cbb70ba5",
createdAt: 1782731728406,
},
{
hash: "5e060f0fdac0296f4953f707182eb1c872f69d44e9ef89ba75c69fbc9c895851",
createdAt: 1782731936083,
},
{
hash: "b3d35909be61ab7fbffc6c36916d6444833385db517a302abe28cfea2b0ef544",
createdAt: 1782731954122,
},
{
hash: "1ad2b86e381f0d8ef17839a952c30f0ea6f0452367d1dcd19aa11a65d8eeecbd",
createdAt: 1783089081925,
},
{
hash: "f2c279f21375de159981c439e622e4ddcd8415384f7ca7287c3f1598bcb42fc4",
createdAt: 1783613113217,
},
{
hash: "38e26a4313f207b04bb1646f6a303c0f216ad687062d16100e4ca01db5fa74c2",
createdAt: 1785152117830,
},
{
hash: "8a65ae9a4fe4c7933d0d40182dab6da9654c8cb0f1275a8ebb43e1e0f8719256",
createdAt: 1785169071234,
},
] as const;
const sql = postgres(databaseUrl, { max: 1 });
try {
const [schemaState] = await sql<{
form_tables: number;
allowed_roles: boolean;
denied_roles: boolean;
cdn_table: boolean;
image_cdn_id: boolean;
allow_multiple_submissions: boolean;
image_answer_mode: boolean;
submitted_at_index: boolean;
answer_image_fk: boolean;
unique_submission_index: boolean;
legacy_public_tables: boolean;
}[]>`
select
(
select count(*)::integer
from information_schema.tables
where table_schema = 'form'
and table_name in ('answer', 'form', 'question', 'submission')
) as form_tables,
exists (
select 1 from information_schema.columns
where table_schema = 'form'
and table_name = 'form'
and column_name = 'allowed_roles'
) as allowed_roles,
exists (
select 1 from information_schema.columns
where table_schema = 'form'
and table_name = 'form'
and column_name = 'denied_roles'
) as denied_roles,
to_regclass('form.cdn') is not null as cdn_table,
exists (
select 1 from information_schema.columns
where table_schema = 'form'
and table_name = 'answer'
and column_name = 'image_cdn_id'
) as image_cdn_id,
exists (
select 1 from information_schema.columns
where table_schema = 'form'
and table_name = 'form'
and column_name = 'allow_multiple_submissions'
) as allow_multiple_submissions,
exists (
select 1 from information_schema.columns
where table_schema = 'form'
and table_name = 'question'
and column_name = 'image_answer_mode'
) as image_answer_mode,
to_regclass('form.submission_form_submitted_at_id_idx') is not null
as submitted_at_index,
exists (
select 1
from information_schema.table_constraints
where constraint_schema = 'form'
and table_name = 'answer'
and constraint_name = 'answer_image_cdn_id_cdn_id_fk'
) as answer_image_fk,
to_regclass('form.submission_form_user_discord_id_unique') is not null
as unique_submission_index,
exists (
select 1
from information_schema.tables
where table_schema = 'public'
and table_name in ('answer', 'form', 'question', 'submission')
) as legacy_public_tables
`;
const matchesExpectedSchema =
schemaState?.form_tables === 4 &&
schemaState.allowed_roles &&
schemaState.denied_roles &&
schemaState.cdn_table &&
schemaState.image_cdn_id &&
schemaState.allow_multiple_submissions &&
schemaState.image_answer_mode &&
schemaState.submitted_at_index &&
schemaState.answer_image_fk &&
!schemaState.unique_submission_index &&
!schemaState.legacy_public_tables;
if (!matchesExpectedSchema) {
throw new Error(
"The database does not match the expected schema through migration 0013. Refusing to change the migration ledger."
);
}
await sql.begin(async (transaction) => {
await transaction`lock table drizzle.__drizzle_migrations in exclusive mode`;
for (const migration of expectedMigrations) {
await transaction`
insert into drizzle.__drizzle_migrations (hash, created_at)
select ${migration.hash}, ${migration.createdAt}
where not exists (
select 1
from drizzle.__drizzle_migrations
where hash = ${migration.hash}
)
`;
await transaction`
update drizzle.__drizzle_migrations
set created_at = ${migration.createdAt}
where hash = ${migration.hash}
`;
}
});
console.log("Reconciled the Drizzle migration ledger through 0013.");
} finally {
await sql.end();
}