Architecture¶
Container stack¶
βββββββββββββββββββββββββ host βββββββββββββββββββββββββ
Internet βββΊ reverse β βββββββββββββββββββββββ βββββββββββββββββββ β
proxy βββββββΌββΊβ web (php:8.2-apache)βββββββΊβ db (mysql:8.4) β β
(Apache/ β β 127.0.0.1:8081 β 80 β β internal net β β
nginx,TLS) β ββββββββββββ¬βββββββββββ ββββββββββ¬βββββββββ β
β β bind mounts β named volume β
β archive/ img/ lund_db_data β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
webβ built from the projectDockerfile(PHP 8.2 + Apache, extensionspdo_mysql,mysqli,zip,mod_rewrite). Serves the PHP pages and the API. Composer dependencies (PHPMailer, ramsey/uuid) are installed at build time.dbβ stock MySQL 8.4 image, attached to an internal Docker network only: it is never exposed to the outside (a loopback port mapping exists solely for administration tools on the host). The compose file passes a set of MySQL options tuned for importing large spatial datasets (administrative boundaries).docker-entrypoint.shβ at container start, aligns the UID/GID of the Apache user (www-data) with the owner of the mountedarchive/directory, so that host-owned files remain readable and writable from both sides.
Request flow¶
- The reverse proxy forwards the request to the web container.
- Pages (
*.phpat the project root) render the HTML skeleton and include shared fragments fromassets/. - The frontend JavaScript calls the internal API (
api/endpoint_private.php) viafetch, posting{class, action, ...params}requests; the API dispatches them to the correspondingAdc\*class (see API). - Data access goes through
Adc\Conn, a thin PDO wrapper providing prepared-statement CRUD helpers.
Code layout¶
βββ *.php # Pages (server-rendered shells; data arrives via the API)
βββ assets/ # Shared fragments: header, menu, footer, meta, forms
βββ api/
β βββ endpoint.php # Public JSON-LD API (read-only)
β βββ endpoint_private.php # Internal RPC-style API used by the frontend
β βββ composer.json # PHP dependencies
β βββ src/ # Adc\ namespace (PSR-4)
β βββ Conn.php # PDO wrapper (CRUD helpers, prepared statements)
β βββ Config.php # Storage paths configuration
β βββ Api.php # JSON-LD / CIDOC-CRM export
β βββ Artifact.php, Model.php, Media.php, Collection.php,
β βββ Institution.php, Person.php, User.php, Vocabulary.php,
β βββ Timeline.php, Geom.php, Stats.php, Get.php, File.php
βββ js/
β βββ config.js # API base URL and constants
β βββ components/ # Reusable UI builders (gallery, viewer, charts, mapsβ¦)
β βββ features/ # Page-level logic (artifact, model, dashboard, timelineβ¦)
β βββ modules/ # Domain modules (collection, user, institutionβ¦)
β βββ helpers/ shared/ # Utilities
β βββ maps/ # Leaflet plugins
βββ css/ img/ # Styles and static images
βββ db-init/ # SQL imported by MySQL on first start
βββ docs/ # This documentation (MkDocs)
There is no JavaScript build step: the frontend is vanilla ES modules served as-is, with Bootstrap for the UI, Leaflet (+ markercluster) for maps and 3DHOP for the 3D viewer.
Frontend state¶
Client-side state is centralised in a state object (see js/): frequently used data is kept in RAM and synchronised with localStorage, instead of having every function read and write localStorage directly. When modifying frontend code, go through the state object rather than touching localStorage yourself.
Storage¶
All persistent state lives in two places:
| What | Where | Notes |
|---|---|---|
| Database | lund_db_data named Docker volume |
Survives container rebuilds |
| Uploaded files | archive/ bind mount |
Subfolders: models/, image/, video/, document/, reference/, thumb/, tmp/ |
| Logos & icons | img/ bind mount |
logo/, ico/ |
Paths inside the application are resolved by Adc\Config, which derives the absolute storage directories from the document root.
Database¶
MySQL 8.4, utf8mb4 everywhere. Beyond the catalogue tables (artefacts, models, media, institutions, persons, users, vocabularies), two aspects deserve attention:
- Spatial data β the find-site forms and the map rely on administrative-boundary geometries (country β province β district β municipality, GADM-derived). These tables are large; the MySQL options in
docker-compose.yml(1 GBmax_allowed_packet, long timeouts, large buffers) exist to support importing and querying them. - Views β the timeline reads from the
time_series_completeview, which resolves each artefact's effective start/end date from the most specific chronological level defined (specific β generic β macro period).
A reference diagram of the database structure is available in the application itself, under db model in the menu.
Sessions and security¶
- Authentication is session-based (PHP sessions); cookies are
httponly, andsecure+SameSite=Nonewhen served over HTTPS. - The API rejects non-POST requests on the private endpoint; write operations require a valid session.
.htaccessrules disable directory listing and block direct access to.ini,.sql,.logand.shfiles; a Content-Security-Policy restricts framing to the platform itself plus the supported video platforms (YouTube, Vimeo).