feat: not uygulaması ve altyapısını ekle

- iOS Memos benzeri PWA ön yüz eklendi (React, Tailwind)
- Express tabanlı arka uç, AnythingLLM API entegrasyonu ve senkronizasyon kuyruğu oluşturuldu
- Docker, TypeScript ve proje konfigürasyonları tanımlandı
This commit is contained in:
2025-12-28 23:37:38 +03:00
commit 05bbe307e0
58 changed files with 2142 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
import { removeDocuments } from "../anythingllm/anythingllm.client.js";
import type { CleanupJob } from "./queue.js";
import { logError } from "../utils/logger.js";
import { readIndex, writeIndex } from "../notes/notes.storage.js";
export async function handleCleanupJob(job: CleanupJob): Promise<void> {
try {
await removeDocuments([job.oldLocation]);
const index = await readIndex();
const updated = index.map((item) => {
if (item.id === job.id) {
return {
...item,
sync: {
...item.sync,
cleanupPending: false
}
};
}
return item;
});
await writeIndex(updated);
} catch (error) {
logError("Temizlik kuyrugu hatasi", { error: (error as Error).message });
throw error;
}
}

View File

@@ -0,0 +1,86 @@
import path from "path";
import { readJsonFile, writeJsonFile } from "../utils/fileUtils.js";
import { config } from "../config.js";
import { logError, logInfo } from "../utils/logger.js";
export type CleanupJob = {
id: string;
oldLocation: string;
attempts: number;
nextRunAt: string;
};
const backoffSeconds = [5, 15, 30];
export class CleanupQueue {
private jobs: CleanupJob[] = [];
private running = false;
private timer?: NodeJS.Timeout;
private readonly filePath: string;
private readonly handler: (job: CleanupJob) => Promise<void>;
constructor(handler: (job: CleanupJob) => Promise<void>) {
this.handler = handler;
this.filePath = path.join(config.NOTES_DIR, "cleanup-queue.json");
}
async load(): Promise<void> {
this.jobs = await readJsonFile<CleanupJob[]>(this.filePath, []);
}
async persist(): Promise<void> {
await writeJsonFile(this.filePath, this.jobs);
}
async enqueue(job: CleanupJob): Promise<void> {
this.jobs.push(job);
await this.persist();
}
start(): void {
if (this.timer) {
return;
}
this.timer = setInterval(() => void this.tick(), 1000);
logInfo("Temizlik kuyrugu calisiyor");
}
stop(): void {
if (this.timer) {
clearInterval(this.timer);
this.timer = undefined;
}
}
private async tick(): Promise<void> {
if (this.running) {
return;
}
const now = Date.now();
const jobIndex = this.jobs.findIndex((job) => new Date(job.nextRunAt).getTime() <= now);
if (jobIndex === -1) {
return;
}
const job = this.jobs[jobIndex];
this.running = true;
try {
await this.handler(job);
this.jobs.splice(jobIndex, 1);
await this.persist();
logInfo("Temizlik basarili", { jobId: job.id });
} catch (error) {
job.attempts += 1;
if (job.attempts >= backoffSeconds.length) {
logError("Temizlik en fazla deneme sayisina ulasti", { jobId: job.id });
this.jobs.splice(jobIndex, 1);
} else {
const delay = backoffSeconds[job.attempts - 1] * 1000;
job.nextRunAt = new Date(Date.now() + delay).toISOString();
}
await this.persist();
} finally {
this.running = false;
}
}
}