diff --git a/api/auth.ts b/api/auth.ts
index dba9aff..864ae59 100644
--- a/api/auth.ts
+++ b/api/auth.ts
@@ -113,30 +113,61 @@ export async function seedLocalUserIfMissing() {
// Check existence without selecting `password_hash` to avoid failures on older schemas
try {
const [rows] = await db.execute(sql`SELECT id FROM users WHERE unionId = ${LocalAuth.defaultAdminEmail} LIMIT 1`);
- if (Array.isArray(rows) && rows.length > 0) return;
} catch (err) {
console.warn('[seed] existence check failed, will attempt to insert anyway', err);
}
- // Try to insert using the typed Drizzle insert (preferred). If that fails because the
- // `password_hash` column is missing, fall back to a raw INSERT that omits the column.
+ // 1. Ensure admin account
try {
- await db.insert(users).values({
- unionId: LocalAuth.defaultAdminEmail,
- name: "Admin Lokal",
- email: LocalAuth.defaultAdminEmail,
- passwordHash: hashPassword(LocalAuth.defaultAdminPassword),
- role: "admin",
- lastSignInAt: new Date(),
- });
+ const [existingAdmin] = await db.execute(sql`SELECT id FROM users WHERE unionId = ${LocalAuth.defaultAdminEmail} LIMIT 1`);
+ if (!existingAdmin || (Array.isArray(existingAdmin) && existingAdmin.length === 0)) {
+ await db.insert(users).values({
+ unionId: LocalAuth.defaultAdminEmail,
+ name: "Admin Lokal",
+ email: LocalAuth.defaultAdminEmail,
+ passwordHash: hashPassword(LocalAuth.defaultAdminPassword),
+ role: "admin",
+ lastSignInAt: new Date(),
+ });
+ console.log(`[seed] Created admin account: ${LocalAuth.defaultAdminEmail}`);
+ }
} catch (err) {
- console.warn('[seed] typed insert failed, attempting fallback insert without password_hash', err);
+ console.warn('[seed] Admin seeding failed, attempting fallback', err);
try {
- await db.execute(sql`INSERT INTO users (unionId, name, email, role, lastSignInAt) VALUES (
+ await db.execute(sql`INSERT IGNORE INTO users (unionId, name, email, role, lastSignInAt) VALUES (
${LocalAuth.defaultAdminEmail}, ${'Admin Lokal'}, ${LocalAuth.defaultAdminEmail}, ${'admin'}, ${new Date()}
)`);
} catch (err2) {
- console.error('[seed] fallback insert also failed', err2);
+ console.error('[seed] Admin fallback failed', err2);
+ }
+ }
+
+ // 2. Seed default roles for testing
+ const defaultAccounts = [
+ { email: "officer@example.com", name: "Petugas Lapangan", role: "officer" as const },
+ { email: "manager@example.com", name: "Pengelola Wilayah", role: "manager" as const },
+ ];
+
+ for (const acc of defaultAccounts) {
+ try {
+ const res = await db.execute(sql`SELECT id FROM users WHERE unionId = ${acc.email} LIMIT 1`);
+ const [existing] = res as any;
+
+ if (existing && (!Array.isArray(existing) || existing.length > 0)) {
+ continue;
+ }
+
+ await db.insert(users).values({
+ unionId: acc.email,
+ name: acc.name,
+ email: acc.email,
+ passwordHash: hashPassword("password123"),
+ role: acc.role,
+ lastSignInAt: new Date(),
+ });
+ console.log(`[seed] Created ${acc.role} account: ${acc.email}`);
+ } catch (err) {
+ console.warn(`[seed] Failed to create ${acc.role} (${acc.email})`, err);
}
}
}
\ No newline at end of file
diff --git a/scratch/check_users.ts b/scratch/check_users.ts
new file mode 100644
index 0000000..c7b2ccf
--- /dev/null
+++ b/scratch/check_users.ts
@@ -0,0 +1,16 @@
+import { sql } from "drizzle-orm";
+import { getDb } from "../api/queries/connection";
+import { users } from "../db/schema";
+
+async function check() {
+ const db = getDb();
+ const res = await db.execute(sql`SELECT id FROM users WHERE unionId = 'officer@example.com' LIMIT 1`);
+ console.log("Raw query result for officer:", res);
+
+ const allUsers = await db.select().from(users);
+ console.log("Current Users in DB:");
+ console.table(allUsers.map(u => ({ id: u.id, email: u.email, role: u.role })));
+ process.exit(0);
+}
+
+check().catch(console.error);
diff --git a/scratch/force_seed.ts b/scratch/force_seed.ts
new file mode 100644
index 0000000..f608604
--- /dev/null
+++ b/scratch/force_seed.ts
@@ -0,0 +1,10 @@
+import { seedLocalUserIfMissing } from "../api/auth";
+
+async function forceSeed() {
+ console.log("Starting force seed...");
+ await seedLocalUserIfMissing();
+ console.log("Force seed completed.");
+ process.exit(0);
+}
+
+forceSeed().catch(console.error);
diff --git a/src/App.tsx b/src/App.tsx
index d58724f..528a0ca 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -11,7 +11,13 @@ import VerificationPage from "./pages/VerificationPage";
import UsersPage from "./pages/UsersPage";
import SettingsPage from "./pages/SettingsPage";
-function ProtectedRoute({ children, adminOnly }: { children: React.ReactNode; adminOnly?: boolean }) {
+function ProtectedRoute({
+ children,
+ allowedRoles,
+}: {
+ children: React.ReactNode;
+ allowedRoles?: string[];
+}) {
const { user, isLoading } = useAuth();
if (isLoading) {
@@ -26,7 +32,7 @@ function ProtectedRoute({ children, adminOnly }: { children: React.ReactNode; ad
return Kelola data rumah ibadah dan cakupan wilayahnyaRumah Ibadah