chore: initial commit — monorepo ReadaBook (API NestJS, web PWA, Docker)

This commit is contained in:
Git Agent
2026-08-23 09:56:53 +02:00
commit 8f1140127f
79 changed files with 6456 additions and 0 deletions

View File

@ -0,0 +1,65 @@
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
import { accessSync, constants, realpathSync, statSync } from "node:fs";
import { resolve } from "node:path";
import { eq } from "drizzle-orm";
import { CreateLibraryDto, UpdateLibraryDto } from "@readabook/shared";
import { DatabaseService } from "../database/database.service.js";
import { libraries } from "../database/schema.js";
@Injectable()
export class LibrariesService {
constructor(private readonly database: DatabaseService) {}
list() {
return this.database.db.select().from(libraries).all();
}
get(id: number) {
const library = this.database.db.select().from(libraries).where(eq(libraries.id, id)).get();
if (!library) {
throw new NotFoundException("Library not found");
}
return library;
}
create(input: CreateLibraryDto) {
const path = this.validatePath(input.path);
const now = this.database.now();
return this.database.db
.insert(libraries)
.values({ name: input.name, path, enabled: input.enabled, createdAt: now, updatedAt: now })
.returning()
.get();
}
update(id: number, input: UpdateLibraryDto) {
const values: Partial<typeof libraries.$inferInsert> = { updatedAt: this.database.now() };
if (input.name) values.name = input.name;
if (input.path) values.path = this.validatePath(input.path);
if (input.enabled !== undefined) values.enabled = input.enabled;
const library = this.database.db.update(libraries).set(values).where(eq(libraries.id, id)).returning().get();
if (!library) {
throw new NotFoundException("Library not found");
}
return library;
}
delete(id: number): void {
this.database.db.delete(libraries).where(eq(libraries.id, id)).run();
}
private validatePath(input: string): string {
const resolved = resolve(input);
try {
accessSync(resolved, constants.R_OK);
const stats = statSync(resolved);
if (!stats.isDirectory()) {
throw new BadRequestException("Library path must be a directory");
}
return realpathSync(resolved);
} catch (error) {
if (error instanceof BadRequestException) throw error;
throw new BadRequestException("Library path is not readable");
}
}
}