Initial commit

This commit is contained in:
Markus Thielker 2024-03-08 20:24:40 +01:00 committed by GitHub
commit 52aed24a96
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 6980 additions and 0 deletions

40
src/lib/actions/signIn.ts Normal file
View file

@ -0,0 +1,40 @@
import { z } from 'zod';
import { Argon2id } from 'oslo/password';
import { lucia, prismaClient } from '@/auth';
import { cookies } from 'next/headers';
import { signInFormSchema } from '@/lib/form-schemas/signInFormSchema';
import { ActionResponse } from '@/lib/actions/types/ActionResponse';
import { URL_HOME } from '@/lib/constants';
export default async function signIn({username, password}: z.infer<typeof signInFormSchema>): Promise<ActionResponse> {
'use server';
const existingUser = await prismaClient.user.findFirst({
where: {
username: username.toLowerCase(),
},
});
if (!existingUser) {
return {
type: 'error',
message: 'Incorrect username or password',
};
}
const validPassword = await new Argon2id().verify(existingUser.password, password);
if (!validPassword) {
return {
type: 'error',
message: 'Incorrect username or password',
};
}
const session = await lucia.createSession(existingUser.id, {});
const sessionCookie = lucia.createSessionCookie(session.id);
cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
return {
type: 'success',
message: 'Signed in successfully',
redirect: URL_HOME,
};
}

View file

@ -0,0 +1,26 @@
import { getSession, lucia } from '@/auth';
import { cookies } from 'next/headers';
import { ActionResponse } from '@/lib/actions/types/ActionResponse';
import { URL_SIGN_IN } from '@/lib/constants';
export default async function signOut(): Promise<ActionResponse> {
'use server';
const session = await getSession();
if (!session) {
return {
type: 'error',
message: 'You aren\'t signed in',
};
}
await lucia.invalidateSession(session.id);
const sessionCookie = lucia.createBlankSessionCookie();
cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
return {
type: 'success',
message: 'Signed out successfully',
redirect: URL_SIGN_IN,
};
}

45
src/lib/actions/signUp.ts Normal file
View file

@ -0,0 +1,45 @@
import { z } from 'zod';
import { Argon2id } from 'oslo/password';
import { generateId } from 'lucia';
import { lucia, prismaClient } from '@/auth';
import { cookies } from 'next/headers';
import { signUpFormSchema } from '@/lib/form-schemas/signUpFormSchema';
import { ActionResponse } from '@/lib/actions/types/ActionResponse';
import { URL_HOME } from '@/lib/constants';
export default async function signUp({username, password}: z.infer<typeof signUpFormSchema>): Promise<ActionResponse> {
'use server';
const hashedPassword = await new Argon2id().hash(password);
const userId = generateId(15);
const existingUser = await prismaClient.user.findFirst({
where: {
username: username.toLowerCase(),
},
});
if (existingUser) {
return {
type: 'error',
message: 'Username already exists',
};
}
await prismaClient.user.create({
data: {
id: userId,
username: username,
password: hashedPassword,
},
});
const session = await lucia.createSession(userId, {});
const sessionCookie = lucia.createSessionCookie(session.id);
cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
return {
type: 'success',
message: 'Signed up successfully',
redirect: URL_HOME,
};
}

View file

@ -0,0 +1,5 @@
export interface ActionResponse {
type: 'success' | 'info' | 'warning' | 'error';
message: string;
redirect?: string;
}

8
src/lib/constants.ts Normal file
View file

@ -0,0 +1,8 @@
// auth urls
export const URL_AUTH = '/auth';
export const URL_SIGN_IN = `${URL_AUTH}/signin`;
export const URL_SIGN_UP = `${URL_AUTH}/signup`;
// main urls
export const URL_HOME = '/';
export const URL_ACCOUNT = '/account';

View file

@ -0,0 +1,6 @@
import { z } from 'zod';
export const signInFormSchema = z.object({
username: z.string().min(3).max(16),
password: z.string().min(8).max(255),
});

View file

@ -0,0 +1,10 @@
import { z } from 'zod';
export const signUpFormSchema = z.object({
username: z.string().min(3).max(16),
password: z.string().min(8).max(255),
confirm: z.string().min(8).max(255),
}).refine(data => data.password === data.confirm, {
message: 'Passwords do not match',
path: ['confirm'],
});

6
src/lib/utils.ts Normal file
View file

@ -0,0 +1,6 @@
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}