rework session work

This commit is contained in:
smanylov
2026-04-06 16:08:36 +07:00
parent e406833057
commit a301e57c8b
26 changed files with 869 additions and 37 deletions
+127
View File
@@ -0,0 +1,127 @@
import { redirect } from 'next/navigation';
import { createSession, deleteSession, getSessionData, updateSession } from '@/app/actions/session';
import { loginFormSchema, API_BASE_URL } from '@/app/actions/definitions';
export async function authorization(
state: {
previousState: {
email: string;
}
error: Record<string, string>
} | undefined,
formData: FormData,
) {
const email = formData.get('email') as string || '';
const password = formData.get('password');
/* для теста */
if (email === 'test' && password === 'test') {
await createSession('1111', 'test');
redirect('/pages/');
}
/* для теста */
const validatedFields = loginFormSchema.safeParse({
email,
password
})
if (!validatedFields.success) {
const errors: Record<string, string> = {}
JSON.parse(validatedFields.error.message).forEach((obj: {
message: string,
path: string
}) => {
if (errors[obj.path[0]]) {
errors[obj.path[0]] = `${errors[obj.path[0]]} ${obj.message}`;
} else {
errors[obj.path[0]] = obj.message;
}
});
return {
previousState: {
email
},
error: errors
};
}
try {
const { email, password } = validatedFields.data;
const response = await fetch(`${API_BASE_URL}/api/v1/data`, {
method: 'POST',
body: JSON.stringify({
version: 1,
msg_id: 20001,
message_body: {
email: email,
password: password
}
}),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
if (response.ok) {
let parsed = await response.json();
if (parsed.message_code === 0) {
await createSession(parsed.message_body.token, email);
} else {
throw (`${parsed.message_code}`);
}
} else {
throw ('request-ended-with-an-error');
}
} catch (error) {
const errors: Record<string, string> = {}
switch (error) {
case '2':
errors['server'] = 'password-does-not-match'
break;
case '4':
errors['server'] = 'email-not-found'
break;
default:
errors['server'] = 'request-ended-with-an-error'
break;
}
if (error) {
return {
previousState: {
email,
password
},
error: errors
};
}
throw error;
}
redirect('/pages/');
}
export async function logout() {
const token = await getSessionData('token');
try {
await fetch(`${API_BASE_URL}/v1/api/auth/logout`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': `Bearer ${token}`,
}
});
} catch (error) {
throw error;
} finally {
await deleteSession();
}
}
+19
View File
@@ -0,0 +1,19 @@
import * as z from 'zod'
export const loginFormSchema = z
.object({
email: z
.string()
.min(1, { error: 'login-error-email' })
.trim(),
password: z
.string()
.min(1, { error: 'login-error-password' })
.trim(),
})
export const localDevelopmentUrl = process.env.DEV_URL ? process.env.DEV_URL : 'http://localhost';
export const API_BASE_URL = process.env.NODE_ENV === 'development'
? localDevelopmentUrl
: 'http://app:8080';
+108
View File
@@ -0,0 +1,108 @@
'use server';
/* import 'server-only'; */
import { cookies } from 'next/headers'
import { SignJWT, jwtVerify } from 'jose'
import { redirect } from 'next/navigation';
/* const secretKey = process.env.SESSION_SECRET */
const secretKey = 'AAA+sq1yKte/gMgUUu/B/OyXqr45/LMYplPVOlWc+uo='
const encodedKey = new TextEncoder().encode(secretKey)
export async function encrypt(payload: any) {
return new SignJWT(payload)
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('7d')
.sign(encodedKey)
}
export async function decrypt(session: string | undefined = '') {
try {
const { payload } = await jwtVerify(session, encodedKey, {
algorithms: ['HS256'],
})
return payload
} catch (error) {
console.log('Failed to verify session')
}
}
export async function createSession(token: string, email: string) {
const expiresAt = new Date(Date.now() + 50 * 60 * 1000);
const session = await encrypt({
token,
email,
expiresAt
});
const cookieStore = await cookies();
cookieStore.set('session', session, {
httpOnly: true,
secure: false,
expires: expiresAt,
sameSite: 'lax',
path: '/',
});
}
export async function updateSession(newToken: string) {
const cookieStore = await cookies();
const session = cookieStore.get('session')?.value;
if (!session) {
return null;
}
const payload = await decrypt(session);
if (!payload) {
return null;
}
const updatedPayload = {
...payload,
token: newToken,
expiresAt: new Date(Date.now() + 50 * 60 * 1000)
};
const updatedSession = await encrypt(updatedPayload);
cookieStore.set('session', updatedSession, {
httpOnly: true,
secure: false,
expires: updatedPayload.expiresAt,
sameSite: 'lax',
path: '/',
});
}
export async function getSessionData(type: 'token' | 'email'): Promise<string | null> {
const cookieStore = await cookies();
const sessionCookie = cookieStore.get('session')?.value;
if (!sessionCookie) {
return null;
}
const payload = await decrypt(sessionCookie);
if (payload && typeof payload === 'object' && type in payload) {
return payload[type] as string;
}
return null;
}
export async function deleteSession() {
console.log('delete session')
try {
const cookieStore = await cookies()
cookieStore.delete('session');
} catch (error) {
} finally {
redirect('/login');
}
}