Teknisk dokumentation · Hitta Ditt Sverige

Systemarkitektur: Kommunvertikalen

Stack: PHP 8.3 · MySQL 8.0 · Vanilla JS/HTML/CSS · nginx · EcoDataCenter (Piteå)
Frontend: Publika sidor
🌐hittadittsverige.se
Nationell startsida. Kommunkort, karta, exploration entry points.
Static HTML
🏛pitea.hittadittsverige.se
Kommunportal. Branding via _applyCommuneBranding(). loadCommune() API-call vid init.
HTML+JSAPI
📄commune-subpage.html
Generisk mall för alla 8 undersidor. Läser page_slug från URL. Renderar blocks från API.
Templateblocks.php
📝artiklar/ · artikel/
Kommunartikellistning med kategorifilter. Detaljsida med breadcrumb och body HTML.
editorial/list.php
hjalp/ · hjalp/artikel/
Kunskapsbas med sökning, kategorigrid och artikelrendering. Feedback i localStorage.
articles.json
Backend: API-lager
🔐_helpers.php
hds_require_auth(), hds_require_entity_access(), hds_audit(), hds_db(), hds_ok/err(). Kärna för all API-logik.
Auth corePDO
🏛commune/settings.php
GET/PATCH kommunens varumärke, navigation, kontakt. Cached via APCu.
GET PATCH
📦commune/blocks.php
CRUD för page_blocks. page_slug param. 10 blocktyper validerade.
GET POST PATCH DELETE
👥commune/users.php
GET team, PATCH roll (inkl. village_admin + village_id), DELETE user. Joins village_admins.
GET PATCH DELETE
📝editorial/ (save, list, delete)
CRUD artiklar. Non-admin scoped till hds_managed_komkod. Kategorifilter i list.php.
POST GET DELETE
📷upload/ (commune-image, entity)
Magic-byte validering, GD resize 2000px, max 20 per kommun. Public GET utan auth.
POST GET DELETE
🏠village/details.php
Byprofil: GET sammanfogar villages.json + village_overrides. PATCH upserts overrides.
GET PATCHvillage_overrides
📋commune/audit-log.php
Returnerar senaste 50 loggposter för alla konton kopplade till kommunen.
GETaudit_log
Dashboard och data
🔧dashboard/index.html
SPA-liknande dashboard. ROLE_CONFIG mappar roller till flikar. Alla administrativa funktioner.
~2500 rader JS
📊ROLE_CONFIG objekt
admin, commune_coordinator, tourism_board, rural_dev, field_agent, village_admin, forening, business, member_local
9 roller
🗃data/entities.json
510+ entiteter. Statisk fil, läses direkt av frontend. Källa för entity_commune-tabell.
JSON statisk
🏠data/villages.json
Byar med namn, koordinater, taxonomy. Bas för village/details.php.
JSON statisk
🗺data/ routes (GPX, JSON)
Cykelleder, vandringsleder, semesterrutter. Visas på karta och i rutter-katalogen.
GPX + JSON
APCu in-process cache
browse/catalog.php och commune/settings.php cachar i PHP-process. TTL 5 min.
In-memory
Viktiga dataflöden
Browser
pitea.hittadittsverige.se
loadCommune()
commune/settings.php
MySQL communes
Branding load (APCu cached 5min)
commune-subpage.html
commune/blocks.php?page_slug=X
commune_page_blocks
renderBlock(type, data)
Block rendering per page slug
Dashboard (coordinator)
commune/invite.php
accounts INSERT
_mailer.php → SMTP
Team invitation flow
Fältagent (app)
entities/update.php
hds_require_entity_access()
entity_commune lookup
Access grant: field_agent via entity_commune table
village_admin
hds_entity_id = village_id
entity.village == hds_entity_id
Village admin scoping via session entity_id field
Databastabeller: kommunvertikalen
accounts
  • id (PK)
  • display_name
  • email
  • role (ENUM)
  • managed_komkod
  • entity_id (village_id)
  • active TINYINT
commune_settings
  • commune_id (PK)
  • display_name
  • tagline
  • accent_color
  • logo_url
  • banner_url
  • hidden_sections JSON
commune_page_blocks
  • id (PK)
  • commune_id
  • page_slug
  • type (ENUM 10)
  • sort_order INT
  • data JSON
editorial_posts
  • id (PK)
  • slug (UNIQUE)
  • commune_id (nullable)
  • category
  • status ENUM
  • featured TINYINT
  • publish_at DATETIME
entity_commune
  • entity_id (PK)
  • commune_id
  • /* 510 rows from entities.json */
village_admins
  • id (PK)
  • account_id
  • village_id
  • created_at
  • UNIQUE(account, village)
village_overrides
  • id (village_id PK)
  • tagline
  • description TEXT
  • image_url
  • updated_at
audit_log
  • id (PK)
  • account_id
  • action VARCHAR
  • entity_id
  • ip_hash
  • created_at
entity_blocks
  • id (PK)
  • entity_id
  • type ENUM
  • sort_order INT
  • data JSON
  • created_at
entity_branding
  • entity_id (PK)
  • accent_color
  • heading_color
  • logo_url
  • updated_at
contact_messages
  • id (PK)
  • entity_id
  • sender_name
  • sender_email
  • message TEXT
  • read_at DATETIME
  • created_at
entity_seo_meta
  • entity_id (PK)
  • seo_title
  • meta_description
  • slug UNIQUE
  • updated_at
