Skip to content

Scallywer/fairytale

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

94 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Priče za laku noć – Kids Nighttime Stories

A web app for Croatian children’s bedtime stories, built with Next.js, SQLite, and Tailwind CSS.

Features

  • 📚 Collection of Croatian bedtime stories for kids (ages 5–10)
  • 🌙 Dark theme with navy, slate, and amber
  • ⭐ 5-star rating system (ratings stored per user, averages computed)
  • 💬 Comments per story with simple math CAPTCHA and IP rate limiting
  • 📖 List and gallery view modes with search, filters (author, rating, reading time, read/unread), and sort
  • ✅ Mark stories as read (stored in localStorage) with grayed-out styling
  • 📄 “Load more” pagination on the story list
  • 🎯 Preporučeno za večeras – recommended unread stories by reading time
  • 🔗 Ostale priče – related stories (same author, then others) on each story page
  • 🔐 Admin area: approve/delete stories, HTTP-only cookie auth, login rate limiting
  • ♿ Keyboard and screen-reader friendly (ARIA, focus styles, scroll-to-comments)
  • 📱 Responsive layout
  • 🎨 Optional AI-generated illustrations (OpenAI DALL-E 3)

Tech stack

  • Framework: Next.js 16 (App Router)
  • Database: SQLite with better-sqlite3
  • Styling: Tailwind CSS 4
  • Language: TypeScript
  • Validation: Zod (API request bodies)
  • Image generation: OpenAI DALL-E 3 (optional, via scripts)

Getting started

Prerequisites

  • Node.js 20+ (or Docker and Docker Compose)

Environment

Copy .env.example to .env:

cp .env.example .env

Configure:

Variable Purpose
ADMIN_PASSWORD Password for /admin (required in production)
OPENAI_API_KEY For image-generation scripts (optional)
NEXT_PUBLIC_SITE_URL Base URL for SEO/canonical links (e.g. https://pricezalakunoc.hr)
DEBUG / NEXT_PUBLIC_DEBUG Set to true to enable debug logging
LOG_JSON Set to 1 or true for JSON-structured logs (e.g. in production)

The .env file is gitignored. If any secret was ever committed, rotate it and update .env and deployment secrets.

Run locally

npm install
npm run dev

Open http://localhost:3000.

Docker deployment

Docker Compose (recommended)

docker compose pull
# OR build locally
docker compose build

docker compose up -d

App: http://localhost:8889.

Docker run

docker run -d \
  --name fairytale-app \
  -p 8889:3000 \
  -v $(pwd)/data:/app/data \
  -e ADMIN_PASSWORD=your-secure-password \
  ghcr.io/scallywer/fairytale:latest

Data persistence

The SQLite database lives in ./data, mounted as a volume. Back up this directory regularly.

API overview

Endpoint Method Description
/api/stories GET Approved stories (optional ?limit=&offset= for pagination)
/api/stories POST Submit a new story (Zod-validated)
/api/comments?storyId= GET Comments for a story
/api/comments POST Add comment (Zod, math check, IP rate limit)
/api/ratings POST Submit rating 1–5 (Zod)
/api/admin GET/POST Admin actions (cookie auth, rate-limited login)
/api/health GET Health check (returns 200 and DB status)
/api/analytics POST Optional event logging (no persistence, returns 204)

Database schema

SQLite with three main tables:

  • stories – id, title, author, body, imageUrl, isApproved, createdAt, updatedAt. Reading time and average rating are computed at read time from ratings.
  • ratings – id, storyId, userId, rating (1–5), createdAt. One rating per user per story.
  • comments – id, storyId, authorName, content, isApproved, createdAt.

Domain logic lives in lib/storiesService.ts, lib/ratingsService.ts, and lib/commentsService.ts.

Scripts

Command Description
npm run dev Start dev server
npm run build Production build
npm run start Run production server
npm run lint Run ESLint
npm test Run tests (Vitest)
npm run generate-images Generate story images (OpenAI; needs API key)
npm run export-prompts Export DALL-E prompts to file
npm run update-image-links Sync image URLs in DB from local files
npm run review-prompts Review prompts
npm run regenerate-specific Regenerate images for selected stories
npm run expand-stories Expand stories script
npm run export-stories-list Export story list to file

After adding stories or images, run npm run update-image-links then npm test.

Testing

npm test

Runs Vitest (e.g. image mapping, stories API, UI components). For a single file:

npx vitest tests/image-mapping.test.ts
npx vitest tests/api-stories.test.ts

GitHub Actions

  • CI (.github/workflows/ci.yml) – on push/PR to main/master: npm ci, npm run lint, npx tsc --noEmit, npm test.
  • Docker (.github/workflows/docker-build.yml) – build and push image to GitHub Container Registry; image ghcr.io/scallywer/fairytale:latest, tags for branch, SHA, and version.

Operations / runbook

Backups

The SQLite DB lives at ./data/stories.db and is mounted into the container. WAL mode is enabled, so plain file copies can corrupt the backup — always use the .backup command. A helper is included:

docker exec fairytale-app /app/scripts/backup-db.sh
# → writes /app/data/backups/stories-<UTC-timestamp>.db inside the volume

Suggested host-side cron (daily at 03:00, 14-day retention):

0 3 * * * docker exec fairytale-app /app/scripts/backup-db.sh

Restore

docker compose stop
cp ./data/backups/stories-<timestamp>.db ./data/stories.db
docker compose start

Rotate ADMIN_PASSWORD

# Set new value in your .env / secret store, then:
docker compose up -d

Sessions issued under the old password remain valid until they expire naturally (24h) because session signing now uses ADMIN_SESSION_SECRET, which is independent of the password. To revoke immediately, also rotate ADMIN_SESSION_SECRET.

Update the public image

docker compose pull
docker compose up -d

The ./data host bind mount preserves the SQLite DB across image refreshes. If you ever see "first-run" behavior after a pull, verify the volume mount in docker-compose.yml is uncommented.

Seed DB for fresh installs

Before publishing a Docker image, scrub private moderation state from the seed DB:

./scripts/scrub-seed-db.sh

This drops unapproved stories/comments, all ratings, and all analytics events. The result is what new operators receive on first boot.

License

See LICENSE. Source code is proprietary; user-submitted content is licensed to the operator under the terms stated there.

About

Bajkosvijet 2.0

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors