A Go-based REST API backend for Tulis, a content management system built with Fiber framework.
- Framework: Go Fiber v2
- ORM: GORM (MySQL & SQLite support)
- Authentication: JWT with golang-jwt/jwt
- Storage: Local filesystem or Cloudflare R2 (S3-compatible)
- Documentation: Swagger/OpenAPI via swaggo
- Logging: Logrus
- Container: Docker & Docker Compose
backend/
├── cmd/api/ # Application entry point
│ └── main.go
├── config/ # Configuration loading
├── domain/ # Domain-driven architecture
│ ├── importer/ # WordPress WXR import functionality
│ ├── media/ # Media management
│ ├── plugin/ # Plugin system
│ ├── post/ # Posts, taxonomies, post types
│ ├── user/ # User authentication & management
│ └── workspace/ # Multi-tenant workspace management
├── middleware/ # Fiber middleware (auth, tenant scoping)
├── routes/ # Route registration
├── storage/ # Storage abstraction (local/R2)
├── utils/ # Utilities (JWT service)
├── docs/ # Swagger documentation
├── docker-compose.yml # Docker services
├── Dockerfile
└── .env.example # Environment template
- Go 1.21+
- MySQL 8.0+ or SQLite
- Docker & Docker Compose (optional)
- Clone and install dependencies
cd tulis-go
go mod download- Setup environment variables
cp .env.example .env
# Edit .env with your database credentials and secrets- Configure database
Edit .env with your database settings:
APP_ENV=development
APP_PORT=8080
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=root
DB_PASSWORD=your_password
DB_NAME=tulis_app
JWT_SECRET=your-secret-key- Run with live reload
# Using Air for live reload
airOr directly:
go run cmd/api/main.go- Access Swagger documentation
Open http://localhost:8080/swagger after starting the server.
# Build and run all services
docker-compose up --build
# Run only the API container
docker-compose up --build apiThe API will be available at http://localhost:8080.
| Variable | Description | Default |
|---|---|---|
APP_ENV |
Environment mode | development |
APP_PORT |
Server port | 8080 |
DB_HOST |
Database host | 127.0.0.1 |
DB_PORT |
Database port | 3306 |
DB_USER |
Database user | root |
DB_PASSWORD |
Database password | - |
DB_NAME |
Database name | tulis_app |
JWT_SECRET |
JWT signing secret | super-secret-key |
JWT_EXPIRY_HOURS |
Token expiry in hours | 24 |
WORKSPACE_RESTRICTED |
Restrict workspace creation | false |
ALLOW_REGISTRATION |
Allow new user signup | true |
R2_ACCOUNT_ID |
Cloudflare R2 account ID | - |
R2_ACCESS_KEY_ID |
R2 access key | - |
R2_SECRET_ACCESS_KEY |
R2 secret key | - |
R2_BUCKET_NAME |
R2 bucket name | tulis-media |
R2_PUBLIC_URL |
R2 public URL | - |
To enable login with Google, GitHub, or GitLab, you need to obtain OAuth credentials from each provider.
| Variable | Description |
|---|---|
GOOGLE_CLIENT_ID |
Google OAuth 2.0 Client ID |
GOOGLE_CLIENT_SECRET |
Google OAuth 2.0 Client Secret |
GOOGLE_REDIRECT_URL |
Google OAuth callback URL |
GITHUB_CLIENT_ID |
GitHub OAuth App Client ID |
GITHUB_CLIENT_SECRET |
GitHub OAuth App Client Secret |
GITHUB_REDIRECT_URL |
GitHub OAuth callback URL |
GITLAB_CLIENT_ID |
GitLab OAuth Application Client ID |
GITLAB_CLIENT_SECRET |
GitLab OAuth Application Client Secret |
GITLAB_REDIRECT_URL |
GitLab OAuth callback URL |
- Go to Google Cloud Console
- Create a new project or select existing one
- Navigate to APIs & Services > Credentials
- Click Create Credentials > OAuth client ID
- Select Web application as application type
- Add authorized redirect URI:
http://localhost:8080/api/auth/google/callback(for development) - Copy Client ID and Client Secret
- In Google Cloud Console, enable Google+ API if not already enabled:
- Go to APIs & Services > Library
- Search for "Google+ API" and enable it
- Go to GitHub Developer Settings
- Click OAuth Apps > New OAuth App
- Fill in application details:
- Application name: Tulis CMS (or your choice)
- Homepage URL:
http://localhost:3000 - Authorization callback URL:
http://localhost:8080/api/auth/github/callback
- Click Register application
- Copy Client ID and generate a new Client Secret
- Go to GitLab > Preferences > Applications
- Click Add new application
- Fill in application details:
- Name: Tulis CMS (or your choice)
- Redirect URI:
http://localhost:8080/api/auth/gitlab/callback - Scopes: Select
read_userscope
- Click Save application
- Copy Application ID (as Client ID) and Secret
For production, update the redirect URLs to match your domain:
| Provider | Production Redirect URL |
|---|---|
https://api.yourdomain.com/api/auth/google/callback |
|
| GitHub | https://api.yourdomain.com/api/auth/github/callback |
| GitLab | https://api.yourdomain.com/api/auth/gitlab/callback |
GET /api/v1/public/posts # List published posts
GET /api/v1/public/posts/:slug # Get post by slug
GET /api/v1/public/media/:id # Get media by ID
POST /api/user/register # User registration
POST /api/user/login # User login
GET /api/user/me # Get current user
# OAuth Authentication
GET /api/auth/google # Redirect to Google OAuth
GET /api/auth/google/callback # Google OAuth callback
GET /api/auth/github # Redirect to GitHub OAuth
GET /api/auth/github/callback # GitHub OAuth callback
GET /api/auth/gitlab # Redirect to GitLab OAuth
GET /api/auth/gitlab/callback # GitLab OAuth callback
# Workspace
GET /api/workspaces # List user's workspaces
POST /api/workspaces # Create workspace
GET /api/workspaces/:id # Get workspace
PUT /api/workspaces/:id # Update workspace
DELETE /api/workspaces/:id # Delete workspace
POST /api/workspaces/:id/members # Add member
DELETE /api/workspaces/:id/members/:userId # Remove member
# Posts
GET /api/posts # List posts (tenant-scoped)
POST /api/posts # Create post
GET /api/posts/:id # Get post
PUT /api/posts/:id # Update post
DELETE /api/posts/:id # Delete post
POST /api/posts/:id/revisions # Create revision
# Taxonomies
GET /api/taxonomies # List taxonomies
POST /api/taxonomies # Create taxonomy
PUT /api/taxonomies/:id # Update taxonomy
DELETE /api/taxonomies/:id # Delete taxonomy
# Media
GET /api/media # List media
POST /api/media/upload # Upload media
DELETE /api/media/:id # Delete media
# Plugins
GET /api/plugins # List plugins
POST /api/plugins # Install plugin
PUT /api/plugins/:id # Update plugin
DELETE /api/plugins/:id # Uninstall plugin
# Importer
POST /api/import/wxr # Import WordPress WXR
GET /api/import/logs # Import logs
Protected endpoints require:
Authorization: Bearer <jwt_token>
X-Workspace-ID: <workspace_id>
- Each workspace operates as an independent tenant
- Members can be assigned roles within workspace
- All data is scoped to workspace context
- Custom post types
- Taxonomy support (categories, tags)
- Post revisions for content history
- Markdown support
- File upload with automatic optimization
- Cloudflare R2 or local storage
- Image processing support
- Import posts from WordPress WXR format
- Preserves media attachments
- Migration tool for content transfer
- Extensible workspace plugins
- Enable/disable plugins per workspace
- Login/Register with Google, GitHub, or GitLab
- Automatic account linking by email
- Workspace auto-creation for new users (when not restricted)
- Email automatically verified for OAuth users
# Run all tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Run specific domain tests
go test ./domain/post/...swag init -g cmd/api/main.go -o docsgo fmt ./...
go mod tidyPrivate project. All rights reserved.