mirror of
https://github.com/FranP-code/Reflecto.git
synced 2025-10-13 00:43:31 +00:00
feat: migrate authentication to Appwrite and remove Better-Auth references
This commit is contained in:
@@ -5,7 +5,6 @@ import { trpcServer } from "@hono/trpc-server";
|
||||
import { Hono } from "hono";
|
||||
import { cors } from "hono/cors";
|
||||
import { logger as honoLogger } from "hono/logger";
|
||||
import { auth } from "./lib/auth";
|
||||
import { createContext } from "./lib/context";
|
||||
import { logger } from "./lib/logger";
|
||||
import { appRouter } from "./routers/index";
|
||||
@@ -35,8 +34,6 @@ app.use(
|
||||
})
|
||||
);
|
||||
|
||||
app.on(["POST", "GET"], "/api/auth/**", (c) => auth.handler(c.req.raw));
|
||||
|
||||
app.use(
|
||||
"/trpc/*",
|
||||
trpcServer({
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { betterAuth } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import { db } from "../db";
|
||||
import * as schema from "../db/schema/auth";
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: drizzleAdapter(db, {
|
||||
provider: "pg",
|
||||
|
||||
schema,
|
||||
}),
|
||||
trustedOrigins: [process.env.CORS_ORIGIN || ""],
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
},
|
||||
advanced: {
|
||||
defaultCookieAttributes: {
|
||||
sameSite: "none",
|
||||
secure: true,
|
||||
httpOnly: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,17 +1,77 @@
|
||||
import type { Context as HonoContext } from "hono";
|
||||
import { auth } from "./auth";
|
||||
import { Account, Client } from "node-appwrite";
|
||||
|
||||
// Hoisted regex for performance and linting
|
||||
const BEARER_REGEX = /^Bearer\s+(.+)$/i;
|
||||
|
||||
// Minimal user shape to avoid leaking node-appwrite model types
|
||||
export type AuthUser = {
|
||||
$id: string;
|
||||
name?: string | null;
|
||||
email?: string | null;
|
||||
} | null;
|
||||
|
||||
export type CreateContextOptions = {
|
||||
context: HonoContext;
|
||||
};
|
||||
|
||||
export async function createContext({ context }: CreateContextOptions) {
|
||||
const session = await auth.api.getSession({
|
||||
headers: context.req.raw.headers,
|
||||
});
|
||||
return {
|
||||
session,
|
||||
};
|
||||
const endpoint = process.env.APPWRITE_ENDPOINT;
|
||||
const projectId = process.env.APPWRITE_PROJECT_ID;
|
||||
|
||||
if (!(endpoint && projectId)) {
|
||||
// Appwrite not configured; treat as unauthenticated
|
||||
return { user: null as AuthUser };
|
||||
}
|
||||
|
||||
// Initialize client per request
|
||||
const client = new Client().setEndpoint(endpoint).setProject(projectId);
|
||||
|
||||
// Prefer JWT from Authorization header if provided
|
||||
const authHeader = context.req.header("Authorization");
|
||||
const bearer = authHeader?.match(BEARER_REGEX)?.[1];
|
||||
|
||||
if (bearer) {
|
||||
client.setJWT(bearer);
|
||||
} else {
|
||||
// Fallback: Appwrite session cookie from browser
|
||||
// Cookie name format: a_session_<PROJECT_ID>
|
||||
const cookieHeader =
|
||||
context.req.header("Cookie") || context.req.header("cookie");
|
||||
if (cookieHeader) {
|
||||
const cookieName = `a_session_${projectId}`;
|
||||
const cookies = Object.fromEntries(
|
||||
cookieHeader.split("; ").map((c) => {
|
||||
const idx = c.indexOf("=");
|
||||
return idx === -1
|
||||
? [c, ""]
|
||||
: [c.substring(0, idx), decodeURIComponent(c.substring(idx + 1))];
|
||||
})
|
||||
);
|
||||
const session = cookies[cookieName];
|
||||
if (session) {
|
||||
client.setSession(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const account = new Account(client);
|
||||
|
||||
try {
|
||||
const user = (await account.get()) as unknown as {
|
||||
$id: string;
|
||||
name?: string | null;
|
||||
email?: string | null;
|
||||
};
|
||||
const minimal: AuthUser = {
|
||||
$id: user.$id,
|
||||
name: user.name ?? null,
|
||||
email: user.email ?? null,
|
||||
};
|
||||
return { user: minimal };
|
||||
} catch {
|
||||
return { user: null as AuthUser };
|
||||
}
|
||||
}
|
||||
|
||||
export type Context = Awaited<ReturnType<typeof createContext>>;
|
||||
|
||||
@@ -8,17 +8,17 @@ export const router = t.router;
|
||||
export const publicProcedure = t.procedure;
|
||||
|
||||
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
|
||||
if (!ctx.session) {
|
||||
if (!ctx.user) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "Authentication required",
|
||||
cause: "No session",
|
||||
cause: "No user",
|
||||
});
|
||||
}
|
||||
return next({
|
||||
ctx: {
|
||||
...ctx,
|
||||
session: ctx.session,
|
||||
user: ctx.user,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@ export const appRouter = router({
|
||||
privateData: protectedProcedure.query(({ ctx }) => {
|
||||
return {
|
||||
message: "This is private",
|
||||
user: ctx.session.user,
|
||||
user: ctx.user,
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user