Sposta l'autenticazione da Supabase a PocketBase
Login Google via PocketBase invece che Supabase Auth: nuovi client in src/integrations/pocketbase/ (browser, server con superuser, attacher del bearer token per le server function). Il ruolo admin/user vive ora come campo diretto sul record utente PocketBase invece che nella tabella separata user_roles, quindi ruoli.ts e la verifica in auth-route.server.ts non hanno più bisogno di una query aggiuntiva. requireSupabaseAuth/auth-middleware.ts non viene riportato: non era importato da nessun file, era codice morto. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -55,6 +55,15 @@ Il progetto richiede le seguenti variabili:
|
|||||||
- `VITE_SUPABASE_URL`
|
- `VITE_SUPABASE_URL`
|
||||||
- `VITE_SUPABASE_PUBLISHABLE_KEY`
|
- `VITE_SUPABASE_PUBLISHABLE_KEY`
|
||||||
|
|
||||||
|
Solo per lo sviluppo locale con PocketBase al posto di Supabase (vedi
|
||||||
|
[docs/PORTABILITA.md](docs/PORTABILITA.md)):
|
||||||
|
|
||||||
|
- `VITE_POCKETBASE_URL` / `POCKETBASE_URL` — `http://127.0.0.1:8090` con
|
||||||
|
`docker-compose.pocketbase.yml`
|
||||||
|
- `POCKETBASE_SUPERUSER_EMAIL` / `POCKETBASE_SUPERUSER_PASSWORD` — credenziali del
|
||||||
|
superuser creato al primo avvio, usate solo dalle route server che oggi usano
|
||||||
|
`supabaseAdmin`
|
||||||
|
|
||||||
## Documentazione
|
## Documentazione
|
||||||
|
|
||||||
Indice in [docs/README.md](docs/README.md). Le regole per gli assistenti AI stanno in
|
Indice in [docs/README.md](docs/README.md). Le regole per gli assistenti AI stanno in
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.575.0",
|
"lucide-react": "^0.575.0",
|
||||||
"motion": "^13.2.0",
|
"motion": "^13.2.0",
|
||||||
|
"pocketbase": "^0.28.1",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
@@ -651,6 +652,8 @@
|
|||||||
|
|
||||||
"picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
|
"picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
|
||||||
|
|
||||||
|
"pocketbase": ["pocketbase@0.28.1", "", {}, "sha512-/3ihkq+rvfcs0MQgrK4sEElOg6gfenHW9S/O+3BSO+V1yT+4B10+9aT22uTQUPqze9ItwNtwWyY5ZmdgVCTdVA=="],
|
||||||
|
|
||||||
"postcss": ["postcss@8.5.24", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-8RyVklq0owXUTa4xlpzu4l9AaVKIdQvAcOHZWaMh98HgySsUtxRVf/chRe3dsSLqb6i40BzGRzEUddRaI+9TSw=="],
|
"postcss": ["postcss@8.5.24", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-8RyVklq0owXUTa4xlpzu4l9AaVKIdQvAcOHZWaMh98HgySsUtxRVf/chRe3dsSLqb6i40BzGRzEUddRaI+9TSw=="],
|
||||||
|
|
||||||
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
||||||
|
|||||||
+5
-1
@@ -14,7 +14,10 @@
|
|||||||
"test": "bun test/run.ts",
|
"test": "bun test/run.ts",
|
||||||
"test:integration": "bun test/run.ts integration",
|
"test:integration": "bun test/run.ts integration",
|
||||||
"test:e2e": "bun test/run.ts e2e",
|
"test:e2e": "bun test/run.ts e2e",
|
||||||
"test:all": "bun test/run.ts all"
|
"test:all": "bun test/run.ts all",
|
||||||
|
"pocketbase:start": "docker compose -f docker-compose.pocketbase.yml up -d",
|
||||||
|
"pocketbase:stop": "docker compose -f docker-compose.pocketbase.yml down",
|
||||||
|
"pocketbase:reset": "bash scripts/pocketbase-reset.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@supabase/supabase-js": "^2.111.0",
|
"@supabase/supabase-js": "^2.111.0",
|
||||||
@@ -27,6 +30,7 @@
|
|||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.575.0",
|
"lucide-react": "^0.575.0",
|
||||||
"motion": "^13.2.0",
|
"motion": "^13.2.0",
|
||||||
|
"pocketbase": "^0.28.1",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { createMiddleware } from "@tanstack/react-start";
|
||||||
|
import { pb } from "./client";
|
||||||
|
|
||||||
|
// Deve essere registrato come `functionMiddleware` globale in `src/start.ts`; altrimenti
|
||||||
|
// il browser non allega il bearer token alle RPC delle server function.
|
||||||
|
export const attachPocketBaseAuth = createMiddleware({ type: "function" }).client(
|
||||||
|
async ({ next }) => {
|
||||||
|
const token = pb.authStore.token;
|
||||||
|
return next({
|
||||||
|
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// Client server-side con privilegi superuser - bypassa le API rules delle collection
|
||||||
|
// (equivalente del service role Supabase, con una differenza: PocketBase non ha una
|
||||||
|
// chiave statica, l'autenticazione è email+password contro la collection _superusers e
|
||||||
|
// va rinnovata quando il token scade).
|
||||||
|
//
|
||||||
|
// SECURITY: usare solo per operazioni server-side fidate, mai esporre al client.
|
||||||
|
// Carica dentro gli handler server: const { pbAdmin } = await import("@/integrations/pocketbase/client.server");
|
||||||
|
// L'import a livello di modulo è sicuro solo in altri moduli *.server.ts - le route e le
|
||||||
|
// server function finiscono nel bundle client.
|
||||||
|
import PocketBase from "pocketbase";
|
||||||
|
|
||||||
|
let _pb: PocketBase | undefined;
|
||||||
|
|
||||||
|
export async function pbAdmin(): Promise<PocketBase> {
|
||||||
|
const POCKETBASE_URL = process.env["POCKETBASE_URL"];
|
||||||
|
const POCKETBASE_SUPERUSER_EMAIL = process.env["POCKETBASE_SUPERUSER_EMAIL"];
|
||||||
|
const POCKETBASE_SUPERUSER_PASSWORD = process.env["POCKETBASE_SUPERUSER_PASSWORD"];
|
||||||
|
|
||||||
|
if (!POCKETBASE_URL || !POCKETBASE_SUPERUSER_EMAIL || !POCKETBASE_SUPERUSER_PASSWORD) {
|
||||||
|
const missing = [
|
||||||
|
...(!POCKETBASE_URL ? ["POCKETBASE_URL"] : []),
|
||||||
|
...(!POCKETBASE_SUPERUSER_EMAIL ? ["POCKETBASE_SUPERUSER_EMAIL"] : []),
|
||||||
|
...(!POCKETBASE_SUPERUSER_PASSWORD ? ["POCKETBASE_SUPERUSER_PASSWORD"] : []),
|
||||||
|
];
|
||||||
|
const message = `Variabili d'ambiente PocketBase mancanti: ${missing.join(", ")}.`;
|
||||||
|
console.error(`[PocketBase] ${message}`);
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_pb) _pb = new PocketBase(POCKETBASE_URL);
|
||||||
|
|
||||||
|
if (!_pb.authStore.isValid) {
|
||||||
|
await _pb
|
||||||
|
.collection("_superusers")
|
||||||
|
.authWithPassword(POCKETBASE_SUPERUSER_EMAIL, POCKETBASE_SUPERUSER_PASSWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _pb;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import PocketBase from "pocketbase";
|
||||||
|
|
||||||
|
function createPocketBaseClient(): PocketBase {
|
||||||
|
// Use import.meta.env for client-side (Vite build-time replacement)
|
||||||
|
// Fall back to process.env for SSR (server-side rendering)
|
||||||
|
const POCKETBASE_URL = import.meta.env["VITE_POCKETBASE_URL"] || process.env["POCKETBASE_URL"];
|
||||||
|
|
||||||
|
if (!POCKETBASE_URL) {
|
||||||
|
const message = "Manca la variabile d'ambiente POCKETBASE_URL (o VITE_POCKETBASE_URL).";
|
||||||
|
console.error(`[PocketBase] ${message}`);
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PocketBase persiste la sessione in localStorage lato browser e in memoria lato SSR
|
||||||
|
// di default: nessuna configurazione aggiuntiva serve (a differenza del client Supabase).
|
||||||
|
return new PocketBase(POCKETBASE_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
let _pb: PocketBase | undefined;
|
||||||
|
|
||||||
|
// Import the PocketBase client like this:
|
||||||
|
// import { pb } from "@/integrations/pocketbase/client";
|
||||||
|
export const pb = new Proxy({} as PocketBase, {
|
||||||
|
get(_, prop, receiver) {
|
||||||
|
if (!_pb) _pb = createPocketBaseClient();
|
||||||
|
return Reflect.get(_pb, prop, receiver);
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -2,18 +2,19 @@
|
|||||||
* Controllo di accesso per le route in `src/routes/api/public/` che inviano notifiche
|
* Controllo di accesso per le route in `src/routes/api/public/` che inviano notifiche
|
||||||
* a tutta la squadra (DD-024).
|
* a tutta la squadra (DD-024).
|
||||||
*
|
*
|
||||||
* Quelle route usano la service role e saltano la RLS: senza un controllo qui, chiunque
|
* Quelle route usano il superuser PocketBase e saltano le API rules: senza un controllo
|
||||||
* conosca l'URL può far suonare i telefoni di tutti. L'id di un evento non è un segreto —
|
* qui, chiunque conosca l'URL può far suonare i telefoni di tutti. L'id di un evento non
|
||||||
* è un timestamp in base 36 e compare negli URL che la squadra si scambia — quindi non
|
* è un segreto — è un timestamp in base 36 e compare negli URL che la squadra si scambia —
|
||||||
* può fare da credenziale.
|
* quindi non può fare da credenziale.
|
||||||
*
|
*
|
||||||
* Tutte e tre le route che mandano notifiche partono da un pulsante riservato agli
|
* Tutte e tre le route che mandano notifiche partono da un pulsante riservato agli
|
||||||
* amministratori, quindi il controllo è uno solo: `richiediAdmin` verifica il token della
|
* amministratori, quindi il controllo è uno solo: `richiediAdmin` verifica il token della
|
||||||
* sessione Supabase e poi il ruolo in `user_roles`. Torna `null` quando la richiesta può
|
* sessione PocketBase e il campo `role` sul record utente. Torna `null` quando la
|
||||||
* proseguire, altrimenti la `Response` di rifiuto già pronta.
|
* richiesta può proseguire, altrimenti la `Response` di rifiuto già pronta.
|
||||||
*/
|
*/
|
||||||
|
import PocketBase from "pocketbase";
|
||||||
|
|
||||||
/** Il token della sessione Supabase, se la richiesta ne porta uno ben formato. */
|
/** Il token della sessione PocketBase, se la richiesta ne porta uno ben formato. */
|
||||||
function tokenDaRichiesta(request: Request): string | null {
|
function tokenDaRichiesta(request: Request): string | null {
|
||||||
const intestazione = request.headers.get("authorization");
|
const intestazione = request.headers.get("authorization");
|
||||||
if (!intestazione?.startsWith("Bearer ")) return null;
|
if (!intestazione?.startsWith("Bearer ")) return null;
|
||||||
@@ -31,19 +32,25 @@ export async function richiediAdmin(request: Request): Promise<Response | null>
|
|||||||
const token = tokenDaRichiesta(request);
|
const token = tokenDaRichiesta(request);
|
||||||
if (!token) return new Response("Autenticazione richiesta", { status: 401 });
|
if (!token) return new Response("Autenticazione richiesta", { status: 401 });
|
||||||
|
|
||||||
const { supabaseAdmin } = await import("@/integrations/supabase/client.server");
|
const POCKETBASE_URL = process.env["POCKETBASE_URL"];
|
||||||
|
if (!POCKETBASE_URL) {
|
||||||
|
console.error("[PocketBase] Manca la variabile d'ambiente POCKETBASE_URL.");
|
||||||
|
return new Response("Configurazione server non valida", { status: 500 });
|
||||||
|
}
|
||||||
|
|
||||||
const { data: utente, error } = await supabaseAdmin.auth.getUser(token);
|
// Client "vuoto" con il token dell'utente allegato: authRefresh() lo valida e restituisce
|
||||||
if (error || !utente?.user) return new Response("Sessione non valida", { status: 401 });
|
// il record aggiornato in un'unica chiamata, ruolo incluso (con Supabase servivano due
|
||||||
|
// round trip: getUser() più una query separata su user_roles).
|
||||||
|
const pb = new PocketBase(POCKETBASE_URL);
|
||||||
|
pb.authStore.save(token, null);
|
||||||
|
|
||||||
// Stessa fonte di `src/lib/ruoli.ts`: i permessi stanno solo in `user_roles` (DD-011).
|
try {
|
||||||
const { data: ruolo } = await supabaseAdmin
|
const { record } = await pb.collection("users").authRefresh();
|
||||||
.from("user_roles")
|
if (record["role"] !== "admin") {
|
||||||
.select("role")
|
return new Response("Riservato agli amministratori", { status: 403 });
|
||||||
.eq("user_id", utente.user.id)
|
}
|
||||||
.eq("role", "admin")
|
return null;
|
||||||
.maybeSingle();
|
} catch {
|
||||||
|
return new Response("Sessione non valida", { status: 401 });
|
||||||
if (!ruolo) return new Response("Riservato agli amministratori", { status: 403 });
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-36
@@ -1,49 +1,39 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import type { Session } from "@supabase/supabase-js";
|
import type { AuthRecord } from "pocketbase";
|
||||||
import { supabase } from "@/integrations/supabase/client";
|
import { pb } from "@/integrations/pocketbase/client";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Autenticazione reale con Google (DD-011). Il login ha sostituito la selezione del
|
* Autenticazione reale con Google (DD-011). Il login ha sostituito la selezione del
|
||||||
* giocatore: senza sessione non si entra, e i permessi di amministrazione arrivano solo
|
* giocatore: senza sessione non si entra, e i permessi di amministrazione arrivano solo
|
||||||
* da `user_roles` (vedi `ruoli.ts`).
|
* dal campo `role` sul record utente (vedi `ruoli.ts`).
|
||||||
*/
|
*/
|
||||||
export function useSessione() {
|
export function useSessione() {
|
||||||
const [sessione, setSessione] = useState<Session | null>(null);
|
const [utente, setUtente] = useState<AuthRecord>(null);
|
||||||
const [pronta, setPronta] = useState(false);
|
const [pronta, setPronta] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let attivo = true;
|
// Il client PocketBase esplode alla costruzione se manca la variabile d'ambiente: qui
|
||||||
// Il client Supabase esplode alla costruzione se mancano le variabili d'ambiente:
|
// va assorbito, altrimenti la schermata di accesso non si disegna proprio e resta
|
||||||
// qui va assorbito, altrimenti la schermata di accesso non si disegna proprio e
|
// irraggiungibile anche la selezione del giocatore.
|
||||||
// resta irraggiungibile anche la selezione del giocatore.
|
|
||||||
try {
|
try {
|
||||||
supabase.auth
|
setUtente(pb.authStore.record);
|
||||||
.getSession()
|
|
||||||
.then(({ data }) => {
|
|
||||||
if (!attivo) return;
|
|
||||||
setSessione(data.session);
|
|
||||||
setPronta(true);
|
|
||||||
})
|
|
||||||
.catch(() => attivo && setPronta(true));
|
|
||||||
const { data } = supabase.auth.onAuthStateChange((_evento, nuova) => setSessione(nuova));
|
|
||||||
return () => {
|
|
||||||
attivo = false;
|
|
||||||
data.subscription.unsubscribe();
|
|
||||||
};
|
|
||||||
} catch (errore) {
|
|
||||||
console.error("[auth] Supabase non disponibile", errore);
|
|
||||||
setPronta(true);
|
setPronta(true);
|
||||||
return () => {
|
// A differenza di Supabase, l'authStore di PocketBase è già popolato in modo
|
||||||
attivo = false;
|
// sincrono all'avvio (legge localStorage nel costruttore): non serve una chiamata
|
||||||
};
|
// asincrona equivalente a getSession().
|
||||||
|
return pb.authStore.onChange((_token, record) => setUtente(record));
|
||||||
|
} catch (errore) {
|
||||||
|
console.error("[auth] PocketBase non disponibile", errore);
|
||||||
|
setPronta(true);
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sessione,
|
sessione: utente,
|
||||||
pronta,
|
pronta,
|
||||||
utenteId: sessione?.user.id ?? null,
|
utenteId: utente?.id ?? null,
|
||||||
emailUtente: sessione?.user.email ?? null,
|
emailUtente: (utente?.["email"] as string | undefined) ?? null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,20 +43,19 @@ export function useSessione() {
|
|||||||
* un token già scaduto.
|
* un token già scaduto.
|
||||||
*/
|
*/
|
||||||
export async function intestazioniAutenticate(): Promise<Record<string, string>> {
|
export async function intestazioniAutenticate(): Promise<Record<string, string>> {
|
||||||
const { data } = await supabase.auth.getSession();
|
const token = pb.authStore.token;
|
||||||
const token = data.session?.access_token;
|
|
||||||
return token ? { Authorization: `Bearer ${token}` } : {};
|
return token ? { Authorization: `Bearer ${token}` } : {};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function accediConGoogle(): Promise<void> {
|
export async function accediConGoogle(): Promise<void> {
|
||||||
const { error } = await supabase.auth.signInWithOAuth({
|
// createData copre il primo accesso: PocketBase crea il record utente al volo se non
|
||||||
|
// esiste ancora per questa identità Google, e il campo `role` è obbligatorio.
|
||||||
|
await pb.collection("users").authWithOAuth2({
|
||||||
provider: "google",
|
provider: "google",
|
||||||
options: { redirectTo: window.location.origin },
|
createData: { role: "user" },
|
||||||
});
|
});
|
||||||
if (error) throw error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function esci(): Promise<void> {
|
export async function esci(): Promise<void> {
|
||||||
const { error } = await supabase.auth.signOut();
|
pb.authStore.clear();
|
||||||
if (error) throw error;
|
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-28
@@ -1,34 +1,15 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
|
||||||
import { supabase } from "@/integrations/supabase/client";
|
|
||||||
import { useSessione } from "./auth";
|
import { useSessione } from "./auth";
|
||||||
|
|
||||||
export const RUOLI_KEY = ["ruolo-admin"] as const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Permessi di amministrazione: unica fonte è `user_roles` nel database (DD-011).
|
* Permessi di amministrazione: unica fonte è il campo `role` sul record utente
|
||||||
* Nessuna lista di nomi, altrimenti basterebbe scegliere il nome giusto per amministrare.
|
* PocketBase (DD-011). Nessuna lista di nomi, altrimenti basterebbe scegliere il nome
|
||||||
|
* giusto per amministrare.
|
||||||
|
*
|
||||||
|
* A differenza di Supabase (dove il ruolo stava nella tabella separata `user_roles` e
|
||||||
|
* richiedeva una query dedicata), qui il ruolo arriva già con la sessione: nessuna
|
||||||
|
* richiesta di rete o cache aggiuntiva serve.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** `null` = nessuna sessione, quindi il database non ha una risposta da dare. */
|
|
||||||
async function fetchRuoloAdmin(utenteId: string | null): Promise<boolean | null> {
|
|
||||||
if (!utenteId) return null;
|
|
||||||
const { data, error } = await supabase
|
|
||||||
.from("user_roles")
|
|
||||||
.select("role")
|
|
||||||
.eq("user_id", utenteId)
|
|
||||||
.eq("role", "admin")
|
|
||||||
.maybeSingle();
|
|
||||||
if (error) throw error;
|
|
||||||
return !!data;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useIsAdmin(): boolean {
|
export function useIsAdmin(): boolean {
|
||||||
const { utenteId } = useSessione();
|
const { sessione } = useSessione();
|
||||||
// Il ruolo cambia solo quando un admin lo assegna: una lettura per sessione basta.
|
return sessione?.["role"] === "admin";
|
||||||
const query = useQuery({
|
|
||||||
queryKey: [...RUOLI_KEY, utenteId],
|
|
||||||
queryFn: () => fetchRuoloAdmin(utenteId),
|
|
||||||
staleTime: 30 * 60_000,
|
|
||||||
});
|
|
||||||
return query.data === true;
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
import { createStart, createCsrfMiddleware, createMiddleware } from "@tanstack/react-start";
|
import { createStart, createCsrfMiddleware, createMiddleware } from "@tanstack/react-start";
|
||||||
|
|
||||||
import { renderErrorPage } from "./lib/error-page";
|
import { renderErrorPage } from "./lib/error-page";
|
||||||
import { attachSupabaseAuth } from "@/integrations/supabase/auth-attacher";
|
import { attachPocketBaseAuth } from "@/integrations/pocketbase/auth-attacher";
|
||||||
|
|
||||||
const errorMiddleware = createMiddleware().server(async ({ next }) => {
|
const errorMiddleware = createMiddleware().server(async ({ next }) => {
|
||||||
try {
|
try {
|
||||||
@@ -26,6 +26,6 @@ const csrfMiddleware = createCsrfMiddleware({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const startInstance = createStart(() => ({
|
export const startInstance = createStart(() => ({
|
||||||
functionMiddleware: [attachSupabaseAuth],
|
functionMiddleware: [attachPocketBaseAuth],
|
||||||
requestMiddleware: [errorMiddleware, csrfMiddleware],
|
requestMiddleware: [errorMiddleware, csrfMiddleware],
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user