refactor: migrate from ESLint to Biome and extract SQL queries to data.ts
- Replace ESLint with Biome for linting and formatting - Configure Biome with tabs, double quotes, and organized imports - Move all SQL/Drizzle queries from page.tsx files to data.ts files - Create new data.ts files for: ajustes, dashboard, relatorios/categorias - Update existing data.ts files: extrato, fatura (add lancamentos queries) - Remove all drizzle-orm imports from page.tsx files - Update README.md with new tooling info Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,9 +4,10 @@
|
||||
* Handles JWT generation, validation, and token hashing for device authentication.
|
||||
*/
|
||||
|
||||
import crypto from "crypto";
|
||||
import crypto from "node:crypto";
|
||||
|
||||
const JWT_SECRET = process.env.BETTER_AUTH_SECRET || "opensheets-secret-change-me";
|
||||
const JWT_SECRET =
|
||||
process.env.BETTER_AUTH_SECRET || "opensheets-secret-change-me";
|
||||
const ACCESS_TOKEN_EXPIRY = 7 * 24 * 60 * 60; // 7 days in seconds
|
||||
const REFRESH_TOKEN_EXPIRY = 90 * 24 * 60 * 60; // 90 days in seconds
|
||||
|
||||
@@ -15,19 +16,19 @@ const REFRESH_TOKEN_EXPIRY = 90 * 24 * 60 * 60; // 90 days in seconds
|
||||
// ============================================================================
|
||||
|
||||
export interface JwtPayload {
|
||||
sub: string; // userId
|
||||
type: "api_access" | "api_refresh";
|
||||
tokenId: string;
|
||||
deviceId?: string;
|
||||
iat: number;
|
||||
exp: number;
|
||||
sub: string; // userId
|
||||
type: "api_access" | "api_refresh";
|
||||
tokenId: string;
|
||||
deviceId?: string;
|
||||
iat: number;
|
||||
exp: number;
|
||||
}
|
||||
|
||||
export interface TokenPair {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
tokenId: string;
|
||||
expiresAt: Date;
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
tokenId: string;
|
||||
expiresAt: Date;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -38,56 +39,59 @@ export interface TokenPair {
|
||||
* Base64URL encode a string
|
||||
*/
|
||||
function base64UrlEncode(str: string): string {
|
||||
return Buffer.from(str)
|
||||
.toString("base64")
|
||||
.replace(/\+/g, "-")
|
||||
.replace(/\//g, "_")
|
||||
.replace(/=/g, "");
|
||||
return Buffer.from(str)
|
||||
.toString("base64")
|
||||
.replace(/\+/g, "-")
|
||||
.replace(/\//g, "_")
|
||||
.replace(/=/g, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64URL decode a string
|
||||
*/
|
||||
function base64UrlDecode(str: string): string {
|
||||
str = str.replace(/-/g, "+").replace(/_/g, "/");
|
||||
const pad = str.length % 4;
|
||||
if (pad) {
|
||||
str += "=".repeat(4 - pad);
|
||||
}
|
||||
return Buffer.from(str, "base64").toString();
|
||||
str = str.replace(/-/g, "+").replace(/_/g, "/");
|
||||
const pad = str.length % 4;
|
||||
if (pad) {
|
||||
str += "=".repeat(4 - pad);
|
||||
}
|
||||
return Buffer.from(str, "base64").toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create HMAC-SHA256 signature
|
||||
*/
|
||||
function createSignature(data: string): string {
|
||||
return crypto
|
||||
.createHmac("sha256", JWT_SECRET)
|
||||
.update(data)
|
||||
.digest("base64")
|
||||
.replace(/\+/g, "-")
|
||||
.replace(/\//g, "_")
|
||||
.replace(/=/g, "");
|
||||
return crypto
|
||||
.createHmac("sha256", JWT_SECRET)
|
||||
.update(data)
|
||||
.digest("base64")
|
||||
.replace(/\+/g, "-")
|
||||
.replace(/\//g, "_")
|
||||
.replace(/=/g, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JWT token
|
||||
*/
|
||||
export function createJwt(payload: Omit<JwtPayload, "iat" | "exp">, expiresIn: number): string {
|
||||
const header = { alg: "HS256", typ: "JWT" };
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
export function createJwt(
|
||||
payload: Omit<JwtPayload, "iat" | "exp">,
|
||||
expiresIn: number,
|
||||
): string {
|
||||
const header = { alg: "HS256", typ: "JWT" };
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
|
||||
const fullPayload: JwtPayload = {
|
||||
...payload,
|
||||
iat: now,
|
||||
exp: now + expiresIn,
|
||||
};
|
||||
const fullPayload: JwtPayload = {
|
||||
...payload,
|
||||
iat: now,
|
||||
exp: now + expiresIn,
|
||||
};
|
||||
|
||||
const headerEncoded = base64UrlEncode(JSON.stringify(header));
|
||||
const payloadEncoded = base64UrlEncode(JSON.stringify(fullPayload));
|
||||
const signature = createSignature(`${headerEncoded}.${payloadEncoded}`);
|
||||
const headerEncoded = base64UrlEncode(JSON.stringify(header));
|
||||
const payloadEncoded = base64UrlEncode(JSON.stringify(fullPayload));
|
||||
const signature = createSignature(`${headerEncoded}.${payloadEncoded}`);
|
||||
|
||||
return `${headerEncoded}.${payloadEncoded}.${signature}`;
|
||||
return `${headerEncoded}.${payloadEncoded}.${signature}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,30 +99,37 @@ export function createJwt(payload: Omit<JwtPayload, "iat" | "exp">, expiresIn: n
|
||||
* @returns The decoded payload or null if invalid
|
||||
*/
|
||||
export function verifyJwt(token: string): JwtPayload | null {
|
||||
try {
|
||||
const parts = token.split(".");
|
||||
if (parts.length !== 3) return null;
|
||||
try {
|
||||
const parts = token.split(".");
|
||||
if (parts.length !== 3) return null;
|
||||
|
||||
const [headerEncoded, payloadEncoded, signature] = parts;
|
||||
const expectedSignature = createSignature(`${headerEncoded}.${payloadEncoded}`);
|
||||
const [headerEncoded, payloadEncoded, signature] = parts;
|
||||
const expectedSignature = createSignature(
|
||||
`${headerEncoded}.${payloadEncoded}`,
|
||||
);
|
||||
|
||||
// Constant-time comparison to prevent timing attacks
|
||||
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) {
|
||||
return null;
|
||||
}
|
||||
// Constant-time comparison to prevent timing attacks
|
||||
if (
|
||||
!crypto.timingSafeEqual(
|
||||
Buffer.from(signature),
|
||||
Buffer.from(expectedSignature),
|
||||
)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const payload: JwtPayload = JSON.parse(base64UrlDecode(payloadEncoded));
|
||||
const payload: JwtPayload = JSON.parse(base64UrlDecode(payloadEncoded));
|
||||
|
||||
// Check expiration
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
if (payload.exp < now) {
|
||||
return null;
|
||||
}
|
||||
// Check expiration
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
if (payload.exp < now) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return payload;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return payload;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -129,76 +140,86 @@ export function verifyJwt(token: string): JwtPayload | null {
|
||||
* Generate a random token ID
|
||||
*/
|
||||
export function generateTokenId(): string {
|
||||
return crypto.randomUUID();
|
||||
return crypto.randomUUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random API token with prefix
|
||||
*/
|
||||
export function generateApiToken(): string {
|
||||
const randomPart = crypto.randomBytes(32).toString("base64url");
|
||||
return `os_${randomPart}`;
|
||||
const randomPart = crypto.randomBytes(32).toString("base64url");
|
||||
return `os_${randomPart}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a token using SHA-256
|
||||
*/
|
||||
export function hashToken(token: string): string {
|
||||
return crypto.createHash("sha256").update(token).digest("hex");
|
||||
return crypto.createHash("sha256").update(token).digest("hex");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the display prefix of a token (first 8 chars after prefix)
|
||||
*/
|
||||
export function getTokenPrefix(token: string): string {
|
||||
// Remove "os_" prefix and get first 8 chars
|
||||
const withoutPrefix = token.replace(/^os_/, "");
|
||||
return `os_${withoutPrefix.substring(0, 8)}...`;
|
||||
// Remove "os_" prefix and get first 8 chars
|
||||
const withoutPrefix = token.replace(/^os_/, "");
|
||||
return `os_${withoutPrefix.substring(0, 8)}...`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a complete token pair (access + refresh)
|
||||
*/
|
||||
export function generateTokenPair(userId: string, deviceId?: string): TokenPair {
|
||||
const tokenId = generateTokenId();
|
||||
const expiresAt = new Date(Date.now() + ACCESS_TOKEN_EXPIRY * 1000);
|
||||
export function generateTokenPair(
|
||||
userId: string,
|
||||
deviceId?: string,
|
||||
): TokenPair {
|
||||
const tokenId = generateTokenId();
|
||||
const expiresAt = new Date(Date.now() + ACCESS_TOKEN_EXPIRY * 1000);
|
||||
|
||||
const accessToken = createJwt(
|
||||
{ sub: userId, type: "api_access", tokenId, deviceId },
|
||||
ACCESS_TOKEN_EXPIRY
|
||||
);
|
||||
const accessToken = createJwt(
|
||||
{ sub: userId, type: "api_access", tokenId, deviceId },
|
||||
ACCESS_TOKEN_EXPIRY,
|
||||
);
|
||||
|
||||
const refreshToken = createJwt(
|
||||
{ sub: userId, type: "api_refresh", tokenId, deviceId },
|
||||
REFRESH_TOKEN_EXPIRY
|
||||
);
|
||||
const refreshToken = createJwt(
|
||||
{ sub: userId, type: "api_refresh", tokenId, deviceId },
|
||||
REFRESH_TOKEN_EXPIRY,
|
||||
);
|
||||
|
||||
return {
|
||||
accessToken,
|
||||
refreshToken,
|
||||
tokenId,
|
||||
expiresAt,
|
||||
};
|
||||
return {
|
||||
accessToken,
|
||||
refreshToken,
|
||||
tokenId,
|
||||
expiresAt,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh an access token using a refresh token
|
||||
*/
|
||||
export function refreshAccessToken(refreshToken: string): { accessToken: string; expiresAt: Date } | null {
|
||||
const payload = verifyJwt(refreshToken);
|
||||
export function refreshAccessToken(
|
||||
refreshToken: string,
|
||||
): { accessToken: string; expiresAt: Date } | null {
|
||||
const payload = verifyJwt(refreshToken);
|
||||
|
||||
if (!payload || payload.type !== "api_refresh") {
|
||||
return null;
|
||||
}
|
||||
if (!payload || payload.type !== "api_refresh") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const expiresAt = new Date(Date.now() + ACCESS_TOKEN_EXPIRY * 1000);
|
||||
const expiresAt = new Date(Date.now() + ACCESS_TOKEN_EXPIRY * 1000);
|
||||
|
||||
const accessToken = createJwt(
|
||||
{ sub: payload.sub, type: "api_access", tokenId: payload.tokenId, deviceId: payload.deviceId },
|
||||
ACCESS_TOKEN_EXPIRY
|
||||
);
|
||||
const accessToken = createJwt(
|
||||
{
|
||||
sub: payload.sub,
|
||||
type: "api_access",
|
||||
tokenId: payload.tokenId,
|
||||
deviceId: payload.deviceId,
|
||||
},
|
||||
ACCESS_TOKEN_EXPIRY,
|
||||
);
|
||||
|
||||
return { accessToken, expiresAt };
|
||||
return { accessToken, expiresAt };
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -209,9 +230,9 @@ export function refreshAccessToken(refreshToken: string): { accessToken: string;
|
||||
* Extract bearer token from Authorization header
|
||||
*/
|
||||
export function extractBearerToken(authHeader: string | null): string | null {
|
||||
if (!authHeader) return null;
|
||||
const match = authHeader.match(/^Bearer\s+(.+)$/i);
|
||||
return match ? match[1] : null;
|
||||
if (!authHeader) return null;
|
||||
const match = authHeader.match(/^Bearer\s+(.+)$/i);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,20 +240,23 @@ export function extractBearerToken(authHeader: string | null): string | null {
|
||||
* @deprecated Use validateHashToken for os_xxx tokens
|
||||
*/
|
||||
export function validateApiToken(token: string): JwtPayload | null {
|
||||
const payload = verifyJwt(token);
|
||||
if (!payload || payload.type !== "api_access") {
|
||||
return null;
|
||||
}
|
||||
return payload;
|
||||
const payload = verifyJwt(token);
|
||||
if (!payload || payload.type !== "api_access") {
|
||||
return null;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a hash-based API token (os_xxx format)
|
||||
* Returns the token hash for database lookup
|
||||
*/
|
||||
export function validateHashToken(token: string): { valid: boolean; tokenHash?: string } {
|
||||
if (!token || !token.startsWith("os_")) {
|
||||
return { valid: false };
|
||||
}
|
||||
return { valid: true, tokenHash: hashToken(token) };
|
||||
export function validateHashToken(token: string): {
|
||||
valid: boolean;
|
||||
tokenHash?: string;
|
||||
} {
|
||||
if (!token || !token.startsWith("os_")) {
|
||||
return { valid: false };
|
||||
}
|
||||
return { valid: true, tokenHash: hashToken(token) };
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { createAuthClient } from "better-auth/react";
|
||||
const baseURL = process.env.BETTER_AUTH_URL?.replace(/\/$/, "");
|
||||
|
||||
export const authClient = createAuthClient({
|
||||
...(baseURL ? { baseURL } : {}),
|
||||
...(baseURL ? { baseURL } : {}),
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
* Suporta email/password e Google OAuth.
|
||||
*/
|
||||
|
||||
import { betterAuth } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import type { GoogleProfile } from "better-auth/social-providers";
|
||||
import { seedDefaultCategoriesForUser } from "@/lib/categorias/defaults";
|
||||
import { db, schema } from "@/lib/db";
|
||||
import { ensureDefaultPagadorForUser } from "@/lib/pagadores/defaults";
|
||||
import { normalizeNameFromEmail } from "@/lib/pagadores/utils";
|
||||
import { betterAuth } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import type { GoogleProfile } from "better-auth/social-providers";
|
||||
|
||||
// ============================================================================
|
||||
// GOOGLE OAUTH CONFIGURATION
|
||||
@@ -28,20 +28,20 @@ const googleClientSecret = process.env.GOOGLE_CLIENT_SECRET;
|
||||
* 4. "Usuário" (fallback final)
|
||||
*/
|
||||
function getNameFromGoogleProfile(profile: GoogleProfile): string {
|
||||
const fullName = profile.name?.trim();
|
||||
if (fullName) return fullName;
|
||||
const fullName = profile.name?.trim();
|
||||
if (fullName) return fullName;
|
||||
|
||||
const fromGivenFamily = [profile.given_name, profile.family_name]
|
||||
.filter(Boolean)
|
||||
.join(" ")
|
||||
.trim();
|
||||
if (fromGivenFamily) return fromGivenFamily;
|
||||
const fromGivenFamily = [profile.given_name, profile.family_name]
|
||||
.filter(Boolean)
|
||||
.join(" ")
|
||||
.trim();
|
||||
if (fromGivenFamily) return fromGivenFamily;
|
||||
|
||||
const fromEmail = profile.email
|
||||
? normalizeNameFromEmail(profile.email)
|
||||
: undefined;
|
||||
const fromEmail = profile.email
|
||||
? normalizeNameFromEmail(profile.email)
|
||||
: undefined;
|
||||
|
||||
return fromEmail ?? "Usuário";
|
||||
return fromEmail ?? "Usuário";
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -49,99 +49,99 @@ function getNameFromGoogleProfile(profile: GoogleProfile): string {
|
||||
// ============================================================================
|
||||
|
||||
export const auth = betterAuth({
|
||||
// Base URL configuration
|
||||
baseURL: process.env.BETTER_AUTH_URL || "http://localhost:3000",
|
||||
// Base URL configuration
|
||||
baseURL: process.env.BETTER_AUTH_URL || "http://localhost:3000",
|
||||
|
||||
// Trust host configuration for production environments
|
||||
trustedOrigins: process.env.BETTER_AUTH_URL
|
||||
? [process.env.BETTER_AUTH_URL]
|
||||
: [],
|
||||
// Trust host configuration for production environments
|
||||
trustedOrigins: process.env.BETTER_AUTH_URL
|
||||
? [process.env.BETTER_AUTH_URL]
|
||||
: [],
|
||||
|
||||
// Email/Password authentication
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
autoSignIn: true,
|
||||
},
|
||||
// Email/Password authentication
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
autoSignIn: true,
|
||||
},
|
||||
|
||||
// Database adapter (Drizzle + PostgreSQL)
|
||||
database: drizzleAdapter(db, {
|
||||
provider: "pg",
|
||||
schema,
|
||||
camelCase: true,
|
||||
}),
|
||||
// Database adapter (Drizzle + PostgreSQL)
|
||||
database: drizzleAdapter(db, {
|
||||
provider: "pg",
|
||||
schema,
|
||||
camelCase: true,
|
||||
}),
|
||||
|
||||
// Session configuration - Safari compatibility
|
||||
session: {
|
||||
cookieCache: {
|
||||
enabled: true,
|
||||
maxAge: 60 * 5, // 5 minutes
|
||||
},
|
||||
},
|
||||
// Session configuration - Safari compatibility
|
||||
session: {
|
||||
cookieCache: {
|
||||
enabled: true,
|
||||
maxAge: 60 * 5, // 5 minutes
|
||||
},
|
||||
},
|
||||
|
||||
// Advanced configuration for Safari compatibility
|
||||
advanced: {
|
||||
cookieOptions: {
|
||||
sameSite: "lax", // Safari compatible
|
||||
secure: process.env.NODE_ENV === "production", // HTTPS in production only
|
||||
httpOnly: true,
|
||||
},
|
||||
crossSubDomainCookies: {
|
||||
enabled: false, // Disable for better Safari compatibility
|
||||
},
|
||||
},
|
||||
// Advanced configuration for Safari compatibility
|
||||
advanced: {
|
||||
cookieOptions: {
|
||||
sameSite: "lax", // Safari compatible
|
||||
secure: process.env.NODE_ENV === "production", // HTTPS in production only
|
||||
httpOnly: true,
|
||||
},
|
||||
crossSubDomainCookies: {
|
||||
enabled: false, // Disable for better Safari compatibility
|
||||
},
|
||||
},
|
||||
|
||||
// Google OAuth (se configurado)
|
||||
socialProviders:
|
||||
googleClientId && googleClientSecret
|
||||
? {
|
||||
google: {
|
||||
clientId: googleClientId,
|
||||
clientSecret: googleClientSecret,
|
||||
mapProfileToUser: (profile) => ({
|
||||
name: getNameFromGoogleProfile(profile),
|
||||
email: profile.email,
|
||||
image: profile.picture,
|
||||
emailVerified: profile.email_verified,
|
||||
}),
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
// Google OAuth (se configurado)
|
||||
socialProviders:
|
||||
googleClientId && googleClientSecret
|
||||
? {
|
||||
google: {
|
||||
clientId: googleClientId,
|
||||
clientSecret: googleClientSecret,
|
||||
mapProfileToUser: (profile) => ({
|
||||
name: getNameFromGoogleProfile(profile),
|
||||
email: profile.email,
|
||||
image: profile.picture,
|
||||
emailVerified: profile.email_verified,
|
||||
}),
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
|
||||
// Database hooks - Executados após eventos do DB
|
||||
databaseHooks: {
|
||||
user: {
|
||||
create: {
|
||||
/**
|
||||
* Após criar novo usuário, inicializa:
|
||||
* 1. Categorias padrão (Receitas/Despesas)
|
||||
* 2. Pagador padrão (vinculado ao usuário)
|
||||
*/
|
||||
after: async (user) => {
|
||||
// Se falhar aqui, o usuário já foi criado - considere usar queue para retry
|
||||
try {
|
||||
await seedDefaultCategoriesForUser(user.id);
|
||||
await ensureDefaultPagadorForUser({
|
||||
id: user.id,
|
||||
name: user.name ?? undefined,
|
||||
email: user.email ?? undefined,
|
||||
image: user.image ?? undefined,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"[Auth] Falha ao criar dados padrão do usuário:",
|
||||
error
|
||||
);
|
||||
// TODO: Considere enfileirar para retry ou notificar admin
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// Database hooks - Executados após eventos do DB
|
||||
databaseHooks: {
|
||||
user: {
|
||||
create: {
|
||||
/**
|
||||
* Após criar novo usuário, inicializa:
|
||||
* 1. Categorias padrão (Receitas/Despesas)
|
||||
* 2. Pagador padrão (vinculado ao usuário)
|
||||
*/
|
||||
after: async (user) => {
|
||||
// Se falhar aqui, o usuário já foi criado - considere usar queue para retry
|
||||
try {
|
||||
await seedDefaultCategoriesForUser(user.id);
|
||||
await ensureDefaultPagadorForUser({
|
||||
id: user.id,
|
||||
name: user.name ?? undefined,
|
||||
email: user.email ?? undefined,
|
||||
image: user.image ?? undefined,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"[Auth] Falha ao criar dados padrão do usuário:",
|
||||
error,
|
||||
);
|
||||
// TODO: Considere enfileirar para retry ou notificar admin
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Aviso em desenvolvimento se Google OAuth não estiver configurado
|
||||
if (!googleClientId && process.env.NODE_ENV === "development") {
|
||||
console.warn(
|
||||
"[Auth] Google OAuth não configurado. Defina GOOGLE_CLIENT_ID e GOOGLE_CLIENT_SECRET."
|
||||
);
|
||||
console.warn(
|
||||
"[Auth] Google OAuth não configurado. Defina GOOGLE_CLIENT_ID e GOOGLE_CLIENT_SECRET.",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
* to /login if the user is not authenticated.
|
||||
*/
|
||||
|
||||
import { auth } from "@/lib/auth/config";
|
||||
import { headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
import { auth } from "@/lib/auth/config";
|
||||
|
||||
/**
|
||||
* Gets the current authenticated user
|
||||
@@ -20,13 +20,13 @@ import { redirect } from "next/navigation";
|
||||
* @throws Redirects to /login if user is not authenticated
|
||||
*/
|
||||
export async function getUser() {
|
||||
const session = await auth.api.getSession({ headers: await headers() });
|
||||
const session = await auth.api.getSession({ headers: await headers() });
|
||||
|
||||
if (!session?.user) {
|
||||
redirect("/login");
|
||||
}
|
||||
if (!session?.user) {
|
||||
redirect("/login");
|
||||
}
|
||||
|
||||
return session.user;
|
||||
return session.user;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,13 +35,13 @@ export async function getUser() {
|
||||
* @throws Redirects to /login if user is not authenticated
|
||||
*/
|
||||
export async function getUserId() {
|
||||
const session = await auth.api.getSession({ headers: await headers() });
|
||||
const session = await auth.api.getSession({ headers: await headers() });
|
||||
|
||||
if (!session?.user) {
|
||||
redirect("/login");
|
||||
}
|
||||
if (!session?.user) {
|
||||
redirect("/login");
|
||||
}
|
||||
|
||||
return session.user.id;
|
||||
return session.user.id;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,13 +50,13 @@ export async function getUserId() {
|
||||
* @throws Redirects to /login if user is not authenticated
|
||||
*/
|
||||
export async function getUserSession() {
|
||||
const session = await auth.api.getSession({ headers: await headers() });
|
||||
const session = await auth.api.getSession({ headers: await headers() });
|
||||
|
||||
if (!session?.user) {
|
||||
redirect("/login");
|
||||
}
|
||||
if (!session?.user) {
|
||||
redirect("/login");
|
||||
}
|
||||
|
||||
return session;
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,5 +65,5 @@ export async function getUserSession() {
|
||||
* @note This function does not redirect if user is not authenticated
|
||||
*/
|
||||
export async function getOptionalUserSession() {
|
||||
return auth.api.getSession({ headers: await headers() });
|
||||
return auth.api.getSession({ headers: await headers() });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user