A complaint board for members of Incheon Science High School (students and teachers). Anyone in the school can file a complaint anonymously, support others' complaints with a daily-limited upvote, and get answers from teachers and staff, whose comments carry a visible badge. The goal is a transparent, democratic channel for raising issues inside the school.
Identity is verified against the school's Riro School (리로스쿨) portal, which has no official API, so a small companion service logs in on the user's behalf and returns only their name, student number, generation, and role. Passwords are used at the moment of login and never stored.
├── riro-auth/ FastAPI microservice that authenticates against Riro School
│ by mimicking a browser login and parsing the profile page
├── web/ Main application (Next.js 15 App Router, Prisma, PostgreSQL)
└── 리로그인/ Korean design document for the Riro login flow
- Anonymous complaints. Complaints, votes, and comments never expose the
author's name, student number, or user ID in API responses. Only comments by
staff are marked, with a
교사(teacher) badge. - Daily-limited upvotes. Each user can hold at most 10 active votes per day; duplicate votes on the same complaint are blocked by a unique constraint, and cancelling a vote restores the quota immediately.
- Automatic archiving. A daily batch (
npm run aging) increments a counter for the top 3 complaints by vote count; after 7 days in the top 3, a complaint is moved to the archive and leaves the main ranking. - Moderation. An admin dashboard allows hiding comments, suspending users, and deleting complaints. Suspended users cannot log in or post.
- web/ — Next.js 15.3 (App Router, edge runtime on every API route and
dynamic page), React 19, TypeScript, Tailwind CSS 4, Prisma 6 with the
Neon serverless driver adapter against PostgreSQL,
josefor signed JWT session cookies. Deployed to Cloudflare Pages via@cloudflare/next-on-pages(seeweb/DEPLOY.md). - riro-auth/ — Python, FastAPI,
requests+ BeautifulSoup for scraping the Riro School login and profile pages. Runs separately (any VPS or container host); ships with a Dockerfile.
Full instructions, including the environment variables and database options, are in SETUP.md. The short version:
# Terminal A — auth service
cd riro-auth
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
uvicorn main:app --port 8000 # port 5000 conflicts with macOS AirPlay
# Terminal B — web app
cd web
docker compose up -d # local PostgreSQL, or point DATABASE_URL elsewhere
npm install
cp .env.example .env # set SESSION_SECRET (openssl rand -base64 32)
npm run db:push
npm run seed
npm run dev # http://localhost:3000For local development you can skip the Riro login entirely: with
ALLOW_DEV_LOGIN=true in .env, the password dev logs in any ID. IDs
starting with T become teachers, and hataewook/하태욱 becomes the admin.
The web app targets Cloudflare Pages (build with npm run pages:build);
riro-auth must be hosted separately. The full guide — infrastructure,
environment variables, edge runtime requirements, security checklist — is in
web/DEPLOY.md.
Early-stage school project under active development. Core flows (login,
complaints, voting, comments, archiving, admin dashboard) are implemented;
rate limiting, monitoring, and backups are not in place yet (see the
"known limitations" table in web/DEPLOY.md).