Reference

Every public route, panel REST endpoint, configuration parameter, field constraint and the layout of the media directory — for looking things up quickly.

A page for checking facts rather than reading start to finish. Everything is taken from the sources of the main branch.

Public pages#

MethodAddressWhat it does
GET/front page: the 5 latest posts and the 2 latest albums
GET/recordsthe post feed, one item more than the page size
GET/records/pagea JSON slice of the feed for lazy loading
GET/records/read?id=Na post page
GET/mediathe album feed
GET/media/pagea JSON slice of the album feed
GET/media/album?id=Na photo album; forwards on its own for a video album
GET/media/video_album?id=Na video album
GET/author?id=Nan author page with all of their work
GET/tag?id=Na tag page
GET/historyhistory — static
GET/aboutabout — static
GET/projectsprojects — static
GET/educationeducational programmes — static
GET/auththe login form
POST/process_loginlogin processing, rate limited
POST/auth/logoutlogout, session and cookie cleared
GET/go/{path}a short link, resolved by internal forward
GET/sitemap.xmlthe sitemap, cached for an hour
GET/robots.txta static file
GET/media/**media files: served from disk when behind nginx
*/errorthe error page

The feed parameters page (zero-based) and type are optional. An unknown type value causes no error — the filter simply is not applied. Valid values are NEWS, STORY, ANNOUNCEMENT for posts and PHOTO, VIDEO for albums; case does not matter.

Panel: content#

Requires the ROLE_ADMIN or ROLE_MODERATOR role. Mutating requests require the CSRF token in the X-CSRF-TOKEN header.

MethodAddressWhat it doesLogged
GET/dashboardthe panel front page
GET/dashboard/tagsthe tag manager page
POST/dashboard/tags/createcreate a tagyes
GET/dashboard/tags/edit?id=Ntag data
PUT/dashboard/tags/editchange a tagyes
DELETE/dashboard/tags/delete?id=Ndelete a tagyes
GET/dashboard/authorsthe author manager page
POST/dashboard/authors/createcreate an authoryes
GET/dashboard/authors/edit?id=Nauthor data
PUT/dashboard/authors/editchange an authoryes
DELETE/dashboard/authors/delete?id=Ndelete an author and all their contentyes
GET/dashboard/poststhe post manager page
GET/dashboard/posts/requiredauthor and tag lists for the form
POST/dashboard/posts/createcreate a postyes
GET/dashboard/posts/edit?id=Npost data
PUT/dashboard/posts/editchange a postyes
DELETE/dashboard/posts/delete?id=Ndelete a postyes
GET/dashboard/mediathe media manager page
GET/dashboard/media/pagea JSON slice of the media list
POST/dashboard/media/uploadupload one file into the batchId directory
DELETE/dashboard/media/uploaddelete one file by its public path
POST/dashboard/media/createcreate an album recordyes
GET/dashboard/media/edit?id=Nalbum data
PUT/dashboard/media/editchange an albumyes
DELETE/dashboard/media/delete?id=Ndelete an album and its files on diskyes

Panel: administration#

Requires the ROLE_ADMIN role.

MethodAddressWhat it doesLogged
GET/dashboard/usersthe user manager
POST/dashboard/users/createcreate a useryes
GET/dashboard/users/edit?id=Nuser data
PUT/dashboard/users/editchange a useryes
DELETE/dashboard/users/delete?id=Ndelete a useryes
GET/dashboard/journalthe audit log page
GET/dashboard/journal_datalog entries, paginated
GET/dashboard/seothe SEO manager
GET/dashboard/seo/edit?path=…the current title and description of an address
POST/dashboard/seo/editchange the title and descriptionyes
POST/dashboard/protectionturn protection mode on or off, set the tokenyes
GET/dashboard/postprotectionxsstoggle post sanitizationyes
POST/dashboard/global_notificationbanner text and visibilityyes
POST/dashboard/contacts/addressthe organization addressyes
POST/dashboard/contacts/phonethe phone numberyes
POST/dashboard/contacts/emailthe e-mail addressyes
POST/dashboard/shortcutadd a short linkyes
DELETE/dashboard/shortcut?key=…remove a short linkyes
POST/dashboard/social_mediaadd a social linkyes
DELETE/dashboard/social_media?name=…remove a social link by nameyes
POST/dashboard/clear_cacheflush all cachesyes
GET/actuator/health, /metrics, /infoSpring Boot telemetry

Field constraints#

