{"id":30751300,"url":"https://github.com/arran4/goa4web","last_synced_at":"2026-02-04T05:14:47.222Z","repository":{"id":303602483,"uuid":"673649816","full_name":"arran4/goa4web","owner":"arran4","description":"My everything website (server) from 2005 ported from C to GO. Ready for news, forums, link sharing, image sharing and more","archived":false,"fork":false,"pushed_at":"2026-01-21T02:23:52.000Z","size":11639,"stargazers_count":2,"open_issues_count":8,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-21T10:57:00.485Z","etag":null,"topics":["cms","forum","image-sharing","link-sharing","news","webserver"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arran4.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2023-08-02T05:44:18.000Z","updated_at":"2026-01-20T11:23:46.000Z","dependencies_parsed_at":"2025-07-30T04:25:11.657Z","dependency_job_id":"aad4ea8e-7f51-47b2-ace7-19ed4d0f97f5","html_url":"https://github.com/arran4/goa4web","commit_stats":null,"previous_names":["arran4/goa4web"],"tags_count":154,"template":false,"template_full_name":null,"purl":"pkg:github/arran4/goa4web","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arran4%2Fgoa4web","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arran4%2Fgoa4web/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arran4%2Fgoa4web/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arran4%2Fgoa4web/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arran4","download_url":"https://codeload.github.com/arran4/goa4web/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arran4%2Fgoa4web/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28752696,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-25T10:25:12.305Z","status":"ssl_error","status_checked_at":"2026-01-25T10:25:11.933Z","response_time":113,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cms","forum","image-sharing","link-sharing","news","webserver"],"created_at":"2025-09-04T07:46:49.458Z","updated_at":"2026-02-04T05:14:47.215Z","avatar_url":"https://github.com/arran4.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Goa4Web\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"og-image.png\" alt=\"GoA4Web\" /\u003e\n\u003c/p\u003e\n\n[![CI](https://github.com/arran4/goa4web/actions/workflows/go_test.yaml/badge.svg)](https://github.com/arran4/goa4web/actions/workflows/go_test.yaml)\n\nGoa4Web is composed of modular packages written in Go. It powers the original `arran4` website, providing a collection of community features including blogs, forums, a bookmark manager and an image board.\n\n\n## Features\n\n- **News** – publish posts and allow discussion in comment threads. Writers,\n  moderators and administrators may edit posts.\n- **Help** – manage question and answer entries with administrative tools.\n- **Blogs** – users can write blogs, comment on posts and subscribe to bloggers.\n- **Forum** – a traditional threaded forum with categories, topics and moderation tools.\n- **Linker** – a directory of community links with suggestion and approval queues.\n- **Bookmarks** – authenticated users can maintain personal bookmark lists.\n- **Image BBS** – boards with threaded image posting support.\n- **Search** – full-text search across the various sections of the site.\n- **Writings** – long form articles organised into categories.\n- **User Management** – registration, login and preference pages. Permissions rely on roles and grants.\n\nMost handlers share one package and `cmd/goa4web/main.go` maps them directly for simplicity. [sqlc](https://github.com/kyleconroy/sqlc) generates the `internal/db/models.go` file and the `Queries` type used across handlers.\n\nOptional notification emails can be sent through several providers. See the [Email Provider Configuration](#email-provider-configuration) section for details. The template for these messages lives under `core/templates/email/updateEmail.gotxt`.\n\n## Getting Started\n\n1. Install Go 1.23 or newer and ensure `go` is available in your `PATH`.\n2. Download dependencies and build the application.\n   ```bash\n   go mod download\n   go build -o goa4web ./cmd/goa4web\n   ```\n3. Create a database named `a4web` using your preferred server (MySQL). The schema is defined in `database/schema.mysql.sql`.\n\n   To setup the database, provide the connection string and use the `db setup` command:\n   ```bash\n   # Example for MySQL\n   DB_CONN=\"user:password@tcp(127.0.0.1:3306)/a4web?parseTime=true\" ./goa4web db setup\n   ```\n   This command applies the schema and inserts initial seed data (roles and grants).\n4. Run the application:\n   ```bash\n   # Using environment variables for configuration\n   DB_CONN=\"user:password@tcp(127.0.0.1:3306)/a4web?parseTime=true\" ./goa4web serve\n   ```\n\nDuring development you can load templates directly from disk. Extract the embedded templates and point the server at the directory:\n```bash\ngoa4web templates extract -dir ./tmpl\ngo run ./cmd/goa4web --templates-dir ./tmpl\n```\nThe default build embeds templates and `main.css`, producing a self-contained binary.\n\n## Running\n\nRun the compiled binary and open \u003chttp://localhost:8080\u003e in your browser. By default the server listens on port 8080; change this with the `--listen` flag or `LISTEN` environment variable:\n```bash\n./goa4web\n```\nThe server uses your configured database and optional AWS credentials to send email notifications. Most features require login. Sessions live in signed cookies via `gorilla/sessions`.\nThe server resolves the cookie signing secret in this order:\n1. `--session-secret` flag\n2. `SESSION_SECRET` environment variable\n   the file specified by `--session-secret-file` or `SESSION_SECRET_FILE` (if unset, the server chooses a default)\n   (`GOA4WEB_DOCKER` places it under `/var/lib/goa4web/`)\n\nIf the file is missing, the server generates a random secret and writes it.\nGorilla/csrf protects form submissions. Templates embed tokens and the middleware verifies them on POST requests.\n\n## Repository Layout\n\n```text\n.\n├── cmd/goa4web/         – HTTP router and entry point\n├── config/              – environment variable helpers\n├── core/templates/      – HTML and email templates\n├── examples/            – generated configuration examples\n├── migrations/          – database schema migrations\n├── database/schema.mysql.sql    – initial database schema\n├── core/templates/templates.go – load templates from disk or embedded data\n├── internal/db/models.go       – sqlc generated data models\n└── internal/db/queries-*.sql   – SQL queries consumed by sqlc\n```\n\n### Section registration\n\nSite sections register navigation items with the `navigation` package so menus assemble dynamically.\nUse `navigation.RegisterIndexLink` for public links and `navigation.RegisterAdminControlCenter` for admin navigation. The admin call also accepts a `section` string used to group links. Each call accepts a weight value; lower numbers appear first.\n\nExample weights:\n\n```text\nNews        10\nBlogs       20\nWritings    30\nForum       40\nLinker      50\nImageBBS    60\nBookmarks   70\nSearch      80\nHelp        90\nServer Stats 140\n```\n\n## Testing\n\nUnit tests focus mainly on utility packages and template compilation. Execute all tests with the `nosqlite` tag:\n```bash\ngo test -tags nosqlite ./...\n```\n\n---\n\n## Contributing\n\nThis project is primarily maintained for personal use, but others are welcome to adopt it and contribute improvements.\n\n## Application Configuration File\n\nUse the `--config-file` flag or `CONFIG_FILE` environment variable to load a general configuration file. Command line parsing runs in two phases so this flag can appear early. The file may set any configuration key before the remaining flags are parsed.\n\n## Database Configuration\n\nProvide the database connection string and driver name. The program resolves values in this order:\n\n1. Command line flags (`--db-conn` and `--db-driver`)\n2. Values from a config file specified with `--config-file` or `CONFIG_FILE`\n3. Environment variables such as `DB_CONN`\n\nThe config file uses the same `key=value` format as the email configuration file.\nGenerate example settings with:\n```bash\ngo run ./cmd/goa4web config as-env-file \u003e examples/config.env\n```\n\n`examples/config.env` might contain:\n```conf\n# examples/config.env\nDB_DRIVER=mysql\nDB_CONN=user:password@tcp(127.0.0.1:3306)/a4web?parseTime=true\nLISTEN=:8080\nHOSTNAME=http://localhost:8080\nAUTO_MIGRATE=true\n```\n\nRun `goa4web config options --extended` to see detailed descriptions of all\nconfiguration keys.\n\n## Email Provider Configuration\n\nThe application supports multiple email backends. Choose one by setting `EMAIL_PROVIDER`:\n\n- `ses` (default): Amazon SES. Requires valid AWS credentials and `AWS_REGION`.\n  The provider is built only when the `ses` build tag is enabled.\n- `smtp`: Standard SMTP server using `SMTP_HOST`, optional `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, `SMTP_AUTH`, `SMTP_STARTTLS` and `SMTP_TLS`.\n- `local`: Uses the local `sendmail` binary.\n- `jmap`: Sends mail using JMAP. Requires `JMAP_ENDPOINT`, `JMAP_USER`, and `JMAP_PASS`.\n  When `JMAP_ACCOUNT` or `JMAP_IDENTITY` are omitted they are discovered from the JMAP session.\n- `sendgrid`: Uses the SendGrid API. Requires the `sendgrid` build tag and a `SENDGRID_KEY`.\n- `log`: Writes emails to the application log.\n\nWhen connecting to port `465` set `SMTP_TLS=true` and `SMTP_STARTTLS=false`. Enable only one of these options.\n\nIf any configuration or credentials are missing, email is disabled and a log message appears.\n\nYou can also set values in a file or via command line flags. The program resolves them in this order:\n1. `--smtp-host` and related flags\n2. Values from a config file specified with `--config-file` or `CONFIG_FILE`\n3. Environment variables such as `SMTP_HOST`\n4. Built-in defaults\n\nThe config file uses a simple `key=value` format matching the environment variable names.\n\nAdministrator change notifications are on by default when a mail provider is configured. Set `ADMIN_NOTIFY=false` to disable them.\n\nRun `goa4web config as-env-file` to generate a file with all email settings.\n\n## HTTP Server Configuration\n\nConfigure the HTTP server address and base URL like any other setting:\ncan be configured the same way as other settings:\n\n1. Command line flags (`--listen` and `--hostname`)\n2. Values from a config file specified with `--config-file` or `CONFIG_FILE`\n3. Environment variables `LISTEN` and `HOSTNAME`\n4. Built-in defaults (`:8080` and `http://localhost:8080`)\n\nSee `examples/config.env` for an auto-generated configuration file.\n\n`HOSTNAME` should include the scheme and optional port, e.g. `http://example.com`.\n\nWhen serving traffic over HTTPS via a reverse proxy, set `--hostname` to the external\n`https://` address so generated links use the correct scheme. The server continues to\nlisten on the address specified by `--listen`. Use `--hsts-header` to configure the\n`Strict-Transport-Security` header or disable it by providing an empty value.\n\n## Pagination Configuration\n\nThe program resolves the page size range and default value in this order:\n\n1. Command line flags (`--page-size-min`, `--page-size-max`, `--page-size-default`)\n2. Values from a config file specified with `--config-file` or `CONFIG_FILE`\n3. Environment variables (`PAGE_SIZE_MIN`, `PAGE_SIZE_MAX`, `PAGE_SIZE_DEFAULT`)\n4. Built-in defaults (5, 50 and 15)\nAdministrators can temporarily adjust these limits through `/admin/page-size`. This only changes the running configuration; update the config file to retain the values after a restart.\nIndividual users can override the default value for their account via `/usr/paging`.\n\n## Configuration Reference\n\nYou can supply settings on the command line, in a config file or via environment variables. Flags override the config file, which overrides environment variables. The file uses the same keys as the variables listed below.\n| Key | CLI Flag | Required | Default | Description |\n| --- | --- | --- | --- | --- |\n| `DB_CONN` | `--db-conn` | Yes | - | Database connection string. |\n| `DB_DRIVER` | `--db-driver` | Yes | `mysql` | Database driver name. |\n| `EMAIL_PROVIDER` | `--email-provider` | No | `ses` | Selects the mail sending backend. |\n| `EMAIL_FROM` | `--email-from` | No | - | Default From address for outgoing mail. Must be a valid RFC 5322 address. |\n| `EMAIL_SIGNOFF` | `--email-signoff` | No | - | Optional sign off text appended to emails. |\n| `SMTP_HOST` | `--smtp-host` | No | - | SMTP server hostname. |\n| `SMTP_PORT` | `--smtp-port` | No | - | SMTP server port. |\n| `SMTP_USER` | `--smtp-user` | No | - | SMTP username. |\n| `SMTP_PASS` | `--smtp-pass` | No | - | SMTP password. |\n| `SMTP_AUTH` | `--smtp-auth` | No | `plain` | SMTP authentication method (plain, login, cram-md5). |\n| `SMTP_STARTTLS` | `--smtp-starttls` | No | `true` | Enable or disable STARTTLS. |\n| `AWS_REGION` | `--aws-region` | No | - | AWS region for the SES provider. |\n| `JMAP_ENDPOINT` | `--jmap-endpoint` | No | - | JMAP API endpoint. |\n| `JMAP_ACCOUNT` | `--jmap-account` | No | - | JMAP account identifier. Defaults to the primary mail account returned by `/.well-known/jmap` when omitted. |\n| `JMAP_IDENTITY` | `--jmap-identity` | No | - | JMAP identity identifier. Defaults to the mail identity from the JMAP session when omitted. |\n| `JMAP_USER` | `--jmap-user` | No | - | Username for the JMAP provider. |\n| `JMAP_PASS` | `--jmap-pass` | No | - | Password for the JMAP provider. |\n| `JMAP_INSECURE` | `--jmap-insecure` | No | false | Skip TLS certificate verification. |\n| `CONFIG_FILE` | `--config-file` | No | - | Path to the main configuration file. |\n| `EMAIL_ENABLED` | n/a | No | `true` | Toggles sending queued emails. |\n| `NOTIFICATIONS_ENABLED` | n/a | No | `true` | Toggles the internal notification system. |\n| `CSRF_ENABLED` | n/a | No | `true` | Enables or disables CSRF protection. |\n| `FEEDS_ENABLED` | `--feeds-enabled` | No | `true` | Toggles RSS and Atom feed generation. |\n| `PAGE_SIZE_MIN` | `--page-size-min` | No | `5` | Minimum allowed page size. |\n| `PAGE_SIZE_MAX` | `--page-size-max` | No | `50` | Maximum allowed page size. |\n| `PAGE_SIZE_DEFAULT` | `--page-size-default` | No | `15` | Default page size. |\n| `STATS_START_YEAR` | `--stats-start-year` | No | `2005` | First year displayed on the usage stats page. |\n| `DB_LOG_VERBOSITY` | `--db-log-verbosity` | No | `0` | Database logging verbosity. |\n| `LOG_FLAGS` | `--log-flags` | No | `0` | Bit mask selecting HTTP request logs. |\n| `LISTEN` | `--listen` | No | `:8080` | Network address the HTTP server listens on. |\n| `HOSTNAME` | `--hostname` | No | `http://localhost:8080` | Base URL advertised by the HTTP server. |\n| `SESSION_SECRET` | `--session-secret` | No | generated | Secret used to encrypt session cookies. |\n| `SESSION_SECRET_FILE` | `--session-secret-file` | No | auto | File containing the session secret. |\n| `SESSION_SAME_SITE` | `--session-same-site` | No | `strict` | Cookie SameSite policy for sessions. |\n| `GOA4WEB_DOCKER` | n/a | No | - | Places secret files under `/var/lib/goa4web` when unset paths rely on defaults. |\n| `SENDGRID_KEY` | `--sendgrid-key` | No | - | API key for the SendGrid email provider. |\n| `EMAIL_WORKER_INTERVAL` | `--email-worker-interval` | No | `60` | Minimum seconds between queued email sends. |\n| `EMAIL_VERIFICATION_EXPIRY_HOURS` | `--email-verification-expiry-hours` | No | `24` | Hours an email verification link remains valid. |\n| `PASSWORD_RESET_EXPIRY_HOURS` | `--password-reset-expiry-hours` | No | `24` | Hours a password reset request remains valid. |\n| `LOGIN_ATTEMPT_WINDOW` | `--login-attempt-window` | No | `15` | Minutes to track failed logins for throttling. |\n| `LOGIN_ATTEMPT_THRESHOLD` | `--login-attempt-threshold` | No | `5` | Failed logins allowed within the window. |\n| `ADMIN_EMAILS` | `--admin-emails` | No | - | Comma-separated list of administrator email addresses. |\n| `ADMIN_NOTIFY` | n/a | No | `true` | Toggles sending administrator notification emails. |\n| `IMAGE_UPLOAD_DIR` | `--image-upload-dir` | No | `uploads/images` | Directory where uploaded images are stored when using the local provider. |\n| `IMAGE_UPLOAD_PROVIDER` | `--image-upload-provider` | No | `local` | Upload backend to use. |\n| `IMAGE_UPLOAD_S3_URL` | `--image-upload-s3-url` | No | - | S3 prefix URL for uploads when using the S3 provider. |\n| `IMAGE_CACHE_PROVIDER` | `--image-cache-provider` | No | `local` | Cache backend to use. |\n| `IMAGE_CACHE_S3_URL` | `--image-cache-s3-url` | No | - | S3 prefix URL for cache when using the S3 provider. |\n| `IMAGE_CACHE_DIR` | `--image-cache-dir` | No | `uploads/cache` | Directory for cached thumbnails when using the local provider. |\n| `IMAGE_CACHE_MAX_BYTES` | `--image-cache-max-bytes` | No | `-1` | Maximum image cache size in bytes. |\n| `IMAGE_MAX_BYTES` | `--image-max-bytes` | No | `5242880` | Maximum allowed size of uploaded images. |\n| `DEFAULT_LANGUAGE` | `--default-language` | No | - | Site's default language name. |\n| `DLQ_PROVIDER` | `--dlq-provider` | No | `log` | Dead letter queue provider. |\n| `DLQ_FILE` | `--dlq-file` | No | `dlq.log` | File path for the file or directory DLQ providers. |\n| `AUTO_MIGRATE` | `--auto-migrate` | No | `false` | Run database migrations on startup. |\n| `MIGRATIONS_DIR` | `--migrations-dir` | No | `embedded` | The directory to load migrations from at runtime. |\n| `CREATE_DIRS` | `--create-dirs` | No | `false` | Create missing directories on startup. |\n\nPaths using the `s3://` scheme must include a bucket name and may specify an optional prefix, e.g. `s3://mybucket/uploads`.\n\n### Dead Letter Queue Providers\n\nThe `DLQ_PROVIDER` setting selects how failed messages are recorded:\n\n* `log` – writes messages to the application log (default)\n* `file` – appends messages to a file using separator lines at `DLQ_FILE`. Each entry begins with an RFC3339 timestamp\n* `dir` – creates one file per message under the directory `DLQ_FILE` using a KSUID name\n* `db` – stores messages in the database\n* `email` – sends messages to administrator addresses using the configured mail provider\n\nMessages include any error details and full email contents when available.\nExample config file:\n\n```conf\nDB_CONN=myuser:secret@tcp(localhost:3306)/a4web?parseTime=true\nDB_DRIVER=mysql\nEMAIL_PROVIDER=smtp\nLISTEN=:8080\nHOSTNAME=http://example.com:8080\n```\n\nExample files under `examples/` are generated automatically.\n\n### Implementing Custom Providers\n\nNew email backends can be added by satisfying the `Provider` interface\ndefined in `internal/email/provider.go`:\n\n```go\ntype Provider interface {\n    Send(ctx context.Context, to mail.Address, rawEmailMessage []byte) error\n}\n```\n\nCreate a new file implementing this interface and add a case in\n`providerFromConfig` that returns your provider. Providers that rely on optional\ndependencies should live behind a build tag. See `internal/email/sendgrid.go` for an\nexample provider built with the `sendgrid` tag.\n\n## Database Upgrades\n\nDatabase schema changes are stored in the `migrations/` directory. Run\n`goa4web db migrate` to apply all pending scripts using your configured\ndatabase connection. Set `AUTO_MIGRATE=true` or pass `--auto-migrate=1`\nto perform this step automatically when the server starts.\nEvery new migration must conclude with an `UPDATE schema_version` statement, and the `ExpectedSchemaVersion` constant in `handlers/constants.go` should be incremented.\n\nWhen upgrading from v0.0.1 the script `migrations/0002.mysql.sql` must be applied.\nThis can be done manually using the `mysql` client:\n\n```bash\nmysql -u a4web -p a4web \u003c migrations/0002.mysql.sql\n```\n\nThe script adds tables for notifications and email queues, updates existing columns and records the schema version.\n\n## Admin tools\n\n### Permission section checker\n\nThe `/admin/permissions/sections` page lists all distinct values found in the `grants.section` column. It provides tools to convert any legacy `writings` entries to `writing` so older migrations remain consistent.\n\nThe linked counts let you drill down to view all permissions for a section via `/admin/permissions/sections/view?section=\u003cname\u003e`.\n\n## Command Line Interface\n\nThe `goa4web` binary includes many administrative commands in addition to\n`serve`, which starts the web server. Run `goa4web help` to see the full list of\nsubcommands. Most commands share the same configuration mechanism as the web\nserver and honour flags, config files and environment variables.\n\nWhen running `user add` or `user add-admin`, omit `--password` to be prompted securely.\n\nTypical workflow:\n\n```bash\n# build the tool\ngo build -o goa4web ./cmd/goa4web\n```\n\n### Creating users\n\n```bash\n# create a regular account\n./goa4web user add --username alice --email alice@example.com --password secret\n\n# create an administrator\n./goa4web user add-admin --username admin --email admin@example.com --password changeme\n\n# promote an existing user to administrator\n./goa4web user make-admin --username alice\n```\n\n### Managing permissions\n\n```bash\n# grant a permission\n./goa4web perm grant --user alice --section forum --role moderator\n\n# list all permissions\n./goa4web perm list\n\n# revoke a permission by ID\n./goa4web perm revoke --id 42\n```\n\n### Database operations\n\nRefer to the [Database Upgrades](#database-upgrades) section for migration\ninstructions.\n\n```bash\n# create a backup\n./goa4web db backup --file backup.sql\n\n# restore from a backup\n./goa4web db restore --file backup.sql\n```\n\n### Configuration utilities\n\n```bash\n# show all available options\n./goa4web config options --extended\n\n# generate an env file with current values\n./goa4web config as-env-file \u003e config.env\n\n# reload configuration without restarting\n./goa4web config reload\n```\n\n### Additional subcommands\n\nThe CLI exposes many other commands for day‑to‑day maintenance. Some commonly\nused examples include:\n\n- `role` – manage site roles and view users assigned to each role.\n- `grant` – control the default permission grants applied to new users.\n- `board` – create and update image boards.\n- `blog` – inspect blog posts and their comments.\n- `writing` – access writing articles and comment threads.\n- `news` – list news items and manage comments.\n- `faq` – administer frequently asked questions.\n- `ipban` – list or update IP bans.\n- `images` – view uploaded images and metadata.\n- `email queue` – inspect, resend or delete queued emails.\n- `audit` – display recent audit log entries.\n- `notifications` – trigger notification tasks.\n- `server shutdown` – gracefully stop a running instance.\n- `repl` – start an interactive shell for running commands.\n\n## Docker Deployment\n\nA pre-built container image is available from the GitHub Container Registry.\nPull the latest version with:\n\n```bash\ndocker pull ghcr.io/arran4/goa4web:latest\n```\n\nStart the container with environment variables for your database connection:\n\n```bash\ndocker run -p 8080:8080 \\\n  -e DB_DRIVER=mysql \\\n  -e DB_CONN=\"user:password@tcp(host.docker.internal:3306)/a4web?parseTime=true\" \\\n  -e AUTO_MIGRATE=true \\\n  ghcr.io/arran4/goa4web:latest\n```\n\n**Note**: The example above assumes a MySQL database is accessible from the container. `host.docker.internal` is used to reach the host machine on some Docker installations. For a self-contained setup including a database, see the Docker Compose section.\n\nSetting `GOA4WEB_DOCKER=1` tells the application to store generated secret files\nsuch as `session_secret` under `/var/lib/goa4web`, and sets default storage paths\nto `/var/lib/goa4web/images` (uploads) and `/var/cache/goa4web/thumbnails` (cache).\nMount `/var/lib/goa4web` as a volume to persist secrets and images across container restarts.\nThe container runs as the unprivileged `goa4web` user (UID 65532), so ensure any mounted directories\nare writable by that UID on the host.\n\nExisting users who wish to preserve their custom paths can explicitly set `IMAGE_UPLOAD_DIR` and `IMAGE_CACHE_DIR`.\n\n### Docker Compose\n\nThe following `docker-compose.yaml` example runs MySQL and applies migrations on startup.\n\n```yaml\nversion: '3.8'\nservices:\n  db:\n    image: mysql:8\n    restart: always\n    environment:\n      MYSQL_DATABASE: goa4web\n      MYSQL_ROOT_PASSWORD: changeme\n    volumes:\n      - db-data:/var/lib/mysql\n\n  app:\n    image: ghcr.io/arran4/goa4web:latest\n    ports:\n      - \"8080:8080\"\n    environment:\n      GOA4WEB_DOCKER: \"1\"\n      DB_DRIVER: mysql\n      DB_CONN: root:changeme@tcp(db:3306)/goa4web?parseTime=true\n      AUTO_MIGRATE: \"true\"\n    volumes:\n      - app-data:/var/lib/goa4web\n      - app-cache:/var/cache/goa4web\n    depends_on:\n      - db\n\nvolumes:\n  db-data:\n  app-data:\n  app-cache:\n```\n\nSave the file as `docker-compose.yaml` and run:\n\n```bash\ndocker compose up\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farran4%2Fgoa4web","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farran4%2Fgoa4web","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farran4%2Fgoa4web/lists"}