Compare commits

..

2 Commits

Author SHA1 Message Date
Max Richter
47d32d68c7 fix: lazily import sharp to fix commonjs error 2026-01-10 20:13:15 +01:00
Max Richter
c74a19b527 fix(ci): make dockerfile work 2026-01-10 19:49:07 +01:00
19 changed files with 147 additions and 70 deletions

View File

@@ -17,9 +17,9 @@ ENV DATA_DIR=/app/data
RUN mkdir -p $DATA_DIR && \ RUN mkdir -p $DATA_DIR && \
deno install --allow-import --allow-ffi -e main.ts &&\ deno install --allow-import --allow-ffi -e main.ts &&\
sed -i -e 's/"deno"/"no-deno"/' node_modules/@libsql/client/package.json &&\ sed -i -e 's/"deno"/"no-deno"/' node_modules/@libsql/client/package.json &&\
deno install npm:@libsql/linux-x64-gnu &&\
deno task build deno task build
EXPOSE 8000 EXPOSE 8000
CMD ["run", "-A", "main.ts"] CMD ["task", "start"]

View File

@@ -30,6 +30,7 @@
"@lib/": "./lib/", "@lib/": "./lib/",
"@/": "./", "@/": "./",
"@libsql/client": "npm:@libsql/client@^0.17.0", "@libsql/client": "npm:@libsql/client@^0.17.0",
"@libsql/linux-x64-gnu": "npm:@libsql/linux-x64-gnu@^0.5.22",
"@openai/openai": "jsr:@openai/openai@^6.16.0", "@openai/openai": "jsr:@openai/openai@^6.16.0",
"@preact-icons/tb": "jsr:@preact-icons/tb@^1.0.14", "@preact-icons/tb": "jsr:@preact-icons/tb@^1.0.14",
"@std/http": "jsr:@std/http@^1.0.23", "@std/http": "jsr:@std/http@^1.0.23",
@@ -88,13 +89,11 @@
}, },
"allowScripts": { "allowScripts": {
"allow": [ "allow": [
"npm:sharp@0.34.5" "npm:sharp@0.34.5",
], "npm:esbuild@0.27.2",
"deny": [
"npm:esbuild@0.18.20", "npm:esbuild@0.18.20",
"npm:esbuild@0.25.12", "npm:esbuild@0.25.12",
"npm:esbuild@0.25.7", "npm:esbuild@0.25.7"
"npm:esbuild@0.27.2"
] ]
} }
} }

2
deno.lock generated
View File

@@ -44,6 +44,7 @@
"npm:@babel/core@^7.28.0": "7.28.5", "npm:@babel/core@^7.28.0": "7.28.5",
"npm:@babel/preset-react@^7.27.1": "7.28.5_@babel+core@7.28.5", "npm:@babel/preset-react@^7.27.1": "7.28.5_@babel+core@7.28.5",
"npm:@libsql/client@0.17": "0.17.0", "npm:@libsql/client@0.17": "0.17.0",
"npm:@libsql/linux-x64-gnu@~0.5.22": "0.5.22",
"npm:@mjackson/node-fetch-server@0.7": "0.7.0", "npm:@mjackson/node-fetch-server@0.7": "0.7.0",
"npm:@opentelemetry/api@^1.9.0": "1.9.0", "npm:@opentelemetry/api@^1.9.0": "1.9.0",
"npm:@preact/signals@^2.2.1": "2.5.1_preact@10.28.2", "npm:@preact/signals@^2.2.1": "2.5.1_preact@10.28.2",
@@ -2887,6 +2888,7 @@
"jsr:@std/media-types@^1.1.0", "jsr:@std/media-types@^1.1.0",
"jsr:@zaubrik/djwt@^3.0.2", "jsr:@zaubrik/djwt@^3.0.2",
"npm:@libsql/client@0.17", "npm:@libsql/client@0.17",
"npm:@libsql/linux-x64-gnu@~0.5.22",
"npm:@preact/signals@^2.5.0", "npm:@preact/signals@^2.5.0",
"npm:@tailwindcss/vite@^4.1.12", "npm:@tailwindcss/vite@^4.1.12",
"npm:defuddle@~0.6.6", "npm:defuddle@~0.6.6",