EntityFieldRule
Userloginat least 4 characters, unique
Userpasswordat least 8 characters, at least 1 Latin letter and 1 digit
Userrole, namenot blank
Posttitle4–50 characters
Postintroductionfrom 4 characters
Postbodynot blank
PosttypeNEWS, STORY or ANNOUNCEMENT
Postauthorexactly one, required on creation
Albumtitle4–50 characters
AlbumbatchIdrequired on creation, unique
AlbumtypePHOTO or VIDEO
Albumauthorexactly one, required on creation
Tagname2–30 characters, unique
Tagdescriptionfrom 4 characters
Authorname2–30 characters
Authoraboutunconstrained, optional

When editing a user, an empty password means "keep the current one".

Upload constraints in the panel UI: the cover accepts .jpg only, one file, up to 15 MB; album contents accept images and video up to 20 GB per file. The real maximum is the smaller of spring.servlet.multipart.max-file-size and client_max_body_size in nginx.

Configuration parameters#

Entries marked ! must be changed when deploying.

ParameterDefaultMeaning
server.port${SERVER_PORT:80}! the application port
server.forward-headers-strategyoff! honour proxy headers; required for correct absolute addresses
system.storage.patha Windows path from the developer's machine! the root of the media storage
web.storage.url-prefix/media/the prefix of public file addresses
web.storage.default-preview/media/author.jpgthe fallback cover for albums without one
web.storage.local-servingtrue! serve files from Spring; set to false behind nginx
spring.servlet.multipart.max-file-size10GB! per-file limit
spring.servlet.multipart.max-request-size50GB! per-request limit
spring.datasource.urljdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:otryadwebsite}the database address
spring.datasource.username${DB_USER:postgres}! the database user
spring.datasource.password${DB_PASSWORD:postgres}! the database password
spring.jpa.hibernate.ddl-autoupdateautomatic schema creation
spring.jpa.open-in-viewfalsethe session closes before rendering; leave it alone
page-size6the page size in the feeds
app.cache.lifetime60minutes a cache entry lives after last access
app.cache.maxsize500entries per cache
server.compression.enabledtruecompression for html, xml, text and JSON
security.admin.logina value committed to the repository! the first administrator's login
security.admin.passworda value committed to the repository! the first administrator's password
security.attempts.limit5never read anywhere; the limits are hardcoded
management.endpoints.web.exposure.includehealth,metrics,infowhich telemetry is exposed
devtruea development flag; has no business being on in production
spring.thymeleaf.prefixoffread templates from a directory on disk instead of the jar

The environment variables SERVER_PORT, DB_HOST, DB_PORT, DB_NAME, DB_USER and DB_PASSWORD are substituted into the corresponding properties.

Layout of the media directory#

<system.storage.path>/
├── 3f9c1b2e-.../          one album's directory, named after its batchId
│   ├── preview.jpg        the cover; always this name, never in the gallery
│   ├── 01-first.jpg       contents; gallery order is alphabetical
│   ├── 02-second.jpg
│   └── 03-video.mp4
└── 7a1d4c8f-.../
    ├── preview.jpg
    └── 01-clip.mp4

A file's public address is assembled as web.storage.url-prefix + batchId + / + file name, that is /media/3f9c1b2e-.../01-first.jpg.

Database tables#

TableContents
postsposts; indexed by type and date
media_collectionsalbums; path holds the batchId and is unique
content_authorscontent authors
content_tagstags
post_tags_mapthe post-to-tag join
collection_tags_mapthe album-to-tag join
userspanel staff
action_logsthe audit log

Caches#

NameWhatLifetime
postposts by id60 minutes after last access
mediumalbums by id60 minutes after last access
authorauthors with their work60 minutes after last access
tagtags with their material60 minutes after last access
sitemapsitemap addresses60 minutes after being written
bucketsrate-limit counters24 hours, up to 10,000 entries

Technologies and versions#

WhatVersion or role
Java17 in the build settings; newer works too
Spring Boot4.0.2
StartersWeb MVC, Data JPA, Security, Validation, Cache, Actuator, Thymeleaf, DevTools
PostgreSQLdriver in the dependencies; any supported server version
Caffeinein-memory cache
Bucket4j8.10.0, rate limiting
AspectJauditing and limits
Lombokgetters, setters, logger
Thymeleafserver-side templates
EasyMDEthe Markdown editor in the panel
Marked.jsMarkdown rendering in the browser
Dropzone.jsdrag-and-drop file uploads
SweetAlert2dialogs and notifications
H2tests only

The build is Maven, with a wrapper (./mvnw). The artifact is an executable jar at target/OtryadWebsite-1.0.jar. There is no frontend build: every library ships as a ready-made file in the static resources.