No description
  • JavaScript 67.6%
  • TypeScript 31.4%
  • CSS 0.9%
  • Dockerfile 0.1%
Find a file
cola 7cfbc9744e feat: /admin/drafts memperbarui sendiri selagi AI berjalan
Job AI menulis ke database dari worker latar, jadi tidak ada yang
mendorong hasilnya ke halaman. Halaman ini mengambil data sekali saat
mount (useEffect dengan dependency kosong) dan tidak pernah lagi — jadi
menunggu hasil AI berarti me-refresh browser berulang kali sampai
statusnya berubah.

Ini bukan keterbatasan Next.js: halamannya sudah client component yang
memanggil fetch(), cuma tidak punya polling.

- Polling tiap 5 detik, tapi HANYA selama masih ada baris yang belum
  selesai (status bukan done/failed). Antrean kosong = nol permintaan.
- Berhenti saat tab tidak terlihat, dan langsung menarik data begitu tab
  kembali aktif tanpa menunggu satu interval penuh.
- Hasil polling DIGABUNG, bukan menggantikan: baris yang sedang diedit
  pustakawan dipertahankan. Tanpa ini, satu buku lain yang selesai
  dianalisis akan menghapus koreksi yang belum sempat disimpan.
- Header menyebut berapa buku sedang diproses dan bahwa halaman
  memperbarui sendiri, supaya tidak ada yang refresh karena kebiasaan.
- Spinner badge "Menunggu AI" akhirnya benar-benar berputar; tanpa
  animate-spin ia terbaca sebagai macet, bukan sedang bekerja.

Aturan penggabungan dan syarat kapan polling berhenti diuji terpisah:
baris diedit tidak tertimpa, baris baru tetap muncul, baris yang sudah
hilang di server tidak dihidupkan lagi.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 09:31:45 +07:00
drizzle feat: katalogisasi AI multi-provider, rekonsiliasi ISBN, dan jalur buku pindai 2026-09-07 09:09:21 +07:00
public feat: Implement core digital library functionalities including book management, PDF viewing, user features, and API endpoints. 2026-03-25 00:24:06 +07:00
src feat: /admin/drafts memperbarui sendiri selagi AI berjalan 2026-09-07 09:31:45 +07:00
.gitignore refactor: improve UI/UX with Lucide icons, updated styling, and responsive layout adjustments. 2026-03-25 22:14:22 +07:00
components.json feat: initial commit 2026-03-22 11:59:20 +07:00
DESIGN.md feat: koleksi fisik — bibliografi/eksemplar, OPAC, dan katalogisasi lewat ISBN 2026-09-07 05:47:24 +07:00
docker-compose.yml feat: katalogisasi AI multi-provider, rekonsiliasi ISBN, dan jalur buku pindai 2026-09-07 09:09:21 +07:00
Dockerfile build: Add ghostscript to Dockerfile system dependencies. 2026-03-25 18:23:51 +07:00
drizzle.config.ts feat: Implement core digital library functionalities including book management, PDF viewing, user features, and API endpoints. 2026-03-25 00:24:06 +07:00
empty-module.js feat: Implement core digital library functionalities including book management, PDF viewing, user features, and API endpoints. 2026-03-25 00:24:06 +07:00
eslint.config.mjs refactor: Optimize reader client initialization by using useState initializers for settings and saved page, and improve favorites page data fetching and rendering logic. 2026-03-25 21:22:18 +07:00
migrate-s3.js feat: Implement core digital library functionalities including book management, PDF viewing, user features, and API endpoints. 2026-03-25 00:24:06 +07:00
next.config.ts feat: Implement core digital library functionalities including book management, PDF viewing, user features, and API endpoints. 2026-03-25 00:24:06 +07:00
package-lock.json feat: katalogisasi AI multi-provider, rekonsiliasi ISBN, dan jalur buku pindai 2026-09-07 09:09:21 +07:00
package.json feat: katalogisasi AI multi-provider, rekonsiliasi ISBN, dan jalur buku pindai 2026-09-07 09:09:21 +07:00
postcss.config.mjs feat: initial commit 2026-03-22 11:59:20 +07:00
README.md feat: katalogisasi AI multi-provider, rekonsiliasi ISBN, dan jalur buku pindai 2026-09-07 09:09:21 +07:00
ROADMAP.md feat: koleksi fisik — bibliografi/eksemplar, OPAC, dan katalogisasi lewat ISBN 2026-09-07 05:47:24 +07:00
rustfs-docker-compose.yml feat: Implement core digital library functionalities including book management, PDF viewing, user features, and API endpoints. 2026-03-25 00:24:06 +07:00
test-s3.js feat: Implement core digital library functionalities including book management, PDF viewing, user features, and API endpoints. 2026-03-25 00:24:06 +07:00
tsconfig.json feat: initial commit 2026-03-22 11:59:20 +07:00

