tlc-claude-code 1.2.28 → 1.2.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -4
- package/package.json +15 -4
- package/scripts/capture-screenshots.js +170 -0
- package/scripts/docs-update.js +253 -0
- package/scripts/generate-screenshots.js +321 -0
- package/scripts/project-docs.js +377 -0
- package/scripts/vps-setup.sh +477 -0
- package/templates/docs-sync.yml +91 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# TLC VPS Setup Script
|
|
4
|
+
# Sets up TLC deployment server on Ubuntu VPS
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# curl -fsSL https://raw.githubusercontent.com/jurgencalleja/TLC/main/scripts/vps-setup.sh | bash
|
|
8
|
+
#
|
|
9
|
+
# Or download and run:
|
|
10
|
+
# wget https://raw.githubusercontent.com/jurgencalleja/TLC/main/scripts/vps-setup.sh
|
|
11
|
+
# chmod +x vps-setup.sh
|
|
12
|
+
# ./vps-setup.sh
|
|
13
|
+
#
|
|
14
|
+
|
|
15
|
+
set -e
|
|
16
|
+
|
|
17
|
+
# Colors
|
|
18
|
+
RED='\033[0;31m'
|
|
19
|
+
GREEN='\033[0;32m'
|
|
20
|
+
YELLOW='\033[1;33m'
|
|
21
|
+
BLUE='\033[0;34m'
|
|
22
|
+
NC='\033[0m' # No Color
|
|
23
|
+
|
|
24
|
+
log() { echo -e "${BLUE}[TLC]${NC} $1"; }
|
|
25
|
+
success() { echo -e "${GREEN}[TLC]${NC} $1"; }
|
|
26
|
+
warn() { echo -e "${YELLOW}[TLC]${NC} $1"; }
|
|
27
|
+
error() { echo -e "${RED}[TLC]${NC} $1"; exit 1; }
|
|
28
|
+
|
|
29
|
+
# Banner
|
|
30
|
+
echo ""
|
|
31
|
+
echo -e "${BLUE} ████████╗██╗ ██████╗${NC}"
|
|
32
|
+
echo -e "${BLUE} ╚══██╔══╝██║ ██╔════╝${NC}"
|
|
33
|
+
echo -e "${BLUE} ██║ ██║ ██║ ${NC}"
|
|
34
|
+
echo -e "${BLUE} ██║ ██║ ██║ ${NC}"
|
|
35
|
+
echo -e "${BLUE} ██║ ███████╗╚██████╗${NC}"
|
|
36
|
+
echo -e "${BLUE} ╚═╝ ╚══════╝ ╚═════╝${NC}"
|
|
37
|
+
echo ""
|
|
38
|
+
echo -e " ${GREEN}TLC VPS Setup${NC}"
|
|
39
|
+
echo -e " ${YELLOW}Deployment Server for Teams${NC}"
|
|
40
|
+
echo ""
|
|
41
|
+
|
|
42
|
+
# Check if running as root
|
|
43
|
+
if [ "$EUID" -eq 0 ]; then
|
|
44
|
+
error "Don't run as root. Script will use sudo when needed."
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Configuration
|
|
48
|
+
TLC_DIR="/opt/tlc"
|
|
49
|
+
TLC_USER="tlc"
|
|
50
|
+
TLC_PORT=3147
|
|
51
|
+
NODE_VERSION="20"
|
|
52
|
+
|
|
53
|
+
# Prompt for configuration
|
|
54
|
+
read -p "Enter your domain (e.g., project.example.com): " DOMAIN
|
|
55
|
+
if [ -z "$DOMAIN" ]; then
|
|
56
|
+
error "Domain is required"
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
read -p "Enter admin email: " ADMIN_EMAIL
|
|
60
|
+
if [ -z "$ADMIN_EMAIL" ]; then
|
|
61
|
+
error "Admin email is required"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
read -p "Enter Slack webhook URL (optional, press Enter to skip): " SLACK_WEBHOOK
|
|
65
|
+
|
|
66
|
+
# Generate secrets
|
|
67
|
+
JWT_SECRET=$(openssl rand -hex 32)
|
|
68
|
+
WEBHOOK_SECRET=$(openssl rand -hex 16)
|
|
69
|
+
ADMIN_PASSWORD=$(openssl rand -base64 12)
|
|
70
|
+
|
|
71
|
+
log "Starting TLC VPS setup..."
|
|
72
|
+
log "Domain: $DOMAIN"
|
|
73
|
+
log "Admin: $ADMIN_EMAIL"
|
|
74
|
+
|
|
75
|
+
# Step 1: System updates
|
|
76
|
+
log "Updating system packages..."
|
|
77
|
+
sudo apt-get update -qq
|
|
78
|
+
sudo apt-get upgrade -y -qq
|
|
79
|
+
|
|
80
|
+
# Step 2: Install dependencies
|
|
81
|
+
log "Installing dependencies..."
|
|
82
|
+
sudo apt-get install -y -qq \
|
|
83
|
+
curl \
|
|
84
|
+
git \
|
|
85
|
+
nginx \
|
|
86
|
+
certbot \
|
|
87
|
+
python3-certbot-nginx \
|
|
88
|
+
postgresql \
|
|
89
|
+
postgresql-contrib
|
|
90
|
+
|
|
91
|
+
# Step 3: Install Node.js
|
|
92
|
+
log "Installing Node.js $NODE_VERSION..."
|
|
93
|
+
if ! command -v node &> /dev/null; then
|
|
94
|
+
curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | sudo -E bash -
|
|
95
|
+
sudo apt-get install -y -qq nodejs
|
|
96
|
+
fi
|
|
97
|
+
success "Node.js $(node -v) installed"
|
|
98
|
+
|
|
99
|
+
# Step 4: Install Docker
|
|
100
|
+
log "Installing Docker..."
|
|
101
|
+
if ! command -v docker &> /dev/null; then
|
|
102
|
+
curl -fsSL https://get.docker.com | sudo sh
|
|
103
|
+
sudo usermod -aG docker $USER
|
|
104
|
+
fi
|
|
105
|
+
success "Docker installed"
|
|
106
|
+
|
|
107
|
+
# Step 5: Create TLC user
|
|
108
|
+
log "Creating TLC user..."
|
|
109
|
+
if ! id "$TLC_USER" &>/dev/null; then
|
|
110
|
+
sudo useradd -r -s /bin/bash -d $TLC_DIR $TLC_USER
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
# Step 6: Create directories
|
|
114
|
+
log "Creating directories..."
|
|
115
|
+
sudo mkdir -p $TLC_DIR/{deployments,logs,config}
|
|
116
|
+
sudo chown -R $TLC_USER:$TLC_USER $TLC_DIR
|
|
117
|
+
|
|
118
|
+
# Step 7: Setup PostgreSQL
|
|
119
|
+
log "Setting up PostgreSQL..."
|
|
120
|
+
sudo -u postgres psql -c "CREATE USER tlc WITH PASSWORD '$JWT_SECRET';" 2>/dev/null || true
|
|
121
|
+
sudo -u postgres psql -c "CREATE DATABASE tlc OWNER tlc;" 2>/dev/null || true
|
|
122
|
+
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE tlc TO tlc;" 2>/dev/null || true
|
|
123
|
+
|
|
124
|
+
# Create users table
|
|
125
|
+
sudo -u postgres psql -d tlc -c "
|
|
126
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
127
|
+
id SERIAL PRIMARY KEY,
|
|
128
|
+
email VARCHAR(255) UNIQUE NOT NULL,
|
|
129
|
+
password_hash VARCHAR(255) NOT NULL,
|
|
130
|
+
name VARCHAR(255),
|
|
131
|
+
role VARCHAR(50) DEFAULT 'engineer',
|
|
132
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
133
|
+
last_login TIMESTAMP
|
|
134
|
+
);
|
|
135
|
+
" 2>/dev/null || true
|
|
136
|
+
|
|
137
|
+
success "PostgreSQL configured"
|
|
138
|
+
|
|
139
|
+
# Step 8: Install TLC server
|
|
140
|
+
log "Installing TLC server..."
|
|
141
|
+
sudo -u $TLC_USER bash -c "
|
|
142
|
+
cd $TLC_DIR
|
|
143
|
+
npm init -y
|
|
144
|
+
npm install tlc-claude-code express ws http-proxy-middleware jsonwebtoken bcryptjs pg dotenv
|
|
145
|
+
"
|
|
146
|
+
|
|
147
|
+
# Step 9: Create server configuration
|
|
148
|
+
log "Creating server configuration..."
|
|
149
|
+
sudo tee $TLC_DIR/.env > /dev/null <<EOF
|
|
150
|
+
# TLC Server Configuration
|
|
151
|
+
NODE_ENV=production
|
|
152
|
+
PORT=$TLC_PORT
|
|
153
|
+
DOMAIN=$DOMAIN
|
|
154
|
+
|
|
155
|
+
# Security
|
|
156
|
+
JWT_SECRET=$JWT_SECRET
|
|
157
|
+
WEBHOOK_SECRET=$WEBHOOK_SECRET
|
|
158
|
+
|
|
159
|
+
# Database
|
|
160
|
+
DATABASE_URL=postgres://tlc:$JWT_SECRET@localhost:5432/tlc
|
|
161
|
+
|
|
162
|
+
# Slack (optional)
|
|
163
|
+
SLACK_WEBHOOK_URL=$SLACK_WEBHOOK
|
|
164
|
+
|
|
165
|
+
# Admin
|
|
166
|
+
ADMIN_EMAIL=$ADMIN_EMAIL
|
|
167
|
+
ADMIN_PASSWORD_HASH=\$(node -e "console.log(require('bcryptjs').hashSync('$ADMIN_PASSWORD', 10))")
|
|
168
|
+
EOF
|
|
169
|
+
|
|
170
|
+
# Step 10: Create TLC server script
|
|
171
|
+
log "Creating TLC server..."
|
|
172
|
+
sudo tee $TLC_DIR/server.js > /dev/null <<'SERVEREOF'
|
|
173
|
+
const express = require('express');
|
|
174
|
+
const { createServer } = require('http');
|
|
175
|
+
const { WebSocketServer } = require('ws');
|
|
176
|
+
const { createProxyMiddleware } = require('http-proxy-middleware');
|
|
177
|
+
const jwt = require('jsonwebtoken');
|
|
178
|
+
const bcrypt = require('bcryptjs');
|
|
179
|
+
const { Pool } = require('pg');
|
|
180
|
+
const path = require('path');
|
|
181
|
+
const fs = require('fs');
|
|
182
|
+
const { execSync, spawn } = require('child_process');
|
|
183
|
+
require('dotenv').config();
|
|
184
|
+
|
|
185
|
+
const app = express();
|
|
186
|
+
const server = createServer(app);
|
|
187
|
+
const wss = new WebSocketServer({ server });
|
|
188
|
+
|
|
189
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
190
|
+
const PORT = process.env.PORT || 3147;
|
|
191
|
+
const DEPLOYMENTS_DIR = '/opt/tlc/deployments';
|
|
192
|
+
|
|
193
|
+
// Middleware
|
|
194
|
+
app.use(express.json());
|
|
195
|
+
app.use(express.static(path.join(__dirname, 'node_modules/tlc-claude-code/server/dashboard')));
|
|
196
|
+
|
|
197
|
+
// Branch port mapping
|
|
198
|
+
const branchPorts = new Map();
|
|
199
|
+
let nextPort = 10000;
|
|
200
|
+
|
|
201
|
+
// Auth middleware
|
|
202
|
+
const authenticate = async (req, res, next) => {
|
|
203
|
+
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
204
|
+
if (!token) return res.status(401).json({ error: 'No token' });
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
|
208
|
+
req.user = decoded;
|
|
209
|
+
next();
|
|
210
|
+
} catch (e) {
|
|
211
|
+
res.status(401).json({ error: 'Invalid token' });
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// Login
|
|
216
|
+
app.post('/api/login', async (req, res) => {
|
|
217
|
+
const { email, password } = req.body;
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
const result = await pool.query('SELECT * FROM users WHERE email = $1', [email]);
|
|
221
|
+
const user = result.rows[0];
|
|
222
|
+
|
|
223
|
+
if (!user || !bcrypt.compareSync(password, user.password_hash)) {
|
|
224
|
+
return res.status(401).json({ error: 'Invalid credentials' });
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const token = jwt.sign(
|
|
228
|
+
{ id: user.id, email: user.email, role: user.role },
|
|
229
|
+
process.env.JWT_SECRET,
|
|
230
|
+
{ expiresIn: '7d' }
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
await pool.query('UPDATE users SET last_login = NOW() WHERE id = $1', [user.id]);
|
|
234
|
+
|
|
235
|
+
res.json({ token, user: { email: user.email, role: user.role } });
|
|
236
|
+
} catch (e) {
|
|
237
|
+
res.status(500).json({ error: e.message });
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// List deployments
|
|
242
|
+
app.get('/api/deployments', authenticate, async (req, res) => {
|
|
243
|
+
const deployments = [];
|
|
244
|
+
|
|
245
|
+
if (fs.existsSync(DEPLOYMENTS_DIR)) {
|
|
246
|
+
const branches = fs.readdirSync(DEPLOYMENTS_DIR);
|
|
247
|
+
for (const branch of branches) {
|
|
248
|
+
const port = branchPorts.get(branch) || 'stopped';
|
|
249
|
+
deployments.push({
|
|
250
|
+
branch,
|
|
251
|
+
port,
|
|
252
|
+
url: `https://${branch}.${process.env.DOMAIN}`,
|
|
253
|
+
status: port !== 'stopped' ? 'running' : 'stopped'
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
res.json(deployments);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Deploy branch
|
|
262
|
+
app.post('/api/deploy', authenticate, async (req, res) => {
|
|
263
|
+
const { branch, repo } = req.body;
|
|
264
|
+
if (!branch) return res.status(400).json({ error: 'Branch required' });
|
|
265
|
+
|
|
266
|
+
const safeBranch = branch.replace(/[^a-zA-Z0-9-]/g, '-');
|
|
267
|
+
const deployDir = path.join(DEPLOYMENTS_DIR, safeBranch);
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
// Clone or pull
|
|
271
|
+
if (!fs.existsSync(deployDir)) {
|
|
272
|
+
execSync(`git clone -b ${branch} ${repo} ${deployDir}`);
|
|
273
|
+
} else {
|
|
274
|
+
execSync(`cd ${deployDir} && git pull`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Install deps
|
|
278
|
+
execSync(`cd ${deployDir} && npm install`);
|
|
279
|
+
|
|
280
|
+
// Assign port
|
|
281
|
+
const port = nextPort++;
|
|
282
|
+
branchPorts.set(safeBranch, port);
|
|
283
|
+
|
|
284
|
+
// Start container or process
|
|
285
|
+
spawn('npm', ['start'], {
|
|
286
|
+
cwd: deployDir,
|
|
287
|
+
env: { ...process.env, PORT: port.toString() },
|
|
288
|
+
detached: true,
|
|
289
|
+
stdio: 'ignore'
|
|
290
|
+
}).unref();
|
|
291
|
+
|
|
292
|
+
// Notify Slack
|
|
293
|
+
if (process.env.SLACK_WEBHOOK_URL) {
|
|
294
|
+
fetch(process.env.SLACK_WEBHOOK_URL, {
|
|
295
|
+
method: 'POST',
|
|
296
|
+
headers: { 'Content-Type': 'application/json' },
|
|
297
|
+
body: JSON.stringify({
|
|
298
|
+
text: `🚀 Deployed ${branch} to https://${safeBranch}.${process.env.DOMAIN}`
|
|
299
|
+
})
|
|
300
|
+
}).catch(() => {});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
res.json({
|
|
304
|
+
success: true,
|
|
305
|
+
url: `https://${safeBranch}.${process.env.DOMAIN}`,
|
|
306
|
+
port
|
|
307
|
+
});
|
|
308
|
+
} catch (e) {
|
|
309
|
+
res.status(500).json({ error: e.message });
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// GitHub webhook
|
|
314
|
+
app.post('/api/webhook', (req, res) => {
|
|
315
|
+
const signature = req.headers['x-hub-signature-256'];
|
|
316
|
+
// Verify signature in production
|
|
317
|
+
|
|
318
|
+
const { ref, repository } = req.body;
|
|
319
|
+
const branch = ref?.replace('refs/heads/', '');
|
|
320
|
+
|
|
321
|
+
if (branch) {
|
|
322
|
+
// Trigger deployment
|
|
323
|
+
fetch(`http://localhost:${PORT}/api/deploy`, {
|
|
324
|
+
method: 'POST',
|
|
325
|
+
headers: {
|
|
326
|
+
'Content-Type': 'application/json',
|
|
327
|
+
'Authorization': `Bearer ${jwt.sign({ role: 'system' }, process.env.JWT_SECRET)}`
|
|
328
|
+
},
|
|
329
|
+
body: JSON.stringify({ branch, repo: repository.clone_url })
|
|
330
|
+
}).catch(() => {});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
res.json({ received: true });
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// Proxy to branch deployments
|
|
337
|
+
app.use('/preview/:branch', (req, res, next) => {
|
|
338
|
+
const branch = req.params.branch;
|
|
339
|
+
const port = branchPorts.get(branch);
|
|
340
|
+
|
|
341
|
+
if (!port) {
|
|
342
|
+
return res.status(404).send('Deployment not found');
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
createProxyMiddleware({
|
|
346
|
+
target: `http://localhost:${port}`,
|
|
347
|
+
changeOrigin: true,
|
|
348
|
+
pathRewrite: { [`^/preview/${branch}`]: '' }
|
|
349
|
+
})(req, res, next);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// WebSocket for logs
|
|
353
|
+
wss.on('connection', (ws) => {
|
|
354
|
+
ws.send(JSON.stringify({ type: 'connected' }));
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// Start server
|
|
358
|
+
server.listen(PORT, () => {
|
|
359
|
+
console.log(`TLC Deploy Server running on port ${PORT}`);
|
|
360
|
+
console.log(`Dashboard: https://dashboard.${process.env.DOMAIN}`);
|
|
361
|
+
});
|
|
362
|
+
SERVEREOF
|
|
363
|
+
|
|
364
|
+
sudo chown $TLC_USER:$TLC_USER $TLC_DIR/.env $TLC_DIR/server.js
|
|
365
|
+
|
|
366
|
+
# Step 11: Create systemd service
|
|
367
|
+
log "Creating systemd service..."
|
|
368
|
+
sudo tee /etc/systemd/system/tlc.service > /dev/null <<EOF
|
|
369
|
+
[Unit]
|
|
370
|
+
Description=TLC Deployment Server
|
|
371
|
+
After=network.target postgresql.service
|
|
372
|
+
|
|
373
|
+
[Service]
|
|
374
|
+
Type=simple
|
|
375
|
+
User=$TLC_USER
|
|
376
|
+
WorkingDirectory=$TLC_DIR
|
|
377
|
+
ExecStart=/usr/bin/node server.js
|
|
378
|
+
Restart=on-failure
|
|
379
|
+
RestartSec=10
|
|
380
|
+
Environment=NODE_ENV=production
|
|
381
|
+
|
|
382
|
+
[Install]
|
|
383
|
+
WantedBy=multi-user.target
|
|
384
|
+
EOF
|
|
385
|
+
|
|
386
|
+
sudo systemctl daemon-reload
|
|
387
|
+
sudo systemctl enable tlc
|
|
388
|
+
sudo systemctl start tlc
|
|
389
|
+
|
|
390
|
+
# Step 12: Configure nginx
|
|
391
|
+
log "Configuring nginx..."
|
|
392
|
+
sudo tee /etc/nginx/sites-available/tlc > /dev/null <<EOF
|
|
393
|
+
# TLC Dashboard
|
|
394
|
+
server {
|
|
395
|
+
listen 80;
|
|
396
|
+
server_name dashboard.$DOMAIN;
|
|
397
|
+
|
|
398
|
+
location / {
|
|
399
|
+
proxy_pass http://localhost:$TLC_PORT;
|
|
400
|
+
proxy_http_version 1.1;
|
|
401
|
+
proxy_set_header Upgrade \$http_upgrade;
|
|
402
|
+
proxy_set_header Connection "upgrade";
|
|
403
|
+
proxy_set_header Host \$host;
|
|
404
|
+
proxy_set_header X-Real-IP \$remote_addr;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
# Branch deployments (wildcard)
|
|
409
|
+
server {
|
|
410
|
+
listen 80;
|
|
411
|
+
server_name ~^(?<branch>.+)\.$DOMAIN\$;
|
|
412
|
+
|
|
413
|
+
location / {
|
|
414
|
+
proxy_pass http://localhost:$TLC_PORT/preview/\$branch;
|
|
415
|
+
proxy_http_version 1.1;
|
|
416
|
+
proxy_set_header Upgrade \$http_upgrade;
|
|
417
|
+
proxy_set_header Connection "upgrade";
|
|
418
|
+
proxy_set_header Host \$host;
|
|
419
|
+
proxy_set_header X-Real-IP \$remote_addr;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
EOF
|
|
423
|
+
|
|
424
|
+
sudo ln -sf /etc/nginx/sites-available/tlc /etc/nginx/sites-enabled/
|
|
425
|
+
sudo nginx -t && sudo systemctl reload nginx
|
|
426
|
+
|
|
427
|
+
# Step 13: Setup SSL
|
|
428
|
+
log "Setting up SSL certificates..."
|
|
429
|
+
sudo certbot --nginx -d "dashboard.$DOMAIN" -d "*.$DOMAIN" --email "$ADMIN_EMAIL" --agree-tos --non-interactive || {
|
|
430
|
+
warn "SSL setup failed. Run manually: sudo certbot --nginx -d dashboard.$DOMAIN"
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
# Step 14: Create admin user
|
|
434
|
+
log "Creating admin user..."
|
|
435
|
+
ADMIN_HASH=$(node -e "console.log(require('bcryptjs').hashSync('$ADMIN_PASSWORD', 10))")
|
|
436
|
+
sudo -u postgres psql -d tlc -c "
|
|
437
|
+
INSERT INTO users (email, password_hash, name, role)
|
|
438
|
+
VALUES ('$ADMIN_EMAIL', '$ADMIN_HASH', 'Admin', 'admin')
|
|
439
|
+
ON CONFLICT (email) DO UPDATE SET password_hash = '$ADMIN_HASH';
|
|
440
|
+
"
|
|
441
|
+
|
|
442
|
+
# Step 15: Print summary
|
|
443
|
+
echo ""
|
|
444
|
+
echo -e "${GREEN}════════════════════════════════════════════════════════════${NC}"
|
|
445
|
+
echo -e "${GREEN} TLC VPS Setup Complete!${NC}"
|
|
446
|
+
echo -e "${GREEN}════════════════════════════════════════════════════════════${NC}"
|
|
447
|
+
echo ""
|
|
448
|
+
echo -e " ${BLUE}Dashboard:${NC} https://dashboard.$DOMAIN"
|
|
449
|
+
echo -e " ${BLUE}Deployments:${NC} https://{branch}.$DOMAIN"
|
|
450
|
+
echo ""
|
|
451
|
+
echo -e " ${BLUE}Admin Login:${NC}"
|
|
452
|
+
echo -e " Email: $ADMIN_EMAIL"
|
|
453
|
+
echo -e " Password: ${YELLOW}$ADMIN_PASSWORD${NC}"
|
|
454
|
+
echo ""
|
|
455
|
+
echo -e " ${BLUE}Webhook URL:${NC} https://dashboard.$DOMAIN/api/webhook"
|
|
456
|
+
echo -e " ${BLUE}Webhook Secret:${NC} $WEBHOOK_SECRET"
|
|
457
|
+
echo ""
|
|
458
|
+
echo -e " ${BLUE}DNS Configuration:${NC}"
|
|
459
|
+
echo -e " Add these records to your DNS:"
|
|
460
|
+
echo -e " dashboard.$DOMAIN A $(curl -s ifconfig.me)"
|
|
461
|
+
echo -e " *.$DOMAIN A $(curl -s ifconfig.me)"
|
|
462
|
+
echo ""
|
|
463
|
+
echo -e " ${BLUE}GitHub Webhook Setup:${NC}"
|
|
464
|
+
echo -e " 1. Go to your repo Settings > Webhooks"
|
|
465
|
+
echo -e " 2. Add webhook: https://dashboard.$DOMAIN/api/webhook"
|
|
466
|
+
echo -e " 3. Content type: application/json"
|
|
467
|
+
echo -e " 4. Secret: $WEBHOOK_SECRET"
|
|
468
|
+
echo -e " 5. Events: Push events"
|
|
469
|
+
echo ""
|
|
470
|
+
echo -e " ${BLUE}Files:${NC}"
|
|
471
|
+
echo -e " Config: $TLC_DIR/.env"
|
|
472
|
+
echo -e " Logs: journalctl -u tlc -f"
|
|
473
|
+
echo -e " Service: sudo systemctl {start|stop|restart} tlc"
|
|
474
|
+
echo ""
|
|
475
|
+
echo -e "${GREEN}════════════════════════════════════════════════════════════${NC}"
|
|
476
|
+
echo ""
|
|
477
|
+
success "Setup complete! Save the credentials above."
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# TLC Documentation Sync
|
|
2
|
+
# Auto-generated by /tlc:docs setup
|
|
3
|
+
# Automatically syncs documentation on push
|
|
4
|
+
|
|
5
|
+
name: Documentation Sync
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
branches: [main, master]
|
|
10
|
+
paths:
|
|
11
|
+
- 'docs/**'
|
|
12
|
+
- 'README.md'
|
|
13
|
+
- 'src/**'
|
|
14
|
+
- 'package.json'
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
sync-docs:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
permissions:
|
|
20
|
+
contents: write
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- name: Checkout
|
|
24
|
+
uses: actions/checkout@v4
|
|
25
|
+
with:
|
|
26
|
+
fetch-depth: 0
|
|
27
|
+
|
|
28
|
+
- name: Setup Node.js
|
|
29
|
+
uses: actions/setup-node@v4
|
|
30
|
+
with:
|
|
31
|
+
node-version: '20'
|
|
32
|
+
cache: 'npm'
|
|
33
|
+
|
|
34
|
+
- name: Install dependencies
|
|
35
|
+
run: npm ci
|
|
36
|
+
|
|
37
|
+
- name: Update version in docs
|
|
38
|
+
run: |
|
|
39
|
+
VERSION=$(node -p "require('./package.json').version" 2>/dev/null || echo "1.0.0")
|
|
40
|
+
echo "Updating docs to version $VERSION"
|
|
41
|
+
|
|
42
|
+
# Update version references
|
|
43
|
+
if [ -d "docs" ]; then
|
|
44
|
+
find docs -name "*.md" -exec sed -i "s/v[0-9]\+\.[0-9]\+\.[0-9]\+/v$VERSION/g" {} \; 2>/dev/null || true
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
- name: Generate API docs
|
|
48
|
+
run: |
|
|
49
|
+
# Generate API documentation if TypeScript/JSDoc present
|
|
50
|
+
if [ -f "tsconfig.json" ]; then
|
|
51
|
+
npx typedoc --out docs/api src/ 2>/dev/null || echo "Skipping TypeDoc"
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
- name: Capture screenshots
|
|
55
|
+
run: |
|
|
56
|
+
# If Playwright is installed and app can run, capture screenshots
|
|
57
|
+
if npm ls playwright >/dev/null 2>&1; then
|
|
58
|
+
npm run docs:capture 2>/dev/null || echo "Skipping screenshots"
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
- name: Sync to GitHub Wiki
|
|
62
|
+
if: github.event.repository.has_wiki
|
|
63
|
+
env:
|
|
64
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
65
|
+
run: |
|
|
66
|
+
if [ -d "docs" ]; then
|
|
67
|
+
git clone https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.wiki.git wiki 2>/dev/null || exit 0
|
|
68
|
+
|
|
69
|
+
cp -r docs/* wiki/ 2>/dev/null || true
|
|
70
|
+
|
|
71
|
+
cd wiki
|
|
72
|
+
git config user.name "github-actions[bot]"
|
|
73
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
74
|
+
|
|
75
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
76
|
+
git add -A
|
|
77
|
+
git commit -m "docs: auto-sync from main"
|
|
78
|
+
git push
|
|
79
|
+
fi
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
- name: Commit doc updates
|
|
83
|
+
run: |
|
|
84
|
+
git config user.name "github-actions[bot]"
|
|
85
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
86
|
+
|
|
87
|
+
if [ -n "$(git status --porcelain docs/ README.md)" ]; then
|
|
88
|
+
git add docs/ README.md 2>/dev/null || true
|
|
89
|
+
git commit -m "docs: auto-update" || true
|
|
90
|
+
git push || true
|
|
91
|
+
fi
|