This repository is the real Ktor + PostgreSQL-backed admin backend and website for LocaPin content operations.
- It is scoped to San Juan City only (no Cities module in product UI).
- It serves authenticated admin pages and backend APIs used to manage map-driven attraction content.
- It is intended to be the content source for mobile integration (backend APIs + database), not a mock/demo app.
- Kotlin + Ktor
- Exposed ORM
- PostgreSQL
- Static admin frontend (HTML/CSS/JavaScript)
- Login / session auth
- Dashboard
- Areas
- Attractions
- Attraction detail
- Photos
- Plans
- Users
- JDK 21
- PostgreSQL 14+
Create a .env file in the repository root.
DB_URL=jdbc:postgresql://localhost:5432/locapin
DB_USER=postgres
DB_PASSWORD=postgres
ADMIN_INITIAL_NAME=Super Admin
ADMIN_INITIAL_EMAIL=admin@locapin.com
ADMIN_INITIAL_PASSWORD=change-me-now
# Required outside development; min 32 chars
SESSION_SECRET=replace-with-a-long-random-secret-valueAPP_ENV=development
APP_PORT=9000
FILE_UPLOAD_DIR=uploads- Default server port is 9000.
APP_PORToverrides the default at runtime.- Config source:
src/main/resources/application.conf.
- Create a new PostgreSQL database:
CREATE DATABASE locapin;
- Ensure
.envDB_URL/DB_USER/DB_PASSWORDmatch your local DB. - Run the app once:
./gradlew run
- On first run, schema tables are created and bootstrap seeds deterministic San Juan data.
Bootstrap now ensures required baseline content exists and safely handles partially populated databases.
On a fresh DB, the full baseline seed is inserted. On a partially populated DB, missing required records are added without crashing startup.
- City scope seed: San Juan City
- Areas: Pinaglabanan, Greenhills, Little Baguio, West Crame, Addition Hills
- Attractions: 8 San Juan attractions (including Museo ng Katipunan, Pinaglabanan Shrine, Ronac Art Center, Fundacion Sanso, Art Sector Gallery, Greenhills Shopping Center, Greenhills Promenade, V-Mall)
- Plans: Explorer, Premium, Annual Premium
- End users + subscriptions for dashboard/testing counts
- Photo rows only if local image asset exists
- Required plans are ensured by name:
Explorer,Premium,Annual Premium - Required seed users are ensured by name:
Andrea Dela Cruz,Miguel Tan,Jasmine Flores - Subscription seed insertions are guarded, so missing keys no longer throw:
NoSuchElementException: Key Premium is missing in the map
When admin users table initially contains only the env superadmin, additional deterministic test accounts are added:
admin.test@locapin.local/AdminTest123!editor.test@locapin.local/EditorTest123!
These credentials are logged in development mode only.
From repository root:
./gradlew runAdmin login URL:
After first startup on a fresh DB, verify:
- Login succeeds with env superadmin.
- Dashboard shows non-zero San Juan seeded counts.
- Areas page shows seeded San Juan areas.
- Attractions page shows seeded attractions.
- Attraction detail page loads full detail payload (name/description/knownFor/highlights/coords).
- Photos page loads, lets you pick an attraction, and hits real upload/reorder/delete backend routes.
- Plans page loads seeded plans.
- Users page loads admin users.
- No Cities module is present in active admin product scope.
- Session-cookie auth is required for
/admin/api/*endpoints. - Static assets are served under
/staticand uploaded files under/uploads.