View File

@@ -1,20 +1,22 @@
CREATE TABLE `performance` ( CREATE TABLE `performance` (
`path` text NOT NULL, `path` text NOT NULL,
`search` text, `search` text,
`time` integer NOT NULL, `time` integer NOT NULL,
`created_at` integer DEFAULT (current_timestamp) `created_at` integer DEFAULT (CURRENT_TIMESTAMP)
); );
--> statement-breakpoint --> statement-breakpoint
CREATE TABLE `session` ( CREATE TABLE `session` (
`id` text PRIMARY KEY NOT NULL, `id` text PRIMARY KEY NOT NULL,
`created_at` integer DEFAULT (current_timestamp), `created_at` integer DEFAULT (CURRENT_TIMESTAMP),
`expires_at` integer NOT NULL, `expires_at` integer NOT NULL,
`user_id` text NOT NULL `user_id` text NOT NULL
); );
--> statement-breakpoint --> statement-breakpoint
CREATE TABLE `user` ( CREATE TABLE `user` (
`id` text PRIMARY KEY NOT NULL, `id` text PRIMARY KEY NOT NULL,
`created_at` integer DEFAULT (current_timestamp) NOT NULL, `created_at` integer DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
`email` text NOT NULL, `email` text NOT NULL,
`name` text NOT NULL `name` text NOT NULL
); );

View File

@@ -1 +1,4 @@
ALTER TABLE `performance` ALTER COLUMN "created_at" TO "created_at" integer DEFAULT (STRFTIME('%s', 'now') * 1000); ALTER TABLE
`performance`
ALTER COLUMN
"created_at" TO "created_at" integer DEFAULT (STRFTIME('%s', 'now') * 1000);

View File

@@ -1,7 +1,7 @@
CREATE TABLE `image` ( CREATE TABLE `image` (
`created_at` integer DEFAULT (current_timestamp), `created_at` integer DEFAULT (CURRENT_TIMESTAMP),
`url` text NOT NULL, `url` text NOT NULL,
`average` text NOT NULL, `average` text NOT NULL,
`blurhash` text NOT NULL, `blurhash` text NOT NULL,
`mime` text NOT NULL `mime` text NOT NULL
); );

View File

@@ -1,7 +1,7 @@
CREATE TABLE `document` ( CREATE TABLE `document` (
`name` text NOT NULL, `name` text NOT NULL,
`last_modified` integer NOT NULL, `last_modified` integer NOT NULL,
`contentType` text NOT NULL, `contentType` text NOT NULL,
`size` integer NOT NULL, `size` integer NOT NULL,
`perm` text NOT NULL `perm` text NOT NULL
); );

View File

@@ -1,13 +1,38 @@
PRAGMA foreign_keys=OFF;--> statement-breakpoint PRAGMA foreign_keys = OFF;
CREATE TABLE `__new_document` (
`name` text PRIMARY KEY NOT NULL,
`last_modified` integer NOT NULL,
`contentType` text NOT NULL,
`size` integer NOT NULL,
`perm` text NOT NULL
);
--> statement-breakpoint --> statement-breakpoint
INSERT INTO `__new_document`("name", "last_modified", "contentType", "size", "perm") SELECT "name", "last_modified", "contentType", "size", "perm" FROM `document`;--> statement-breakpoint CREATE TABLE `__new_document` (
DROP TABLE `document`;--> statement-breakpoint `name` text PRIMARY KEY NOT NULL,
ALTER TABLE `__new_document` RENAME TO `document`;--> statement-breakpoint `last_modified` integer NOT NULL,
PRAGMA foreign_keys=ON; `contentType` text NOT NULL,
`size` integer NOT NULL,
`perm` text NOT NULL
);
--> statement-breakpoint
INSERT INTO
`__new_document`(
"name",
"last_modified",
"contentType",
"size",
"perm"
)
SELECT
"name",
"last_modified",
"contentType",
"size",
"perm"
FROM
`document`;
--> statement-breakpoint
DROP TABLE `document`;
--> statement-breakpoint
ALTER TABLE
`__new_document` RENAME TO `document`;
--> statement-breakpoint
PRAGMA foreign_keys = ON;