gallery_albums
  • id (PK)
  • entity_id
  • name
  • cover_image_url
  • sort_order INT
  • created_at
Technical Documentation · Hitta Ditt Sverige

System Architecture: The Municipal Vertical

Stack: PHP 8.3 · MySQL 8.0 · Vanilla JS/HTML/CSS · nginx · EcoDataCenter (Pitea)
Frontend: Public Pages
🌐hittadittsverige.se
National homepage. Municipality cards, map, exploration entry points.
Static HTML
🏛pitea.hittadittsverige.se
Municipality portal. Branding via _applyCommuneBranding(). loadCommune() API call on init.
HTML+JSAPI
📄commune-subpage.html
Generic template for all 8 sub-pages. Reads page_slug from URL. Renders blocks from API.
Templateblocks.php
📝artiklar/ · artikel/
Municipality article listing with category filter. Detail page with breadcrumb and body HTML.
editorial/list.php
hjalp/ · hjalp/artikel/
Knowledge base with search, category grid, and article rendering. Feedback in localStorage.
articles.json
Backend: API Layer
🔐_helpers.php
hds_require_auth(), hds_require_entity_access(), hds_audit(), hds_db(), hds_ok/err(). Core for all API logic.
Auth corePDO
🏛commune/settings.php
GET/PATCH municipality brand, navigation, contact. Cached via APCu.
GET PATCH
📦commune/blocks.php
CRUD for page_blocks. page_slug param. 10 block types validated.
GET POST PATCH DELETE
👥commune/users.php
GET team, PATCH role (incl. village_admin + village_id), DELETE user. Joins village_admins.
GET PATCH DELETE
📝editorial/ (save, list, delete)
CRUD articles. Non-admin scoped to hds_managed_komkod. Category filter in list.php.
POST GET DELETE
📷upload/ (commune-image, entity)
Magic-byte validation, GD resize 2000px, max 20 per municipality. Public GET without auth.
POST GET DELETE
🏠village/details.php
Village profile: GET merges villages.json + village_overrides. PATCH upserts overrides.
GET PATCHvillage_overrides
📋commune/audit-log.php
Returns the last 50 log entries for all accounts linked to the municipality.
GETaudit_log
Dashboard and Data
🔧dashboard/index.html
SPA-style dashboard. ROLE_CONFIG maps roles to tabs. All administrative functions.
~2500 lines JS
📊ROLE_CONFIG object
admin, commune_coordinator, tourism_board, rural_dev, field_agent, village_admin, forening, business, member_local
9 roles
🗃data/entities.json
510+ entities. Static file, read directly by frontend. Source for entity_commune table.
JSON static
🏠data/villages.json
Villages with name, coordinates, taxonomy. Base for village/details.php.
JSON static
🗺data/ routes (GPX, JSON)
Cycling trails, hiking routes, holiday routes. Shown on map and in routes catalogue.
GPX + JSON
APCu in-process cache
browse/catalog.php and commune/settings.php cache in PHP process. TTL 5 min.
In-memory
Key Data Flows
Browser
pitea.hittadittsverige.se
loadCommune()
commune/settings.php
MySQL communes
Branding load (APCu cached 5min)
commune-subpage.html
commune/blocks.php?page_slug=X
commune_page_blocks
renderBlock(type, data)
Block rendering per page slug
Dashboard (coordinator)
commune/invite.php
accounts INSERT
_mailer.php → SMTP
Team invitation flow
Field Agent (app)
entities/update.php
hds_require_entity_access()
entity_commune lookup
Access grant: field_agent via entity_commune table
village_admin
hds_entity_id = village_id
entity.village == hds_entity_id
Village admin scoping via session entity_id field
Database Tables: the municipal vertical
accounts
  • id (PK)
  • display_name
  • email
  • role (ENUM)
  • managed_komkod
  • entity_id (village_id)
  • active TINYINT
commune_settings
  • commune_id (PK)
  • display_name
  • tagline
  • accent_color
  • logo_url
  • banner_url
  • hidden_sections JSON
commune_page_blocks
  • id (PK)
  • commune_id
  • page_slug
  • type (ENUM 10)
  • sort_order INT
  • data JSON
editorial_posts
  • id (PK)
  • slug (UNIQUE)
  • commune_id (nullable)
  • category
  • status ENUM
  • featured TINYINT
  • publish_at DATETIME
entity_commune
  • entity_id (PK)
  • commune_id
  • /* 510 rows from entities.json */
village_admins
  • id (PK)
  • account_id
  • village_id
  • created_at
  • UNIQUE(account, village)
village_overrides
  • id (village_id PK)
  • tagline
  • description TEXT
  • image_url
  • updated_at
audit_log
  • id (PK)
  • account_id
  • action VARCHAR
  • entity_id
  • ip_hash
  • created_at
entity_blocks
  • id (PK)
  • entity_id
  • type ENUM
  • sort_order INT
  • data JSON
  • created_at
entity_branding
  • entity_id (PK)
  • accent_color
  • heading_color
  • logo_url
  • updated_at
contact_messages
  • id (PK)
  • entity_id
  • sender_name
  • sender_email
  • message TEXT
  • read_at DATETIME
  • created_at
entity_seo_meta
  • entity_id (PK)
  • seo_title
  • meta_description
  • slug UNIQUE
  • updated_at
gallery_albums
  • id (PK)
  • entity_id
  • name
  • cover_image_url
  • sort_order INT
  • created_at