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:
+15
-91
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user