6 Commits

Author SHA1 Message Date
static
dfffa004ac Merge pull request #13 from kmc7468/dev
v0.5.1
2025-07-12 19:56:12 +09:00
static
0cd55a413d Merge pull request #12 from kmc7468/dev
v0.5.0
2025-07-12 06:01:08 +09:00
static
361d966a59 Merge pull request #10 from kmc7468/dev
v0.4.0
2025-01-30 21:06:50 +09:00
static
aef43b8bfa Merge pull request #6 from kmc7468/dev
v0.3.0
2025-01-18 13:29:09 +09:00
static
7f128cccf6 Merge pull request #5 from kmc7468/dev
v0.2.0
2025-01-13 03:53:14 +09:00
static
a198e5f6dc Merge pull request #2 from kmc7468/dev
v0.1.0
2025-01-09 06:24:31 +09:00
13 changed files with 883 additions and 1091 deletions

View File

@@ -2,7 +2,11 @@
FROM node:22-alpine AS base FROM node:22-alpine AS base
WORKDIR /app WORKDIR /app
RUN npm install -g pnpm@10 RUN apk add --no-cache bash curl && \
curl -o /usr/local/bin/wait-for-it https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh && \
chmod +x /usr/local/bin/wait-for-it
RUN npm install -g pnpm@9
COPY pnpm-lock.yaml . COPY pnpm-lock.yaml .
# Build Stage # Build Stage
@@ -25,4 +29,4 @@ COPY --from=build /app/build ./build
EXPOSE 3000 EXPOSE 3000
ENV BODY_SIZE_LIMIT=Infinity ENV BODY_SIZE_LIMIT=Infinity
CMD ["node", "./build/index.js"] CMD ["bash", "-c", "wait-for-it ${DATABASE_HOST:-localhost}:${DATABASE_PORT:-5432} -- node ./build/index.js"]

View File

@@ -3,8 +3,7 @@ services:
build: . build: .
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
database: - database
condition: service_healthy
user: ${CONTAINER_UID:-0}:${CONTAINER_GID:-0} user: ${CONTAINER_UID:-0}:${CONTAINER_GID:-0}
volumes: volumes:
- ./data/library:/app/data/library - ./data/library:/app/data/library
@@ -36,8 +35,3 @@ services:
environment: environment:
- POSTGRES_USER=arkvault - POSTGRES_USER=arkvault
- POSTGRES_PASSWORD=${DATABASE_PASSWORD:?} - POSTGRES_PASSWORD=${DATABASE_PASSWORD:?}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
interval: 5s
timeout: 5s
retries: 5

View File

@@ -16,55 +16,53 @@
"db:migrate": "kysely migrate" "db:migrate": "kysely migrate"
}, },
"devDependencies": { "devDependencies": {
"@eslint/compat": "^1.4.1", "@eslint/compat": "^1.3.1",
"@iconify-json/material-symbols": "^1.2.44", "@iconify-json/material-symbols": "^1.2.29",
"@sveltejs/adapter-node": "^5.4.0", "@sveltejs/adapter-node": "^5.2.13",
"@sveltejs/kit": "^2.48.4", "@sveltejs/kit": "^2.22.5",
"@sveltejs/vite-plugin-svelte": "^6.2.1", "@sveltejs/vite-plugin-svelte": "^4.0.4",
"@trpc/client": "^11.7.1",
"@types/file-saver": "^2.0.7", "@types/file-saver": "^2.0.7",
"@types/ms": "^0.7.34", "@types/ms": "^0.7.34",
"@types/node-schedule": "^2.1.8", "@types/node-schedule": "^2.1.8",
"@types/pg": "^8.15.6", "@types/pg": "^8.15.4",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"axios": "^1.13.1", "axios": "^1.10.0",
"dexie": "^4.2.1", "dexie": "^4.0.11",
"eslint": "^9.39.0", "eslint": "^9.30.1",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.5",
"eslint-plugin-svelte": "^3.13.0", "eslint-plugin-svelte": "^3.10.1",
"eslint-plugin-tailwindcss": "^3.18.2", "eslint-plugin-tailwindcss": "^3.18.0",
"exifreader": "^4.32.0", "exifreader": "^4.31.1",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"globals": "^16.5.0", "globals": "^16.3.0",
"heic2any": "^0.0.4", "heic2any": "^0.0.4",
"kysely-ctl": "^0.19.0", "kysely-ctl": "^0.13.1",
"lru-cache": "^11.2.2", "lru-cache": "^11.1.0",
"mime": "^4.1.0", "mime": "^4.0.7",
"p-limit": "^7.2.0", "p-limit": "^6.2.0",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"prettier-plugin-svelte": "^3.4.0", "prettier-plugin-svelte": "^3.4.0",
"prettier-plugin-tailwindcss": "^0.7.1", "prettier-plugin-tailwindcss": "^0.6.14",
"svelte": "^5.43.2", "svelte": "^5.35.6",
"svelte-check": "^4.3.3", "svelte-check": "^4.2.2",
"tailwindcss": "^3.4.18", "tailwindcss": "^3.4.17",
"typescript": "^5.9.3", "typescript": "^5.8.3",
"typescript-eslint": "^8.46.2", "typescript-eslint": "^8.36.0",
"unplugin-icons": "^22.5.0", "unplugin-icons": "^22.1.0",
"vite": "^7.1.12" "vite": "^5.4.19"
}, },
"dependencies": { "dependencies": {
"@fastify/busboy": "^3.2.0", "@fastify/busboy": "^3.1.1",
"@trpc/server": "^11.7.1", "argon2": "^0.43.0",
"argon2": "^0.44.0", "kysely": "^0.28.2",
"kysely": "^0.28.8",
"ms": "^2.1.3", "ms": "^2.1.3",
"node-schedule": "^2.1.1", "node-schedule": "^2.1.1",
"pg": "^8.16.3", "pg": "^8.16.3",
"uuid": "^13.0.0", "uuid": "^11.1.0",
"zod": "^3.25.76" "zod": "^3.25.76"
}, },
"engines": { "engines": {
"node": "^22.0.0", "node": "^22.0.0",
"pnpm": "^10.0.0" "pnpm": "^9.0.0"
} }
} }

