OtryadWebsite

A website and content management system for a youth organization: news, photo and video galleries, an admin panel, SEO with Schema.org markup. Spring Boot 4 and PostgreSQL, with no frontend build step at all.

preparation for production July 26, 2026
Java 17Spring Boot 4.0Spring SecuritySpring Data JPAPostgreSQLThymeleafCaffeineBucket4jAspectJnginx

OtryadWebsite is a public-facing website and a full content management system, written for a youth organization. The organization publishes news, trip reports, photo albums and video; all of that had to end up in the hands of people who never open a code editor.

Hence the project comes in two halves:

  • the public site — a news feed, galleries, static pages, SEO, short links, sitemap.xml;
  • the admin panel (/dashboard) — managers for posts, media, authors, tags and users, an SEO editor, an audit log, site settings.

The key architectural idea: content lives in PostgreSQL, files live on disk, site settings live in a JSON file next to the application. None of it requires a rebuild — add a post through the panel and it is on the site.

The whole thing at a glance#

Browser / search crawler nginx — TLS, compression, file serving, proxying everything else /media/** Spring Boot — all the logic files from disk, bypassing Java PostgreSQL properties.json media files directory
Three sources of state: the database holds content, the disk holds files, the JSON file holds settings.

The interesting parts#

Two-phase pagination. spring.jpa.open-in-view=false, yet lists have to be rendered together with their authors and tags. First a page of identifiers is queried, then each entity is fetched by id from the Caffeine cache. This sidesteps the well-known "pagination plus fetch join" problem and makes the second page nearly free into the bargain.

SEO as data, not as code. Titles, descriptions and the Schema.org type for every page live in properties.json and are edited from the panel. Templates for dynamic pages live there too, with placeholders such as <title> and <author>. JSON-LD is assembled by walking the chain of parent pages, so breadcrumbs come out automatically.

Auditing and rate limits as aspects. Annotate a method with @AuditAction("Create post") and an entry shows up in the log along with the arguments; annotate a parameter with @AuditIgnore and ****** is written instead of the value. Rate limiting works the same way: @RateLimit, with a key that can be given as a SpEL expression.

Protection mode. While the site is being prepared for launch it is closed to everyone except staff and anyone holding a link with a token. The token is remembered in the session and stripped from the address bar by a redirect.

Documentation#

  • Installation and deployment behind nginx — from a bare server to HTTPS and systemd, including which settings you must change.
  • Usage — working in the admin panel: posts, albums, authors, tags, SEO, short links, protection mode, the audit log.
  • How it works — the full technical description: layers, request lifecycle, data model, caching, security, the SEO pipeline.
  • Reference — every route, REST endpoint and configuration parameter in one set of tables.

Заметка

The source code is distributed under the MIT license. The texts, photographs and trademarks belong to the organization the site was built for — if you use the code as a template, replace the content with your own.