78 |
79 | *1
80 | = 1 gambar
81 |
82 |
83 | **Belum termasuk biaya admin
84 |
85 |
***Non-akumulatif
86 |
87 |
--------------------------------------------------------------------------------
/src/lib/server/auth.ts:
--------------------------------------------------------------------------------
1 | import { db } from '$lib/server/db';
2 | import * as table from '$lib/server/db/schema';
3 | import { sha256 } from '@oslojs/crypto/sha2';
4 | import { encodeBase64url, encodeHexLowerCase } from '@oslojs/encoding';
5 | import type { RequestEvent } from '@sveltejs/kit';
6 | import { withCatch } from '@tfkhdyt/with-catch';
7 | import { eq } from 'drizzle-orm';
8 |
9 | const DAY_IN_MS = 1000 * 60 * 60 * 24;
10 |
11 | export const sessionCookieName = 'auth-session';
12 |
13 | export function generateSessionToken() {
14 | const bytes = crypto.getRandomValues(new Uint8Array(18));
15 | const token = encodeBase64url(bytes);
16 | return token;
17 | }
18 |
19 | export async function createSession(token: string, userId: number) {
20 | const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
21 | const session: table.Session = {
22 | id: sessionId,
23 | userId,
24 | expiresAt: new Date(Date.now() + DAY_IN_MS * 30)
25 | };
26 |
27 | const [err] = await withCatch(db.insert(table.session).values(session));
28 | if (err) {
29 | throw new Error('Error creating session', { cause: err });
30 | }
31 |
32 | return session;
33 | }
34 |
35 | export async function validateSessionToken(token: string) {
36 | const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
37 |
38 | const [result] = await db
39 | .select({
40 | user: {
41 | id: table.user.id,
42 | name: table.user.name,
43 | email: table.user.email,
44 | picture: table.user.picture,
45 | creditsAmount: table.credits.amount
46 | },
47 | session: table.session
48 | })
49 | .from(table.session)
50 | .innerJoin(table.user, eq(table.session.userId, table.user.id))
51 | .leftJoin(table.credits, eq(table.user.id, table.credits.id))
52 | .where(eq(table.session.id, sessionId));
53 |
54 | if (!result) {
55 | return { session: null, user: null };
56 | }
57 | const { session, user } = result;
58 |
59 | const sessionExpired = Date.now() >= session.expiresAt.getTime();
60 | if (sessionExpired) {
61 | const [err] = await withCatch(db.delete(table.session).where(eq(table.session.id, session.id)));
62 | if (err) {
63 | throw new Error('Error deleting session', { cause: err });
64 | }
65 |
66 | return { session: null, user: null };
67 | }
68 |
69 | const renewSession = Date.now() >= session.expiresAt.getTime() - DAY_IN_MS * 15;
70 | if (renewSession) {
71 | session.expiresAt = new Date(Date.now() + DAY_IN_MS * 30);
72 |
73 | const [err] = await withCatch(
74 | db
75 | .update(table.session)
76 | .set({ expiresAt: session.expiresAt })
77 | .where(eq(table.session.id, session.id))
78 | );
79 | if (err) {
80 | throw new Error('Error updating session', { cause: err });
81 | }
82 | }
83 |
84 | return { session, user };
85 | }
86 |
87 | export type SessionValidationResult = Awaited