Perpustakaan Digital

Perpustakaan digital untuk siswa sekolah dasar. Baca buku gratis kapan saja, di mana saja 📚

Stack

  • Next.js 16 — full-stack React framework
  • SQLite + Drizzle ORM — lightweight database
  • Casdoor — OIDC authentication
  • react-pdf — PDF viewer
  • Tailwind CSS + shadcn/ui — UI framework
  • Docker — containerized deployment

Development

npm install
npm run dev

Database

npx drizzle-kit generate   # Generate migration
npx drizzle-kit migrate    # Apply migration

Docker Deployment

# Copy and configure environment
cp .env.example .env

# Build and run
docker compose up -d --build

Environment Variables

NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret-key
CASDOOR_ENDPOINT=http://casdoor:8000
CASDOOR_CLIENT_ID=your-client-id
CASDOOR_CLIENT_SECRET=your-client-secret
GEMINI_API_KEY=your-gemini-api-key    # Optional, for AI categorization
DATA_DIR=/data                         # Docker volume mount point

# Lihat bagian "Katalogisasi AI" di bawah untuk provider cadangan
# (DEEPSEEK_API_KEY, MOONSHOT_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY).

# Optional. Pola nomor induk (accession number) untuk koleksi fisik.
# {YYYY}/{YY} = tahun, satu deret N = nomor urut (panjangnya = zero-padding).
# Deretnya tidak harus di akhir. Default: SD/{YYYY}/{NNNNN}
# Setel SEBELUM memasukkan eksemplar massal — menggantinya nanti berarti
# menomori ulang rak fisik.
INVENTORY_CODE_PATTERN=SD/{YYYY}/{NNNNN}

# Sangat disarankan. Kunci Google Books API untuk pengisian otomatis dari ISBN
# di /admin/tambah. Gratis, dibuat di Google Cloud Console (aktifkan "Books API").
# TANPA kunci ini, permintaan memakai kuota anonim bersama yang sering sudah
# habis dan membalas HTTP 429 — lookup lalu jatuh ke OpenLibrary, yang jalan
# tetapi tanpa sinopsis dan cakupan Indonesianya lebih tipis.
GOOGLE_BOOKS_API_KEY=your-google-books-api-key

Katalogisasi AI

Saat sebuah PDF diunggah, antrean latar belakang membaca beberapa halaman pertama dan meminta model bahasa mengisi judul, penulis, kategori, jenjang kelas, sinopsis, penerbit, dan tahun terbit.

Permintaan dicoba ke beberapa provider secara berurutan. Untuk tiap provider ada maksimum 3 percobaan dengan jeda yang membesar (backoff + jitter), tapi hanya untuk kegagalan sementara — 429, 5xx, dan timeout. Kegagalan permanen seperti 401 langsung lompat ke provider berikutnya. Ini yang mencegah 503 Service Unavailable dari Gemini free tier menandai buku sebagai gagal permanen.

Dua jalur pembacaan: teks dan gambar

Jalur teks (utama). pdf-parse menarik lapisan teks dari 12 halaman pertama. Murah dan akurat — dipakai selama PDF-nya punya teks.

Jalur gambar (cadangan). Buku yang dipindai dari kertas tidak punya lapisan teks sama sekali; ekstraksi mengembalikan nol karakter, dan mengulang atau berganti provider tidak menolong karena tidak ada teks untuk dibaca. Dalam kasus itu 6 halaman pertama dirender jadi JPEG dengan Ghostscript lalu dikirim ke model vision. Jauh lebih mahal dalam token, jadi hanya dipakai kalau jalur teks memang kosong.

