Compare commits
4 Commits
fab072f668
...
v1
| Author | SHA1 | Date | |
|---|---|---|---|
|
18cd952a1a
|
|||
|
b60de5f97c
|
|||
|
529dd0460d
|
|||
|
54daf46d06
|
@@ -12,8 +12,8 @@ GID=1000
|
|||||||
ADMIN_TOKEN=your-secure-admin-token-here
|
ADMIN_TOKEN=your-secure-admin-token-here
|
||||||
|
|
||||||
# Optional: Service configuration (defaults shown)
|
# Optional: Service configuration (defaults shown)
|
||||||
DEPLOY_ROOT=/var/www/tingz-docs
|
DEPLOY_ROOT=/var/www/docs
|
||||||
RELEASE_ROOT=/var/www/tingz-deploys
|
RELEASE_ROOT=/var/www/deploys
|
||||||
DB_PATH=/data/deployer.db
|
DB_PATH=/data/deployer.db
|
||||||
|
|
||||||
# Optional: Upload and retention settings
|
# Optional: Upload and retention settings
|
||||||
|
|||||||
19
API.md
19
API.md
@@ -7,6 +7,7 @@ Complete guide to tingz API with cURL.
|
|||||||
- Running deployer service on http://localhost:8080
|
- Running deployer service on http://localhost:8080
|
||||||
- Admin token (set in `.env.development` as `ADMIN_TOKEN`) set to `devadmintoken`
|
- Admin token (set in `.env.development` as `ADMIN_TOKEN`) set to `devadmintoken`
|
||||||
- API version is `v1`
|
- API version is `v1`
|
||||||
|
- **Note:** The deployer service only handles API requests (`/api/v1/*`). For serving deployed static sites, you need a separate web server (Caddy, nginx, etc.) configured to serve files from `/var/www/docs`
|
||||||
|
|
||||||
## Step 1: Create a User (Admin Only)
|
## Step 1: Create a User (Admin Only)
|
||||||
|
|
||||||
@@ -137,12 +138,21 @@ ls -la deploys/alice/myproject/20251014T180806/
|
|||||||
|
|
||||||
### Access Your Site
|
### Access Your Site
|
||||||
|
|
||||||
|
**Note:** The deployer service only handles API requests. Deployed sites are served by a separate web server (like Caddy or nginx) that reads from `/var/www/docs`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl http://localhost:8080/alice/myproject/
|
# If you have a web server configured to serve from /var/www/docs:
|
||||||
# or
|
|
||||||
curl https://docs.yigid.dev/alice/myproject/
|
curl https://docs.yigid.dev/alice/myproject/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**For local development:** You'll need to set up a web server to serve files from the `docs/` directory, or use a simple HTTP server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Example: serve files from docs directory
|
||||||
|
cd docs && python3 -m http.server 3000
|
||||||
|
# Then access: http://localhost:3000/alice/myproject/
|
||||||
|
```
|
||||||
|
|
||||||
## Complete Workflow Example
|
## Complete Workflow Example
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -176,8 +186,9 @@ curl -s -X POST "http://localhost:8080/api/v1/deploy" \
|
|||||||
-F "project=mysite" \
|
-F "project=mysite" \
|
||||||
-F "file=@site.tar.gz" | jq
|
-F "file=@site.tar.gz" | jq
|
||||||
|
|
||||||
# 6. Verify
|
# 6. Verify (requires web server setup)
|
||||||
curl http://localhost:8080/alice/mysite/
|
# If you have a web server serving from docs/ directory:
|
||||||
|
curl http://localhost:3000/alice/mysite/
|
||||||
```
|
```
|
||||||
|
|
||||||
## Health Check
|
## Health Check
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ services:
|
|||||||
args:
|
args:
|
||||||
UID: ${UID}
|
UID: ${UID}
|
||||||
GID: ${GID}
|
GID: ${GID}
|
||||||
|
# for the volumes below, ensure the server user has write access
|
||||||
|
# "server user" is the user that UID and GID's are passed with env variables
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/data
|
- ./volumes/data:/data # SQLite DB
|
||||||
- ./docs:/var/www/docs
|
- ./volumes/docs:/var/www/docs # published files
|
||||||
- ./deploys:/var/www/deploys
|
- ./volumes/deploys:/var/www/deploys # releases
|
||||||
ports:
|
ports:
|
||||||
- "${HOST}:${PORT}:8080"
|
- "${HOST}:${PORT}:8080"
|
||||||
user: "${UID}:${GID}"
|
user: "${UID}:${GID}"
|
||||||
|
|||||||
21
example.caddy
Normal file
21
example.caddy
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
docs.example.org {
|
||||||
|
# API requests go to Docker container
|
||||||
|
handle /api* {
|
||||||
|
reverse_proxy HOST:PORT # TODO: set to HOST and PORT from .env
|
||||||
|
}
|
||||||
|
|
||||||
|
handle {
|
||||||
|
file_server {
|
||||||
|
root $COMPOSE_DIR/volumes/docs # TODO: change $COMPOSE_DIR to the actual path
|
||||||
|
index index.html
|
||||||
|
browse off
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#log {
|
||||||
|
# output_file /var/log/caddy/docs.example.org.log {
|
||||||
|
# roll_size 10MB
|
||||||
|
# roll_keep 10
|
||||||
|
# }
|
||||||
|
#}
|
||||||
|
}
|
||||||
@@ -202,6 +202,14 @@ func (s *Server) handleStatus(w http.ResponseWriter, r *http.Request) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleHello(w http.ResponseWriter, r *http.Request) {
|
||||||
|
writeJSON(w, http.StatusOK, map[string]string{
|
||||||
|
"message": "Close the world, .txen eht nepO",
|
||||||
|
"author": "Yigid BALABAN <hey@yigid.dev>",
|
||||||
|
"authorHomepage": "https://yigid.dev/",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func writeJSON(w http.ResponseWriter, status int, data interface{}) {
|
func writeJSON(w http.ResponseWriter, status int, data interface{}) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(status)
|
w.WriteHeader(status)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ func NewRouter(cfg *config.Config, userMgr *user.Manager, deployMgr *deploy.Mana
|
|||||||
mux.Handle("DELETE /api/v1/auth", adminAuth(http.HandlerFunc(server.handleAuthDelete)))
|
mux.Handle("DELETE /api/v1/auth", adminAuth(http.HandlerFunc(server.handleAuthDelete)))
|
||||||
mux.Handle("POST /api/v1/deploy", userAuth(maxBytes(http.HandlerFunc(server.handleDeploy))))
|
mux.Handle("POST /api/v1/deploy", userAuth(maxBytes(http.HandlerFunc(server.handleDeploy))))
|
||||||
mux.HandleFunc("GET /api/v1/status", server.handleStatus)
|
mux.HandleFunc("GET /api/v1/status", server.handleStatus)
|
||||||
|
mux.HandleFunc("GET /api/hello", server.handleHello)
|
||||||
|
|
||||||
return LoggingMiddleware(logger)(mux)
|
return LoggingMiddleware(logger)(mux)
|
||||||
}
|
}
|
||||||
|
|||||||
444
openapi.yaml
Normal file
444
openapi.yaml
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
openapi: 3.0.3
|
||||||
|
info:
|
||||||
|
title: Tingz Deployer API
|
||||||
|
description: A secure, lightweight HTTP service for deploying static sites. The service receives tarball artifacts, validates and publishes them under a per-user directory structure for docs.yigid.dev. Designed for CI/CD automation with GitHub Actions, GitLab CI, etc.
|
||||||
|
version: 1.0.0
|
||||||
|
contact:
|
||||||
|
name: Yigid BALABAN
|
||||||
|
email: hey@yigid.dev
|
||||||
|
url: https://yigid.dev/
|
||||||
|
license:
|
||||||
|
name: AGPL-3.0-only
|
||||||
|
url: https://opensource.org/license/agpl-v3
|
||||||
|
servers:
|
||||||
|
- url: https://docs.yigid.dev
|
||||||
|
description: Production server
|
||||||
|
- url: http://localhost:8080
|
||||||
|
description: Development server
|
||||||
|
paths:
|
||||||
|
/api/v1/auth:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- Authentication
|
||||||
|
summary: Create or reroll user token
|
||||||
|
description: Creates a new user or generates a new token for an existing user. Admin only endpoint.
|
||||||
|
operationId: createOrRerollUser
|
||||||
|
security:
|
||||||
|
- AdminAuth: []
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CreateUserRequest'
|
||||||
|
example:
|
||||||
|
username: alice
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: User created or token rerolled successfully
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/AuthResponse'
|
||||||
|
example:
|
||||||
|
status: ok
|
||||||
|
token: 82lz0BVOWtV4uVj9DBZBuKMeyVDwidL2
|
||||||
|
'400':
|
||||||
|
description: Invalid request body or username format
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
examples:
|
||||||
|
invalidBody:
|
||||||
|
summary: Invalid request body
|
||||||
|
value:
|
||||||
|
status: error
|
||||||
|
error: invalid request body
|
||||||
|
invalidUsername:
|
||||||
|
summary: Invalid username format
|
||||||
|
value:
|
||||||
|
status: error
|
||||||
|
error: invalid username format
|
||||||
|
'401':
|
||||||
|
description: Unauthorized - Invalid admin token
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
example:
|
||||||
|
status: error
|
||||||
|
error: invalid admin token
|
||||||
|
'500':
|
||||||
|
description: Internal server error
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
example:
|
||||||
|
status: error
|
||||||
|
error: failed to create user
|
||||||
|
delete:
|
||||||
|
tags:
|
||||||
|
- Authentication
|
||||||
|
summary: Delete user
|
||||||
|
description: Deletes a user and optionally their files. Admin only endpoint.
|
||||||
|
operationId: deleteUser
|
||||||
|
security:
|
||||||
|
- AdminAuth: []
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/DeleteUserRequest'
|
||||||
|
examples:
|
||||||
|
persistFiles:
|
||||||
|
summary: Delete user but keep files
|
||||||
|
value:
|
||||||
|
username: alice
|
||||||
|
files: persist
|
||||||
|
deleteFiles:
|
||||||
|
summary: Delete user and all files
|
||||||
|
value:
|
||||||
|
username: alice
|
||||||
|
files: delete
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: User deleted successfully
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SuccessResponse'
|
||||||
|
example:
|
||||||
|
status: ok
|
||||||
|
'400':
|
||||||
|
description: Invalid request body, username format, or files parameter
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
examples:
|
||||||
|
invalidBody:
|
||||||
|
summary: Invalid request body
|
||||||
|
value:
|
||||||
|
status: error
|
||||||
|
error: invalid request body
|
||||||
|
invalidUsername:
|
||||||
|
summary: Invalid username format
|
||||||
|
value:
|
||||||
|
status: error
|
||||||
|
error: invalid username format
|
||||||
|
invalidFiles:
|
||||||
|
summary: Invalid files parameter
|
||||||
|
value:
|
||||||
|
status: error
|
||||||
|
error: files must be 'persist' or 'delete'
|
||||||
|
'401':
|
||||||
|
description: Unauthorized - Invalid admin token
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
example:
|
||||||
|
status: error
|
||||||
|
error: invalid admin token
|
||||||
|
'500':
|
||||||
|
description: Internal server error
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
example:
|
||||||
|
status: error
|
||||||
|
error: user not found
|
||||||
|
/api/v1/deploy:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- Deployment
|
||||||
|
summary: Deploy static site
|
||||||
|
description: Uploads and deploys a static site tarball. Creates a new release and atomically updates the live site via symlink swap.
|
||||||
|
operationId: deploySite
|
||||||
|
security:
|
||||||
|
- UserAuth: []
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
multipart/form-data:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- project
|
||||||
|
- file
|
||||||
|
properties:
|
||||||
|
project:
|
||||||
|
type: string
|
||||||
|
description: Project name (must match username validation pattern)
|
||||||
|
pattern: ^[a-z0-9]([a-z0-9_-]{0,62}[a-z0-9])?$
|
||||||
|
minLength: 1
|
||||||
|
maxLength: 64
|
||||||
|
example: myproject
|
||||||
|
file:
|
||||||
|
type: string
|
||||||
|
format: binary
|
||||||
|
description: Tarball file (.tar.gz) containing the static site
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Deployment successful
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/DeployResponse'
|
||||||
|
example:
|
||||||
|
status: ok
|
||||||
|
project: myproject
|
||||||
|
username: alice
|
||||||
|
url: https://docs.yigid.dev/alice/myproject/
|
||||||
|
release: 20251014T180806
|
||||||
|
'400':
|
||||||
|
description: Invalid request - missing file, invalid project name, or multipart form parsing error
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
examples:
|
||||||
|
missingFile:
|
||||||
|
summary: Missing or invalid file
|
||||||
|
value:
|
||||||
|
status: error
|
||||||
|
error: missing or invalid file
|
||||||
|
invalidProject:
|
||||||
|
summary: Invalid project name format
|
||||||
|
value:
|
||||||
|
status: error
|
||||||
|
error: invalid project name format
|
||||||
|
parseError:
|
||||||
|
summary: Failed to parse multipart form
|
||||||
|
value:
|
||||||
|
status: error
|
||||||
|
error: failed to parse multipart form
|
||||||
|
'401':
|
||||||
|
description: Unauthorized - Invalid user token
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
example:
|
||||||
|
status: error
|
||||||
|
error: unauthorized
|
||||||
|
'413':
|
||||||
|
description: Request entity too large - Upload exceeds maximum size limit
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
example:
|
||||||
|
status: error
|
||||||
|
error: request body too large
|
||||||
|
'500':
|
||||||
|
description: Internal server error during deployment
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
examples:
|
||||||
|
extractionError:
|
||||||
|
summary: Failed to extract tarball
|
||||||
|
value:
|
||||||
|
status: error
|
||||||
|
error: 'failed to extract tarball: path contains ''..'' component which is not allowed: ../../etc/passwd'
|
||||||
|
generalError:
|
||||||
|
summary: General deployment error
|
||||||
|
value:
|
||||||
|
status: error
|
||||||
|
error: deployment failed
|
||||||
|
/api/v1/status:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Health
|
||||||
|
summary: Health check
|
||||||
|
description: Returns the current status of the service
|
||||||
|
operationId: getStatus
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Service is healthy
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/StatusResponse'
|
||||||
|
example:
|
||||||
|
status: ok
|
||||||
|
/api/hello:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Info
|
||||||
|
summary: Service information
|
||||||
|
description: Returns basic information about the service and author
|
||||||
|
operationId: getHello
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Service information
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/HelloResponse'
|
||||||
|
example:
|
||||||
|
message: Close the world, .txen eht nepO
|
||||||
|
author: Yigid BALABAN <hey@yigid.dev>
|
||||||
|
authorHomepage: https://yigid.dev/
|
||||||
|
components:
|
||||||
|
securitySchemes:
|
||||||
|
AdminAuth:
|
||||||
|
type: http
|
||||||
|
scheme: bearer
|
||||||
|
description: Admin bearer token for user management operations
|
||||||
|
UserAuth:
|
||||||
|
type: http
|
||||||
|
scheme: bearer
|
||||||
|
description: User bearer token for deployment operations
|
||||||
|
schemas:
|
||||||
|
CreateUserRequest:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- username
|
||||||
|
properties:
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
description: Username for the new user (lowercase letters, numbers, hyphens, underscores only)
|
||||||
|
pattern: ^[a-z0-9]([a-z0-9_-]{0,62}[a-z0-9])?$
|
||||||
|
minLength: 1
|
||||||
|
maxLength: 64
|
||||||
|
example: alice
|
||||||
|
DeleteUserRequest:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- username
|
||||||
|
properties:
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
description: Username of the user to delete
|
||||||
|
pattern: ^[a-z0-9]([a-z0-9_-]{0,62}[a-z0-9])?$
|
||||||
|
minLength: 1
|
||||||
|
maxLength: 64
|
||||||
|
example: alice
|
||||||
|
files:
|
||||||
|
type: string
|
||||||
|
description: Whether to persist or delete user files
|
||||||
|
enum:
|
||||||
|
- persist
|
||||||
|
- delete
|
||||||
|
default: persist
|
||||||
|
example: persist
|
||||||
|
AuthResponse:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
- token
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- ok
|
||||||
|
example: ok
|
||||||
|
token:
|
||||||
|
type: string
|
||||||
|
description: 32-character alphanumeric token for API access
|
||||||
|
minLength: 32
|
||||||
|
maxLength: 32
|
||||||
|
example: 82lz0BVOWtV4uVj9DBZBuKMeyVDwidL2
|
||||||
|
DeployResponse:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
- project
|
||||||
|
- username
|
||||||
|
- url
|
||||||
|
- release
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- ok
|
||||||
|
example: ok
|
||||||
|
project:
|
||||||
|
type: string
|
||||||
|
description: Project name
|
||||||
|
example: myproject
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
description: Username who deployed
|
||||||
|
example: alice
|
||||||
|
url:
|
||||||
|
type: string
|
||||||
|
format: uri
|
||||||
|
description: Public URL where the site is accessible
|
||||||
|
example: https://docs.yigid.dev/alice/myproject/
|
||||||
|
release:
|
||||||
|
type: string
|
||||||
|
description: 'Release ID (timestamp format: YYYYMMDDTHHMMSS)'
|
||||||
|
pattern: ^[0-9]{8}T[0-9]{6}$
|
||||||
|
example: 20251014T180806
|
||||||
|
StatusResponse:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- ok
|
||||||
|
example: ok
|
||||||
|
HelloResponse:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- message
|
||||||
|
- author
|
||||||
|
- authorHomepage
|
||||||
|
properties:
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
description: Service message
|
||||||
|
example: Close the world, .txen eht nepO
|
||||||
|
author:
|
||||||
|
type: string
|
||||||
|
description: Author information
|
||||||
|
example: Yigid BALABAN <hey@yigid.dev>
|
||||||
|
authorHomepage:
|
||||||
|
type: string
|
||||||
|
format: uri
|
||||||
|
description: Author homepage URL
|
||||||
|
example: https://yigid.dev/
|
||||||
|
SuccessResponse:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- ok
|
||||||
|
example: ok
|
||||||
|
ErrorResponse:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- status
|
||||||
|
- error
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- error
|
||||||
|
example: error
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
description: Error message describing what went wrong
|
||||||
|
example: invalid username format
|
||||||
|
tags:
|
||||||
|
- name: Authentication
|
||||||
|
description: User management operations (admin only)
|
||||||
|
- name: Deployment
|
||||||
|
description: Static site deployment operations
|
||||||
|
- name: Health
|
||||||
|
description: Service health and status endpoints
|
||||||
|
- name: Info
|
||||||
|
description: Service information endpoints
|
||||||
56
pre-docker-deploy.sh
Normal file
56
pre-docker-deploy.sh
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
echo "=== tingz pre-docker script ==="
|
||||||
|
echo "This script assumes you're running it on a Debian 12+ system."
|
||||||
|
echo "This script assumes you haven't created a tingz user & group yet."
|
||||||
|
echo "This script assumes you are using the default volumes directory structure."
|
||||||
|
echo
|
||||||
|
echo "Description:"
|
||||||
|
echo "This script creates a tingz user & group,"
|
||||||
|
echo "creates and sets permissions for volumes,"
|
||||||
|
echo "and creates a .env.development file."
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "1. Check dependencies"
|
||||||
|
commands=(docker adduser addgroup cut getent)
|
||||||
|
for cmd in "${commands[@]}"; do
|
||||||
|
if ! command -v "$cmd" &> /dev/null; then
|
||||||
|
echo "Error: $cmd could not be found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "2. Add tingz user & group"
|
||||||
|
addgroup --system tingz
|
||||||
|
GID=$(getent group tingz | cut -d: -f3)
|
||||||
|
adduser --ingroup tingz --system --no-create-home --uid ${GID} --shell /usr/sbin/nologin tingz
|
||||||
|
|
||||||
|
echo "3. Verify tingz group and user"
|
||||||
|
getent group tingz
|
||||||
|
getent passwd tingz
|
||||||
|
|
||||||
|
echo "4. Create and set permissions for volumes"
|
||||||
|
mkdir -p volumes/data volumes/docs volumes/deploys
|
||||||
|
chown -R tingz:tingz volumes
|
||||||
|
|
||||||
|
echo "5. Create .env.tmp file"
|
||||||
|
ADMIN_TOKEN=$(openssl rand -hex 16)
|
||||||
|
cat > .env.tmp << EOF
|
||||||
|
ADMIN_TOKEN=${ADMIN_TOKEN}
|
||||||
|
HOST=127.0.0.1
|
||||||
|
PORT=8080
|
||||||
|
UID=${GID}
|
||||||
|
GID=${GID}
|
||||||
|
DEPLOY_ROOT=/var/www/docs
|
||||||
|
RELEASE_ROOT=/var/www/deploys
|
||||||
|
DB_PATH=/data/deployer.db
|
||||||
|
MAX_UPLOAD_SIZE=104857600
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "Please verify .env.temp and move into .env if everything is correct"
|
||||||
|
echo "=== .env.tmp ==="
|
||||||
|
cat .env.tmp
|
||||||
|
echo "=== .env ==="
|
||||||
|
|
||||||
|
echo "Done"
|
||||||
48
workflows/gh-actions.yml
Normal file
48
workflows/gh-actions.yml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
name: Deploy with mdBook to docs.yigid.dev
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- docs/**
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||||
|
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
||||||
|
concurrency:
|
||||||
|
group: "mdbook"
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_and_deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup mdBook
|
||||||
|
uses: jontze/action-mdbook@v3
|
||||||
|
with:
|
||||||
|
token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
use-linkcheck: true
|
||||||
|
use-mermaid: true
|
||||||
|
|
||||||
|
- name: Build with mdBook
|
||||||
|
run: mdbook build docs
|
||||||
|
|
||||||
|
- name: Create tarball
|
||||||
|
run: |
|
||||||
|
cd docs/book
|
||||||
|
tar -czf ../docs.tar.gz .
|
||||||
|
|
||||||
|
- name: Deploy to yigiDDocs
|
||||||
|
env:
|
||||||
|
DEPLOYER_TOKEN: ${{ secrets.DEPLOYER_TOKEN }} # TODO: set secret to token from auth endpoint
|
||||||
|
DEPLOYER_URL: https://docs.yigid.dev
|
||||||
|
PROJECT_NAME: project_name # TODO: change to the actual project name
|
||||||
|
run: |
|
||||||
|
curl -f -X POST "${DEPLOYER_URL}/api/v1/deploy" \
|
||||||
|
-H "Authorization: Bearer ${DEPLOYER_TOKEN}" \
|
||||||
|
-F "project=${PROJECT_NAME}" \
|
||||||
|
-F "file=@docs/docs.tar.gz"
|
||||||
Reference in New Issue
Block a user