Integrate Go auth with HttpOnly cookies
This commit is contained in:
parent
11bff5af03
commit
0764274d65
7 changed files with 186 additions and 237 deletions
|
|
@ -6,6 +6,8 @@ NEXT_PUBLIC_APPWRITE_PROJECT_ID=seu_projeto_id
|
|||
NEXT_PUBLIC_APPWRITE_DATABASE_ID=seu_banco_de_dados_id
|
||||
# URL Frontend
|
||||
NEXT_PUBLIC_FRONTEND_URL=https://seu-frontend.com
|
||||
# API Go SaveInMed
|
||||
NEXT_PUBLIC_API_URL=http://localhost:8214
|
||||
# API BFF SaveInMed
|
||||
NEXT_PUBLIC_BFF_API_URL=https://bff_url/api/v1
|
||||
NEXT_PUBLIC_BFF_API_URL_MP=https://bff_url
|
||||
|
|
@ -41,4 +43,3 @@ MERCADO_PAGO_ACCESS_TOKEN=APP_USR-seu_token_de_acesso
|
|||
MERCADO_PAGO_PUBLIC_KEY=APP_USR-seu_public_key
|
||||
NEXT_PUBLIC_MERCADO_PAGO_PUBLIC_KEY=APP_USR-seu_public_key
|
||||
# Em produção, use tokens do tipo APP_USR-... e mantenha esses segredos apenas no servidor (não no client)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { toast } from "react-hot-toast";
|
|||
import Header from "@/components/Header";
|
||||
import { useCarrinho } from "@/contexts/CarrinhoContext";
|
||||
import { pedidoApiService } from "@/services/pedidoApiService";
|
||||
import { authService, GO_API_V1_BASE_URL } from "@/services/auth";
|
||||
import { useEmpresa } from "@/contexts/EmpresaContext";
|
||||
import { CheckCircle, Truck, CreditCard, ChevronLeft, MapPin } from "lucide-react";
|
||||
|
||||
|
|
@ -38,40 +39,23 @@ export default function CheckoutPage() {
|
|||
// Fetch user profile and addresses
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const token = pedidoApiService.getAuthToken();
|
||||
if (!token) return;
|
||||
|
||||
// Fetch User
|
||||
const meRes = await fetch(`${process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8214'}/api/v1/auth/me`, {
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
if (meRes.ok) {
|
||||
const userData = await meRes.json();
|
||||
setUserProfile(userData);
|
||||
}
|
||||
const userData = await authService.me();
|
||||
if (!userData) return;
|
||||
setUserProfile(userData);
|
||||
|
||||
// Fetch Addresses
|
||||
const addrRes = await fetch(`${process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8214'}/api/v1/enderecos`, {
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
const addrRes = await fetch(`${GO_API_V1_BASE_URL}/enderecos`, {
|
||||
headers: { accept: 'application/json' },
|
||||
credentials: 'include'
|
||||
});
|
||||
if (addrRes.ok) {
|
||||
const addrData = await addrRes.json();
|
||||
if (Array.isArray(addrData) && addrData.length > 0) {
|
||||
setAddresses(addrData);
|
||||
setSelectedAddressId(addrData[0].id);
|
||||
} else if (!addrData || addrData.length === 0) {
|
||||
const mock = {
|
||||
id: 'mock-1',
|
||||
logradouro: empresa?.endereco || "Rua Principal",
|
||||
numero: empresa?.numero || "100",
|
||||
bairro: empresa?.bairro || "Centro",
|
||||
cidade: empresa?.cidade || "São Paulo",
|
||||
uf: empresa?.estado || "SP",
|
||||
cep: empresa?.cep || "01000-000",
|
||||
titulo: "Endereço Padrão (Mock)"
|
||||
};
|
||||
setAddresses([mock]);
|
||||
setSelectedAddressId('mock-1');
|
||||
} else {
|
||||
setAddresses([]);
|
||||
setSelectedAddressId(null);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useState, useEffect, Suspense } from "react";
|
|||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useEmpresa } from "@/contexts/EmpresaContext";
|
||||
import { translateError } from "@/lib/error-translator";
|
||||
import { authService } from "@/services/auth";
|
||||
import Image from "next/image";
|
||||
|
||||
/**
|
||||
|
|
@ -23,7 +24,6 @@ const LoginPageContent = () => {
|
|||
const [isLogin, setIsLogin] = useState<boolean>(true); // Alterna entre login e registro
|
||||
const [checkingAuth, setCheckingAuth] = useState<boolean>(true); // Verificação inicial de autenticação
|
||||
const [showPassword, setShowPassword] = useState<boolean>(false); // Controla visibilidade da senha
|
||||
const [accessToken, setAccessToken] = useState<string>(""); // Token de acesso do BFF
|
||||
const [showSuccessModal, setShowSuccessModal] = useState<boolean>(false); // Modal de sucesso do cadastro
|
||||
const [showPendingModal, setShowPendingModal] = useState<boolean>(false); // Modal de cadastro pendente
|
||||
|
||||
|
|
@ -44,38 +44,9 @@ const LoginPageContent = () => {
|
|||
useEffect(() => {
|
||||
const checkAuth = async () => {
|
||||
try {
|
||||
// Verificar se há token armazenado
|
||||
const storedToken = localStorage.getItem('access_token');
|
||||
const headers: HeadersInit = {
|
||||
accept: "application/json",
|
||||
};
|
||||
|
||||
if (storedToken) {
|
||||
headers.Authorization = `Bearer ${storedToken}`; // Usar Authorization ao invés de Cookie
|
||||
}
|
||||
|
||||
// Verificar autenticação usando BFF com o token no header Authorization
|
||||
const response = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_BFF_API_URL}/auth/me`,
|
||||
{
|
||||
method: "GET",
|
||||
headers,
|
||||
credentials: "include",
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
if (response.ok) {
|
||||
const userData = await response.json();
|
||||
// ... (log logic)
|
||||
} else {
|
||||
const errorText = await response.text();
|
||||
console.log("❌ Falha no /me:", errorText);
|
||||
|
||||
// Only remove token if explicitly unauthorized (401)
|
||||
if (response.status === 401 && storedToken) {
|
||||
localStorage.removeItem('access_token');
|
||||
}
|
||||
const userData = await authService.me();
|
||||
if (userData) {
|
||||
localStorage.setItem('user', JSON.stringify(userData));
|
||||
}
|
||||
} catch (error) {
|
||||
} finally {
|
||||
|
|
@ -95,44 +66,25 @@ const LoginPageContent = () => {
|
|||
setError("");
|
||||
try {
|
||||
|
||||
// 1. Fazer login no BFF
|
||||
const baseUrl = process.env.NEXT_PUBLIC_BFF_API_URL!;
|
||||
const response = await fetch(`${baseUrl}/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include', // Permite que o browser receba e armazene cookies
|
||||
mode: 'cors', // Habilita CORS explicitamente
|
||||
body: JSON.stringify({
|
||||
email: email,
|
||||
password: password
|
||||
})
|
||||
});
|
||||
// 1. Fazer login na API Go (cookie HttpOnly)
|
||||
const loginData = await authService.login(email, password);
|
||||
|
||||
// Ler e logar o corpo da resposta (usando clone para não consumir o body original)
|
||||
const respClone = response.clone();
|
||||
const respText = await respClone.text();
|
||||
let respBody: any = respText;
|
||||
try {
|
||||
respBody = JSON.parse(respText);
|
||||
} catch {
|
||||
// corpo não é JSON, manter como texto
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
if (!loginData || (loginData as any).error) {
|
||||
// Extrair mensagem de erro do backend
|
||||
let errorMessage = respBody?.message || respBody?.error || respBody?.detail || respText;
|
||||
let errorMessage =
|
||||
(loginData as any)?.message ||
|
||||
(loginData as any)?.error ||
|
||||
(loginData as any)?.detail ||
|
||||
"Falha no login";
|
||||
|
||||
// Tratar códigos de status específicos
|
||||
if (response.status === 401) {
|
||||
if ((loginData as any)?.status === 401) {
|
||||
errorMessage = "Email ou senha incorretos. Verifique suas credenciais.";
|
||||
} else if (response.status === 403) {
|
||||
} else if ((loginData as any)?.status === 403) {
|
||||
errorMessage = "Acesso negado. Sua conta pode estar inativa.";
|
||||
} else if (response.status === 404) {
|
||||
} else if ((loginData as any)?.status === 404) {
|
||||
errorMessage = "Usuário não encontrado. Verifique o email informado.";
|
||||
} else if (response.status >= 500) {
|
||||
} else if ((loginData as any)?.status >= 500) {
|
||||
errorMessage = "Erro interno do servidor. Tente novamente em alguns minutos.";
|
||||
}
|
||||
|
||||
|
|
@ -141,37 +93,13 @@ const LoginPageContent = () => {
|
|||
throw new Error(translatedError);
|
||||
}
|
||||
|
||||
const loginData = await response.json();
|
||||
|
||||
// 2. Capturar e armazenar o access_token
|
||||
if (loginData.access_token) {
|
||||
const token = loginData.access_token;
|
||||
localStorage.setItem('access_token', token);
|
||||
setAccessToken(token);
|
||||
// 2. Verificar imediatamente se o /me funciona (cookie httpOnly)
|
||||
const meData = await authService.me();
|
||||
if (meData) {
|
||||
localStorage.setItem('user', JSON.stringify(meData));
|
||||
}
|
||||
|
||||
// 3. Verificar imediatamente se o /me funciona (cookie httpOnly ou Authorization)
|
||||
const meHeaders: HeadersInit = {
|
||||
accept: "application/json",
|
||||
};
|
||||
if (loginData.access_token) {
|
||||
meHeaders.Authorization = `Bearer ${loginData.access_token}`;
|
||||
}
|
||||
|
||||
const meResponse = await fetch(`${baseUrl}/auth/me`, {
|
||||
method: "GET",
|
||||
headers: meHeaders,
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
|
||||
if (meResponse.ok) {
|
||||
const userData = await meResponse.json();
|
||||
} else {
|
||||
console.log("❌ Falha no /me:", await meResponse.text());
|
||||
}
|
||||
|
||||
// 4. Armazenar informações do usuário no localStorage
|
||||
// 3. Armazenar informações do usuário no localStorage
|
||||
if (loginData.user) {
|
||||
localStorage.setItem('user', JSON.stringify(loginData.user));
|
||||
|
||||
|
|
@ -195,12 +123,12 @@ const LoginPageContent = () => {
|
|||
}
|
||||
}
|
||||
|
||||
// 5. Verificar e armazenar empresa ID se disponível
|
||||
// 4. Verificar e armazenar empresa ID se disponível
|
||||
if (loginData.user?.empresaId) {
|
||||
setEmpresaId(loginData.user.empresaId);
|
||||
}
|
||||
|
||||
// 6. Redirecionar para o dashboard
|
||||
// 5. Redirecionar para o dashboard
|
||||
router.push("/dashboard");
|
||||
|
||||
} catch (error: any) {
|
||||
|
|
@ -208,6 +136,17 @@ const LoginPageContent = () => {
|
|||
|
||||
// Definir mensagem de erro amigável
|
||||
let friendlyMessage = error.message;
|
||||
const status = error?.status;
|
||||
|
||||
if (status === 401) {
|
||||
friendlyMessage = "Email ou senha incorretos. Verifique suas credenciais.";
|
||||
} else if (status === 403) {
|
||||
friendlyMessage = "Acesso negado. Sua conta pode estar inativa.";
|
||||
} else if (status === 404) {
|
||||
friendlyMessage = "Usuário não encontrado. Verifique o email informado.";
|
||||
} else if (status >= 500) {
|
||||
friendlyMessage = "Erro interno do servidor. Tente novamente em alguns minutos.";
|
||||
}
|
||||
|
||||
// Se for um erro de rede
|
||||
if (error.name === 'TypeError' || error.message.includes('fetch')) {
|
||||
|
|
@ -218,7 +157,7 @@ const LoginPageContent = () => {
|
|||
friendlyMessage = "Erro inesperado. Verifique suas credenciais e tente novamente.";
|
||||
}
|
||||
|
||||
setError(friendlyMessage);
|
||||
setError(translateError(friendlyMessage));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
|
@ -233,77 +172,24 @@ const LoginPageContent = () => {
|
|||
setError("");
|
||||
try {
|
||||
|
||||
// 1. Fazer registro no BFF com dados corretos
|
||||
const baseUrl = process.env.NEXT_PUBLIC_BFF_API_URL!;
|
||||
const response = await fetch(`${baseUrl}/auth/register`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
role: "Seller",
|
||||
name: name,
|
||||
username: email,
|
||||
email: email,
|
||||
password: password
|
||||
})
|
||||
// 1. Fazer registro na API Go
|
||||
await authService.register({
|
||||
role: "Seller",
|
||||
name: name,
|
||||
username: email,
|
||||
email: email,
|
||||
password: password
|
||||
});
|
||||
|
||||
// 2. Fazer login automático após registro para obter cookie HttpOnly
|
||||
const loginData = await authService.login(email, password);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
|
||||
// Tratar códigos de status específicos
|
||||
let errorMessage = errorData.message || 'Falha no registro';
|
||||
|
||||
if (response.status === 409 || errorMessage.includes('already exists') || errorMessage.includes('já existe')) {
|
||||
errorMessage = 'Este email já está cadastrado. Tente fazer login ou use outro email.';
|
||||
} else if (response.status === 400) {
|
||||
errorMessage = 'Dados inválidos. Verifique se todos os campos estão preenchidos corretamente.';
|
||||
} else if (response.status >= 500) {
|
||||
errorMessage = 'Erro interno do servidor. Tente novamente em alguns minutos.';
|
||||
}
|
||||
|
||||
// Aplicar tradução se disponível
|
||||
const translatedError = translateError(errorMessage);
|
||||
throw new Error(translatedError);
|
||||
}
|
||||
|
||||
const registerData = await response.json();
|
||||
|
||||
// 2. Fazer login automático após registro para obter token
|
||||
const loginResponse = await fetch(`${baseUrl}/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
mode: 'cors',
|
||||
body: JSON.stringify({
|
||||
email: email,
|
||||
password: password
|
||||
})
|
||||
});
|
||||
|
||||
if (!loginResponse.ok) {
|
||||
console.error("❌ Erro no login automático:", loginResponse.status);
|
||||
throw new Error('Erro ao fazer login automático após registro');
|
||||
}
|
||||
|
||||
const loginData = await loginResponse.json();
|
||||
|
||||
// 3. Armazenar token e dados do usuário
|
||||
if (loginData.access_token) {
|
||||
localStorage.setItem('access_token', loginData.access_token);
|
||||
}
|
||||
|
||||
// 3. Armazenar dados do usuário
|
||||
if (loginData.user) {
|
||||
localStorage.setItem('user', JSON.stringify(loginData.user));
|
||||
}
|
||||
|
||||
// 4. Redirecionar para completar registro com token válido
|
||||
// 4. Redirecionar para completar registro com sessão válida
|
||||
router.push(`/completar-registro?nome=${encodeURIComponent(name)}&email=${encodeURIComponent(email)}`);
|
||||
|
||||
} catch (error: any) {
|
||||
|
|
@ -311,6 +197,15 @@ const LoginPageContent = () => {
|
|||
|
||||
// Definir mensagem de erro amigável
|
||||
let friendlyMessage = error.message;
|
||||
const status = error?.status;
|
||||
|
||||
if (status === 409 || friendlyMessage.includes('already exists') || friendlyMessage.includes('já existe')) {
|
||||
friendlyMessage = 'Este email já está cadastrado. Tente fazer login ou use outro email.';
|
||||
} else if (status === 400) {
|
||||
friendlyMessage = 'Dados inválidos. Verifique se todos os campos estão preenchidos corretamente.';
|
||||
} else if (status >= 500) {
|
||||
friendlyMessage = 'Erro interno do servidor. Tente novamente em alguns minutos.';
|
||||
}
|
||||
|
||||
// Se for um erro de rede
|
||||
if (error.name === 'TypeError' || error.message.includes('fetch')) {
|
||||
|
|
@ -321,7 +216,7 @@ const LoginPageContent = () => {
|
|||
friendlyMessage = "Erro inesperado durante o cadastro. Tente novamente.";
|
||||
}
|
||||
|
||||
setError(friendlyMessage);
|
||||
setError(translateError(friendlyMessage));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
|
@ -362,7 +257,6 @@ const LoginPageContent = () => {
|
|||
// Fazer logout para limpar dados do usuário
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('user');
|
||||
setAccessToken("");
|
||||
// Redirecionar para página inicial
|
||||
router.push('/');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import React, { createContext, useContext, useState, useEffect } from "react";
|
||||
import { UserRole } from "@/types/auth";
|
||||
import { authService } from "@/services/auth";
|
||||
|
||||
interface UserData {
|
||||
$id: string;
|
||||
|
|
@ -32,22 +33,14 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||
useEffect(() => {
|
||||
const checkAuth = async () => {
|
||||
try {
|
||||
// Verificação simples baseada em token
|
||||
const storedToken = localStorage.getItem('access_token');
|
||||
const userData = await authService.me();
|
||||
|
||||
if (storedToken) {
|
||||
if (userData) {
|
||||
setIsAuthenticated(true);
|
||||
const storedUser = localStorage.getItem('user');
|
||||
if (storedUser) {
|
||||
try {
|
||||
const parsedUser = JSON.parse(storedUser) as UserData;
|
||||
setUser(parsedUser);
|
||||
setUserRole(parsedUser.nivel ?? null);
|
||||
} catch (e) {
|
||||
console.error("Erro ao fazer parse do usuário:", e);
|
||||
// Opcional: tentar buscar da API se o parse falhar ou se não tiver no storage
|
||||
}
|
||||
}
|
||||
localStorage.setItem('user', JSON.stringify(userData));
|
||||
const parsedUser = userData as UserData;
|
||||
setUser(parsedUser);
|
||||
setUserRole(parsedUser.nivel ?? null);
|
||||
} else {
|
||||
setIsAuthenticated(false);
|
||||
setUser(null);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { authService } from '@/services/auth';
|
||||
import { UserRole, UserData, AuthContextType, ROLE_PERMISSIONS, ROLE_ROUTES } from '@/types/auth';
|
||||
|
||||
/**
|
||||
|
|
@ -90,10 +91,9 @@ export const useAuth = (): AuthContextType => {
|
|||
try {
|
||||
setLoading(true);
|
||||
|
||||
// Verificar se há token BFF armazenado
|
||||
const storedToken = localStorage.getItem('access_token');
|
||||
const userData = await authService.me();
|
||||
|
||||
if (!storedToken) {
|
||||
if (!userData) {
|
||||
setIsAuthenticated(false);
|
||||
setUser(null);
|
||||
setUserRole(null);
|
||||
|
|
@ -101,28 +101,15 @@ export const useAuth = (): AuthContextType => {
|
|||
return;
|
||||
}
|
||||
|
||||
// Marcar como autenticado se há token
|
||||
setIsAuthenticated(true);
|
||||
|
||||
const storedUser = localStorage.getItem('user');
|
||||
if (storedUser) {
|
||||
try {
|
||||
const parsedUser = JSON.parse(storedUser) as UserData;
|
||||
setUser(parsedUser);
|
||||
const storedRole =
|
||||
parsedUser?.nivel ||
|
||||
(parsedUser as unknown as { role?: string; userRole?: string })?.role ||
|
||||
(parsedUser as unknown as { role?: string; userRole?: string })?.userRole;
|
||||
setUserRole(normalizeRole(storedRole));
|
||||
} catch (parseError) {
|
||||
console.error('Erro ao ler usuário salvo:', parseError);
|
||||
setUser(null);
|
||||
setUserRole(null);
|
||||
}
|
||||
} else {
|
||||
setUser(null);
|
||||
setUserRole(null);
|
||||
}
|
||||
localStorage.setItem('user', JSON.stringify(userData));
|
||||
const parsedUser = userData as UserData;
|
||||
setUser(parsedUser);
|
||||
const storedRole =
|
||||
parsedUser?.nivel ||
|
||||
(parsedUser as unknown as { role?: string; userRole?: string })?.role ||
|
||||
(parsedUser as unknown as { role?: string; userRole?: string })?.userRole;
|
||||
setUserRole(normalizeRole(storedRole));
|
||||
|
||||
} catch (error) {
|
||||
console.error('Erro na verificação de autenticação:', error);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { authService } from "@/services/auth";
|
||||
|
||||
// Hook desabilitado - sempre permite acesso para usuários com token
|
||||
// Hook de autenticação usando sessão via cookie HttpOnly
|
||||
export const useAuthGuard = () => {
|
||||
const router = useRouter();
|
||||
const [user, setUser] = useState<any>(null);
|
||||
|
|
@ -14,18 +15,17 @@ export const useAuthGuard = () => {
|
|||
try {
|
||||
setAuthError(null);
|
||||
|
||||
// Verificação simples baseada em token
|
||||
const storedToken = localStorage.getItem('access_token');
|
||||
|
||||
if (!storedToken) {
|
||||
console.log("ℹ️ useAuthGuard: Nenhum token encontrado, redirecionando para login");
|
||||
const userData = await authService.me();
|
||||
|
||||
if (!userData) {
|
||||
console.log("ℹ️ useAuthGuard: Nenhuma sessão encontrada, redirecionando para login");
|
||||
router.push("/login");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("✅ useAuthGuard: Token encontrado, usuário autenticado");
|
||||
setUser({ email: 'usuario@exemplo.com' }); // Mock user
|
||||
setRegistroCompleto(true); // Sempre completo
|
||||
console.log("✅ useAuthGuard: Sessão encontrada, usuário autenticado");
|
||||
setUser(userData);
|
||||
setRegistroCompleto(userData["registro-completo"] !== false);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ useAuthGuard: Erro na verificação:', error);
|
||||
|
|
@ -40,4 +40,4 @@ export const useAuthGuard = () => {
|
|||
}, [router]);
|
||||
|
||||
return { user, loading, registroCompleto, authError };
|
||||
};
|
||||
};
|
||||
|
|
|
|||
90
saveinmed-frontend/src/services/auth.ts
Normal file
90
saveinmed-frontend/src/services/auth.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
export const GO_API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8214';
|
||||
export const GO_API_V1_BASE_URL = `${GO_API_BASE_URL}/api/v1`;
|
||||
|
||||
export interface AuthResponse {
|
||||
user?: Record<string, any>;
|
||||
access_token?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const AUTH_BASE_URL = `${GO_API_V1_BASE_URL}/auth`;
|
||||
|
||||
const buildError = async (response: Response) => {
|
||||
const message = await response.text();
|
||||
const error = new Error(message || response.statusText);
|
||||
(error as Error & { status?: number }).status = response.status;
|
||||
return error;
|
||||
};
|
||||
|
||||
export const authService = {
|
||||
login: async (email: string, password: string): Promise<AuthResponse> => {
|
||||
const response = await fetch(`${AUTH_BASE_URL}/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({ email, password }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw await buildError(response);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
register: async (payload: Record<string, any>): Promise<AuthResponse> => {
|
||||
const response = await fetch(`${AUTH_BASE_URL}/register`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw await buildError(response);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
me: async (): Promise<Record<string, any> | null> => {
|
||||
const response = await fetch(`${AUTH_BASE_URL}/me`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
accept: 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
if (response.status === 401) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw await buildError(response);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data.data || data;
|
||||
},
|
||||
|
||||
logout: async (): Promise<void> => {
|
||||
const response = await fetch(`${AUTH_BASE_URL}/logout`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
accept: 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
if (!response.ok && response.status !== 204) {
|
||||
throw await buildError(response);
|
||||
}
|
||||
},
|
||||
};
|
||||
Loading…
Reference in a new issue