Add self-hosted auth, admin API, and owner web dashboard

- Replace Clerk with self-hosted JWT auth (register/login/verify, bcrypt
  passwords, Gmail OTP with console fallback)
- Add lib/db.ts pg pool + transaction helpers; seed script migrates legacy
  Clerk-era schema (drop clerk_id, enforce UUID ids and unique email)
- Add owner-gated admin API: stats, users, drivers CRUD, rides
- Add dashboard/ Vite React owner dashboard (login, overview, users,
  fleet, rides) with dev-server proxy to avoid Expo CORS middleware
- Add scripts/set-owner.mjs for role management
This commit is contained in:
Krikorios
2026-08-23 16:38:41 +03:00
parent fbe92c9d16
commit a0b297285a
75 changed files with 5158 additions and 2837 deletions
+15 -91
View File
@@ -1,97 +1,21 @@
import type {
StartOAuthFlowParams,
StartOAuthFlowReturnType,
} from "@clerk/clerk-expo";
import * as Linking from "expo-linking";
import * as SecureStore from "expo-secure-store";
import { fetchAPI } from "./fetch";
import type { SessionUser } from "./session";
export interface TokenCache {
getToken: (key: string) => Promise<string | undefined | null>;
saveToken: (key: string, token: string) => Promise<void>;
clearToken?: (key: string) => void;
}
export const tokenCache = {
async getToken(key: string) {
try {
const item = await SecureStore.getItemAsync(key);
return item;
} catch (error) {
console.error("GET_TOKEN_CACHE: ", error);
await SecureStore.deleteItemAsync(key);
return null;
}
},
async saveToken(key: string, value: string) {
try {
return SecureStore.setItemAsync(key, value);
} catch (err) {
console.error("SAVE_TOKEN_CACHE: ", err);
return;
}
},
export type AuthResult = {
token: string;
user: SessionUser;
};
type StartOAuthFlowType = (
startOAuthFlowParams?: StartOAuthFlowParams,
) => Promise<StartOAuthFlowReturnType>;
// Exchanges a Google idToken (obtained via expo-auth-session on the client)
// for a Waseel session issued by our own server.
export const googleAuth = async (
idToken: string,
): Promise<AuthResult> => {
const response = await fetchAPI("/(api)/auth/google", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ idToken }),
});
export const googleOAuth = async (startOAuthFlow: StartOAuthFlowType) => {
try {
// No explicit scheme: in Expo Go this resolves to exp://... so the
// browser can redirect back into the app; in a standalone build it
// automatically uses the app.json scheme ("waseel").
const { createdSessionId, signUp, setActive } = await startOAuthFlow({
redirectUrl: Linking.createURL("/(root)/(tabs)/home"),
});
if (createdSessionId) {
if (setActive) {
await setActive!({ session: createdSessionId });
if (signUp && signUp.createdUserId) {
await fetchAPI("/(api)/user", {
method: "POST",
body: JSON.stringify({
name: `${signUp.firstName} ${signUp.lastName}`,
email: signUp.emailAddress,
clerkId: signUp.createdUserId,
}),
});
}
return {
success: true,
code: "success",
message: "You are logged in.",
};
}
return {
success: false,
code: "failed",
message: "An error occured.",
};
} else {
// Use signIn or signUp for next steps such as MFA
}
} catch (err) {
console.log("[OAUTH]: ", err);
return {
success: false,
code: (
err as {
code?: string;
}
)?.code,
message: "Internal Server Error.",
};
}
return response.data as AuthResult;
};