⚠️ Under Development: Floe CMS is actively being developed. Features may be incomplete, and breaking changes may occur. Please check the issues page for updates or to report bugs.
Floe CMS is a lightweight, production-ready Content Management System written in Go. It's designed to be deployed as a single binary with all features included, making it easy to install and maintain.
- Single Binary Deployment: All components, including the admin UI, are embedded in a single executable.
- Multiple Database Support: SQLite (default, embedded), MySQL, and PostgreSQL.
- RESTful API: Complete API for managing content and media.
- Markdown-Based Content: Store and serve content in Markdown format.
- Role-Based Access Control: Admin, Editor, and Viewer roles with appropriate permissions.
- Multi-Tenancy: Support for multiple workspaces/organizations.
- Media Management: Upload, store, and serve images and other media.
- Modern Admin UI: Built with React and TailwindCSS.
- Extensible: Designed to be extended with additional features.
- Installation
- Quick Start
- Configuration
- Deployment
- API Documentation
- Development
- Troubleshooting
- Contributing
- License
Download the latest release from the Releases page.
# Download the binary
curl -L https://github.com/randilt/floe-cms/releases/latest/download/floe-cms-linux-amd64 -o floe-cms
# Make it executable
chmod +x floe-cms
# Run it
./floe-cmsdocker pull randilt/floe-cms:latest
docker run -p 8080:8080 -v floe-data:/app/data randilt/floe-cms:latest# Clone the repository
git clone https://github.com/randilt/floe-cms.git
cd floe-cms
# Build the frontend
cd web/admin
npm install
npm run build
cd ../..
# Build the Go binary
go build -o floe-cms
# Run it
./floe-cms-
Start the CMS:
./floe-cms
-
Access the Admin UI: Open your browser and navigate to
http://localhost:8080 -
Login:
- Email:
[email protected] - Password:
adminpassword
- Email:
-
Create Content:
- Create a content type
- Add content items
- Publish your content
-
Access Your Content:
- Via API:
http://localhost:8080/api/content/{workspace}/{slug} - Or build a frontend that consumes the API
- Via API:
Floe CMS can be configured using a configuration file, environment variables, or command-line flags.
Create a config.yaml file in the same directory as the binary:
server:
host: 0.0.0.0
port: 8080
graceful_shutdown: 30
timeouts:
read: 15
write: 15
idle: 60
database:
type: sqlite # sqlite, mysql, or postgres
url: floe.db # Used for SQLite
host: localhost
port: 5432
username: floe
password: floe
name: floe
ssl_mode: disable
auth:
jwt_secret: "" # Will be generated if empty
access_token_expiry: 900 # 15 minutes
refresh_token_expiry: 604800 # 7 days
admin_email: [email protected]
admin_password: adminpassword
password_min_length: 8
rate_limit_requests: 60
rate_limit_expiry: 60
storage:
type: local
uploads_dir: ./uploads
cache:
type: memory # memory or redis
redis_url: redis://localhost:6379/0
ttl: 300 # 5 minutesEnvironment variables override settings in the configuration file. All variables are prefixed with FLOE_.
# Example
export FLOE_SERVER_PORT=9000
export FLOE_DATABASE_TYPE=postgres
export FLOE_DATABASE_URL=postgres://user:pass@localhost:5432/floe
export FLOE_AUTH_JWT_SECRET=your-secret-keyCommand line flags override both environment variables and configuration file settings.
# Example
./floe-cms --port 9000 --db-url "postgres://user:pass@localhost:5432/floe" --reset-adminAvailable flags:
| Flag | Description |
|---|---|
--config |
Path to configuration file (default: "config.yaml") |
--port |
Override port defined in configuration |
--reset-admin |
Reset admin credentials to those in config |
--db-url |
Override database URL defined in configuration |
# Create a Docker volume for persistent data
docker volume create floe-data
# Run the container
docker run -d \
--name floe-cms \
-p 8080:8080 \
-v floe-data:/app/data \
-e FLOE_DATABASE_URL=/app/data/floe.db \
-e FLOE_STORAGE_UPLOADS_DIR=/app/data/uploads \
randilt/floe-cms:latestCreate a systemd service file at /etc/systemd/system/floe-cms.service:
[Unit]
Description=Floe CMS
After=network.target
[Service]
Type=simple
User=floe
WorkingDirectory=/opt/floe-cms
ExecStart=/opt/floe-cms/floe-cms
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.targetEnable and start the service:
sudo systemctl enable floe-cms
sudo systemctl start floe-cmsExample Nginx configuration:
server {
listen 80;
server_name cms.example.com;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}# Install Certbot
sudo apt install certbot python3-certbot-nginx
# Get certificate
sudo certbot --nginx -d cms.example.com
# Certbot will update your Nginx configurationPOST /api/auth/login
Request:
{
"email": "[email protected]",
"password": "adminpassword"
}Response:
{
"success": true,
"data": {
"access_token": "eyJhbGc...",
"refresh_token": "eyJhbGc..."
}
}POST /api/auth/refresh
Request:
{
"refresh_token": "eyJhbGc..."
}Response:
{
"success": true,
"data": {
"access_token": "eyJhbGc..."
}
}POST /api/auth/logout
Request:
{
"refresh_token": "eyJhbGc..."
}Response:
{
"success": true,
"data": {
"message": "Logged out successfully"
}
}GET /api/content?workspace_id=1&limit=10&offset=0
Response:
{
"success": true,
"data": {
"contents": [
{
"id": 1,
"title": "Example Post",
"slug": "example-post",
"body": "# Example\n\nThis is an example post.",
"status": "published",
"author": {
"id": 1,
"email": "[email protected]",
"first_name": "Admin",
"last_name": "User"
},
"content_type": {
"id": 1,
"name": "Blog Post",
"slug": "blog-post"
},
"created_at": "2023-01-01T00:00:00Z",
"updated_at": "2023-01-01T00:00:00Z",
"published_at": "2023-01-01T00:00:00Z"
}
],
"total": 1,
"limit": 10,
"offset": 0
}
}POST /api/content
Request:
{
"workspace_id": 1,
"content_type_id": 1,
"title": "New Post",
"slug": "new-post",
"body": "# New Post\n\nThis is a new post.",
"status": "draft"
}Response:
{
"success": true,
"data": {
"id": 2,
"title": "New Post",
"slug": "new-post",
"body": "# New Post\n\nThis is a new post.",
"status": "draft",
"created_at": "2023-01-02T00:00:00Z",
"updated_at": "2023-01-02T00:00:00Z"
}
}POST /api/media
Request (Form Data):
workspace_id: 1
file: [file upload]
name: example.jpg
Response:
{
"success": true,
"data": {
"id": 1,
"name": "example.jpg",
"file_name": "example.jpg",
"file_path": "/uploads/2023/01/01/example.jpg",
"mime_type": "image/jpeg",
"size": 12345,
"created_at": "2023-01-01T00:00:00Z"
}
}For complete API documentation, see API.md or the Swagger documentation at /swagger/index.html when running the CMS.
- Go 1.19 or higher
- Node.js 16 or higher
- npm or yarn
floe-cms/
├── cmd/
│ └── floe-cms/ # Command line entry point
├── internal/ # Internal packages
│ ├── api/ # API router and handlers
│ ├── auth/ # Authentication and authorization
│ ├── config/ # Configuration management
│ ├── db/ # Database management
│ ├── handlers/ # HTTP handlers
│ ├── middleware/ # HTTP middleware
│ ├── models/ # Data models
│ ├── storage/ # Storage management
│ └── utils/ # Utility functions
├── web/
│ └── admin/ # Admin UI (React)
├── config.yaml # Sample configuration
├── Dockerfile # Docker build file
├── go.mod # Go module definition
├── go.sum # Go module checksums
└── README.md # This file
-
Clone the Repository:
git clone https://github.com/randilt/floe-cms.git cd floe-cms -
Set Up the Frontend:
cd web/admin npm install npm run devThis will start the frontend development server at
http://localhost:5173. -
Run the Backend: In a separate terminal:
go run cmd/floe-cms/main.go
This will start the backend at
http://localhost:8080. -
Building for Production:
# Build the frontend cd web/admin npm run build cd ../.. # Build the backend with the embedded frontend go build -o floe-cms
This is caused by the rate limiting middleware. You can increase the limit in the configuration:
auth:
rate_limit_requests: 100 # Increase this value
rate_limit_expiry: 60If you're having trouble connecting to a database, check:
- Database credentials in your configuration
- That the database server is running
- Network connectivity to the database server
For SQLite, ensure the directory where the database file will be created is writable.
If the admin UI isn't loading, ensure:
- The frontend has been built (
cd web/admin && npm run build) - The go:embed directive is correctly pointing to the frontend build directory
- You've rebuilt the Go binary after building the frontend
To enable more verbose logging:
# In config.yaml
logging:
level: debug # debug, info, warn, errorOr set the environment variable:
export FLOE_LOGGING_LEVEL=debugWe welcome contributions to Floe CMS! Here's how to get started:
-
Fork the Repository: Fork the repository on GitHub.
-
Create a Branch:
git checkout -b feature/my-feature
-
Make Your Changes: Implement your feature or fix.
-
Run the Tests:
go test ./... -
Submit a Pull Request: Push to your fork and submit a pull request to the main repository.
- Follow Go best practices and coding standards
- Write tests for new features
- Document your code and update documentation as needed
Floe CMS is licensed under the MIT License. See LICENSE for more information.
For questions, issues, or support, please open an issue on GitHub.
Floe CMS - Seamless content management flow