Provider yang tidak bisa membaca gambar otomatis dilewati untuk buku pindaian. Buku yang lewat jalur ini ditandai di catatan tinjauan agar pustakawan tahu metadatanya lebih rawan salah baca.

Ghostscript sudah terpasang di image Docker (Dockerfile baris 4) dan juga dipakai untuk membuat sampul. Kalau di mesin pengembangan Ghostscript ada tapi tidak di PATH, setel GHOSTSCRIPT_PATH.

Semua provider opsional. Yang API key-nya kosong dilewati tanpa suara, jadi mengisi GEMINI_API_KEY saja membuat aplikasi berperilaku persis seperti sebelumnya.

Provider yang didukung

Nama API key Model default Model untuk gambar Harga per 1 juta token (masuk / keluar)
gemini GEMINI_API_KEY gemini-flash-latest sama Ada kuota gratis
deepseek DEEPSEEK_API_KEY deepseek-v4-flash deepseek-v4-flash-vision-exp $0,220,44 / $0,661,32
moonshot (Kimi) MOONSHOT_API_KEY kimi-k2.6 sama $0,95 / $4,00
openai OPENAI_API_KEY gpt-5.6-luna sama $0,20 / $1,20
anthropic (Claude) ANTHROPIC_API_KEY claude-opus-5 sama $5,00 / $25,00

Harga di atas tercatat pada September 2026 dan bisa berubah — periksa ke halaman harga masing-masing vendor. DeepSeek memberi tarif lebih murah di luar jam sibuk.

Langganan tidak bisa dipakai di sini. Claude Pro/Max hanya untuk claude.ai dan Claude Code; ChatGPT Plus hanya untuk chatgpt.com. Untuk memanggil API dari aplikasi ini diperlukan API key terpisah yang ditagih per token.

Pengaturan

# Urutan percobaan. Default: gemini,deepseek,moonshot
# Gemini didahulukan karena punya kuota gratis; provider berbayar baru dipakai
# ketika Gemini sedang padat atau kuotanya habis.
AI_PROVIDER_ORDER=gemini,deepseek,moonshot

DEEPSEEK_API_KEY=your-deepseek-api-key
MOONSHOT_API_KEY=your-moonshot-api-key
OPENAI_API_KEY=your-openai-api-key
ANTHROPIC_API_KEY=your-anthropic-api-key

# Opsional: ganti model tanpa menyentuh kode.
GEMINI_MODEL=
DEEPSEEK_MODEL=
MOONSHOT_MODEL=
OPENAI_MODEL=
ANTHROPIC_MODEL=

# Opsional: model khusus untuk membaca gambar (buku hasil pindai).
DEEPSEEK_VISION_MODEL=
MOONSHOT_VISION_MODEL=
OPENAI_VISION_MODEL=

# Opsional: lokasi Ghostscript kalau tidak ada di PATH (mis. Nix, Homebrew).
GHOSTSCRIPT_PATH=

# Opsional: batas waktu per percobaan, dalam milidetik. Default 60000.
# Naikkan kalau memakai endpoint sendiri yang lambat.
AI_REQUEST_TIMEOUT_MS=60000

# Opsional: arahkan ke endpoint lain yang kompatibel-OpenAI (mis. OpenRouter,
# atau endpoint Moonshot wilayah lain).
DEEPSEEK_BASE_URL=
MOONSHOT_BASE_URL=
OPENAI_BASE_URL=

DeepSeek, Kimi, dan OpenAI dilayani oleh satu adapter yang sama karena ketiganya berbicara protokol chat-completions OpenAI. Menambah vendor kompatibel lain cukup satu entri di src/lib/ai/registry.ts — tidak perlu file baru.

Jalankan npm test untuk memverifikasi logika retry, fallback, dan timeout.

Server Requirements

  • Ubuntu 20.04+
  • Docker & Docker Compose
  • 4GB RAM minimum
  • 100GB disk space