diff --git a/AGENTS.md b/AGENTS.md index 329b289..582b75c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,28 +1,166 @@ -# Repository Guidelines +# AGENTS.md -## Project Structure & Module Organization +Guidance for coding agents working in this repository. Keep changes focused, +preserve existing behavior unless the task explicitly changes it, and verify +all claims with fresh commands. -`main.go` wires the Fiber application, middleware, routes, database startup, and template engine. Keep HTTP handlers in `controllers/`, persistence logic and data types in `repositories/`, database setup in `db/`, and environment parsing in `config/`. Gateway integration belongs in `gateway/`; shared helpers live in `utils/` and `files/`. Server-rendered HTML is organized under `templates/{pages,components,head}`, while browser assets live in `static/`. Add ordered PostgreSQL changes to `migrations/` using the existing `-.sql` pattern. Load-testing scripts belong in `tests/`. +## Project Overview -## Build, Test, and Development Commands +micrach is a small, server-rendered, single-board imageboard. It uses Go, +Fiber v2, Fiber HTML templates, PostgreSQL through `pgx`, and filesystem-backed +image uploads. It intentionally uses little browser-side JavaScript. -- `cp .env.example .env` creates local configuration; adjust `POSTGRES_URL` before starting. -- `go run .` starts the service and applies migrations against the configured PostgreSQL database. -- `make dev` runs the app through `nodemon` for automatic restarts. -- `go build .` produces the local `micrach` binary. -- `GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build .` matches the CI Linux build. -- `go test ./...` runs all Go tests. -- `go vet ./...` performs standard static checks. -- `sh tests/load.sh` runs the optional Vegeta load test; review its target URL first. +The service supports thread creation, replies, JPEG/PNG attachments, +thumbnails, CAPTCHA, rate limiting, pagination, bump limits, `sage`, thread +archival, and optional external gateway registration. -## Coding Style & Naming Conventions +## Repository Map -Use `gofmt` (tabs for Go indentation) and run `go fmt ./...` before submitting. Keep package names short and lowercase. Use `PascalCase` for exported identifiers, `camelCase` for unexported identifiers, and descriptive handler names such as `CreateThread`. Keep controllers thin: validation and HTTP concerns stay in handlers, while SQL and persistence behavior stay in repositories. Follow existing kebab-case names for CSS, templates, and migration descriptions. +- `main.go`: application startup, middleware, static mounts, routes, and server. +- `controllers/`: Fiber handlers, request parsing, HTTP responses, and form flow. +- `repositories/`: SQL queries, persistence types, and template view data. +- `db/`: PostgreSQL pool initialization and startup migration runner. +- `config/`: environment parsing and defaults. +- `gateway/`: optional gateway registration and authenticated ping endpoint. +- `utils/`: post validation, upload directories, and image thumbnail handling. +- `files/`: shared filesystem read helpers. +- `build/`: production CSS cache-busting rewrite. +- `templates/{pages,components,head}`: server-rendered HTML. +- `static/`: checked-in CSS, icons, and error images. +- `uploads/`: generated runtime files; ignored by Git and never commit them. +- `migrations/`: ordered PostgreSQL changes named `-.sql`. +- `tests/`: optional Vegeta load test, not the unit-test suite. -## Testing Guidelines +## Runtime Flow and Routes -The repository currently has no unit tests or enforced coverage threshold. Add tests beside the code as `*_test.go`, using `TestXxx` and table-driven cases where practical. Avoid shared production databases; isolate repository tests with disposable test data or a dedicated database. Run `go test ./...` and `go vet ./...` for every change. Use Vegeta only for explicit performance work. +Startup loads `.env`, initializes configuration and the DB pool, applies +missing migrations, optionally seeds data, performs production CSS rewriting, +creates `uploads/`, then starts Fiber. -## Commit & Pull Request Guidelines +| Method | Path | Handler / purpose | +| --- | --- | --- | +| `GET` | `/` | Render paginated thread catalog. | +| `POST` | `/` | Validate and create a thread. | +| `GET` | `/:threadID` | Render one thread and its replies. | +| `POST` | `/:threadID` | Validate and add a reply. | +| `GET` | `/captcha/:captchaID` | Render CAPTCHA PNG. | +| `GET` | `/api/ping` | Gateway health endpoint; registered only when `GATEWAY_URL` is set. | -History mixes terse imperative subjects with `feat:` and `fix:` prefixes. Prefer focused Conventional Commit subjects, for example `fix: reject oversized uploads`. Keep each commit buildable. Pull requests should explain behavior changes, configuration or migration impact, and verification commands. Link relevant issues and include screenshots for template or CSS changes. Never commit `.env`, uploads, generated load-test artifacts, or credentials. +Static content is served from `/static`; generated files are served from +`/uploads`. Thread and reply creation spans PostgreSQL plus filesystem writes, +so review both paths when changing upload behavior. + +## Local Setup and Commands + +Create a PostgreSQL database, then: + +```sh +cp .env.example .env +# Edit POSTGRES_URL and normally set IS_DB_SEEDED=false. +go run . +``` + +Common checks: + +```sh +go fmt ./... +go test ./... +go vet ./... +go build . +GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build . +``` + +`make dev` requires `nodemon`. `sh tests/load.sh` requires Vegeta and targets a +historical public URL by default; change the target to a controlled environment +before use. Do not run the load test as routine verification. + +The module declares Go 1.15 and CI installs Go 1.17. Avoid language or standard +library features newer than Go 1.17 unless the task also upgrades the supported +Go version and CI configuration. + +## Configuration and Generated State + +- `.env` is local and must never be committed. +- `POSTGRES_URL` selects the database; startup applies migrations automatically. +- `IS_DB_SEEDED=true` adds sample data on every startup, not only once. +- `ENV=production` renames CSS files and rewrites templates in place at startup. + Do not use this mode in a development checkout unless that mutation is wanted. +- `GATEWAY_URL=""` disables gateway routes and registration. +- Uploads are limited to four JPEG/PNG files, 3 MiB each. Titles allow 100 + runes; post text allows 1,000 runes. +- Runtime uploads, built binaries, `.env`, and load-test artifacts are ignored + and must remain untracked. + +## Architecture and Coding Conventions + +- Keep handlers thin: HTTP parsing/status/rendering in `controllers/`; SQL and + persistence behavior in `repositories/`. +- Keep DB startup and migration mechanics in `db/`, environment parsing in + `config/`, gateway behavior in `gateway/`, and reusable helpers in `utils/` + or `files/`. +- Follow existing package naming: short, lowercase names. Use `PascalCase` for + exported identifiers and `camelCase` for unexported identifiers. +- Format Go with `gofmt`/`go fmt`; do not hand-format with spaces. +- Follow existing kebab-case naming for templates, CSS, and migration + descriptions. Preserve current server-rendered approach and minimal-JS goal. +- Use parameterized SQL. Keep related DB operations transactional where the + existing workflow requires atomicity. +- Avoid unrelated refactors. If a task exposes adjacent problems, report them + separately unless they block the requested work. + +## Change Guide + +- Route or request behavior: update `main.go` and/or `controllers/`, then cover + status codes, redirects, validation errors, and template data. +- Persistence behavior: update `repositories/`; add a migration for schema + changes rather than editing an already-applied migration. +- Schema changes: add the next numeric file in `migrations/`. Keep filename + shape exactly `-.sql`; the runner parses it directly. +- Page or component changes: edit `templates/` and matching `static/styles/`. + Check catalog, thread, error, empty, and pagination states as relevant. +- Upload changes: inspect controller transaction flow, `utils/`, repository file + records, original/thumbnail paths, validation limits, and cleanup behavior. +- Config changes: update `config/config.go`, `.env.example`, README config table, + and deployment environment forwarding together. +- Deployment changes: note that `Dockerfile` expects a prebuilt Linux binary and + `docker-compose.yml` assumes Swarm, Traefik, an external network, and a + persistent host upload mount. + +## Testing Expectations + +There are currently no checked-in Go unit tests and no coverage threshold. Add +`*_test.go` files beside changed code, use `TestXxx`, and prefer table-driven +cases when practical. Repository tests must use disposable test data or a +dedicated database, never a shared production database. + +Minimum verification for code changes: + +1. Run `go fmt ./...` and review the resulting diff. +2. Run `go test ./...`. +3. Run `go vet ./...`. +4. Run `go build .` (or the Linux CI build for deployment-sensitive work). +5. For templates/CSS, start the app and manually inspect affected responsive + states; include screenshots in the PR. + +For documentation-only changes, inspect the rendered Markdown, verify all local +links and commands against the repository, and review `git diff --check`. + +## Git, Commits, and Pull Requests + +- Inspect `git status` before editing. Preserve user changes and unrelated files. +- Never commit `.env`, credentials, uploads, binaries, or generated load-test + outputs. +- Prefer focused Conventional Commit subjects, such as + `fix: reject oversized uploads` or `docs: expand setup guide`. +- Keep each commit buildable. Do not rewrite history or discard local changes + unless explicitly requested. +- PRs should describe behavior, config/migration impact, verification commands, + linked issues, and screenshots for UI changes. + +## Agent Completion Checklist + +- Requested scope is fully addressed; unrelated behavior is untouched. +- New behavior has tests where practical. +- Documentation and `.env.example` match config/code changes. +- No secret, upload, binary, or generated artifact is included. +- Fresh verification commands pass, and final report names commands actually run. diff --git a/README.md b/README.md index c2abb54..023f0e3 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,140 @@ # micrach -Go tryout. Single board imageboard. -UPD: stopped the service from the link above, too much shit posted from TOR, don't have time to moderate :( +micrach is a small, server-rendered imageboard written in Go. It provides one +board where visitors can create threads, reply with text or images, bump or +`sage` threads, and browse the thread catalog without a client-side SPA. -## Motivation -Tired of a fucking SPAs, so I decided to write this simple and lightweight imageboard (microboard, actually) with as less JS as possible. -Also, I wanted to learn Go, so I decided to write this in Go. +The project began as a Go learning project and as an experiment in building a +lightweight imageboard with very little JavaScript. The former public instance +is offline because it required more moderation than the maintainer could +provide; the source remains available for local use and development. -Maybe I'll add an api and create a SPA with the Next.js framework in the future, but right now I'm trying to make this as simple as possible. +## Features -## Prerequisites -1. Go 1.13+. -2. PostgreSQL. -3. Create `.env` file from `.env.example`, change env vars to your needs. -4. Create db with the name that is specified in `.env` file in `POSTGRES_URL` env var. +- Server-rendered HTML using Fiber templates +- Thread creation, replies, catalog pagination, bump limits, and `sage` +- JPEG and PNG uploads with generated thumbnails +- Optional CAPTCHA and request rate limiting +- Automatic PostgreSQL migrations on startup +- Automatic archival when the active-thread limit is reached +- Optional registration with an external board gateway + +## Stack + +- Go (the module declares Go 1.15; CI builds with Go 1.17) +- [Fiber v2](https://github.com/gofiber/fiber) +- PostgreSQL via `pgx` +- Plain HTML templates and CSS + +## Run locally + +### Prerequisites + +- Go 1.17 or newer +- PostgreSQL +- A PostgreSQL database that the configured user can create tables in + +### Setup + +1. Create the database. With standard local PostgreSQL tooling: + + ```sh + createdb micrach + ``` + +2. Copy the example configuration: + + ```sh + cp .env.example .env + ``` + +3. Edit `.env`. At minimum, make `POSTGRES_URL` match your local database. + For normal development, set `IS_DB_SEEDED=false`; when true, sample rows are + inserted every time the application starts. + +4. Start the application from the repository root: + + ```sh + go run . + ``` + +5. Open [http://localhost:3000](http://localhost:3000), or the port configured + by `PORT`. + +Startup connects to PostgreSQL, applies missing files from `migrations/`, and +creates the ignored local `uploads/` directory. Uploaded originals and +thumbnails are stored on disk, so keep that directory persistent when running +the service in a container. + +## Configuration + +`.env` is loaded automatically and is ignored by Git. + +| Variable | Default in code | Purpose | +| --- | --- | --- | +| `ENV` | `release` | Runtime mode. `production` enables CSS filename rewriting at startup. | +| `PORT` | `3000` | HTTP listen port. | +| `POSTGRES_URL` | `postgresql://localhost/micrach` | PostgreSQL connection URL. | +| `IS_DB_SEEDED` | `false` | Insert sample posts at startup. Avoid enabling repeatedly. | +| `IS_RATE_LIMITER_ENABLED` | `true` | Enable Fiber request limiting. | +| `THREADS_MAX_COUNT` | `50` | Maximum active threads before old threads are archived. | +| `THREAD_BUMP_LIMIT` | `500` | Reply count after which a thread no longer bumps. | +| `IS_CAPTCHA_ACTIVE` | `true` | Require CAPTCHA for new threads and replies. | +| `GATEWAY_URL` | empty | External gateway base URL. Empty disables gateway integration. | +| `GATEWAY_API_KEY` | empty | Shared key used for gateway registration and `/api/ping`. | +| `GATEWAY_BOARD_ID` | empty | Board identifier sent to the gateway. | +| `GATEWAY_BOARD_URL` | empty | Parsed from the environment but not currently used by the gateway client. | +| `GATEWAY_BOARD_DESCRIPTION` | empty | Board description sent to the gateway. | + +See [.env.example](.env.example) for a complete example. + +## Development commands -## Run -Just run: ```sh -go run main.go +# Run with automatic restart; requires nodemon. +make dev + +# Format, test, vet, and build. +go fmt ./... +go test ./... +go vet ./... +go build . + +# Reproduce the Linux CI build. +GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build . ``` -**In development** I prefer to run it with [nodemon](https://github.com/remy/nodemon) for live reload. +`tests/load.sh` is an optional Vegeta load test. Its checked-in target is a +historical public URL, so change it to an environment you own before running +the script. Generated load-test files are ignored by Git. -## How it looks -![SCR-20230509-jreo](https://user-images.githubusercontent.com/15846431/237027105-481f2d55-541b-4c3b-8dd0-db464c25102f.jpeg) +## Containers and deployment + +The `Dockerfile` packages a prebuilt `micrach` binary together with templates, +static assets, and migrations. Build the Linux binary before building the +image. The checked-in `docker-compose.yml` is production-oriented: it expects +Docker Swarm, an existing `web` overlay network, Traefik, a published image, +and host-mounted upload storage. It is not a standalone local PostgreSQL setup. + +## Project layout + +```text +controllers/ HTTP handlers and request validation flow +repositories/ PostgreSQL queries and view data types +db/ connection pool and migration runner +config/ environment parsing +gateway/ optional external gateway integration +templates/ server-rendered pages, components, and head fragments +static/ CSS, icons, and error images +uploads/ runtime uploads (created locally, ignored by Git) +migrations/ ordered PostgreSQL schema changes +tests/ optional load-test scripts +``` + +## Screenshot + +![micrach catalog](https://user-images.githubusercontent.com/15846431/237027105-481f2d55-541b-4c3b-8dd0-db464c25102f.jpeg) + +## License + +[MIT](LICENSE)