Content Management System

Secure, multilingual CMS
built without compromise

UV Web CMS is a full-featured, Flask-based content management system with granular RBAC, dynamic routing, per-page CSS/JS injection, SEO automation, and a plugin architecture - built from scratch for full ownership and control.

Get in touch Admin panel
68
Granular permissions
EN/ET
Multilingual
0
External CMS deps
92%
Production ready

Why build a CMS instead of using one?

Third-party CMS platforms impose trade-offs: plugin ecosystems that age badly, security models you cannot audit, content APIs locked behind vendor contracts, and deployment constraints tied to their infrastructure.

UV Web CMS was built to give organisations complete ownership. Pages, routes, permissions, content, CSS, JS, SEO settings, and analytics configuration all live in your database, served by code you control, deployed wherever you need it.

The RBAC model goes beyond simple admin/editor splits - 68 individual permissions cover every content operation, making it possible to give precisely the right access to each user or role without overreach.

System Architecture
Presentation
Public website Admin panel REST API
Application
Route handlers RBAC decorators Content mgmt SEO / Analytics
Extension
Plugin hooks Module registry Chameleon
Data
SQLAlchemy ORM SQLite / PgSQL File uploads

What the CMS manages

Dynamic pages and routing

New pages activate immediately from the database - no restart, no redeploy. Routes, navbar visibility, and page templates are all database-driven.

RBAC with 68 permissions

Users, roles, and permissions modelled separately with full many-to-many resolution. Page-level access grants per user and per role. Admin hierarchy bypasses all permission checks.

Multilingual content

Content items stored per language with automatic fallback chain (fr-CA to fr to en). Language switcher and cookie persistence. Currently EN and ET.

Per-page CSS and JS

Stylesheets and scripts stored in the database, assigned to specific pages with load order. Base template injects them dynamically - no build step required.

SEO automation

Keyword extraction, auto meta generation, per-page scoring, bulk optimisation, and audit history. Complete Open Graph and Twitter Card support built in.

Google Analytics 4

GDPR-compliant GA4 integration with gtag.js event tracking. Analytics dashboard in admin panel. Optional GA Data API integration for in-platform reporting.

Plugin and module system

Hook-based extension architecture with a module registry. Add new capabilities - integrations, custom processors, rendering plugins - without touching core code.

Content export API

REST API for content export and import with API key authentication. Enables content synchronisation between environments and external integrations.

Enterprise security

CSRF protection via Flask-WTF, XSS prevention, SQL injection protection via ORM, PBKDF2 password hashing, and session security baked in throughout.

68 permissions, not just admin/editor

Each permission covers a specific resource and action combination. Assign them individually to roles, or use the admin hierarchy to bypass all checks for trusted operators.

Page-level grants can be applied per-user or per-role independently of global permissions, enabling scenarios like giving a single editor access to one page without touching any other configuration.

Pages view, create, edit, delete, publish, permissions
Content view, create, edit, delete, bulk_edit
Users view, create, edit, delete, manage_roles
SEO view, write, auto_optimize, settings
Analytics view, configure, export
Components view, create, edit, delete, assign
CSS/JS view, upload, edit, delete, assign
System admin, settings, backups, modules

Technology stack

Layer Technology
Web FrameworkFlask 2.x
ORM / MigrationsSQLAlchemy + Flask-Migrate (Alembic)
AuthenticationFlask-Login, PBKDF2 hashing
Forms / CSRFFlask-WTF
Rich Text EditorCKEditor 5 (open source)
FrontendBootstrap 5, Vanilla JS
SEO ParsingBeautifulSoup4
AnalyticsGoogle Analytics 4 (gtag.js)
Testingpytest, pytest-flask, selenium
Database (dev)SQLite
Database (prod)PostgreSQL (planned)
App Server (prod)Gunicorn + Nginx (planned)

Feature status

User Management and RBAC Complete
Content Management Complete
Dynamic Routing and Navbar Complete
Per-page CSS/JS Management Complete
SEO Automation Complete
Google Analytics Integration Complete
Plugin / Module Architecture Complete
Multilingual (EN/ET) Complete
Content Export/Import API Complete
Image Management Complete
Page Template System (UI) Partial
Production Deployment (AWS) Planned

What changes without a release. What does not.

Every page rendered by UV Web CMS is composed of two independent layers with different change characteristics. Understanding this boundary is what distinguishes a CMS from a templated website.

No release required

Infrastructure layer - 100% DB-driven

Every page's structural metadata is resolved from the database on every request. Changes take effect immediately:

  • Route and URL -Page.route: URL changes instantly, no deploy
  • Active / inactive -Page.is_active: take a page offline in one toggle
  • Navbar label and position -Page.name, Page.nav_order, Page.show_in_navbar
  • CSS stylesheets -CSSStyle records assigned to a page; injected into <head>, full visual theming without touching a file
  • JavaScript behaviors -JSScript records, delivered to header or footer per-page; analytics, interactivity, A/B testing
  • SEO metadata -PageSEO: meta title, meta description, keywords, Open Graph title/description, canonical URL, schema.org type
  • Google Analytics tracking -SEOSettings.google_analytics_id: GA4 ID toggled from admin
  • Page title (browser tab) -Page.title (available; product pages currently override with {% block title %})
Requires a release

Content layer - 0% DB-driven on product pages (currently)

The visible body sections of all four product pages (wormwood.html, deltaprism.html, nexus.html, uvwebcms.html) are hardcoded in template files. Changing any of the following requires editing the .html file and redeploying:

  • Hero headline, subheadline, and CTA button text
  • Metric numbers and labels (36K ops/sec, 294 rules, etc.)
  • Section body copy (problem, solution, use-case paragraphs)
  • Feature card content and capability descriptions
  • Technology stack table rows
  • Status badge labels (Running, Roadmap, Planned)
  • Progress bar percentages
  • Navbar dropdown product links (hardcoded in base.html)
  • Section layout and grid structure
100%
Infrastructure DB-driven
routing, nav, SEO, CSS, JS
0%
Body content DB-driven
on product pages today
~70%
Content reachable via API
if templates are wired

The gap and the path

The PageContent model already exists - keyed HTML blocks, per-language, with a many-to-many relationship to pages. The admin panel can create and edit these blocks. The content export/import API already serves them. The only missing piece is template wiring: product templates referencing page.contents instead of hardcoded text.

With template wiring in place, editorial copy changes (headlines, descriptions, body paragraphs, CTA text) would be manageable from the admin panel without a code release. Structural changes (section layout, card count, table columns) would continue to require a release - approximately 30% of visible surface area that is purely HTML structure.

Note on flat vs. subdirectory templates: Pages using a flat template (no / in the template path) are rendered via render_page_content(page.key) in base.html, which reads directly from PageContent blocks - so content is fully DB-driven for those pages today. Product pages use subdirectory templates (products/wormwood.html) and fall through to {% block content %}, bypassing the content block system entirely.

Need a CMS you actually own?

UV Web CMS is available for deployment on your infrastructure. Get in touch to discuss licensing, customisation, or hosting.

Contact the team