veza/ansible/playbooks/40-veza-apps.yml
2025-12-03 22:56:50 +01:00

599 lines
No EOL
21 KiB
YAML

---
# Deploy Veza V5 Ultra applications in containers
# Builds and runs backend, chat, stream, and web services
- name: Deploy Veza V5 Ultra applications
hosts: edge
become: true
gather_facts: true
vars:
domain: "{{ domain | default('veza.talas.fr') }}"
backend_container: "veza-backend"
chat_container: "veza-chat"
stream_container: "veza-stream"
web_container: "veza-web"
tasks:
- name: Deploy Go Backend API
block:
- name: Install Go in backend container
command: |
incus exec {{ backend_container }} -- apt update
incus exec {{ backend_container }} -- apt install -y wget git
incus exec {{ backend_container }} -- wget https://go.dev/dl/go1.21.5.linux-amd64.tar.gz
incus exec {{ backend_container }} -- tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz
incus exec {{ backend_container }} -- echo 'export PATH=$PATH:/usr/local/go/bin' >> /root/.bashrc
register: go_install_result
failed_when: false
- name: Display Go installation result
debug:
var: go_install_result.stdout_lines
- name: Create backend application directory
command: |
incus exec {{ backend_container }} -- mkdir -p /opt/veza-backend
register: backend_dir_result
failed_when: false
- name: Copy backend source code (placeholder)
command: |
incus exec {{ backend_container }} -- bash -c 'cat > /opt/veza-backend/main.go << "EOF"
package main
import (
"fmt"
"log"
"net/http"
"os"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
http.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{"status":"ok","service":"veza-backend"}`)
})
http.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{"message":"Veza V5 Ultra Backend API","version":"1.0.0"}`)
})
log.Printf("Backend API server starting on port %s", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
EOF'
register: backend_code_result
failed_when: false
- name: Build backend application
command: |
incus exec {{ backend_container }} -- bash -c "cd /opt/veza-backend && /usr/local/go/bin/go mod init veza-backend && /usr/local/go/bin/go build -ldflags '-s -w' -o veza-backend main.go"
register: backend_build_result
failed_when: false
- name: Create backend systemd service
command: |
incus exec {{ backend_container }} -- bash -c 'cat > /etc/systemd/system/veza-backend.service << "EOF"
[Unit]
Description=Veza V5 Ultra Backend API
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/veza-backend
ExecStart=/opt/veza-backend/veza-backend
Restart=always
RestartSec=5
Environment=PORT=8080
Environment=DATABASE_URL=postgresql://veza:password@localhost:5432/veza_db
Environment=REDIS_URL=redis://localhost:6379
Environment=JWT_SECRET=super-secret-jwt-key
Environment=JWT_REFRESH_SECRET=super-secret-refresh-key
[Install]
WantedBy=multi-user.target
EOF'
register: backend_service_result
failed_when: false
- name: Start backend service
command: |
incus exec {{ backend_container }} -- systemctl daemon-reload
incus exec {{ backend_container }} -- systemctl enable veza-backend
incus exec {{ backend_container }} -- systemctl start veza-backend
register: backend_start_result
failed_when: false
- name: Check backend service status
command: |
incus exec {{ backend_container }} -- systemctl status veza-backend
register: backend_status
failed_when: false
- name: Display backend status
debug:
var: backend_status.stdout_lines
rescue:
- name: Backend deployment failed
debug:
msg: "Backend deployment failed, continuing with other services"
- name: Deploy Rust Chat Server
block:
- name: Install Rust in chat container
command: |
incus exec {{ chat_container }} -- apt update
incus exec {{ chat_container }} -- apt install -y curl git
incus exec {{ chat_container }} -- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
incus exec {{ chat_container }} -- bash -c "source /root/.cargo/env && cargo --version"
register: rust_install_result
failed_when: false
- name: Display Rust installation result
debug:
var: rust_install_result.stdout_lines
- name: Create chat application directory
command: |
incus exec {{ chat_container }} -- mkdir -p /opt/veza-chat
register: chat_dir_result
failed_when: false
- name: Copy chat source code (placeholder)
command: |
incus exec {{ chat_container }} -- bash -c 'cat > /opt/veza-chat/Cargo.toml << "EOF"
[package]
name = "veza-chat"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1.0", features = ["full"] }
axum = "0.7"
tower = "0.4"
tower-http = { version = "0.5", features = ["cors"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
uuid = { version = "1.0", features = ["v4"] }
tracing = "0.1"
tracing-subscriber = "0.3"
EOF'
register: chat_cargo_result
failed_when: false
- name: Create chat main.rs
command: |
incus exec {{ chat_container }} -- tee /opt/veza-chat/src/main.rs << 'EOF'
use axum::{
extract::ws::{Message, WebSocket, WebSocketUpgrade},
response::Response,
routing::get,
Router,
};
use std::net::SocketAddr;
use tokio::net::TcpListener;
use tracing::{info, warn};
#[tokio::main]
async fn main() {
tracing_subscriber::init();
let app = Router::new()
.route("/ws", get(websocket_handler))
.route("/health", get(health_handler));
let addr = SocketAddr::from(([0, 0, 0, 0], 8081));
info!("Chat server starting on {}", addr);
let listener = TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
}
async fn websocket_handler(ws: WebSocketUpgrade) -> Response {
ws.on_upgrade(handle_websocket)
}
async fn handle_websocket(socket: WebSocket) {
info!("New WebSocket connection");
// Simple echo server for now
let (mut sender, mut receiver) = socket.split();
while let Some(msg) = receiver.recv().await {
match msg {
Ok(Message::Text(text)) => {
info!("Received: {}", text);
if sender.send(Message::Text(format!("Echo: {}", text))).await.is_err() {
break;
}
}
Ok(Message::Close(_)) => break,
Err(e) => {
warn!("WebSocket error: {}", e);
break;
}
_ => {}
}
}
info!("WebSocket connection closed");
}
async fn health_handler() -> &'static str {
"OK"
}
EOF
register: chat_main_result
failed_when: false
- name: Build chat application
command: |
incus exec {{ chat_container }} -- bash -c "cd /opt/veza-chat && source /root/.cargo/env && cargo build --release"
register: chat_build_result
failed_when: false
- name: Create chat systemd service
command: |
incus exec {{ chat_container }} -- tee /etc/systemd/system/veza-chat.service << 'EOF'
[Unit]
Description=Veza V5 Ultra Chat Server
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/veza-chat
ExecStart=/opt/veza-chat/target/release/veza-chat
Restart=always
RestartSec=5
Environment=SQLX_OFFLINE=true
[Install]
WantedBy=multi-user.target
EOF
register: chat_service_result
failed_when: false
- name: Start chat service
command: |
incus exec {{ chat_container }} -- systemctl daemon-reload
incus exec {{ chat_container }} -- systemctl enable veza-chat
incus exec {{ chat_container }} -- systemctl start veza-chat
register: chat_start_result
failed_when: false
- name: Check chat service status
command: |
incus exec {{ chat_container }} -- systemctl status veza-chat
register: chat_status
failed_when: false
- name: Display chat status
debug:
var: chat_status.stdout_lines
rescue:
- name: Chat deployment failed
debug:
msg: "Chat deployment failed, continuing with other services"
- name: Deploy Rust Stream Server
block:
- name: Install Rust in stream container
command: |
incus exec {{ stream_container }} -- apt update
incus exec {{ stream_container }} -- apt install -y curl git
incus exec {{ stream_container }} -- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
register: stream_rust_install_result
failed_when: false
- name: Create stream application directory
command: |
incus exec {{ stream_container }} -- mkdir -p /opt/veza-stream
register: stream_dir_result
failed_when: false
- name: Copy stream source code (placeholder)
command: |
incus exec {{ stream_container }} -- tee /opt/veza-stream/Cargo.toml << 'EOF'
[package]
name = "veza-stream"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1.0", features = ["full"] }
axum = "0.7"
tower = "0.4"
tower-http = { version = "0.5", features = ["cors", "fs"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tracing = "0.1"
tracing-subscriber = "0.3"
EOF
register: stream_cargo_result
failed_when: false
- name: Create stream main.rs
command: |
incus exec {{ stream_container }} -- tee /opt/veza-stream/src/main.rs << 'EOF'
use axum::{
extract::Path,
http::StatusCode,
response::Response,
routing::get,
Router,
};
use std::net::SocketAddr;
use tokio::net::TcpListener;
use tracing::{info, warn};
#[tokio::main]
async fn main() {
tracing_subscriber::init();
let app = Router::new()
.route("/stream/health", get(health_handler))
.route("/stream/:file", get(stream_handler));
let addr = SocketAddr::from(([0, 0, 0, 0], 8082));
info!("Stream server starting on {}", addr);
let listener = TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
}
async fn health_handler() -> &'static str {
"OK"
}
async fn stream_handler(Path(file): Path<String>) -> Result<Response, StatusCode> {
info!("Stream request for: {}", file);
// Simple file serving for now
if file.ends_with(".m3u8") {
Ok(Response::builder()
.status(200)
.header("Content-Type", "application/vnd.apple.mpegurl")
.body(format!("#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-TARGETDURATION:10\n#EXTINF:10.0,\n{}.ts\n#EXT-X-ENDLIST\n", file.replace(".m3u8", "")))
.unwrap())
} else {
Err(StatusCode::NOT_FOUND)
}
}
EOF
register: stream_main_result
failed_when: false
- name: Build stream application
command: |
incus exec {{ stream_container }} -- bash -c "cd /opt/veza-stream && source /root/.cargo/env && cargo build --release"
register: stream_build_result
failed_when: false
- name: Create stream systemd service
command: |
incus exec {{ stream_container }} -- tee /etc/systemd/system/veza-stream.service << 'EOF'
[Unit]
Description=Veza V5 Ultra Stream Server
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/veza-stream
ExecStart=/opt/veza-stream/target/release/veza-stream
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
register: stream_service_result
failed_when: false
- name: Start stream service
command: |
incus exec {{ stream_container }} -- systemctl daemon-reload
incus exec {{ stream_container }} -- systemctl enable veza-stream
incus exec {{ stream_container }} -- systemctl start veza-stream
register: stream_start_result
failed_when: false
- name: Check stream service status
command: |
incus exec {{ stream_container }} -- systemctl status veza-stream
register: stream_status
failed_when: false
- name: Display stream status
debug:
var: stream_status.stdout_lines
rescue:
- name: Stream deployment failed
debug:
msg: "Stream deployment failed, continuing with web service"
- name: Deploy React Web Application
block:
- name: Install Node.js in web container
command: |
incus exec {{ web_container }} -- apt update
incus exec {{ web_container }} -- apt install -y curl
incus exec {{ web_container }} -- curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
incus exec {{ web_container }} -- apt install -y nodejs nginx
register: node_install_result
failed_when: false
- name: Display Node.js installation result
debug:
var: node_install_result.stdout_lines
- name: Create web application directory
command: |
incus exec {{ web_container }} -- mkdir -p /opt/veza-web
register: web_dir_result
failed_when: false
- name: Create simple React app (placeholder)
command: |
incus exec {{ web_container }} -- tee /opt/veza-web/package.json << 'EOF'
{
"name": "veza-web",
"version": "1.0.0",
"description": "Veza V5 Ultra Web Application",
"main": "index.js",
"scripts": {
"start": "node server.js",
"build": "echo 'Build completed'"
},
"dependencies": {
"express": "^4.18.2"
}
}
EOF
register: web_package_result
failed_when: false
- name: Create simple web server
command: |
incus exec {{ web_container }} -- tee /opt/veza-web/server.js << 'EOF'
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.use(express.static('public'));
app.get('/', (req, res) => {
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>Veza V5 Ultra</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.container { max-width: 800px; margin: 0 auto; }
.header { background: #2c3e50; color: white; padding: 20px; border-radius: 5px; }
.content { padding: 20px; }
.status { background: #27ae60; color: white; padding: 10px; border-radius: 3px; margin: 10px 0; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🎵 Veza V5 Ultra</h1>
<p>Collaborative Audio Streaming Platform</p>
</div>
<div class="content">
<div class="status">✅ System Online</div>
<h2>Services Status</h2>
<ul>
<li>Backend API: <span id="api-status">Checking...</span></li>
<li>Chat WebSocket: <span id="chat-status">Checking...</span></li>
<li>Stream HLS: <span id="stream-status">Checking...</span></li>
</ul>
<h2>Features</h2>
<ul>
<li>Real-time collaborative audio streaming</li>
<li>WebSocket chat integration</li>
<li>HLS video streaming</li>
<li>Modern React frontend</li>
</ul>
</div>
</div>
<script>
// Simple health checks
fetch('/api/health').then(r => r.json()).then(d => {
document.getElementById('api-status').textContent = '✅ Online';
}).catch(() => {
document.getElementById('api-status').textContent = '❌ Offline';
});
</script>
</body>
</html>
`);
});
app.listen(port, '0.0.0.0', () => {
console.log(`Veza V5 Ultra web server running on port ${port}`);
});
EOF
register: web_server_result
failed_when: false
- name: Install web dependencies
command: |
incus exec {{ web_container }} -- bash -c "cd /opt/veza-web && npm install"
register: web_install_result
failed_when: false
- name: Create web systemd service
command: |
incus exec {{ web_container }} -- tee /etc/systemd/system/veza-web.service << 'EOF'
[Unit]
Description=Veza V5 Ultra Web Application
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/veza-web
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=5
Environment=PORT=3000
[Install]
WantedBy=multi-user.target
EOF
register: web_service_result
failed_when: false
- name: Start web service
command: |
incus exec {{ web_container }} -- systemctl daemon-reload
incus exec {{ web_container }} -- systemctl enable veza-web
incus exec {{ web_container }} -- systemctl start veza-web
register: web_start_result
failed_when: false
- name: Check web service status
command: |
incus exec {{ web_container }} -- systemctl status veza-web
register: web_status
failed_when: false
- name: Display web status
debug:
var: web_status.stdout_lines
rescue:
- name: Web deployment failed
debug:
msg: "Web deployment failed"
post_tasks:
- name: Show all running services
command: |
incus exec {{ backend_container }} -- systemctl list-units --type=service --state=running | grep veza || true
incus exec {{ chat_container }} -- systemctl list-units --type=service --state=running | grep veza || true
incus exec {{ stream_container }} -- systemctl list-units --type=service --state=running | grep veza || true
incus exec {{ web_container }} -- systemctl list-units --type=service --state=running | grep veza || true
register: all_services
failed_when: false
- name: Display all services
debug:
var: all_services.stdout_lines