Skip to content
Hassan Zayyan
← All work

Case study · Aug – Sep 2026 · Backend Developer (freelance)

SIPANDAI Rembes

Attendance and performance recaps for village officials: a REST API with an accuracy-aware geofence and sealed monthly reports.

Client
Pemerintah Desa Rembes
Role
Backend developer: Laravel API, PostgreSQL schema, authentication and roles, business and scoring rules, photo validation, scheduled jobs, PDF/Excel reports, CI/CD, server and backups
Team
Two people: a teammate built the Next.js PWA
Stack
Laravel 12 (PHP 8.3), PostgreSQL 16, Sanctum, Pest, OpenAPI (Scramble), Nginx, GitHub Actions
Frontend
Next.js PWA, static export (teammate)
Status
Delivered, September 2026

Context

A village office of 13 officials needed attendance with photo and location checks, plus monthly performance recaps the village head signs. It had to ship as a link rather than an APK, and work on both phones and laptops.

What I built

  • A REST API under /api/v1 with Sanctum tokens and policies for three roles: admin, employee, and a read-only village leader.
  • Check-in and check-out with photo and location, validated on the server.
  • Leave requests with approval, and daily work reports.
  • Monthly PDF and Excel recaps, sealed once signed, with an audit log of corrections.
  • A scheduled daily close and monthly seal.
  • OpenAPI documentation generated from the code, and Pest tests on the scoring and geofence rules.
  • Deployment on one VPS behind Nginx, with a GitHub Actions CI/CD pipeline, a database-backed queue, and scheduled jobs.

Architecture

Architecture of SIPANDAI RembesPhones and laptops load the Next.js PWA, a static export. On a single VPS, Nginx serves the PWA at the root of one domain and forwards /api/v1 requests to the Laravel 12 API on PHP-FPM, which applies Sanctum authentication, policies, and the geofence and scoring rules. The API stores data in PostgreSQL 16 and keeps photos outside the web root, served through short-lived signed URLs. A Laravel scheduler and a database-backed queue under Supervisor run the daily close and the monthly seal.One VPS2 vCPUs, 4 GB RAMPhone or laptop browserNext.js PWA (static export)Nginx, one domain/ → static PWA/api/v1/* → PHP-FPMLaravel 12 APIPHP-FPM, Sanctum,policiesGeofence andscoring rulesScheduler +queueDaily close,monthly sealDatabase queue,SupervisorPhoto storageOutside the webroot, short-livedsigned URLsPostgreSQL 16

Decisions

  1. 01

    A unique (user, date) constraint in PostgreSQL

    Two check-in requests arriving at the same moment both pass an application-level check, and only the database constraint is atomic. The API turns the violation into a friendly “already checked in” message.

  2. 02

    The server clock decides the score

    Otherwise moving a phone's clock back earns a perfect score.

  3. 03

    An accuracy-aware geofence

    Laptops have no GPS and can report positions anywhere from tens of meters to kilometers off. The check combines distance with the device's reported accuracy, from most to least certain (clearly inside, then office network, then borderline and flagged for review), so laptops can check in without opening a loophole.

  4. 04

    Sealed monthly recaps with an audit log

    A signed report must not change silently when an old record is corrected, so reopening a sealed month is an explicit, logged action.

  5. 05

    Built for a small VPS

    Everything runs on one server with 2 vCPUs and 4 GB of RAM, so the system uses a database queue instead of Redis, DomPDF instead of headless Chromium, and a static frontend with no Node process on the server.

  6. 06

    Face detection, not recognition

    The system confirms that one face is in front of the camera and rejects photos that carry camera EXIF data (gallery uploads). It does not verify identity. That limitation is documented for the client, with identity checks proposed as a later phase.

Outcome

Delivered in September 2026 for the office's 13 officials: attendance with photo and location checks on phones and laptops, and monthly recaps that stay sealed once the village head signs them.

Screenshots

Screenshot pending
OpenAPI documentation generated from the code.
Screenshot pending
Dashboard with dummy data.