mirror of
https://github.com/Drop-OSS/drop.git
synced 2026-07-13 22:26:43 +10:00
initial commit
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import { AuthMec } from "@prisma/client";
|
||||
import { JsonArray } from "@prisma/client/runtime/library";
|
||||
import prisma from "~/server/internal/db/database";
|
||||
import { checkHash } from "~/server/internal/security/simple";
|
||||
|
||||
export default defineEventHandler(async (h3) => {
|
||||
const body = await readBody(h3);
|
||||
|
||||
const username = body.username;
|
||||
const password = body.password;
|
||||
if (username === undefined || password === undefined)
|
||||
throw createError({ statusCode: 403, statusMessage: "Username or password missing from request." });
|
||||
|
||||
const authMek = await prisma.linkedAuthMec.findFirst({
|
||||
where: {
|
||||
mec: AuthMec.Simple,
|
||||
credentials: {
|
||||
array_starts_with: username
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!authMek) throw createError({ statusCode: 401, statusMessage: "Invalid username or password." });
|
||||
|
||||
const credentials = authMek.credentials as JsonArray;
|
||||
const hash = credentials.at(1);
|
||||
|
||||
if (!hash) throw createError({ statusCode: 403, statusMessage: "Invalid or disabled account. Please contact the server administrator." });
|
||||
|
||||
if (!await checkHash(password, hash.toString()))
|
||||
throw createError({ statusCode: 401, statusMessage: "Invalid username or password." });
|
||||
|
||||
await h3.context.session.setUserId(h3, authMek.userId);
|
||||
|
||||
return { result: true, userId: authMek.userId }
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
import { AuthMec } from "@prisma/client";
|
||||
import prisma from "~/server/internal/db/database";
|
||||
import { createHash } from "~/server/internal/security/simple";
|
||||
|
||||
export default defineEventHandler(async (h3) => {
|
||||
const body = await readBody(h3);
|
||||
|
||||
const username = body.username;
|
||||
const password = body.password;
|
||||
if (username === undefined || password === undefined)
|
||||
throw createError({ statusCode: 403, statusMessage: "Username or password missing from request." });
|
||||
|
||||
const existing = await prisma.user.count({ where: { username: username } });
|
||||
if (existing > 0) throw createError({ statusCode: 400, statusMessage: "Username already taken." })
|
||||
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
username,
|
||||
}
|
||||
});
|
||||
|
||||
const hash = await createHash(password);
|
||||
const authMek = await prisma.linkedAuthMec.create({
|
||||
data: {
|
||||
mec: AuthMec.Simple,
|
||||
credentials: [username, hash],
|
||||
userId: user.id
|
||||
}
|
||||
});
|
||||
|
||||
return user;
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
export default defineEventHandler(async (h3) => {
|
||||
const user = await h3.context.session.getUser(h3);
|
||||
|
||||
return user ?? {};
|
||||
});
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
import { SessionHandler } from "./internal/session";
|
||||
|
||||
export * from "h3";
|
||||
declare module "h3" {
|
||||
interface H3EventContext {
|
||||
session: SessionHandler
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
const prismaClientSingleton = () => {
|
||||
return new PrismaClient()
|
||||
}
|
||||
|
||||
declare const globalThis: {
|
||||
prismaGlobal: ReturnType<typeof prismaClientSingleton>;
|
||||
} & typeof global;
|
||||
|
||||
const prisma = globalThis.prismaGlobal ?? prismaClientSingleton()
|
||||
|
||||
export default prisma
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma
|
||||
@@ -0,0 +1,11 @@
|
||||
import bcrypt from 'bcrypt';
|
||||
|
||||
const rounds = 10;
|
||||
|
||||
export async function createHash(password: string) {
|
||||
return bcrypt.hashSync(password, rounds);
|
||||
}
|
||||
|
||||
export async function checkHash(password: string, hash: string) {
|
||||
return bcrypt.compareSync(password, hash);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import { H3Event, Session } from "h3";
|
||||
import createMemorySessionProvider from "./memory";
|
||||
import { SessionProvider } from "./types";
|
||||
import prisma from "../db/database";
|
||||
|
||||
/*
|
||||
This is a poorly organised implemention.
|
||||
|
||||
It exposes an API that should stay static, but there are plenty of opportunities for optimisation/organisation under the hood
|
||||
*/
|
||||
|
||||
const userSessionKey = "_userSession";
|
||||
const userIdKey = "_userId";
|
||||
|
||||
export class SessionHandler {
|
||||
private sessionProvider: SessionProvider;
|
||||
|
||||
constructor() {
|
||||
// Create a new provider
|
||||
this.sessionProvider = createMemorySessionProvider();
|
||||
}
|
||||
|
||||
async getSession<T extends Session>(h3: H3Event) {
|
||||
const data = await this.sessionProvider.getSession<{ [userSessionKey]: T }>(h3);
|
||||
if (!data) return undefined;
|
||||
|
||||
return data[userSessionKey];
|
||||
}
|
||||
async setSession(h3: H3Event, data: any) {
|
||||
const result = await this.sessionProvider.updateSession(h3, userSessionKey, data);
|
||||
if (!result) {
|
||||
const toCreate = { [userSessionKey]: data };
|
||||
await this.sessionProvider.setSession(h3, toCreate);
|
||||
}
|
||||
}
|
||||
async clearSession(h3: H3Event) {
|
||||
await this.sessionProvider.clearSession(h3);
|
||||
}
|
||||
|
||||
async getUserId(h3: H3Event) {
|
||||
const session = await this.sessionProvider.getSession<{ [userIdKey]: string | undefined }>(h3);
|
||||
if (!session) return undefined;
|
||||
|
||||
return session[userIdKey];
|
||||
}
|
||||
|
||||
async getUser(h3: H3Event) {
|
||||
const userId = await this.getUserId(h3);
|
||||
if (!userId) return undefined;
|
||||
|
||||
const user = await prisma.user.findFirst({ where: { id: userId } });
|
||||
return user;
|
||||
}
|
||||
|
||||
async setUserId(h3: H3Event, userId: string) {
|
||||
const result = await this.sessionProvider.updateSession(h3, userIdKey, userId);
|
||||
if (!result) {
|
||||
const toCreate = { [userIdKey]: userId };
|
||||
await this.sessionProvider.setSession(h3, toCreate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new SessionHandler();
|
||||
@@ -0,0 +1,45 @@
|
||||
import moment from "moment";
|
||||
import { Session, SessionProvider } from "./types";
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export default function createMemorySessionHandler() {
|
||||
const sessions: { [key: string]: Session } = {}
|
||||
|
||||
const sessionCookieName = "drop-session";
|
||||
|
||||
const memoryProvider: SessionProvider = {
|
||||
async setSession(h3, data) {
|
||||
const existingCookie = getCookie(h3, sessionCookieName);
|
||||
if (existingCookie) delete sessions[existingCookie]; // Clear any previous session
|
||||
|
||||
const cookie = uuidv4();
|
||||
const expiry = moment().add(31, 'day');
|
||||
setCookie(h3, sessionCookieName, cookie, { expires: expiry.toDate() });
|
||||
|
||||
sessions[cookie] = data;
|
||||
return true;
|
||||
},
|
||||
async updateSession(h3, key, data) {
|
||||
const cookie = getCookie(h3, sessionCookieName);
|
||||
if (!cookie) return false;
|
||||
|
||||
sessions[cookie] = Object.assign({}, sessions[cookie], { [key]: data });
|
||||
return true;
|
||||
},
|
||||
async getSession(h3) {
|
||||
const cookie = getCookie(h3, sessionCookieName);
|
||||
if (!cookie) return undefined;
|
||||
|
||||
return sessions[cookie] as any; // Wild type cast because we let the user specify types if they want
|
||||
},
|
||||
async clearSession(h3) {
|
||||
const cookie = getCookie(h3, sessionCookieName);
|
||||
if (!cookie) return;
|
||||
|
||||
delete sessions[cookie];
|
||||
deleteCookie(h3, sessionCookieName);
|
||||
},
|
||||
};
|
||||
|
||||
return memoryProvider;
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { H3Event } from "h3";
|
||||
|
||||
export type Session = { [key: string]: any };
|
||||
|
||||
export interface SessionProvider {
|
||||
setSession: (h3: H3Event, data: Session) => Promise<boolean>;
|
||||
updateSession: (h3: H3Event, key: string, data: any) => Promise<boolean>;
|
||||
getSession: <T extends Session>(h3: H3Event) => Promise<T | undefined>;
|
||||
clearSession: (h3: H3Event) => Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import session from "../internal/session";
|
||||
|
||||
export default defineNitroPlugin((nitro) => {
|
||||
nitro.hooks.hook('request', (h3) => {
|
||||
h3.context.session = session;
|
||||
})
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../.nuxt/tsconfig.server.json"
|
||||
}
|
||||
Reference in New Issue
Block a user