Compare commits
No commits in common. "1d8220d92c27802bc0b5ef858cef4ca786b9276b" and "500a9ad59505c7b9d005b36e5f0c4b59d390332c" have entirely different histories.
1d8220d92c
...
500a9ad595
8 changed files with 19 additions and 75 deletions
|
@ -7,13 +7,14 @@ type optionalMap<Optional> = Optional extends true ? undefined : string | number
|
||||||
export type fieldDefinition<Optional extends boolean = boolean> = {
|
export type fieldDefinition<Optional extends boolean = boolean> = {
|
||||||
key: string,
|
key: string,
|
||||||
label?: string,
|
label?: string,
|
||||||
type: "text" | "password" | "number",
|
type: "text" | "number",
|
||||||
optional?: Optional,
|
optional?: Optional,
|
||||||
value?: optionalMap<Optional>,
|
value?: optionalMap<Optional>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
fields: Array<fieldDefinition>,
|
fields: Array<fieldDefinition>,
|
||||||
|
modelValue?: any,
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// eslint-disable-next-line func-call-spacing
|
// eslint-disable-next-line func-call-spacing
|
||||||
|
|
|
@ -9,7 +9,7 @@ import Alerts, { type AlertData } from '~/components/alerts.vue';
|
||||||
|
|
||||||
const editorFields: Array<fieldDefinition> = [
|
const editorFields: Array<fieldDefinition> = [
|
||||||
{ key: "username", type: "text", label: "Username", optional: false },
|
{ key: "username", type: "text", label: "Username", optional: false },
|
||||||
{ key: "password", type: "password", label: "Password", optional: false },
|
{ key: "password", type: "text", label: "Password", optional: false },
|
||||||
{ key: "email", type: "text", label: "email", optional: false },
|
{ key: "email", type: "text", label: "email", optional: false },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -20,14 +20,12 @@ model User {
|
||||||
}
|
}
|
||||||
|
|
||||||
model Session {
|
model Session {
|
||||||
id BigInt @id @default(dbgenerated("(((unix_timestamp() * 1000) * pow(2,22)) + floor((rand() * pow(2,12))))")) @db.UnsignedBigInt
|
id BigInt @id @default(dbgenerated("(((unix_timestamp() * 1000) * pow(2,22)) + floor((rand() * pow(2,12))))")) @db.UnsignedBigInt
|
||||||
userId BigInt @map("user") @db.UnsignedBigInt
|
userId BigInt @map("user") @db.UnsignedBigInt
|
||||||
sessionToken Bytes @db.Binary(64)
|
expiry_date DateTime? @default(dbgenerated("(now() + interval 30 day)")) @db.Timestamp(0)
|
||||||
expiry_date DateTime? @default(dbgenerated("(now() + interval 30 day)")) @db.Timestamp(0)
|
user User @relation(fields: [userId], references: [id])
|
||||||
user User @relation(fields: [userId], references: [id])
|
|
||||||
|
|
||||||
@@index([userId], map: "user_idx")
|
@@index([userId], map: "user_idx")
|
||||||
@@index([sessionToken])
|
|
||||||
@@map("sessions")
|
@@map("sessions")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ export default defineEventHandler(async (e) => {
|
||||||
if (typeof email !== "string") throw createError({ message: "email is not string", statusCode: 400 });
|
if (typeof email !== "string") throw createError({ message: "email is not string", statusCode: 400 });
|
||||||
|
|
||||||
execSync("npx prisma db push --force-reset");
|
execSync("npx prisma db push --force-reset");
|
||||||
await database.user.create({
|
database.user.create({
|
||||||
data: {
|
data: {
|
||||||
id: new Snowflake().state,
|
id: new Snowflake().state,
|
||||||
username,
|
username,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { defineEventHandler, getCookie, setCookie, readBody } from "h3";
|
||||||
import { database } from "../utils/database";
|
import { database } from "../utils/database";
|
||||||
import { isString } from "../utils/isString";
|
import { isString } from "../utils/isString";
|
||||||
import { cookieSettings } from "../utils/rootUtils";
|
import { cookieSettings } from "../utils/rootUtils";
|
||||||
import SessionToken from "../utils/SessionToken";
|
import Snowflake from "~/utils/snowflake";
|
||||||
|
|
||||||
import { createError } from "#imports";
|
import { createError } from "#imports";
|
||||||
|
|
||||||
|
@ -40,11 +40,14 @@ export default defineEventHandler(async (e) => {
|
||||||
|
|
||||||
if (account === null) throw createError({ statusCode: 400, message: "Invalid username or password." });
|
if (account === null) throw createError({ statusCode: 400, message: "Invalid username or password." });
|
||||||
|
|
||||||
const session = new SessionToken(account.id);
|
const sessionId = new Snowflake();
|
||||||
|
|
||||||
await database.session.create({
|
await database.session.create({
|
||||||
data: session.toPrisma(),
|
data: {
|
||||||
|
id: sessionId.state,
|
||||||
|
userId: account.id,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
setCookie(e, "token", session.toString(), cookieSettings);
|
setCookie(e, "token", sessionId.toString(), cookieSettings);
|
||||||
return { message: "Login successful", token: session.toString() };
|
return { message: "Login successful", token: sessionId.toString() };
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import { defineEventHandler, getCookie } from "h3";
|
import { defineEventHandler, getCookie } from "h3";
|
||||||
import SessionToken from "../utils/SessionToken";
|
import { createError } from "#imports";
|
||||||
|
|
||||||
import { database } from "~/server/utils/database";
|
import { database } from "~/server/utils/database";
|
||||||
import getRequestingUser from "~/server/utils/getRequestingUser";
|
import getRequestingUser from "~/server/utils/getRequestingUser";
|
||||||
|
|
||||||
import { createError } from "#imports";
|
|
||||||
|
|
||||||
const endpointsWithoutAuth: string[] = [
|
const endpointsWithoutAuth: string[] = [
|
||||||
"/dbtest",
|
"/dbtest",
|
||||||
"/echo",
|
"/echo",
|
||||||
|
@ -39,10 +37,7 @@ export async function isAuthorised(token: string | undefined): Promise<boolean>
|
||||||
try {
|
try {
|
||||||
await database.session.findUniqueOrThrow({
|
await database.session.findUniqueOrThrow({
|
||||||
where: {
|
where: {
|
||||||
...SessionToken.fromString(token).toPrisma(),
|
id: BigInt(token),
|
||||||
expiry_date: {
|
|
||||||
gte: new Date(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
import crypto from "node:crypto";
|
|
||||||
import { type Session } from "@prisma/client";
|
|
||||||
|
|
||||||
import Snowflake from "~/utils/snowflake";
|
|
||||||
|
|
||||||
/** Represents a Session token, without expiry data. */
|
|
||||||
export default class SessionToken {
|
|
||||||
userId: bigint;
|
|
||||||
sessionId: bigint;
|
|
||||||
sessionToken: Buffer;
|
|
||||||
|
|
||||||
constructor(userId: bigint, sessionId?: bigint, sessionToken?: Buffer) {
|
|
||||||
this.userId = userId;
|
|
||||||
this.sessionId = sessionId ?? new Snowflake().state;
|
|
||||||
this.sessionToken = sessionToken ?? crypto.randomBytes(64);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Creates SessionToken from a string.
|
|
||||||
* @param string The strinct to create from.
|
|
||||||
* @returns The SessionToken object.
|
|
||||||
*/
|
|
||||||
static fromString(string: string): SessionToken {
|
|
||||||
const parameters = string.split(".");
|
|
||||||
return new SessionToken(
|
|
||||||
Buffer.from(parameters[0], "base64").readBigUInt64LE(),
|
|
||||||
Buffer.from(parameters[1], "base64").readBigUInt64LE(),
|
|
||||||
Buffer.from(parameters[2], "base64"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toString(): string {
|
|
||||||
const stringUserId = Buffer.copyBytesFrom(new BigUint64Array([this.userId])).toString("base64");
|
|
||||||
const stringSessionId = Buffer.copyBytesFrom(new BigUint64Array([this.sessionId])).toString("base64");
|
|
||||||
const stringSessionToken = this.sessionToken.toString("base64");
|
|
||||||
return `${stringUserId}.${stringSessionId}.${stringSessionToken}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns this SessionToken as Prisma object.
|
|
||||||
* For use in where parameter.
|
|
||||||
* @returns this as prisma object.
|
|
||||||
*/
|
|
||||||
toPrisma(): Omit<Session, "expiry_date"> {
|
|
||||||
return {
|
|
||||||
id: this.sessionId,
|
|
||||||
userId: this.userId,
|
|
||||||
sessionToken: this.sessionToken,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { getCookie, H3Event } from "h3";
|
import { getCookie, H3Event } from "h3";
|
||||||
|
|
||||||
import { database } from "./database";
|
import { database } from "./database";
|
||||||
import SessionToken from "./SessionToken";
|
|
||||||
|
|
||||||
import { createError } from "#imports";
|
import { createError } from "#imports";
|
||||||
|
|
||||||
|
@ -10,10 +9,7 @@ export default async function getRequestingUser(e: H3Event) {
|
||||||
if (!cookie) throw createError("User not found");
|
if (!cookie) throw createError("User not found");
|
||||||
const { user } = await database.session.findUnique({
|
const { user } = await database.session.findUnique({
|
||||||
where: {
|
where: {
|
||||||
...SessionToken.fromString(cookie).toPrisma(),
|
id: BigInt(cookie),
|
||||||
expiry_date: {
|
|
||||||
gte: new Date(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
user: {
|
user: {
|
||||||
|
|
Loading…
Add table
Reference in a new issue