This commit is contained in:
2026-05-28 20:22:30 -04:00
parent d48bbc0fc3
commit 30b2d03c68
17 changed files with 174 additions and 290 deletions

View File

@@ -155,7 +155,9 @@ export const reactionTypeSchema = z.enum([
"moneyEye",
"sick",
"upsideDown",
"worried"
"worried",
"upVote",
"downVote"
]);
/**

View File

@@ -1,10 +1,10 @@
import type { H3Event } from "vinxi/http";
import { getCookie, setCookie } from "vinxi/http";
import { getCookie, setCookie, getHeader } from "vinxi/http";
import { OAuth2Client } from "google-auth-library";
import type { Row } from "@libsql/client/web";
import { SignJWT, jwtVerify } from "jose";
import { env } from "~/env/server";
import { ConnectionFactory } from "./database";
import { ConnectionFactory } from "./db-connections";
import { AUTH_CONFIG, expiryToSeconds, getAccessTokenExpiry } from "~/config";
export const authCookieName = "auth_token";
@@ -30,7 +30,7 @@ function getAuthCookieOptions(rememberMe: boolean) {
}
function getAuthHeaderToken(event: H3Event): string | null {
const requestHeader = event.request?.headers?.get?.("authorization") || null;
const requestHeader = getHeader(event, "authorization") || null;
const eventHeader = event.headers
? typeof (event.headers as any).get === "function"
? (event.headers as any).get("authorization")
@@ -199,6 +199,7 @@ export async function validateLineageRequest({
return false;
}
} catch (err) {
console.error("Failed to verify email auth token:", err);
return false;
}
} else if (provider == "apple") {

View File

@@ -11,43 +11,13 @@ import {
TimeoutError,
APIError
} from "~/server/fetch-utils";
let mainDBConnection: ReturnType<typeof createClient> | null = null;
let lineageDBConnection: ReturnType<typeof createClient> | null = null;
let nessaDBConnection: ReturnType<typeof createClient> | null = null;
export function ConnectionFactory() {
if (!mainDBConnection) {
const config = {
url: env.TURSO_DB_URL,
authToken: env.TURSO_DB_TOKEN
};
mainDBConnection = createClient(config);
}
return mainDBConnection;
}
export function LineageConnectionFactory() {
if (!lineageDBConnection) {
const config = {
url: env.TURSO_LINEAGE_URL,
authToken: env.TURSO_LINEAGE_TOKEN
};
lineageDBConnection = createClient(config);
}
return lineageDBConnection;
}
export function NessaConnectionFactory() {
if (!nessaDBConnection) {
const config = {
url: env.NESSA_DB_URL,
authToken: env.NESSA_DB_TOKEN
};
nessaDBConnection = createClient(config);
}
return nessaDBConnection;
}
import {
ConnectionFactory,
LineageConnectionFactory,
NessaConnectionFactory
} from "~/server/db-connections";
// Re-export connection factories to avoid circular import with auth.ts
export { ConnectionFactory, LineageConnectionFactory, NessaConnectionFactory };
export async function LineageDBInit() {
const turso = createAPIClient({
@@ -209,7 +179,7 @@ export async function getUserBasicInfo(event: H3Event): Promise<{
return { email: null, isAuthenticated: false };
}
const user = res.rows[0] as { email: string | null };
const user = res.rows[0] as unknown as { email: string | null };
return {
email: user.email,
isAuthenticated: true

View File

@@ -1,19 +1,15 @@
import { defineMiddleware } from "vinxi/http";
import { defineMiddleware, setHeaders } from "vinxi/http";
// Security headers middleware — sets CSP and hardening headers on all responses
export default defineMiddleware((_event, next) => {
return next().then((response) => {
response.headers.set(
"Content-Security-Policy",
"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; connect-src 'self' https:; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"
);
response.headers.set("X-Content-Type-Options", "nosniff");
response.headers.set("X-Frame-Options", "DENY");
response.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
response.headers.set(
"Permissions-Policy",
"camera=(), microphone=(), geolocation=()"
);
return response;
});
export default defineMiddleware({
onRequest: (event) => {
setHeaders(event, {
"Content-Security-Policy":
"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; connect-src 'self' https:; frame-ancestors 'none'; base-uri 'self'; form-action 'self'",
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"Referrer-Policy": "strict-origin-when-cross-origin",
"Permissions-Policy": "camera=(), microphone=(), geolocation=()"
});
}
});

View File

@@ -51,7 +51,12 @@ function getCookieValue(event: H3Event, name: string): string | undefined {
try {
const value = getCookie(event, name);
if (value) return value;
} catch (e) {}
} catch (e) {
console.warn(
"[security] getCookie failed, falling back to header parse:",
e
);
}
try {
const cookieHeader =