View File

@@ -1 +1,2 @@
ALTER TABLE `document` RENAME COLUMN "contentType" TO "content_type"; ALTER TABLE
`document` RENAME COLUMN "contentType" TO "content_type";

View File

@@ -1 +1,4 @@
ALTER TABLE `document` ADD `content` text; ALTER TABLE
`document`
ADD
`content` text;

View File

@@ -1 +1,4 @@
ALTER TABLE `document` ALTER COLUMN "content" TO "content" text NOT NULL; ALTER TABLE
`document`
ALTER COLUMN
"content" TO "content" text NOT NULL;

View File

@@ -1 +1,4 @@
ALTER TABLE `document` ALTER COLUMN "content" TO "content" text; ALTER TABLE
`document`
ALTER COLUMN
"content" TO "content" text;

View File

@@ -1,12 +1,17 @@
CREATE TABLE `cache` ( CREATE TABLE `cache` (
`scope` text NOT NULL, `scope` text NOT NULL,
`key` text PRIMARY KEY NOT NULL, `key` text PRIMARY KEY NOT NULL,
`json` text, `json` text,
`binary` blob, `binary` blob,
`created_at` integer DEFAULT (current_timestamp), `created_at` integer DEFAULT (CURRENT_TIMESTAMP),
`expires_at` integer `expires_at` integer
); );
--> statement-breakpoint
CREATE INDEX `key_idx` ON `cache` (`key`);
--> statement-breakpoint
CREATE INDEX `scope_idx` ON `cache` (`scope`);
--> statement-breakpoint --> statement-breakpoint
CREATE INDEX `key_idx` ON `cache` (`key`);--> statement-breakpoint
CREATE INDEX `scope_idx` ON `cache` (`scope`);--> statement-breakpoint
CREATE INDEX `name_idx` ON `document` (`name`); CREATE INDEX `name_idx` ON `document` (`name`);

View File

@@ -1 +1,2 @@
ALTER TABLE `image` RENAME COLUMN "blurhash" TO "thumbhash"; ALTER TABLE
`image` RENAME COLUMN "blurhash" TO "thumbhash";

View File

@@ -1,7 +1,22 @@
DROP INDEX "key_idx";--> statement-breakpoint DROP INDEX "key_idx";
DROP INDEX "scope_idx";--> statement-breakpoint
DROP INDEX "name_idx";--> statement-breakpoint --> statement-breakpoint
ALTER TABLE `image` ALTER COLUMN "created_at" TO "created_at" integer DEFAULT (unixepoch());--> statement-breakpoint DROP INDEX "scope_idx";
CREATE INDEX `key_idx` ON `cache` (`key`);--> statement-breakpoint
CREATE INDEX `scope_idx` ON `cache` (`scope`);--> statement-breakpoint --> statement-breakpoint
DROP INDEX "name_idx";
--> statement-breakpoint
ALTER TABLE
`image`
ALTER COLUMN
"created_at" TO "created_at" integer DEFAULT (unixepoch());
--> statement-breakpoint
CREATE INDEX `key_idx` ON `cache` (`key`);
--> statement-breakpoint
CREATE INDEX `scope_idx` ON `cache` (`scope`);
--> statement-breakpoint
CREATE INDEX `name_idx` ON `document` (`name`); CREATE INDEX `name_idx` ON `document` (`name`);

View File

