chore: initial commit — monorepo ReadaBook (API NestJS, web PWA, Docker)
This commit is contained in:
65
apps/api/src/libraries/libraries.service.ts
Normal file
65
apps/api/src/libraries/libraries.service.ts
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user