FlyCms: a Java CMS where the content model is created in the admin panel
FlyCms 是一个类似知乎以问答为基础的完全开源的JAVA语言开发的社交网络建站程序,基于 Spring Boot+Bootstrap3+MyBatis+MySql+Solr +Ehcache应用架构,专注于社区内容的整理、归类和检索,它集合了问答,digg,wiki 等多个程序的优点,帮助用户轻松搭建专业的知识库和在线问答社区。业务模块包括:权限管理,会员管理,角色管理,定时任务管理(调度管理),问答管理,文章管理,分享管理,短信接口管理和邮件系统发送(注册、找回密码、邮件订阅),跨域登录,消息推送,全文检索、前端国际化等等众多模块,等您自己来体验!
At a glance
- What is it?
- FlyCms is a Spring Boot and Vue 3 system whose central idea is a custom model engine: articles, images, downloads, questions and links are all generated from field definitions made in the admin panel rather than from hardcoded modules. The legacy hardcoded modules are being retired one by one, and the search layer is currently a placeholder.
- Who is it for?
- FlyCms suits a team that has outgrown fixed article and category tables and wants to add a new content type by defining fields in the admin panel rather than by writing a module, and the model, field, dynamic table and template channel pipeline is the part worth studying.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The model engine is the whole architecture in four tables
FlyCms started as a Zhihu-style question and answer community program and, after sustained refactoring, its kernel is a CMS base described as five-way separated. The claim in the README is blunt: the core technology is the custom model engine, where everything the system does with content, articles, images, downloads, questions and links, is generated by online modelling in the admin panel, through a sequence of modelling, automatic table creation, field configuration and template rendering, with no second hardcoded implementation.
The pipeline is spelled out as a chain of four steps: fly_model for model registration, fly_model_field for field metadata, fly_cmodel_{code} for the dynamic data table, and then a generic management page plus a cmodel template channel. The code suffix on the table name is the mechanism, every model gets its own table named after its code.
The stated goal is a universal system where users can generate any program module they want using only the admin settings. The historical hardcoded content modules are being retired one at a time and moved into the model engine, with articles, questions, sharing and topics in that queue, while comments and favourites are being converged into platform capabilities.
That last point is the honest caveat. A system mid-migration has two ways of doing the same thing at once, and the README names the retirement plan explicitly, including the document that tracks it in the docs/01 planning directory. For a project you intend to build on, that means reading which modules are still hardcoded before you design anything around them.
The five planes are content, structure, presentation, interaction and operations. Content covers where content comes from and what it looks like, structure covers the column tree and URL layout, presentation covers template rendering, interaction covers how visitors participate, and operations covers how the site is administered.
A modern Java stack with one deliberately unfinished seam
The technology choices are current, which is unusual for a CMS of this kind.
The backend runs on JDK 24 with the maven-compiler-plugin at proc:full so Lombok works, on Spring Boot 4.1.1 in the Jakarta namespace, with MyBatis through mybatis-spring-boot-starter 4.1.0 where XML and mapper files are packaged together. The front end is rendered by Freemarker with the ALLOWS_NOTHING_RESOLVER sandbox switched on. Caching is Caffeine, which has replaced the earlier EhCache 2. The connection pool is Druid 1.2.28, manually assembled under Boot 4 by a DruidConfig class. Scheduling is Quartz 2.3.2, where background tasks can be started and stopped and their execution logs viewed. The database is MySQL 8 with utf8mb4, and the SQL is adapted only for MySQL. The rest is Hutool 5.8, jsoup 1.17, BCrypt and Log4j2, plus the Alibaba Cloud SMS SDK, used for captchas, XSS cleaning and password hashing.
The front end is Vue 3.5.41 with TypeScript 6.0, Vite 8.3 and Ant Design Vue 4.2, with Pinia 4 and Vue Router 5 for state and routing, on the Vue Vben Admin 5 base with its internal @vben packages. The project is a pnpm 11 workspace with turbo, and the Node requirement is ^22.18 || ^24.12. The admin is a single page application in apps/web-antd talking to the backend over the /api/ path, while the public site is still server-rendered Freemarker, and the two do not interfere with each other.
The one component to flag is search. The table describes Lucene 7.4 as a technology-neutral seam, noting that the original Solr has been removed, that SearchService keeps an empty implementation, and that Elasticsearch integration is planned. The deployment section repeats it: Solr is no longer needed, the retrieval module has been changed to a technology-neutral seam, and it runs normally without Elasticsearch, with search returning empty results.
So a project with a full text search requirement is looking at a stub. That is stated plainly rather than hidden, which is better than a broken Solr config, but it is a missing feature rather than a solved one.
Setting up the database, and the utf8mb4 trap
The quick start is unusually explicit about the database step, and the warning attached to it is the kind of thing that otherwise costs an afternoon.
The sql/ directory keeps a single full backup snapshot containing the table structure and all data together, covering every feature and permission node that is live, so there is no need to run incremental scripts stage by stage:
mysql -u root -p -e "CREATE DATABASE flycms DEFAULT CHARACTER SET utf8mb4;"
mysql -u root -p --default-character-set=utf8mb4 flycms < sql/flycms_20260928_093335.sqlThe default-character-set flag is mandatory, and the reason given is that the mysql client in some terminal environments defaults to a latin1 connection, which causes Chinese data to be genuinely corrupted on write rather than merely displayed wrongly.
The snapshot was exported with MySQL 5.7.44 and is compatible with a MySQL 8 import. After the import, the default administrator is flycms with the password admin123, and the documentation asks you to change it immediately after logging in.
Backups are built in rather than depending on mysqldump. The system includes a pure Java full database backup, reachable from the admin under system tools, database backup, or by calling the endpoint directly, which requires an administrator session:
The endpoint is a POST to /api/system/tools/db/backup and needs an administrator session.
Backup files are written to backend/backup with a flycms prefix and a timestamp, and to refresh the sql/ snapshot you copy the newest one over.
The rest of the setup is short. The backend needs its database connection changed in backend/src/main/resources/application.yml, then it compiles and runs with Maven. The front end is a separate step, with the dev server on port 5666 and a Vite proxy already configured so that the /api/ path forwards to the backend on port 80 and /captcha goes straight through, because the captcha endpoint has no /api prefix. The backend itself listens on http://localhost:80 by default.
Security design, and where the sandbox actually bites
There is a security section in the README, and it is more specific than most CMS documentation, which makes it a good thing to read before judging the project.
Template remote code execution is handled by the Freemarker sandbox using TemplateClassResolver.ALLOWS_NOTHING_RESOLVER, so a template author cannot reach arbitrary classes. That is a meaningful control, because online template editing is a headline feature and template editing is exactly the kind of feature that turns into remote code execution when it is left unguarded.
Upload handling uses UploadSafeUtil.safeImage with a triple whitelist of extension, MIME type and file header magic numbers, and it forces a rename and discards the original filename, which closes the usual route where a file with an image extension turns out to be something else. SQL injection is handled by an ORDER BY whitelist through OrderbyUtils.check, and dynamic table and column names go through SqlSafeUtil.safeColumnName and safeNumber, which matters in a system that creates tables on the fly. Submitted content is cleaned with jsoup using Safelist.none() in the form module, and page output escapes HTML. Access control combines session authentication with requireAdmin() returning 401 and requirePermission() returning 403. Login is protected by a captcha and a failure count limit, with BCrypt for passwords.
The last row is the one worth dwelling on. Form data goes into a unified table as JSON, and no dynamic table is created for it, specifically to avoid DDL permission leakage. In a system whose whole model is automatic table creation, the author has deliberately carved out an exception, and the reason given is instructive.
The platform also has data driven menus, roles and permission points, where the same actionKey is used both for frontend button visibility through accessCodes and as a backend 403 fallback. An AdminLogInterceptor records admin operations with sensitive fields masked, and each template save takes an automatic snapshot so a broken template can be rolled back with one click.
Freemarker tags, the model engine's public face
The template tags are where the model engine becomes visible to whoever writes a theme, and they are the most useful part of the documentation for judging whether the system is really generic.
A paginated model list comes with its own pagination bar:
<@fly_page_model model="loupan" rows="10">
<#list dataList as row>
<a href="/loupan/${row.shortUrl}.html">${row.title}</a>
</#list>
${pageHtml!''}
</@fly_page_model>
<@fly_block key="home_focus">
<#list block.items as it><img src="${it.image}"></#list>
</@fly_block>The first tag is the proof of the model engine. There is no article tag and no question tag, there is a model tag that takes a model code and a row count, and the data comes back as a list. A theme written against fly_page_model works for a housing listing, a download item or a question collection without changes, which is exactly the claim the model engine makes.
The second tag is a block or recommendation slot, addressed by key, so the placement of content on a page is data rather than template code.
Templates live under views/templates with a theme directory, and the template centre in the admin can edit them online with a file tree view, and saving produces a version snapshot.
Elsewhere the feature list shows what the front end does: four column shapes, a list, a single page, an external link and an aggregation that can mix models, with the directory name serving as the URL and unlimited nesting. There is a form tag that outputs a form and submits to a POST endpoint. Registration works by phone or email with a login captcha and a failure count limit, password recovery, phone and email binding, and invitation rewards. There is a sitemap and robots file driven by configuration keys for status and limit. And the content module front end routes by model code, with pagination built in.
Deployment, defaults you must change, and the state of the repository
Packaging the backend for production is a Maven package and a jar run:
cd backend
java -jar target/FlyCms.jar --spring.profiles.active=prod > FlyCms.log 2>&1 &The deployment notes are the part to read carefully. Configuration is externalised by copying application.yml to application-prod.yml and editing it, and the documentation is emphatic that the default database password has to be changed. Static resources and upload directories are configured in spring.web.resources.static-locations as ./views/static/ and ./uploadfiles/, and templates live in ./views/templates/.
The scope of the project is also worth measuring. The README claims 346 Java files, 25 business modules and 21 REST controllers in the backend, and 73 page components in the Vue front end. The directory listing shows backend/src/main/java with config for MVC, CORS, WebSocket, the Freemarker sandbox and Druid, core for entities, utilities, global tags and upload safety, interceptor for login checks and the audit log, a module directory for the business modules and a web directory split into api for the REST surface, front for public page controllers and tags for the custom Freemarker tags. There is an i18n directory, a frontend pnpm workspace, the sql snapshot and a docs directory holding development plans, the model manual, refactoring plans, review reports and operations configuration.
The licence is MIT with a LICENSE file at the root, and there is a lefthook.yml for git hooks, a tools/ directory, a .github/ directory and an .agents/ directory.
The last push was on 2026-10-08 and the repository is not archived, though there are no GitHub releases, so there is no version to pin and the version of the SQL snapshot in the repository is the version you get.
Editorial conclusion
FlyCms suits a team that has outgrown fixed article and category tables and wants to add a new content type by defining fields in the admin panel rather than by writing a module, and the model, field, dynamic table and template channel pipeline is the part worth studying. It does not suit you if you want a stable product today, because the hardcoded modules are still mid-migration into the model engine and the search module is a stub that returns empty results without Elasticsearch. Before you evaluate it, load the sql snapshot and look at how the fly_model and fly_model_field tables drive the dynamic tables, change the default flycms admin123 password immediately, and read the deployment notes about externalising the configuration for production.
Frequently asked questions
What is the custom model engine in FlyCms?
It is the core mechanism: content modules are created online in the admin panel through a chain of model registration in fly_model, field metadata in fly_model_field, a dynamic table named fly_cmodel_{code}, and then a generic management page and cmodel template channel, instead of separate hardcoded implementations.
How do I set up the FlyCms database?
The sql/ directory holds a single full snapshot, so create the database with utf8mb4 and import sql/flycms_20260928_093335.sql with --default-character-set=utf8mb4 on the mysql command, which the documentation says is required to avoid the client connecting with latin1 and corrupting Chinese data on write.
Does FlyCms still need Solr for search?
No. The original Solr integration has been removed, Lucene 7.4 is left as a technology-neutral seam with an empty SearchService implementation, and Elasticsearch integration is planned. The system runs without it, with search returning empty results.
What are the default FlyCms administrator credentials?
After importing the sql snapshot, the default administrator is flycms with the password admin123. The documentation asks you to change the password immediately after logging in, and to change the default database password in the externalised production configuration.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sunkaifei-flycms)Was this useful?