diff --git a/.gitignore b/.gitignore index f1c3c03..d23b738 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,6 @@ .env.local data/ +data-dev/ _fresh/ node_modules/ diff --git a/README.md b/README.md index ec0e33e..844e5b2 100644 --- a/README.md +++ b/README.md @@ -14,3 +14,9 @@ deno task start ``` This will watch the project directory and restart as necessary. + +## FIX + +``` +sed -i -e 's/"deno"/"no-deno"/' node_modules/@libsql/client/package.json +``` diff --git a/deno.json b/deno.json index ddaba23..56634aa 100644 --- a/deno.json +++ b/deno.json @@ -3,7 +3,8 @@ "nodeModulesDir": "auto", "tasks": { "check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx", - "start": "deno run -A --watch=static/,routes/ dev.ts", + "start": "deno run --env-file -A --watch=static/,routes/ dev.ts", + "db": "deno run --env-file -A npm:drizzle-kit", "build": "deno run -A dev.ts build", "preview": "deno run -A main.ts", "update": "deno run -A -r https://fresh.deno.dev/update ." @@ -25,11 +26,13 @@ "@islands/": "./islands/", "@lib": "./lib", "@lib/": "./lib/", + "@libsql/client": "npm:@libsql/client@^0.14.0", "@preact/signals": "https://esm.sh/*@preact/signals@1.2.2", "@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.5.1", - "@std/dotenv": "jsr:@std/dotenv@^0.225.3", "@std/http": "jsr:@std/http@^1.0.12", "@std/yaml": "jsr:@std/yaml@^1.0.5", + "drizzle-kit": "npm:drizzle-kit@^0.30.1", + "drizzle-orm": "npm:drizzle-orm@^0.38.3", "preact": "https://esm.sh/preact@10.22.0", "preact-render-to-string": "https://esm.sh/*preact-render-to-string@6.2.2", "preact/": "https://esm.sh/preact@10.22.0/", @@ -38,9 +41,12 @@ "tailwindcss/": "npm:/tailwindcss@^3.4.17/", "tailwindcss/plugin": "npm:/tailwindcss@^3.4.17/plugin.js", "camelcase-css": "npm:camelcase-css", + "tsx": "npm:tsx@^4.19.2", "typesense": "https://raw.githubusercontent.com/bradenmacdonald/typesense-deno/main/mod.ts", "yaml": "https://deno.land/std@0.197.0/yaml/mod.ts", - "zod": "https://deno.land/x/zod@v3.21.4/mod.ts" + "zod": "https://deno.land/x/zod@v3.21.4/mod.ts", + "fs": "https://deno.land/std/fs/mod.ts", + "imagemagick": "https://deno.land/x/imagemagick_deno@0.0.31/mod.ts" }, "scopes": { "https://deno.land/x/emoji/": { diff --git a/drizzle.config.ts b/drizzle.config.ts new file mode 100644 index 0000000..09c1029 --- /dev/null +++ b/drizzle.config.ts @@ -0,0 +1,18 @@ +import path from "node:path"; +import { defineConfig } from "drizzle-kit"; + +const DATA_DIR = Deno.env.has("DATA_DIR") + ? path.resolve(Deno.env.get("DATA_DIR")!) + : path.resolve(Deno.cwd(), "data"); + +// const DB_FILE = "file://" + path.resolve(DATA_DIR, "db.sqlite"); +const DB_FILE = "file:data-dev/db.sqlite"; + +export default defineConfig({ + out: "./drizzle", + schema: "./lib/sqlite/schema.ts", + dialect: "turso", + dbCredentials: { + url: DB_FILE, + }, +}); diff --git a/drizzle/0000_dashing_sunspot.sql b/drizzle/0000_dashing_sunspot.sql new file mode 100644 index 0000000..a42c6ad --- /dev/null +++ b/drizzle/0000_dashing_sunspot.sql @@ -0,0 +1,20 @@ +CREATE TABLE `performance` ( + `path` text NOT NULL, + `search` text, + `time` integer NOT NULL, + `created_at` integer DEFAULT (current_timestamp) +); +--> statement-breakpoint +CREATE TABLE `session` ( + `id` text PRIMARY KEY NOT NULL, + `created_at` integer DEFAULT (current_timestamp), + `expires_at` integer NOT NULL, + `user_id` text NOT NULL +); +--> statement-breakpoint +CREATE TABLE `user` ( + `id` text PRIMARY KEY NOT NULL, + `created_at` integer DEFAULT (current_timestamp) NOT NULL, + `email` text NOT NULL, + `name` text NOT NULL +); diff --git a/drizzle/0001_classy_justin_hammer.sql b/drizzle/0001_classy_justin_hammer.sql new file mode 100644 index 0000000..73e05a3 --- /dev/null +++ b/drizzle/0001_classy_justin_hammer.sql @@ -0,0 +1 @@ +ALTER TABLE `performance` ALTER COLUMN "created_at" TO "created_at" integer DEFAULT (STRFTIME('%s', 'now') * 1000); \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json new file mode 100644 index 0000000..b3ac4dc --- /dev/null +++ b/drizzle/meta/0000_snapshot.json @@ -0,0 +1,135 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "49f64916-b687-40c5-a37f-de031c1641f1", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "performance": { + "name": "performance", + "columns": { + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "search": { + "name": "search", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "time": { + "name": "time", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(current_timestamp)" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session": { + "name": "session", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0001_snapshot.json b/drizzle/meta/0001_snapshot.json new file mode 100644 index 0000000..34018bf --- /dev/null +++ b/drizzle/meta/0001_snapshot.json @@ -0,0 +1,135 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "3890bdbe-0c06-4619-a35f-e1380ac8f42f", + "prevId": "49f64916-b687-40c5-a37f-de031c1641f1", + "tables": { + "performance": { + "name": "performance", + "columns": { + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "search": { + "name": "search", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "time": { + "name": "time", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(STRFTIME('%s', 'now') * 1000)" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session": { + "name": "session", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json new file mode 100644 index 0000000..c104ad8 --- /dev/null +++ b/drizzle/meta/_journal.json @@ -0,0 +1,20 @@ +{ + "version": "7", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "6", + "when": 1736093081790, + "tag": "0000_dashing_sunspot", + "breakpoints": true + }, + { + "idx": 1, + "version": "6", + "when": 1736093851600, + "tag": "0001_classy_justin_hammer", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/lib/cache/logs.ts b/lib/cache/logs.ts deleted file mode 100644 index 276287c..0000000 --- a/lib/cache/logs.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { createLogger } from "@lib/log.ts"; -import * as cache from "@lib/cache/cache.ts"; -import { getTimeCacheKey, parseTimeCacheKey } from "@lib/string.ts"; - -const log = createLogger(""); -log.addEventListener("log", (data) => { - cache.set(`log:${getTimeCacheKey()}`, JSON.stringify(data.detail), { - noLog: true, - }); -}); - -export type Log = { - scope: string; - level: number; - date: Date; - args: unknown[]; -}; - -export async function getLogs() { - const d = new Date(); - const year = d.getFullYear(); - const month = d.getMonth().toString(); - const day = d.getDate().toString(); - - const keys = await cache.keys( - `log:${year}:${month}:${day}:*`, - ); - - const logs = await Promise.all( - keys.map(async (key) => { - const date = parseTimeCacheKey(key); - return { ...JSON.parse(await cache.get(key)), date } as Log; - }), - ); - - return logs.sort((a, b) => a.date.getTime() > b.date.getTime() ? -1 : 1); -} diff --git a/lib/db.ts b/lib/db.ts deleted file mode 100644 index 754c7a2..0000000 --- a/lib/db.ts +++ /dev/null @@ -1,18 +0,0 @@ -import z from "https://deno.land/x/zod@v3.21.4/index.ts"; -import { createSchema } from "@lib/db/createSchema.ts"; - -const UserSchema = z.object({ - id: z.string().optional().default(() => crypto.randomUUID()), - createdAt: z.date().default(() => new Date()), - email: z.string().email(), - name: z.string(), -}); -export const userDB = createSchema("user", UserSchema); - -const SessionSchema = z.object({ - id: z.string().default(() => crypto.randomUUID()), - createdAt: z.date().default(() => new Date()), - expiresAt: z.date().default(() => new Date()), - userId: z.string(), -}); -export const sessionDB = createSchema("session", SessionSchema); diff --git a/lib/db/createSchema.ts b/lib/db/createSchema.ts deleted file mode 100644 index 5db35a8..0000000 --- a/lib/db/createSchema.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { z } from "zod"; -import * as cache from "@lib/cache/cache.ts"; - -export function createSchema(name: string, schema: T) { - type Data = z.infer; - return { - async create(input: Omit): Promise { - const data = schema.safeParse(input); - if (data.success) { - const d = data.data; - const id = d["id"]; - if (!id) return d; - await cache.set(`${name}:${id}`, JSON.stringify(d)); - return d; - } - return null; - }, - async findAll(): Promise { - const keys = await cache.keys(`${name}:*`); - return Promise.all(keys.map((k) => { - return cache.get(k); - })).then((values) => values.map((v) => JSON.parse(v || "null"))); - }, - async find(id: string) { - const k = await cache.get(`${name}:${id}`); - return JSON.parse(k || "null") as Data | null; - }, - delete(id: string) { - return cache.del(`${name}:${id}`); - }, - }; -} diff --git a/lib/env.ts b/lib/env.ts index 5f72e8e..409cff6 100644 --- a/lib/env.ts +++ b/lib/env.ts @@ -1,4 +1,4 @@ -import "@std/dotenv/load"; +import path from "node:path"; export const SILVERBULLET_SERVER = Deno.env.get("SILVERBULLET_SERVER"); export const REDIS_HOST = Deno.env.get("REDIS_HOST"); @@ -8,7 +8,10 @@ export const OPENAI_API_KEY = Deno.env.get("OPENAI_API_KEY"); export const YOUTUBE_API_KEY = Deno.env.get("YOUTUBE_API_KEY"); export const GITEA_SERVER = Deno.env.get("GITEA_SERVER"); -export const GITEA_CLIENT_ID = Deno.env.get("GITEA_CLIENT_ID"); +export const GITEA_CLIENT_ID = Deno.env.get("GITEA_CLIENT_ID")!; +if (!GITEA_CLIENT_ID) { + throw new Error("GITEA_CLIENT_ID is required"); +} export const GITEA_CLIENT_SECRET = Deno.env.get("GITEA_CLIENT_SECRET"); export const GITEA_REDIRECT_URL = Deno.env.get("GITEA_REDIRECT_URL"); @@ -23,5 +26,9 @@ export const TYPESENSE_URL = Deno.env.get("TYPESENSE_URL") || "http://localhost:8108"; export const TYPESENSE_API_KEY = Deno.env.get("TYPESENSE_API_KEY"); +export const DATA_DIR = Deno.env.has("DATA_DIR") + ? path.resolve(Deno.env.get("DATA_DIR")!) + : path.resolve(Deno.cwd(), "data"); + export const LOG_LEVEL: string = Deno.env.get("LOG_LEVEL") || "warn"; diff --git a/lib/errors.ts b/lib/errors.ts index 0ef2f75..cab221b 100644 --- a/lib/errors.ts +++ b/lib/errors.ts @@ -1,30 +1,30 @@ -import { MiddlewareHandlerContext } from "$fresh/server.ts"; +import { FreshContext } from "$fresh/server.ts"; class DomainError extends Error { status = 500; - render?: (ctx: MiddlewareHandlerContext) => void; + render?: (ctx: FreshContext) => void; constructor(public statusText = "Internal Server Error") { super(); } } class NotFoundError extends DomainError { - status = 404; - constructor(public statusText = "Not Found") { + override status = 404; + constructor(public override statusText = "Not Found") { super(); } } class BadRequestError extends DomainError { - status = 400; - constructor(public statusText = "Bad Request") { + override status = 400; + constructor(public override statusText = "Bad Request") { super(); } } class AccessDeniedError extends DomainError { - status = 403; - constructor(public statusText = "Access Denied") { + override status = 403; + constructor(public override statusText = "Access Denied") { super(); } } diff --git a/lib/hooks/useEventListener.ts b/lib/hooks/useEventListener.ts index 2799729..16d37bd 100644 --- a/lib/hooks/useEventListener.ts +++ b/lib/hooks/useEventListener.ts @@ -3,7 +3,7 @@ import { useEffect, useRef } from "preact/hooks"; export function useEventListener( eventName: string, handler: (event: T) => void, - element: Window | HTMLElement = globalThis, + element: typeof globalThis | HTMLElement = globalThis, ) { // Create a ref that stores handler const savedHandler = useRef<(event: Event) => void>(); diff --git a/lib/log.ts b/lib/log.ts index c77763c..f39cfa6 100644 --- a/lib/log.ts +++ b/lib/log.ts @@ -23,9 +23,9 @@ const logFuncs = { } as const; let longestScope = 0; -let logLevel = (_LOG_LEVEL && _LOG_LEVEL in logMap && logMap[_LOG_LEVEL]) ?? - LOG_LEVEL.WARN; - +let logLevel = _LOG_LEVEL && _LOG_LEVEL in logMap && _LOG_LEVEL in logMap + ? logMap[_LOG_LEVEL] + : LOG_LEVEL.WARN; const ee = new EventEmitter<{ log: { level: LOG_LEVEL; scope: string; args: unknown[] }; }>(); diff --git a/lib/logs.ts b/lib/logs.ts new file mode 100644 index 0000000..aa107ea --- /dev/null +++ b/lib/logs.ts @@ -0,0 +1,66 @@ +import { createLogger } from "@lib/log.ts"; +import { join } from "node:path"; +import { DATA_DIR } from "@lib/env.ts"; +import { ensureDir } from "fs"; + +function getLogFileName() { + const d = new Date(); + const year = d.getFullYear(); + const month = (d.getMonth() + 1).toString().padStart(2, "0"); // Ensure two digits + const day = d.getDate().toString().padStart(2, "0"); // Ensure two digits + return `${year}-${month}-${day}.log`; +} + +const LOG_DIR = join(DATA_DIR, "logs"); + +// Ensure the log directory exists +await ensureDir(LOG_DIR); + +const log = createLogger(""); +log.addEventListener("log", async (data) => { + const logEntry = JSON.stringify(data.detail); + const logFilePath = join(LOG_DIR, getLogFileName()); + + // Append the log entry to the file (creating it if it doesn't exist) + await Deno.writeTextFile( + logFilePath, + new Date().toISOString() + " | " + logEntry + "\n", + { append: true }, + ); +}); + +export type Log = { + scope: string; + level: number; + date: Date; + args: unknown[]; +}; + +export async function getLogs() { + const logFilePath = join(LOG_DIR, getLogFileName()); + + try { + // Read the log file content + const logFileContent = await Deno.readTextFile(logFilePath); + + // Split by lines and parse logs + const logs: Log[] = logFileContent + .split("\n") + .filter((line) => line.trim() !== "") + .map((line) => { + const [date, ...rest] = line.split(" | "); + const parsed = JSON.parse(rest.join(" | ")) as Log; + return { + ...parsed, + date: new Date(date), + } as Log; + }); + console.log(logs); + + // Return the logs sorted by date + return logs.sort((a, b) => a.date.getTime() - b.date.getTime()); + } catch (error) { + // If file does not exist, return an empty array + return []; + } +} diff --git a/lib/cache/performance.ts b/lib/performance.ts similarity index 58% rename from lib/cache/performance.ts rename to lib/performance.ts index 88f1c01..5584562 100644 --- a/lib/cache/performance.ts +++ b/lib/performance.ts @@ -1,5 +1,6 @@ -import * as cache from "@lib/cache/cache.ts"; -import { getTimeCacheKey } from "@lib/string.ts"; +import { db } from "@lib/sqlite/sqlite.ts"; +import { performanceTable } from "@lib/sqlite/schema.ts"; +import { between } from "drizzle-orm/sql"; export type PerformancePoint = { path: string; @@ -16,40 +17,29 @@ export type PerformanceRes = { }[]; }; -export const savePerformance = (url: string, milliseconds: number) => { - const cacheKey = getTimeCacheKey(); - +export const savePerformance = async (url: string, seconds: number) => { const u = new URL(url); if (u.pathname.includes("_frsh/")) return; u.searchParams.delete("__frsh_c"); - cache.set( - `performance:${cacheKey}`, - JSON.stringify({ - path: decodeURIComponent(u.pathname), - search: u.search, - time: Math.floor(milliseconds * 1000), - }), - ); + console.log("Saving performance", u.pathname, u.search, seconds); + const res = await db.insert(performanceTable).values({ + path: decodeURIComponent(u.pathname), + search: u.search, + time: Math.floor(seconds * 1000), + }); + console.log({ res }); }; export async function getPerformances(): Promise { - const d = new Date(); - const year = d.getFullYear(); - const month = d.getMonth().toString(); - const day = d.getDay().toString(); + const now = new Date(); + const startOfDay = new Date(now.setHours(0, 0, 0, 0)); + const endOfDay = new Date(now.setHours(23, 59, 59, 999)); - const keys = await cache.keys( - `performance:${year}:${month}:${day}:*`, - ); - - console.log(`performance:${year}:${month}:${day}:*`); - - const performances = await Promise.all( - keys.map(async (key) => - JSON.parse(await cache.get(key)) as PerformancePoint - ), + const performances = await db.select().from(performanceTable).where( + between(performanceTable.createdAt, startOfDay, endOfDay), ); + console.log({ performances }); let maximum = 0; diff --git a/lib/sqlite/schema.ts b/lib/sqlite/schema.ts new file mode 100644 index 0000000..561274c --- /dev/null +++ b/lib/sqlite/schema.ts @@ -0,0 +1,45 @@ +import { int, integer, sqliteTable, text } from "drizzle-orm/sqlite-core"; +import { sql } from "drizzle-orm/sql"; + +export const userTable = sqliteTable("user", { + id: text() + .primaryKey(), + createdAt: integer("created_at", { mode: "timestamp" }) + .default(sql`(current_timestamp)`) + .notNull(), + email: text() + .notNull(), + name: text() + .notNull(), +}); + +export const sessionTable = sqliteTable("session", { + id: text("id") + .primaryKey(), + createdAt: integer("created_at", { mode: "timestamp_ms" }).default( + sql`(current_timestamp)`, + ), + expiresAt: integer("expires_at", { mode: "timestamp" }) + .notNull(), + userId: text("user_id") + .notNull(), +}); + +export const performanceTable = sqliteTable("performance", { + path: text().notNull(), + search: text(), + time: int().notNull(), + createdAt: integer("created_at", { + mode: "timestamp_ms", + }).default(sql`(STRFTIME('%s', 'now') * 1000)`), +}); + +export const imageTable = sqliteTable("image", { + createdAt: integer("created_at", { mode: "timestamp" }).default( + sql`(current_timestamp)`, + ), + path: text().notNull(), + average: text().notNull(), + blurhash: text().notNull(), + mime: text().notNull(), +}); diff --git a/lib/sqlite/sqlite.ts b/lib/sqlite/sqlite.ts new file mode 100644 index 0000000..4fac1ff --- /dev/null +++ b/lib/sqlite/sqlite.ts @@ -0,0 +1,14 @@ +import { drizzle } from "drizzle-orm/libsql/node"; +import { DATA_DIR } from "@lib/env.ts"; +import path from "node:path"; + +// const DB_FILE = "file://" + path.resolve(DATA_DIR, "db.sqlite"); +const DB_FILE = "file:data-dev/db.sqlite"; + +// You can specify any property from the libsql connection options +export const db = drizzle({ + logger: true, + connection: { + url: DB_FILE, + }, +}); diff --git a/main.ts b/main.ts index 404bfb0..fc93592 100644 --- a/main.ts +++ b/main.ts @@ -4,8 +4,6 @@ /// /// -import "@std/dotenv/load"; - import { start } from "$fresh/server.ts"; import manifest from "./fresh.gen.ts"; import config from "./fresh.config.ts"; diff --git a/routes/_middleware.ts b/routes/_middleware.ts index 18d65d1..64c7f07 100644 --- a/routes/_middleware.ts +++ b/routes/_middleware.ts @@ -1,14 +1,14 @@ //routes/middleware-error-handler/_middleware.ts -import { MiddlewareHandlerContext } from "$fresh/server.ts"; +import { FreshContext } from "$fresh/server.ts"; import { DomainError } from "@lib/errors.ts"; import { getCookies } from "@std/http/cookie"; import { verify } from "https://deno.land/x/djwt@v2.2/mod.ts"; -import * as cache from "@lib/cache/performance.ts"; +import * as perf from "@lib/performance.ts"; import { JWT_SECRET } from "@lib/env.ts"; export async function handler( req: Request, - ctx: MiddlewareHandlerContext, + ctx: FreshContext, ) { try { performance.mark("a"); @@ -29,7 +29,7 @@ export async function handler( const resp = await ctx.next(); performance.mark("b"); const b = performance.measure("a->b", "a", "b"); - cache.savePerformance(req.url, b.duration); + perf.savePerformance(req.url, b.duration); return resp; } catch (error) { console.error("Error", error); diff --git a/routes/admin/log/index.tsx b/routes/admin/log/index.tsx index 808ea3a..69b53eb 100644 --- a/routes/admin/log/index.tsx +++ b/routes/admin/log/index.tsx @@ -1,7 +1,7 @@ import { MainLayout } from "@components/layouts/main.tsx"; import { Handlers, PageProps } from "$fresh/server.ts"; import { AccessDeniedError } from "@lib/errors.ts"; -import { getLogs, Log } from "@lib/cache/logs.ts"; +import { getLogs, Log } from "@lib/logs.ts"; import { formatDate } from "@lib/string.ts"; import { renderMarkdown } from "@lib/documents.ts"; diff --git a/routes/admin/performance/index.tsx b/routes/admin/performance/index.tsx index d399935..a2f2376 100644 --- a/routes/admin/performance/index.tsx +++ b/routes/admin/performance/index.tsx @@ -1,6 +1,6 @@ import { MainLayout } from "@components/layouts/main.tsx"; import { Handlers, PageProps } from "$fresh/server.ts"; -import { getPerformances, PerformanceRes } from "@lib/cache/performance.ts"; +import { getPerformances, PerformanceRes } from "@lib/performance.ts"; import { AccessDeniedError } from "@lib/errors.ts"; export const handler: Handlers = { diff --git a/routes/api/auth/callback.ts b/routes/api/auth/callback.ts index 606986d..e662e00 100644 --- a/routes/api/auth/callback.ts +++ b/routes/api/auth/callback.ts @@ -4,9 +4,11 @@ import { oauth2Client } from "@lib/auth.ts"; import { getCookies, setCookie } from "@std/http/cookie"; import { codeChallengeMap } from "./login.ts"; import { GITEA_SERVER, JWT_SECRET, SESSION_DURATION } from "@lib/env.ts"; -import { userDB } from "@lib/db.ts"; import { GiteaOauthUser } from "@lib/types.ts"; import { BadRequestError } from "@lib/errors.ts"; +import { db } from "@lib/sqlite/sqlite.ts"; +import { userTable } from "@lib/sqlite/schema.ts"; +import { eq } from "drizzle-orm"; export const handler: Handlers = { async GET(request) { @@ -38,15 +40,17 @@ export const handler: Handlers = { const oauthUser = await userResponse.json() as GiteaOauthUser; - const allUsers = await userDB.findAll(); - let user = allUsers.find((u) => u.name === oauthUser.name); + let user = await db.select().from(userTable).where( + eq(userTable.name, oauthUser.name), + ).limit(1).then((users) => users[0]); if (!user) { - user = await userDB.create({ - createdAt: new Date(), + const res = await db.insert(userTable).values({ + id: crypto.randomUUID(), email: oauthUser.email, name: oauthUser.name, - }); + }).returning(); + user = res[0]; } const jwt = await create({ alg: "HS512", type: "JWT" }, {