@@ -12,7 +12,9 @@ export const userTable = sqliteTable("user", {
id: text() id: text()
.primaryKey(), .primaryKey(),
createdAt: integer("created_at", { mode: "timestamp" }) createdAt: integer("created_at", { mode: "timestamp" })
.default(sql`(current_timestamp)`) .default(sql`
(CURRENT_TIMESTAMP)
`)
.notNull(), .notNull(),
email: text() email: text()
.notNull(), .notNull(),
@@ -24,7 +26,9 @@ export const sessionTable = sqliteTable("session", {
id: text("id") id: text("id")
.primaryKey(), .primaryKey(),
createdAt: integer("created_at", { mode: "timestamp_ms" }).default( createdAt: integer("created_at", { mode: "timestamp_ms" }).default(
sql`(current_timestamp)`, sql`
(CURRENT_TIMESTAMP)
`,
), ),
expiresAt: integer("expires_at", { mode: "timestamp" }) expiresAt: integer("expires_at", { mode: "timestamp" })
.notNull(), .notNull(),
@@ -38,12 +42,16 @@ export const performanceTable = sqliteTable("performance", {
time: int().notNull(), time: int().notNull(),
createdAt: integer("created_at", { createdAt: integer("created_at", {
mode: "timestamp_ms", mode: "timestamp_ms",
}).default(sql`(STRFTIME('%s', 'now') * 1000)`), }).default(sql`
(STRFTIME('%s', 'now') * 1000)
`),
}); });
export const imageTable = sqliteTable("image", { export const imageTable = sqliteTable("image", {
createdAt: integer("created_at", { mode: "timestamp" }).default( createdAt: integer("created_at", { mode: "timestamp" }).default(
sql`(unixepoch())`, sql`
(unixepoch())
`,
), ),
url: text().notNull(), url: text().notNull(),
average: text().notNull(), average: text().notNull(),
@@ -70,7 +78,9 @@ export const cacheTable = sqliteTable("cache", {
json: text({ mode: "json" }), json: text({ mode: "json" }),
binary: blob(), binary: blob(),
createdAt: integer("created_at", { mode: "timestamp" }).default( createdAt: integer("created_at", { mode: "timestamp" }).default(
sql`(current_timestamp)`, sql`
(CURRENT_TIMESTAMP)
`,
), ),
expiresAt: integer("expires_at", { mode: "timestamp" }), expiresAt: integer("expires_at", { mode: "timestamp" }),
}, (table) => { }, (table) => {

View File

@@ -8,7 +8,6 @@ import { DATA_DIR } from "@lib/env.ts";
import { db } from "@lib/db/sqlite.ts"; import { db } from "@lib/db/sqlite.ts";
import { imageTable } from "@lib/db/schema.ts"; import { imageTable } from "@lib/db/schema.ts";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import sharp from "sharp";
const log = createLogger("cache/image"); const log = createLogger("cache/image");
@@ -158,6 +157,8 @@ async function resizeImage(
mediaType: string; mediaType: string;
}, },
) { ) {
const sharp = (await import("sharp")).default;
try { try {
log.debug("Resizing image", { params }); log.debug("Resizing image", { params });
@@ -211,6 +212,8 @@ async function resizeImage(
async function createThumbhash( async function createThumbhash(
image: Uint8Array, image: Uint8Array,
): Promise<{ hash: string; average: string }> { ): Promise<{ hash: string; average: string }> {
const sharp = (await import("sharp")).default;
try { try {
const resizedImage = await sharp(image) const resizedImage = await sharp(image)
.resize(100, 100, { fit: "cover" }) // Keep aspect ratio within bounds .resize(100, 100, { fit: "cover" }) // Keep aspect ratio within bounds
@@ -238,6 +241,8 @@ async function createThumbhash(
* Verifies that an image buffer contains valid image data * Verifies that an image buffer contains valid image data
*/ */
async function verifyImage(imageBuffer: Uint8Array): Promise<boolean> { async function verifyImage(imageBuffer: Uint8Array): Promise<boolean> {
const sharp = (await import("sharp")).default;
try { try {
const metadata = await sharp(imageBuffer).metadata(); const metadata = await sharp(imageBuffer).metadata();
return !!(metadata.width && metadata.height && metadata.format); return !!(metadata.width && metadata.height && metadata.format);