A Twitter-clone. The original Python/Flask + SQLite app has been rewritten in Go + MongoDB and runs on a Docker Swarm of DigitalOcean droplets, with Prometheus/Grafana/Loki for observability and a Discord bot for alerts.
The service exposes two interfaces on the same port:
- UI — server-rendered HTML for end users (
/,/login,/timeline,/user/{username}, …) - Simulator API — JSON endpoints consumed by the simulator (
/register,/msgs,/fllws/{username},/latest, …)
Public site: https://itu-minitwit.me
| Path | Purpose |
|---|---|
| src/ | Go application (gorilla/mux router, handlers, middleware, DB helpers) |
| src/bot/ | Discord bot — separate Go module with its own go.mod, built into its own image |
| docker/ | Dockerfiles for the web and bot images |
| monitoring/ | Prometheus, Grafana, Loki, Promtail configs and dashboards |
| infra/ | Terraform definition of the production infra (droplets, firewall, MongoDB) |
| remote_files/ | Files copied to the Swarm manager: authoritative docker-stack.yml, deploy.sh, TLS bootstrap |
| .github/workflows/ | CI (static analysis, tests, E2E) and CD (build → push → deploy) |
| Vagrantfile, setup-swarm.sh | Legacy provisioning path (still works) |
| docker-compose.yml | Local dev stack |
| report/ | Report in PDF format |
Requirements: Docker + Docker Compose. Go 1.22+ only needed for running tests outside containers.
docker compose up --build # web on :8080, MongoDB, Prometheus, Grafana, Loki, Promtail
docker compose down -vRun the Go service directly (needs a MongoDB on localhost:27017):
cd src && go run main.goCommon dev commands:
make verify # full local CI check (build, sim, lint, UI E2E)
make test-sim # run the grading simulator against :8080
make ui-e2e # Selenium UI tests (needs geckodriver)
./test-api-routes.sh # API smoke testsProduction runs a Docker Swarm of three DigitalOcean droplets (1 manager + 2 workers) with a managed MongoDB cluster. DNS for itu-minitwit.me lives at Namecheap (not IaC-managed).
There are two provisioning paths:
cd infra
cp terraform.tfvars.example terraform.tfvars # fill in tls_email, discord_token, grafana_admin_password, …
export DIGITALOCEAN_TOKEN=<your-token>
terraform init
terraform plan
terraform apply # ~5–8 min; provisions droplets + MongoDB + deploys the stackOutputs include the manager IP and MongoDB URI needed for the SSH_HOST and MONGO_URI GitHub Secrets.
The original path is still functional. Requires the same DIGITALOCEAN_TOKEN, an SSH key at ~/.ssh/ssh_key_golang_minitwit registered in DO under the same name, and a local .env with DOCKER_USERNAME, MONGO_URI, DISCORD_TOKEN, GRAFANA_ADMIN_USER, GRAFANA_ADMIN_PASSWORD.
vagrant up # creates droplets, then runs setup-swarm.sh which deploys the stack
vagrant rsync && ./setup-swarm.sh # re-deploy onto existing droplets
vagrant provision # runs the script in Vagrant fileMONGO_URI should point to an external managed MongoDB (e.g. DO Managed MongoDB or Atlas).
The reverse proxy (nginx + Let's Encrypt) is bootstrapped once per manager with remote_files/bootstrap_droplet_tls.sh. It is rerun automatically by the CD workflow's post-deploy verification step.
{fix|feature}/{short-message}
Examples: feature/terraform, fix/login-redirect.
- Branch off
development. - Push your branch and open a PR into
development. CI runs automatically. - Once your PR is merged into
development, the same checks run again. When a release is ready,developmentis merged intomain. - Pushing to
maintriggers continuous deployment to production.
static-analysis.yml builds the images and runs:
- Docker Scout + Trivy + Semgrep security scans
make verify(simulator test,go fmt,golangci-lint, Hadolint)make ui-e2e(Selenium UI E2E tests)
- Builds and pushes the web, bot, Prometheus, and Grafana images to Docker Hub
- SCPs deploy files and a freshly rendered
.envto the Swarm manager - Runs
remote_files/deploy.shon the manager - Verifies the deploy: nginx config, certbot renewal, TLS chain
The required GitHub Secrets are: DOCKER_USERNAME, DOCKERHUB_AUTHTOKEN, SSH_HOST, SSH_USER, SSH_PRIVATE_KEY, MONGO_URI, DISCORD_TOKEN, GRAFANA_ADMIN_USER, GRAFANA_ADMIN_PASSWORD, TLS_DOMAIN, TLS_EMAIL.
- Prometheus scrapes the webserver's
/metrics(custom counters/histograms in src/middleware/metrics.go). - Grafana is served at
/grafana/in production (dashboards and datasources provisioned from monitoring/grafana/). - Loki + Promtail collect container logs; Promtail runs
mode: global(one per Swarm node) reading/var/lib/docker/containers/. - Discord bot posts alerts to a Discord channel via the token in
DISCORD_TOKEN.
Each request gets an X-Request-ID propagated through context; all user-supplied values are sanitized before logging.
make report # build the course report PDF