c20538ef9f
- New: src/lib/storage.ts — S3-compatible client, uploadFile/deleteFile/listFiles/publicUrl helpers, bucket constants - New: docker-compose adds minio + minio_init services - Replaced Supabase fetch PUTs in: - src/actions/brand-settings.ts (3 uploaders) - src/actions/products/upload-image.ts - src/actions/communications/import-contacts.ts - src/app/api/water-photo-upload/route.ts - Replaced hardcoded Supabase URLs in: - src/components/storefront/TuxedoVideoHero.tsx - src/components/time-tracking/TimeTrackingFieldClient.tsx - src/app/tuxedo/about/page.tsx - src/lib/email-service.ts (4 occurrences) - Added @aws-sdk/client-s3 + @aws-sdk/s3-request-presigner - .env.example adds MinIO + storage vars - Migration cleanup: deleted BUNDLE_018_042, 4 XXX drafts, 087/145/099-contact storage migrations - Migration patches: 006 STATIC→STABLE, 135 param reordering - Preflight: added pgcrypto extension, removed storage stub - Verified: MinIO upload/list/delete round-trip works against local instance
122 lines
4.9 KiB
TypeScript
122 lines
4.9 KiB
TypeScript
import { cookies, headers } from "next/headers";
|
|
import { auth } from "@/lib/auth";
|
|
import { Pool } from "pg";
|
|
|
|
const pool = new Pool({
|
|
connectionString: process.env.DATABASE_URL,
|
|
});
|
|
|
|
export type AdminUser = {
|
|
id: string;
|
|
user_id: string;
|
|
brand_id: string | null;
|
|
role: string;
|
|
active: boolean;
|
|
can_manage_products: boolean;
|
|
can_manage_stops: boolean;
|
|
can_manage_orders: boolean;
|
|
can_manage_pickup: boolean;
|
|
can_manage_messages: boolean;
|
|
can_manage_refunds: boolean;
|
|
can_manage_users: boolean;
|
|
can_manage_water_log: boolean;
|
|
can_manage_reports: boolean;
|
|
can_manage_settings: boolean;
|
|
must_change_password: boolean;
|
|
};
|
|
|
|
export async function getAdminUser(): Promise<AdminUser | null> {
|
|
const cookieStore = await cookies();
|
|
|
|
// ── Mock data mode for UI review ─────────────────────────────────
|
|
if (process.env.NEXT_PUBLIC_USE_MOCK_DATA === "true") {
|
|
return buildDevAdmin("platform_admin");
|
|
}
|
|
|
|
// ── Dev session bypass (enabled for testing on all envs) ──────────────
|
|
const dev = cookieStore.get("dev_session")?.value;
|
|
if (dev === "platform_admin" || dev === "brand_admin" || dev === "store_employee") {
|
|
return buildDevAdmin(dev);
|
|
}
|
|
|
|
// ── Better Auth session check ────────────────────────────────────
|
|
const hdrs = await headers();
|
|
const session = await auth.api.getSession({ headers: hdrs });
|
|
if (!session?.user) return null;
|
|
|
|
const uid = session.user.id;
|
|
if (!uid) return null;
|
|
|
|
// Lookup admin_users by user_id
|
|
let adminUsers: unknown[] = [];
|
|
try {
|
|
const res = await pool.query(
|
|
`SELECT * FROM admin_users WHERE user_id = $1 LIMIT 1`,
|
|
[uid]
|
|
);
|
|
adminUsers = res.rows;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
|
|
// First login — auto-create platform_admin
|
|
if (adminUsers.length === 0) {
|
|
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
if (!UUID_REGEX.test(uid)) return null;
|
|
|
|
try {
|
|
const res = await pool.query(
|
|
`INSERT INTO admin_users (user_id, role, active)
|
|
VALUES ($1, 'platform_admin', true)
|
|
ON CONFLICT (user_id) DO UPDATE SET active = EXCLUDED.active
|
|
RETURNING *`,
|
|
[uid]
|
|
);
|
|
if (res.rows.length > 0) {
|
|
return buildAdminUser(res.rows[0] as Record<string, unknown>);
|
|
}
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
const admin = adminUsers[0] as Record<string, unknown>;
|
|
if (!admin.active) return null;
|
|
|
|
return buildAdminUser(admin);
|
|
}
|
|
|
|
function buildDevAdmin(role: string): AdminUser {
|
|
const base = { id: "dev", user_id: "dev", brand_id: null, role, active: true, must_change_password: false };
|
|
if (role === "store_employee") {
|
|
return { ...base, can_manage_products: false, can_manage_stops: false, can_manage_orders: true,
|
|
can_manage_pickup: true, can_manage_messages: false, can_manage_refunds: false,
|
|
can_manage_users: false, can_manage_water_log: false, can_manage_reports: false, can_manage_settings: false };
|
|
}
|
|
return { ...base, can_manage_products: true, can_manage_stops: true, can_manage_orders: true,
|
|
can_manage_pickup: true, can_manage_messages: true, can_manage_refunds: true,
|
|
can_manage_users: true, can_manage_water_log: true, can_manage_reports: true, can_manage_settings: true };
|
|
}
|
|
|
|
function buildAdminUser(r: Record<string, unknown>): AdminUser {
|
|
const role = r.role as string;
|
|
const base = { id: r.id as string, user_id: r.user_id as string, brand_id: r.brand_id as string | null,
|
|
role, active: r.active as boolean, must_change_password: Boolean(r.must_change_password) };
|
|
if (role === "platform_admin") {
|
|
return { ...base, can_manage_products: true, can_manage_stops: true, can_manage_orders: true,
|
|
can_manage_pickup: true, can_manage_messages: true, can_manage_refunds: true,
|
|
can_manage_users: true, can_manage_water_log: true, can_manage_reports: true, can_manage_settings: true };
|
|
}
|
|
if (role === "store_employee") {
|
|
return { ...base, can_manage_products: false, can_manage_stops: false, can_manage_orders: true,
|
|
can_manage_pickup: true, can_manage_messages: false, can_manage_refunds: false,
|
|
can_manage_users: false, can_manage_water_log: false, can_manage_reports: false, can_manage_settings: false };
|
|
}
|
|
return { ...base, can_manage_products: Boolean(r.can_manage_products), can_manage_stops: Boolean(r.can_manage_stops),
|
|
can_manage_orders: Boolean(r.can_manage_orders), can_manage_pickup: Boolean(r.can_manage_pickup),
|
|
can_manage_messages: Boolean(r.can_manage_messages), can_manage_refunds: Boolean(r.can_manage_refunds),
|
|
can_manage_users: Boolean(r.can_manage_users), can_manage_water_log: Boolean(r.can_manage_water_log),
|
|
can_manage_reports: Boolean(r.can_manage_reports), can_manage_settings: Boolean(r.can_manage_settings) };
|
|
}
|