Compare commits
33
Commits
6b494a34fe
..
1.0.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47740e3f4a | ||
|
|
693b4a42f6 | ||
|
|
8f1e227d50 | ||
|
|
0891606321 | ||
|
|
c6f28ff7af | ||
|
|
072a91961f | ||
|
|
7184240dd4 | ||
|
|
9cd0419c0c | ||
|
|
d3266f8feb | ||
|
|
b532507374 | ||
|
|
5c1a8cbe4f | ||
|
|
502fefff33 | ||
|
|
bc3a950d99 | ||
|
|
1af9e59a56 | ||
|
|
13d25a7ae0 | ||
|
|
fc799da000 | ||
|
|
2b091adce8 | ||
|
|
c54210434e | ||
|
|
fcb68cfd8d | ||
|
|
cdecdd2d4a | ||
|
|
47ffae5dea | ||
|
|
43c1a31660 | ||
|
|
afb9a41404 | ||
|
|
1f2082e0fb | ||
|
|
7ce6a24a77 | ||
|
|
450a93150c | ||
|
|
dc53bce6c9 | ||
|
|
32f2b7c7ab | ||
|
|
72422f4336 | ||
|
|
78508ed16e | ||
|
|
8fd80c1a4c | ||
|
|
5fc9bc6191 | ||
|
|
3c2b29ad26 |
No files matched your search
+6
-9
@@ -31,10 +31,12 @@ ADMIN_SESSION_TTL_HOURS=12
|
||||
# VITE_PUBLIC_HLS_URL=http://localhost/hls/playlist.m3u8
|
||||
|
||||
# Gitea Container Registry (for self-hosted Docker images)
|
||||
GITEA_REGISTRY_URL=git.allucanget.biz
|
||||
GITEA_USERNAME=your-gitea-username
|
||||
GITEA_TOKEN=your-gitea-token
|
||||
GITEA_REGISTRY_NAMESPACE=zwitschi/linearvoid
|
||||
CI_REGISTRY_URL=git.allucanget.biz
|
||||
CI_REGISTRY_USER=your-gitea-username
|
||||
CI_REGISTRY_TOKEN=your-gitea-token
|
||||
CI_REGISTRY_NAMESPACE=zwitschi/linearvoid
|
||||
# Gitea API access (optional, for CI/release automation)
|
||||
CI_SERVER_URL=https://git.allucanget.biz
|
||||
|
||||
# Chat / Application Database (compose-safe PostgreSQL by default, SQLite optional)
|
||||
DATABASE_URL=postgres://linearvoid:linearvoid@db:5432/linearvoid?sslmode=disable
|
||||
@@ -62,8 +64,3 @@ GRAFANA_PORT=3001
|
||||
# Development flags
|
||||
NODE_ENV=development
|
||||
GO_ENV=development
|
||||
|
||||
# Gitea Configuration
|
||||
GITEA_SERVER_URL=https://git.allucanget.biz
|
||||
GITEA_TOKEN=your-gitea-token
|
||||
GITEA_USERNAME=your-gitea-username
|
||||
+61
-12
@@ -1,13 +1,17 @@
|
||||
name: CD
|
||||
|
||||
on:
|
||||
push:
|
||||
workflow_run:
|
||||
workflows: ["CI"]
|
||||
types:
|
||||
- completed
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: [ubuntu-latest]
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
|
||||
environment: production
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -22,28 +26,73 @@ jobs:
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: git.allucanget.biz
|
||||
username: ${{ secrets.GITEA_USERNAME }}
|
||||
password: ${{ secrets.GITEA_TOKEN }}
|
||||
username: ${{ secrets.CI_REGISTRY_USER }}
|
||||
password: ${{ secrets.CI_REGISTRY_TOKEN }}
|
||||
|
||||
- name: Pull latest images
|
||||
- name: Prepare environment
|
||||
run: |
|
||||
docker pull git.allucanget.biz/zwitschi/linearvoid/playout:latest || true
|
||||
docker pull git.allucanget.biz/zwitschi/linearvoid/chat:latest || true
|
||||
docker pull git.allucanget.biz/zwitschi/linearvoid/frontend:latest || true
|
||||
docker pull git.allucanget.biz/zwitschi/linearvoid/nginx:latest || true
|
||||
cat > .env << 'EOF'
|
||||
DATABASE_URL="${{ secrets.DATABASE_URL }}"
|
||||
REDIS_URL=redis://redis:6379/0
|
||||
PLAYLIST_PATH=/app/playlist/playlist.txt
|
||||
HLS_SEGMENT_DURATION=6
|
||||
HLS_WINDOW_SIZE=24
|
||||
LOG_LEVEL=info
|
||||
ADMIN_USERNAME="${{ secrets.ADMIN_USERNAME }}"
|
||||
ADMIN_PASSWORD="${{ secrets.ADMIN_PASSWORD }}"
|
||||
ADMIN_SESSION_TTL_HOURS=12
|
||||
NGINX_PORT=80
|
||||
EOF
|
||||
|
||||
- name: Seed Prometheus config
|
||||
run: |
|
||||
mkdir -p monitoring/prometheus
|
||||
cat > monitoring/prometheus/prometheus.yml << 'EOF'
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: 'prometheus'
|
||||
static_configs:
|
||||
- targets: ['localhost:9090']
|
||||
|
||||
- job_name: 'linearvoid'
|
||||
static_configs:
|
||||
- targets: ['playout:8081', 'chat:3000', 'nginx:80']
|
||||
EOF
|
||||
|
||||
- name: Deploy with docker-compose
|
||||
env:
|
||||
DOCKER_HOST: tcp://192.168.88.40:2375
|
||||
run: |
|
||||
docker compose -f docker-compose.prod.yml pull
|
||||
docker compose -f docker-compose.prod.yml up -d --remove-orphans
|
||||
|
||||
- name: Health check
|
||||
env:
|
||||
DOCKER_HOST: tcp://192.168.88.40:2375
|
||||
run: |
|
||||
sleep 10
|
||||
curl -f http://localhost:8080/health || exit 1
|
||||
curl -f http://localhost:3000/health || exit 1
|
||||
curl -f http://localhost:8081/health || exit 1
|
||||
# Wait for playout to stabilize (it may restart during init)
|
||||
sleep 20
|
||||
curl -f http://192.168.88.40:3000/health || exit 1
|
||||
# Retry playout health check since it may still be initializing
|
||||
for i in $(seq 1 6); do
|
||||
if docker exec linearvoid-playout curl -sf http://localhost:8081/health; then
|
||||
echo "Playout is healthy"
|
||||
exit 0
|
||||
fi
|
||||
echo "Playout not ready yet (attempt $i/6), waiting 5s..."
|
||||
sleep 5
|
||||
done
|
||||
echo "Playout failed to become healthy after 6 attempts"
|
||||
docker logs linearvoid-playout --tail 50
|
||||
exit 1
|
||||
curl -f http://192.168.88.40:3000/health || exit 1
|
||||
docker exec linearvoid-playout curl -sf http://localhost:8081/health || exit 1
|
||||
|
||||
- name: Cleanup old images
|
||||
env:
|
||||
DOCKER_HOST: tcp://192.168.88.40:2375
|
||||
run: |
|
||||
docker image prune -f --filter "until=24h"
|
||||
+23
-39
@@ -48,7 +48,7 @@ jobs:
|
||||
|
||||
docker:
|
||||
needs: test
|
||||
runs-on: [self-hosted, linux, docker]
|
||||
runs-on: [ubuntu-latest]
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -63,8 +63,8 @@ jobs:
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: git.allucanget.biz
|
||||
username: ${{ secrets.GITEA_USERNAME }}
|
||||
password: ${{ secrets.GITEA_TOKEN }}
|
||||
username: ${{ secrets.CI_REGISTRY_USER }}
|
||||
password: ${{ secrets.CI_REGISTRY_TOKEN }}
|
||||
|
||||
- name: Extract metadata for Playout
|
||||
id: meta-playout
|
||||
@@ -82,11 +82,10 @@ jobs:
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./backend/playout
|
||||
file: ./backend/playout/Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta-playout.outputs.tags }}
|
||||
labels: ${{ steps.meta-playout.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Extract metadata for Chat
|
||||
id: meta-chat
|
||||
@@ -107,30 +106,6 @@ jobs:
|
||||
push: true
|
||||
tags: ${{ steps.meta-chat.outputs.tags }}
|
||||
labels: ${{ steps.meta-chat.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Extract metadata for Frontend
|
||||
id: meta-frontend
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: git.allucanget.biz/zwitschi/linearvoid/frontend
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
- name: Build and push Frontend image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./frontend
|
||||
push: true
|
||||
tags: ${{ steps.meta-frontend.outputs.tags }}
|
||||
labels: ${{ steps.meta-frontend.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Extract metadata for Nginx
|
||||
id: meta-nginx
|
||||
@@ -145,18 +120,27 @@ jobs:
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
- name: Build and push Nginx image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./infrastructure/nginx
|
||||
push: true
|
||||
tags: ${{ steps.meta-nginx.outputs.tags }}
|
||||
labels: ${{ steps.meta-nginx.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
run: |
|
||||
TAGS="${{ steps.meta-nginx.outputs.tags }}"
|
||||
LABELS="${{ steps.meta-nginx.outputs.labels }}"
|
||||
TAG_ARGS=""
|
||||
for tag in $TAGS; do
|
||||
TAG_ARGS="$TAG_ARGS --tag $tag"
|
||||
done
|
||||
LABEL_ARGS=""
|
||||
while IFS= read -r label; do
|
||||
[ -n "$label" ] && LABEL_ARGS="$LABEL_ARGS --label $label"
|
||||
done <<< "$LABELS"
|
||||
docker buildx build \
|
||||
--push \
|
||||
--file infrastructure/nginx/Dockerfile \
|
||||
$TAG_ARGS \
|
||||
$LABEL_ARGS \
|
||||
.
|
||||
|
||||
release:
|
||||
needs: docker
|
||||
runs-on: [self-hosted, linux, docker]
|
||||
runs-on: [ubuntu-latest]
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -169,4 +153,4 @@ jobs:
|
||||
with:
|
||||
generate_release_notes: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.CI_REGISTRY_TOKEN }}
|
||||
@@ -13,7 +13,6 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITEA_TOKEN }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
@@ -39,20 +38,38 @@ jobs:
|
||||
cd backend/playout && go get -u ./... && go mod tidy
|
||||
cd ../chat && go get -u ./... && go mod tidy
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
with:
|
||||
token: ${{ secrets.GITEA_TOKEN }}
|
||||
commit-message: "chore: update dependencies"
|
||||
title: "chore: weekly dependency updates"
|
||||
body: |
|
||||
Automated weekly dependency updates.
|
||||
- name: Commit and push changes
|
||||
run: |
|
||||
git config user.name "linearvoid-bot"
|
||||
git config user.email "bot@linearvoid.local"
|
||||
git add frontend/package*.json backend/playout/go.* backend/chat/go.*
|
||||
if git diff --cached --quiet; then
|
||||
echo "No dependency changes detected."
|
||||
exit 0
|
||||
fi
|
||||
git commit -m "chore: update dependencies"
|
||||
git push origin HEAD:dependency-updates --force
|
||||
|
||||
This PR updates:
|
||||
- Frontend npm dependencies
|
||||
- Go module dependencies
|
||||
|
||||
Please review the changes and ensure tests pass before merging.
|
||||
branch: dependency-updates
|
||||
delete-branch: true
|
||||
labels: dependencies, automated
|
||||
- name: Create Pull Request via Gitea API
|
||||
run: |
|
||||
RESPONSE=$(curl -s -o /tmp/pr-response.json -w "%{http_code}" \
|
||||
-X POST "$CI_SERVER_URL/api/v1/repos/zwitschi/linearvoid/pulls" \
|
||||
-H "Authorization: token ${{ secrets.CI_REGISTRY_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"title": "chore: weekly dependency updates",
|
||||
"head": "dependency-updates",
|
||||
"base": "main",
|
||||
"body": "Automated weekly dependency updates.\n\nThis PR updates:\n- Frontend npm dependencies\n- Go module dependencies\n\nPlease review the changes and ensure tests pass before merging."
|
||||
}')
|
||||
if [ "$RESPONSE" -eq 201 ]; then
|
||||
PR_URL=$(cat /tmp/pr-response.json | jq -r '.html_url')
|
||||
echo "PR created: $PR_URL"
|
||||
elif [ "$RESPONSE" -eq 409 ]; then
|
||||
echo "PR already exists. Pushing updated commits..."
|
||||
git push origin dependency-updates --force
|
||||
else
|
||||
echo "Error creating PR (HTTP $RESPONSE):"
|
||||
cat /tmp/pr-response.json
|
||||
exit 1
|
||||
fi
|
||||
@@ -27,17 +27,32 @@ jobs:
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: git.allucanget.biz
|
||||
username: ${{ secrets.GITEA_USERNAME }}
|
||||
password: ${{ secrets.GITEA_TOKEN }}
|
||||
username: ${{ secrets.CI_REGISTRY_USER }}
|
||||
password: ${{ secrets.CI_REGISTRY_TOKEN }}
|
||||
|
||||
- name: Extract version
|
||||
id: version
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
|
||||
VERSION="${{ github.event.inputs.version }}"
|
||||
else
|
||||
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
VERSION="${GITHUB_REF#refs/tags/}"
|
||||
fi
|
||||
if [[ -z "$VERSION" ]]; then
|
||||
echo "::error::VERSION is empty. Provide a version via workflow_dispatch input or push a tag."
|
||||
exit 1
|
||||
fi
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Releasing version: $VERSION"
|
||||
|
||||
- name: Print version to gitea runner log
|
||||
run: |
|
||||
echo "Releasing version: ${{ steps.version.outputs.version }}"
|
||||
echo "===DEBUG==="
|
||||
echo "VERSION: $VERSION"
|
||||
echo "GITHUB_REF: $GITHUB_REF"
|
||||
echo "steps.version.outputs.version: ${{ steps.version.outputs.version }}"
|
||||
echo "github.event_name: ${{ github.event_name }}"
|
||||
|
||||
- name: Extract metadata for Playout
|
||||
id: meta-playout
|
||||
@@ -75,24 +90,6 @@ jobs:
|
||||
tags: ${{ steps.meta-chat.outputs.tags }}
|
||||
labels: ${{ steps.meta-chat.outputs.labels }}
|
||||
|
||||
- name: Extract metadata for Frontend
|
||||
id: meta-frontend
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: git.allucanget.biz/zwitschi/linearvoid/frontend
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=raw,value=latest
|
||||
|
||||
- name: Build and push Frontend image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./frontend
|
||||
push: true
|
||||
tags: ${{ steps.meta-frontend.outputs.tags }}
|
||||
labels: ${{ steps.meta-frontend.outputs.labels }}
|
||||
|
||||
- name: Extract metadata for Nginx
|
||||
id: meta-nginx
|
||||
uses: docker/metadata-action@v5
|
||||
@@ -106,15 +103,15 @@ jobs:
|
||||
- name: Build and push Nginx image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./infrastructure/nginx
|
||||
context: .
|
||||
file: infrastructure/nginx/Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta-nginx.outputs.tags }}
|
||||
labels: ${{ steps.meta-nginx.outputs.labels }}
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
generate_release_notes: true
|
||||
tag_name: ${{ steps.version.outputs.version }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
- name: Create Release on Gitea
|
||||
run: |
|
||||
curl -X POST "https://git.allucanget.biz/api/v1/repos/zwitschi/linearvoid/releases" \
|
||||
-H "Authorization: token ${{ secrets.CI_REGISTRY_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"tag_name\":\"${{ steps.version.outputs.version }}\",\"name\":\"${{ steps.version.outputs.version }}\",\"body\":\"Release ${{ steps.version.outputs.version }}\n\nSee [CHANGELOG.md](https://git.allucanget.biz/zwitschi/linearvoid/src/branch/main/CHANGELOG.md) for details.\",\"draft\":false,\"prerelease\":false}"
|
||||
@@ -9,3 +9,13 @@ Agents instructions for the linearvoid project.
|
||||
- Update documentation first before implementing changes to ensure traceability of decisions.
|
||||
- Plan any task in the `TODO.md` file with clear steps and mark them as completed when done. Move completed sections to DONE.md. Write entry in root `CHANGELOG.md` for any significant changes. Avoid using in-memory todo lists or update them frequently to sync from the `TODO.md` file.
|
||||
- Before taking any action except for planning and writing `TODO.md`, always ask user for confirmation, accept `go`, `ok`, `yes`, `next`, or `continue` to proceed. If the user says `no`, `stop`, or `cancel`, abort the action and return to planning.
|
||||
|
||||
## Security & Secrets Protection
|
||||
|
||||
- **Never commit raw secrets or credentials** to the repository.
|
||||
- **Never print or log raw secrets** in terminal commands or output files.
|
||||
- **Use Gitea Actions Secrets** for all production credentials and environment variables.
|
||||
- **Avoid hardcoding sensitive values** in workflow files (`.yml`). Instead, reference them using `${{ secrets.SECRET_NAME }}`.
|
||||
- **Do not use the `GITEA_` prefix** for custom secrets or environment variables in Gitea, as it is reserved internally and will be silently ignored or returned as empty. Use the `CI_` prefix instead (e.g., `CI_REGISTRY_USER`, `CI_REGISTRY_TOKEN`).
|
||||
- **Keep `.env` gitignored** and use `.env.example` as the template for local development.
|
||||
- **For CD deployments**, generate the `.env` file dynamically on the runner using a secure step that populates values from Gitea Actions Secrets.
|
||||
+3
-4
@@ -50,6 +50,9 @@ data/
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
|
||||
# Demo data (tracked in backend/playout/demodata/ for out-of-the-box playout)
|
||||
!backend/playout/demodata/
|
||||
|
||||
# Docker
|
||||
.dockerignore
|
||||
**/.dockerignore
|
||||
@@ -75,10 +78,6 @@ Thumbs.db
|
||||
*.tfstate.backup
|
||||
.terraform/
|
||||
|
||||
# Monitoring & temp
|
||||
prometheus/
|
||||
grafana/
|
||||
|
||||
# Agent / Tool files
|
||||
.github/instructions/TODO.md
|
||||
.github/instructions/DONE.md
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
[](https://git.allucanget.biz/zwitschi/linearvoid/actions)
|
||||
[](LICENSE)
|
||||
[](https://git.allucanget.biz)
|
||||
[](https://git.allucanget.biz/zwitschi/linearvoid/releases)
|
||||
[](CHANGELOG.md)
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -27,6 +29,23 @@ See [`docs/architecture/adr/`](docs/architecture/adr/) for the full set of decis
|
||||
- Video normalization, monitoring, fallback slate, and easy playlist management.
|
||||
- US-centric performance (Ashburn origin + Cloudflare POPs).
|
||||
|
||||
## Architecture Diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
V[US Viewers] -->|HTTPS| CF[Cloudflare CDN]
|
||||
CF -->|Cache HIT| R2[Cloudflare R2]
|
||||
CF -->|Cache MISS| N[Nginx :80]
|
||||
N -->|/ws| CH[Chat :3000]
|
||||
N -->|/admin| CH
|
||||
N -->|/hls| P[Playout :8081]
|
||||
N -->|/*| FE[Frontend SPA]
|
||||
CH --> R[(Redis)]
|
||||
CH --> DB[(PostgreSQL)]
|
||||
P -->|ffmpeg| HLS[/data/hls/]
|
||||
P -->|sync| R2
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```text
|
||||
@@ -97,9 +116,16 @@ This structure directly reflects the building blocks, runtime flows, deployment
|
||||
git clone https://git.allucanget.biz/zwitschi/linearvoid.git
|
||||
cd linearvoid
|
||||
cp .env.example .env
|
||||
# Edit .env with your R2 credentials and domain settings
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
Then open:
|
||||
|
||||
- **Viewer**: [https://linearvoid.tv](https://linearvoid.tv) (or `http://localhost`)
|
||||
- **Admin**: [https://linearvoid.tv/admin](https://linearvoid.tv/admin) (or `http://localhost/admin`)
|
||||
- **Metrics**: [https://linearvoid.tv/metrics](https://linearvoid.tv/metrics)
|
||||
|
||||
Full setup guide: [`SETUP.md`](SETUP.md).
|
||||
|
||||
See the [architecture docs](docs/architecture/README.md) for detailed architecture and ADRs.
|
||||
@@ -111,6 +137,50 @@ See the [architecture docs](docs/architecture/README.md) for detailed architectu
|
||||
- Normalize all source videos before adding to playlist (see ADR-001 for exact codec/fps/keyframe requirements).
|
||||
- Monitor cache hit ratio and R2 operations closely — they determine real cost.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
| ---------------------- | ----------------------------------------------- | -------------------------------- |
|
||||
| `R2_ACCESS_KEY_ID` | — | Cloudflare R2 access key |
|
||||
| `R2_SECRET_ACCESS_KEY` | — | Cloudflare R2 secret key |
|
||||
| `R2_BUCKET_NAME` | `linearvoid-media` | R2 bucket for HLS segments |
|
||||
| `DATABASE_URL` | `postgres://...@db:5432/...` | Chat DB (Postgres or SQLite) |
|
||||
| `REDIS_URL` | `redis://redis:6379/0` | Redis for presence/rate limiting |
|
||||
| `VITE_PUBLIC_APP_URL` | `https://linearvoid.tv` | Public viewer URL |
|
||||
| `VITE_PUBLIC_WS_URL` | `wss://linearvoid.tv/ws` | WebSocket endpoint |
|
||||
| `VITE_PUBLIC_HLS_URL` | `https://media.linearvoid.tv/hls/playlist.m3u8` | HLS playlist URL |
|
||||
| `ADMIN_USERNAME` | `admin` | Admin panel username |
|
||||
| `ADMIN_PASSWORD` | — | Admin panel password |
|
||||
| `HLS_SEGMENT_DURATION` | `6` | HLS segment duration in seconds |
|
||||
| `HLS_WINDOW_SIZE` | `24` | HLS playlist window size |
|
||||
|
||||
Full reference: [`.env.example`](.env.example).
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
| Problem | Check |
|
||||
| ------------------------ | ------------------------------------------------------------------------------------- |
|
||||
| **Playout not starting** | Verify playlist exists in `data/playlist/playlist.txt` and media files are accessible |
|
||||
| **HLS not playing** | Check `docker compose logs playout` for ffmpeg errors; verify `data/hls/` permissions |
|
||||
| **Chat not connecting** | Ensure Redis is running: `docker compose logs redis`; check `DATABASE_URL` |
|
||||
| **Admin panel 401** | Verify `ADMIN_USERNAME`/`ADMIN_PASSWORD` in `.env` match login credentials |
|
||||
| **Docker build fails** | Run `docker compose build --no-cache`; check `backend/playout/.dockerignore` |
|
||||
| **R2 upload failures** | Verify `R2_ACCESS_KEY_ID` and `R2_SECRET_ACCESS_KEY`; check bucket CORS settings |
|
||||
|
||||
### API Endpoints
|
||||
|
||||
| Service | Endpoint | Description |
|
||||
| ----------- | ------------------------ | -------------------------- |
|
||||
| **Chat** | `GET /health` | Health check |
|
||||
| **Chat** | `GET /metrics` | Prometheus metrics |
|
||||
| **Chat** | `GET /ws` | WebSocket upgrade for chat |
|
||||
| **Chat** | `GET /api/admin/*` | Admin API (auth required) |
|
||||
| **Chat** | `POST /api/admin/login` | Admin login |
|
||||
| **Playout** | `GET /health` | Health check (port 8081) |
|
||||
| **Nginx** | `GET /` | Viewer SPA |
|
||||
| **Nginx** | `GET /admin/` | Admin panel |
|
||||
| **Nginx** | `GET /hls/playlist.m3u8` | HLS manifest |
|
||||
|
||||
## Architecture Documentation
|
||||
|
||||
The architecture is fully documented using the [arc42 template](docs/architecture/README.md) (with populated sections, Mermaid context and building-block diagrams, deployment view, cross-cutting concepts, quality goals, risks, and glossary).
|
||||
@@ -141,4 +211,17 @@ Contributions, issues, and further evolution (LL-HLS, ABR, multi-channel, E2EE)
|
||||
|
||||
**Container Registry**: `git.allucanget.biz/zwitschi/linearvoid/<service>:latest`
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! See [`CONTRIBUTING.md`](CONTRIBUTING.md) for guidelines. Quick overview:
|
||||
|
||||
1. Read the [architecture docs](docs/architecture/README.md) and ADRs first.
|
||||
2. Plan tasks in `.github/instructions/TODO.md`.
|
||||
3. Open PRs on [Gitea](https://git.allucanget.biz/zwitschi/linearvoid).
|
||||
4. CI validates builds and tests automatically.
|
||||
|
||||
Since this a linear project, we also rely on a linear development and release model: Only main branch is used for releases, and all features are merged into main after review and CI validation. Releases are tagged with semantic versioning (v1.0.3, v1.0.4, etc.) and automatically built/pushed to the container registry.
|
||||
|
||||
[MIT License](LICENSE)
|
||||
|
||||
The project is ready for use. See [`SETUP.md`](SETUP.md) for detailed installation instructions.
|
||||
@@ -12,6 +12,13 @@ This guide covers prerequisites, environment setup, running the application loca
|
||||
- **Git**: For cloning and version control.
|
||||
- **Cloudflare Account**: For R2 storage and CDN (free tier sufficient for most use cases).
|
||||
- **Gitea Account**: Required if you want to push images to the self-hosted registry or use the repository's CI/CD flow.
|
||||
- Set up the required Actions secrets in Gitea at: [https://git.allucanget.biz/zwitschi/linearvoid/settings/actions/secrets](https://git.allucanget.biz/zwitschi/linearvoid/settings/actions/secrets)
|
||||
- Add the following secrets:
|
||||
- `CI_REGISTRY_USER`: Your Gitea username (e.g., `zwitschi`)
|
||||
- `CI_REGISTRY_TOKEN`: A Gitea Personal Access Token with `write:packages` scope
|
||||
- `ADMIN_USERNAME`: The administrator username for the production admin panel (e.g., `admin`)
|
||||
- `ADMIN_PASSWORD`: A strong password for the production admin panel
|
||||
- `DATABASE_URL`: The production database connection string (e.g., `postgres://linearvoid:<password>@db:5432/linearvoid?sslmode=disable`)
|
||||
- **Hetzner Cloud Account** (optional for production): For the US-East VPS (Ashburn, VA recommended per ADR-001).
|
||||
|
||||
## Development Prerequisites
|
||||
@@ -70,10 +77,12 @@ This guide covers prerequisites, environment setup, running the application loca
|
||||
# VITE_PUBLIC_HLS_URL=http://localhost/hls/playlist.m3u8
|
||||
|
||||
# Gitea Container Registry (for self-hosted Docker images)
|
||||
GITEA_REGISTRY_URL=git.allucanget.biz
|
||||
GITEA_USERNAME=your-gitea-username
|
||||
GITEA_TOKEN=your-gitea-token
|
||||
GITEA_REGISTRY_NAMESPACE=zwitschi/linearvoid
|
||||
CI_REGISTRY_URL=git.allucanget.biz
|
||||
CI_REGISTRY_USER=your-gitea-username
|
||||
CI_REGISTRY_TOKEN=your-gitea-token
|
||||
CI_REGISTRY_NAMESPACE=zwitschi/linearvoid
|
||||
# Gitea API access (optional, for CI/release automation)
|
||||
CI_SERVER_URL=https://git.allucanget.biz
|
||||
|
||||
# Chat / Database
|
||||
DATABASE_URL=postgres://linearvoid:linearvoid@db:5432/linearvoid?sslmode=disable
|
||||
@@ -99,11 +108,6 @@ This guide covers prerequisites, environment setup, running the application loca
|
||||
# Development flags
|
||||
NODE_ENV=development
|
||||
GO_ENV=development
|
||||
|
||||
# Gitea API access (optional, for CI/release automation)
|
||||
GITEA_SERVER_URL=https://git.allucanget.biz
|
||||
GITEA_TOKEN=your-gitea-token
|
||||
GITEA_USERNAME=your-gitea-username
|
||||
```
|
||||
|
||||
4. **Check local value overrides for Docker Compose**:
|
||||
@@ -162,24 +166,33 @@ This guide covers prerequisites, environment setup, running the application loca
|
||||
|
||||
2. **Create an access token**:
|
||||
- Generate a token with the scopes needed for package/registry access and, if applicable, repository automation.
|
||||
- Store it as `GITEA_TOKEN` in `.env`.
|
||||
- Store it as `CI_REGISTRY_TOKEN` in `.env`.
|
||||
|
||||
3. **Configure registry variables**:
|
||||
- Set `GITEA_REGISTRY_URL` to the Gitea host.
|
||||
- Set `GITEA_REGISTRY_NAMESPACE` to the image namespace, for example `zwitschi/linearvoid`.
|
||||
- Set `GITEA_USERNAME` to the account that will push or pull images.
|
||||
3. **Configure Gitea Actions Secrets**:
|
||||
- Go to the repository settings page: [https://git.allucanget.biz/zwitschi/linearvoid/settings/actions/secrets](https://git.allucanget.biz/zwitschi/linearvoid/settings/actions/secrets)
|
||||
- Add the following secrets to enable automated CI/CD and production deployment:
|
||||
- `CI_REGISTRY_USER`: Your Gitea username (e.g., `zwitschi`).
|
||||
- `CI_REGISTRY_TOKEN`: Your Gitea Personal Access Token with `write:packages` scope.
|
||||
- `ADMIN_USERNAME`: The administrator username for the production admin panel (e.g., `admin`).
|
||||
- `ADMIN_PASSWORD`: A strong password for the production admin panel.
|
||||
- `DATABASE_URL`: The production database connection string (e.g., `postgres://linearvoid:<password>@db:5432/linearvoid?sslmode=disable`).
|
||||
|
||||
4. **Authenticate Docker to the registry**:
|
||||
4. **Configure registry variables**:
|
||||
- Set `CI_REGISTRY_URL` to the Gitea host.
|
||||
- Set `CI_REGISTRY_NAMESPACE` to the image namespace, for example `zwitschi/linearvoid`.
|
||||
- Set `CI_REGISTRY_USER` to the account that will push or pull images.
|
||||
|
||||
5. **Authenticate Docker to the registry**:
|
||||
|
||||
```bash
|
||||
docker login git.allucanget.biz
|
||||
```
|
||||
|
||||
5. **Build and publish images when needed**:
|
||||
6. **Build and publish images when needed**:
|
||||
- Local development uses `docker-compose.yml`, which builds from source.
|
||||
- Production uses `docker-compose.prod.yml`, which expects the `playout`, `chat`, and `nginx` images to exist in the configured Gitea registry namespace.
|
||||
|
||||
6. **Validate image availability**:
|
||||
7. **Validate image availability**:
|
||||
- Before a production rollout, verify that the expected `:latest` tags can be pulled from the registry on the target server.
|
||||
|
||||
## Running the Application
|
||||
@@ -230,6 +243,104 @@ This guide covers prerequisites, environment setup, running the application loca
|
||||
docker compose down
|
||||
```
|
||||
|
||||
### Local Test Environment Deployment (Remote Docker Host)
|
||||
|
||||
For testing deployments on a local network server without setting up a full production VPS, you can deploy directly to a remote Docker host (e.g., a home lab server or test machine) using Docker's TCP socket.
|
||||
|
||||
#### Test Environment Prerequisites
|
||||
|
||||
- A remote server running Docker at IP `192.168.88.40` (running Alpine Linux).
|
||||
- Docker TCP socket enabled and listening on port `2375` on the remote server.
|
||||
|
||||
##### Enabling Docker TCP Socket on Alpine Linux
|
||||
|
||||
To allow the Gitea Actions runner (or your local machine) to deploy to the remote Alpine host, you must configure the Docker daemon to listen on the TCP socket:
|
||||
|
||||
1. Edit `/etc/conf.d/docker` on the Alpine host:
|
||||
|
||||
```bash
|
||||
vi /etc/conf.d/docker
|
||||
```
|
||||
|
||||
2. Update the `DOCKER_OPTS` line to include both the unix socket and the TCP socket:
|
||||
|
||||
```bash
|
||||
DOCKER_OPTS="-H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375"
|
||||
```
|
||||
|
||||
3. Restart the Docker service:
|
||||
|
||||
```bash
|
||||
rc-service docker restart
|
||||
```
|
||||
|
||||
4. Verify that Docker is listening on port `2375`:
|
||||
|
||||
```bash
|
||||
netstat -tlnp | grep 2375
|
||||
```
|
||||
|
||||
5. Ensure any firewall (like `awall` or `iptables`) on the Alpine host allows incoming traffic on port `2375` from your local network subnet (e.g., `192.168.88.0/24`).
|
||||
|
||||
#### Deployment Steps
|
||||
|
||||
1. **Configure Docker CLI to use the Remote Host**:
|
||||
You can point your local Docker CLI to the remote server by setting the `DOCKER_HOST` environment variable:
|
||||
- **PowerShell (Windows)**:
|
||||
|
||||
```powershell
|
||||
$env:DOCKER_HOST="tcp://192.168.88.40:2375"
|
||||
```
|
||||
|
||||
- **Bash (Linux/macOS)**:
|
||||
|
||||
```bash
|
||||
export DOCKER_HOST="tcp://192.168.88.40:2375"
|
||||
```
|
||||
|
||||
2. **Verify Connection**:
|
||||
Run `docker info` to confirm your local CLI is communicating with the remote server at `192.168.88.40`:
|
||||
|
||||
```bash
|
||||
docker info
|
||||
```
|
||||
|
||||
The output should show the remote server's hostname and system details.
|
||||
|
||||
3. **Deploy the Stack**:
|
||||
With `DOCKER_HOST` set, any `docker` or `docker compose` commands you run locally will execute directly on the remote server.
|
||||
- **Deploy from Registry (Production-like)**:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml pull
|
||||
docker compose -f docker-compose.prod.yml up -d --remove-orphans
|
||||
```
|
||||
|
||||
- **Build and Deploy from Source**:
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
4. **Access the Test Environment**:
|
||||
- Playout Health: `http://192.168.88.40:8081/health`
|
||||
- Chat Health: `http://192.168.88.40:3000/health`
|
||||
- Nginx Entrypoint: `http://192.168.88.40:8080` (or the port mapped to `NGINX_PORT` in `.env`)
|
||||
|
||||
5. **Reset to Local Docker Host**:
|
||||
Once testing is complete, clear the `DOCKER_HOST` variable to point back to your local machine:
|
||||
- **PowerShell**:
|
||||
|
||||
```powershell
|
||||
Remove-Item env:DOCKER_HOST
|
||||
```
|
||||
|
||||
- **Bash**:
|
||||
|
||||
```bash
|
||||
unset DOCKER_HOST
|
||||
```
|
||||
|
||||
### Production Setup Guide
|
||||
|
||||
1. **Prepare the target host**:
|
||||
@@ -260,7 +371,7 @@ This guide covers prerequisites, environment setup, running the application loca
|
||||
```
|
||||
|
||||
5. **Publish or verify required images in Gitea**:
|
||||
- Ensure the `playout`, `chat`, and `nginx` images exist under `${GITEA_REGISTRY_URL}/${GITEA_REGISTRY_NAMESPACE}`.
|
||||
- Ensure the `playout`, `chat`, and `nginx` images exist under `${CI_REGISTRY_URL}/${CI_REGISTRY_NAMESPACE}`.
|
||||
- Log in to the registry from the VPS before pulling.
|
||||
|
||||
6. **Deploy the production stack**:
|
||||
|
||||
@@ -9,7 +9,7 @@ RUN go mod tidy
|
||||
RUN CGO_ENABLED=1 GOOS=linux go build -o /chat-server ./src/main.go
|
||||
|
||||
FROM alpine:3.20
|
||||
RUN apk --no-cache add ca-certificates
|
||||
RUN apk --no-cache add ca-certificates curl curl
|
||||
WORKDIR /app
|
||||
COPY --from=builder /chat-server /usr/local/bin/chat-server
|
||||
|
||||
|
||||
@@ -16,20 +16,18 @@ RUN apk add --no-cache \
|
||||
ffmpeg \
|
||||
curl \
|
||||
bash
|
||||
|
||||
# Set working directory for playout
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=builder /playlist-manager /app/playlist-manager
|
||||
# Copy the playout start script and the built playlist manager binary
|
||||
COPY start-playout.sh /app/
|
||||
COPY *.sh /app/
|
||||
|
||||
RUN chmod +x /app/*.sh /app/playlist-manager
|
||||
|
||||
# Copy demo data for out-of-the-box playout
|
||||
COPY demodata/ /app/
|
||||
# Copy the built playlist manager binary from the builder stage
|
||||
COPY --from=builder /playlist-manager /app/playlist-manager
|
||||
# Create directories for playlist, media, and HLS output
|
||||
RUN mkdir -p /app/playlist /app/media /app/hls
|
||||
|
||||
RUN mkdir -p /app/hls
|
||||
RUN chmod +x /app/start-playout.sh /app/playlist-manager
|
||||
# Expose HLS directory (will be served by nginx)
|
||||
VOLUME ["/app/hls"]
|
||||
|
||||
# Default command runs the playout loop (which calls the Go validator)
|
||||
CMD ["./start-playout.sh"]
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
# Linear Void Demo Playlist
|
||||
file '/app/media/demo1.mp4'
|
||||
file '/app/media/demo2.mp4'
|
||||
@@ -6,7 +6,7 @@ services:
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- ./data/playlist:/app/playlist:ro
|
||||
- ./data/playlist:/app/playlist:rw
|
||||
- ./data/media:/app/media:ro
|
||||
- ./data/hls:/app/hls:rw
|
||||
networks:
|
||||
@@ -102,7 +102,7 @@ services:
|
||||
container_name: linearvoid-prometheus
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- prometheus-config:/etc/prometheus
|
||||
- prometheus-data:/prometheus
|
||||
ports:
|
||||
- "9090:9090"
|
||||
@@ -142,4 +142,5 @@ volumes:
|
||||
redis-data:
|
||||
postgres-data:
|
||||
prometheus-data:
|
||||
prometheus-config:
|
||||
grafana-data:
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
playout:
|
||||
image: ${GITEA_REGISTRY_URL:-git.allucanget.biz}/${GITEA_REGISTRY_NAMESPACE:-zwitschi/linearvoid}/playout:latest
|
||||
image: ${CI_REGISTRY_URL:-git.allucanget.biz}/${CI_REGISTRY_NAMESPACE:-zwitschi/linearvoid}/playout:latest
|
||||
build:
|
||||
context: ./backend/playout
|
||||
dockerfile: Dockerfile
|
||||
@@ -24,7 +24,7 @@ services:
|
||||
start_period: 10s
|
||||
|
||||
chat:
|
||||
image: ${GITEA_REGISTRY_URL:-git.allucanget.biz}/${GITEA_REGISTRY_NAMESPACE:-zwitschi/linearvoid}/chat:latest
|
||||
image: ${CI_REGISTRY_URL:-git.allucanget.biz}/${CI_REGISTRY_NAMESPACE:-zwitschi/linearvoid}/chat:latest
|
||||
build:
|
||||
context: ./backend/chat
|
||||
dockerfile: Dockerfile
|
||||
@@ -51,10 +51,10 @@ services:
|
||||
start_period: 10s
|
||||
|
||||
nginx:
|
||||
image: ${GITEA_REGISTRY_URL:-git.allucanget.biz}/${GITEA_REGISTRY_NAMESPACE:-zwitschi/linearvoid}/nginx:latest
|
||||
image: ${CI_REGISTRY_URL:-git.allucanget.biz}/${CI_REGISTRY_NAMESPACE:-zwitschi/linearvoid}/nginx:latest
|
||||
build:
|
||||
context: ./infrastructure/nginx
|
||||
dockerfile: Dockerfile
|
||||
context: .
|
||||
dockerfile: infrastructure/nginx/Dockerfile
|
||||
container_name: linearvoid-nginx
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
|
||||
@@ -217,6 +217,65 @@ Notable aspects: Manifest freshness ensures all viewers stay on same timeline; c
|
||||
|
||||
## Deployment View
|
||||
|
||||
### CI/CD Pipeline Architecture
|
||||
|
||||
The deployment follows a two-stage pipeline: **CI builds and pushes images**, then **CD pulls and deploys** them to the production host.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph "Gitea Actions"
|
||||
CI[CI Workflow<br/>Build + Push Images]
|
||||
CD[CD Workflow<br/>Pull + Deploy]
|
||||
end
|
||||
subgraph "Gitea Container Registry"
|
||||
REGISTRY[git.allucanget.biz<br/>playout:latest<br/>chat:latest<br/>nginx:latest]
|
||||
end
|
||||
subgraph "Production Host (192.168.88.40)"
|
||||
DOCKER[Docker Engine<br/>via TCP :2375]
|
||||
PLAYOUT[linearvoid-playout]
|
||||
CHAT[linearvoid-chat]
|
||||
NGINX[linearvoid-nginx]
|
||||
REDIS[linearvoid-redis]
|
||||
DB[linearvoid-db]
|
||||
PROM[linearvoid-prometheus]
|
||||
GRAF[linearvoid-grafana]
|
||||
end
|
||||
|
||||
CI -->|push| REGISTRY
|
||||
REGISTRY -->|pull| CD
|
||||
CD -->|deploy via DOCKER_HOST| DOCKER
|
||||
DOCKER --> PLAYOUT
|
||||
DOCKER --> CHAT
|
||||
DOCKER --> NGINX
|
||||
DOCKER --> REDIS
|
||||
DOCKER --> DB
|
||||
DOCKER --> PROM
|
||||
DOCKER --> GRAF
|
||||
|
||||
classDef ci fill:#e1f5fe,stroke:#0288d1
|
||||
classDef reg fill:#fff3e0,stroke:#f57c00
|
||||
classDef prod fill:#e8f5e9,stroke:#388e3c
|
||||
class CI,CD ci
|
||||
class REGISTRY reg
|
||||
class DOCKER,PLAYOUT,CHAT,NGINX,REDIS,DB,PROM,GRAF prod
|
||||
```
|
||||
|
||||
**Pipeline Sequence:**
|
||||
|
||||
1. **CI Workflow** (`.gitea/workflows/ci.yml`):
|
||||
- Triggers on `push` to `main` or `pull_request`
|
||||
- Runs `test` job: frontend lint/build, Go validation, `docker compose build`
|
||||
- Runs `docker` job (after `test` passes, only on `main` push):
|
||||
- Builds `playout`, `chat`, `nginx` images with Buildx
|
||||
- Pushes to Gitea Container Registry with `main` and `latest` tags
|
||||
2. **CD Workflow** (`.gitea/workflows/cd.yml`):
|
||||
- Triggers on `workflow_run` when CI completes successfully, or manually via `workflow_dispatch`
|
||||
- Pulls latest images from registry
|
||||
- Generates `.env` from secrets
|
||||
- Seeds Prometheus config into named volume
|
||||
- Deploys via `docker compose -f docker-compose.prod.yml up -d` to remote host (`192.168.88.40:2375`)
|
||||
- Runs health checks with retry logic for playout initialization
|
||||
|
||||
### Infrastructure Level 1
|
||||
|
||||
**Overview Diagram**: See Mermaid in Building Block section (VPS + Cloudflare R2/Pages/CDN).
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
# Stage 1: Build frontend
|
||||
FROM node:20-alpine AS builder
|
||||
WORKDIR /build
|
||||
COPY frontend/package*.json ./
|
||||
RUN npm ci
|
||||
COPY frontend/ .
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Nginx with bundled frontend
|
||||
FROM nginx:alpine
|
||||
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
COPY infrastructure/nginx/nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
# Create directories for HLS and frontend
|
||||
RUN mkdir -p /srv/frontend /srv/hls /srv/frontend/admin
|
||||
# Copy prebuilt frontend from builder stage
|
||||
COPY --from=builder /build/dist/ /srv/frontend/
|
||||
|
||||
# Create HLS directory
|
||||
RUN mkdir -p /srv/hls
|
||||
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"title": "Linear Void Monitoring",
|
||||
"panels": [
|
||||
{
|
||||
"title": "Concurrent Viewers",
|
||||
"type": "timeseries",
|
||||
"targets": [{ "expr": "linearvoid_chat_connected_clients" }]
|
||||
},
|
||||
{
|
||||
"title": "Stored Messages",
|
||||
"type": "stat",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "linearvoid_chat_messages_total"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Moderation Actions",
|
||||
"type": "timeseries",
|
||||
"targets": [{ "expr": "linearvoid_chat_moderation_actions_total" }]
|
||||
},
|
||||
{
|
||||
"title": "Redis Reachable",
|
||||
"type": "stat",
|
||||
"targets": [{ "expr": "linearvoid_chat_redis_up" }]
|
||||
},
|
||||
{
|
||||
"title": "Admin Sessions",
|
||||
"type": "timeseries",
|
||||
"targets": [{ "expr": "linearvoid_chat_admin_sessions_active" }]
|
||||
},
|
||||
{
|
||||
"title": "Chat Scrape Health",
|
||||
"type": "stat",
|
||||
"targets": [{ "expr": "up{job=\"chat\"}" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: "chat"
|
||||
static_configs:
|
||||
- targets: ["chat:3000"]
|
||||
- job_name: "prometheus"
|
||||
static_configs:
|
||||
- targets: ["prometheus:9090"]
|
||||
Reference in new issue
Block a user