migrate: replace Supabase REST with Drizzle/pg in water-log/time-tracking/reports/etc (wave 4)

This commit is contained in:
2026-06-07 03:05:00 +00:00
parent 01198111ea
commit b8317a200e
16 changed files with 346 additions and 545 deletions
+11 -22
View File
@@ -1,15 +1,6 @@
"use server";
import { createClient as createServiceClient } from "@supabase/supabase-js";
function getServiceClient() {
const roleKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
if (!roleKey) throw new Error("SUPABASE_SERVICE_ROLE_KEY is not set");
return createServiceClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
roleKey,
);
}
import { pool } from "@/lib/db";
type AdminActionPayload = {
action_type: "create" | "update" | "delete";
@@ -30,35 +21,33 @@ type UserActivityPayload = {
export async function logAdminAction(payload: AdminActionPayload): Promise<void> {
try {
const service = getServiceClient();
await service.rpc("log_admin_action", {
p_payload: {
await pool.query("SELECT log_admin_action($1::jsonb)", [
JSON.stringify({
action_type: payload.action_type,
admin_id: payload.admin_id ?? null,
admin_email: payload.admin_email ?? null,
affected_user_id: payload.affected_user_id ?? null,
brand_id: payload.brand_id ?? null,
details: payload.details ?? {},
},
});
} catch (e) {
}),
]);
} catch {
// logging failed silently
}
}
export async function logUserActivity(payload: UserActivityPayload): Promise<void> {
try {
const service = getServiceClient();
await service.rpc("log_user_activity", {
p_payload: {
await pool.query("SELECT log_user_activity($1::jsonb)", [
JSON.stringify({
user_id: payload.user_id,
activity_type: payload.activity_type,
details: payload.details ?? {},
ip_address: payload.ip_address ?? null,
user_agent: payload.user_agent ?? null,
},
});
} catch (e) {
}),
]);
} catch {
// logging failed silently
}
}
+23 -32
View File
@@ -1,15 +1,6 @@
"use server";
import { createClient as createServiceClient } from "@supabase/supabase-js";
function getServiceClient() {
const roleKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
if (!roleKey) throw new Error("SUPABASE_SERVICE_ROLE_KEY not set");
return createServiceClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
roleKey,
);
}
import { query } from "@/lib/db";
export type ResetAdminPasswordResult =
| { success: true; tempPassword: string }
@@ -17,35 +8,35 @@ export type ResetAdminPasswordResult =
/**
* Emergency recovery action — only usable in development or when the caller
* already has service role access. Resets the password for the specified email
* already has direct DB access. Resets the password for the specified email
* and returns the temp password so it can be displayed to the user.
*
* Now that Supabase auth is gone, this hits the `users` table directly and
* calls the same `update_user_password` SECURITY DEFINER RPC used by the
* self-service password change action.
*/
export async function resetAdminPassword(
email: string,
newPassword: string
): Promise<ResetAdminPasswordResult> {
const service = getServiceClient();
// Look up auth user by email
const { data: authUsers, error: listError } = await service.auth.admin.listUsers();
if (listError || !authUsers?.users) {
return { success: false, error: "Could not list users: " + listError?.message };
}
const authUser = authUsers.users.find((u) => u.email?.toLowerCase() === email.toLowerCase());
if (!authUser) {
// Look up the user by email
const { rows } = await query<{ id: string }>(
"SELECT id FROM users WHERE email = $1 LIMIT 1",
[email.toLowerCase()],
);
const user = rows[0];
if (!user) {
return { success: false, error: "No auth user found for that email address." };
}
// Update password via service role
const { error: updateError } = await service.auth.admin.updateUserById(
authUser.id,
{ password: newPassword, email_confirm: true }
);
if (updateError) {
return { success: false, error: updateError.message };
// Update password via SECURITY DEFINER RPC
try {
await query("SELECT update_user_password($1, $2)", [user.id, newPassword]);
return { success: true, tempPassword: newPassword };
} catch (err) {
return {
success: false,
error: err instanceof Error ? err.message : "Failed to update password",
};
}
return { success: true, tempPassword: newPassword };
}
}