1806
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -32,7 +32,7 @@
}; };
$effect(() => { $effect(() => {
if ($info) { if ($info?.dataKey) {
requestFileThumbnailDownload($info.id, $info.dataKey) requestFileThumbnailDownload($info.id, $info.dataKey)
.then((thumbnailUrl) => { .then((thumbnailUrl) => {
thumbnail = thumbnailUrl ?? undefined; thumbnail = thumbnailUrl ?? undefined;

View File

@@ -67,15 +67,10 @@ const generateVideoThumbnail = (videoUrl: string, time = 0) => {
return new Promise<Blob>((resolve, reject) => { return new Promise<Blob>((resolve, reject) => {
const video = document.createElement("video"); const video = document.createElement("video");
video.onloadedmetadata = () => { video.onloadedmetadata = () => {
if (video.videoWidth === 0 || video.videoHeight === 0) {
return reject();
}
const callbackId = video.requestVideoFrameCallback(() => {
captureVideoThumbnail(video).then(resolve).catch(reject);
video.cancelVideoFrameCallback(callbackId);
});
video.currentTime = Math.min(time, video.duration); video.currentTime = Math.min(time, video.duration);
video.requestVideoFrameCallback(() => {
captureVideoThumbnail(video).then(resolve).catch(reject);
});
}; };
video.onerror = reject; video.onerror = reject;

View File

@@ -48,9 +48,9 @@ export const requestFileThumbnailUpload = async (
return await fetch(`/api/file/${fileId}/thumbnail/upload`, { method: "POST", body: form }); return await fetch(`/api/file/${fileId}/thumbnail/upload`, { method: "POST", body: form });
}; };
export const requestFileThumbnailDownload = async (fileId: number, dataKey?: CryptoKey) => { export const requestFileThumbnailDownload = async (fileId: number, dataKey: CryptoKey) => {
const cache = await getFileThumbnailCache(fileId); const cache = await getFileThumbnailCache(fileId);
if (cache || !dataKey) return cache; if (cache) return cache;
let res = await callGetApi(`/api/file/${fileId}/thumbnail`); let res = await callGetApi(`/api/file/${fileId}/thumbnail`);
if (!res.ok) return null; if (!res.ok) return null;

View File

@@ -34,7 +34,7 @@
}; };
$effect(() => { $effect(() => {
if ($info) { if ($info?.dataKey) {
requestFileThumbnailDownload($info.id, $info.dataKey) requestFileThumbnailDownload($info.id, $info.dataKey)
.then((thumbnailUrl) => { .then((thumbnailUrl) => {
thumbnail = thumbnailUrl ?? undefined; thumbnail = thumbnailUrl ?? undefined;

View File

@@ -1,15 +0,0 @@
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { createContext } from "$trpc/init.server";
import { appRouter } from "$trpc/router.server";
import type { RequestHandler } from "./$types";
const trpcHandler: RequestHandler = (event) =>
fetchRequestHandler({
endpoint: "/trpc",
req: event.request,
router: appRouter,
createContext: () => createContext(event),
});
export const GET = trpcHandler;
export const POST = trpcHandler;

View File

@@ -1,23 +0,0 @@
import { createTRPCClient, httpBatchLink } from "@trpc/client";
import { browser } from "$app/environment";
import type { AppRouter } from "./router.server";
const createClient = (fetch: typeof globalThis.fetch) =>
createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: "/trpc",
fetch,
}),
],
});
let browserClient: ReturnType<typeof createClient>;
export const trpc = (fetch = globalThis.fetch) => {
const client = browserClient ?? createClient(fetch);
if (browser) {
browserClient ??= client;
}
return client;
};

View File

@@ -1,9 +0,0 @@
import type { RequestEvent } from "@sveltejs/kit";
import { initTRPC } from "@trpc/server";
export const createContext = (event: RequestEvent) => event;
const t = initTRPC.context<Awaited<ReturnType<typeof createContext>>>().create();
export const router = t.router;
export const publicProcedure = t.procedure;

View File

@@ -1,13 +0,0 @@
import type { RequestEvent } from "@sveltejs/kit";
import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server";
import { createContext, router } from "./init.server";
export const appRouter = router({
// TODO
});
export const createCaller = (event: RequestEvent) => appRouter.createCaller(createContext(event));
export type AppRouter = typeof appRouter;
export type RouterInputs = inferRouterInputs<AppRouter>;
export type RouterOutputs = inferRouterOutputs<AppRouter>;

View File

@@ -3,12 +3,15 @@ import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
/** @type {import('@sveltejs/kit').Config} */ /** @type {import('@sveltejs/kit').Config} */
const config = { const config = {
// Consult https://svelte.dev/docs/kit/integrations
// for more information about preprocessors
preprocess: vitePreprocess(), preprocess: vitePreprocess(),
kit: { kit: {
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
adapter: adapter(), adapter: adapter(),
alias: {
$trpc: "./src/trpc",
},
}, },
}; };