cleanup: remove stale SAMI Caddy files + add download landing page
This commit is contained in:
@@ -47,12 +47,24 @@
|
|||||||
{
|
{
|
||||||
"from": "../status",
|
"from": "../status",
|
||||||
"to": "status",
|
"to": "status",
|
||||||
"filter": ["**/*", "!node_modules/**", "!.git/**", "!**/*.test.js", "!**/*.spec.js"]
|
"filter": [
|
||||||
|
"**/*",
|
||||||
|
"!node_modules/**",
|
||||||
|
"!.git/**",
|
||||||
|
"!**/*.test.js",
|
||||||
|
"!**/*.spec.js"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": "../dashcaddy-api",
|
"from": "../dashcaddy-api",
|
||||||
"to": "dashcaddy-api",
|
"to": "dashcaddy-api",
|
||||||
"filter": ["**/*", "!node_modules/**", "!.git/**", "!**/*.test.js", "!**/*.spec.js"]
|
"filter": [
|
||||||
|
"**/*",
|
||||||
|
"!node_modules/**",
|
||||||
|
"!.git/**",
|
||||||
|
"!**/*.test.js",
|
||||||
|
"!**/*.spec.js"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "assets/favicon.ico",
|
"icon": "assets/favicon.ico",
|
||||||
@@ -65,7 +77,9 @@
|
|||||||
"signAndEditExecutable": false
|
"signAndEditExecutable": false
|
||||||
},
|
},
|
||||||
"mac": {
|
"mac": {
|
||||||
"target": "dmg",
|
"target": [
|
||||||
|
"zip"
|
||||||
|
],
|
||||||
"icon": "assets/dashcaddy-logo.png"
|
"icon": "assets/dashcaddy-logo.png"
|
||||||
},
|
},
|
||||||
"linux": {
|
"linux": {
|
||||||
@@ -84,4 +98,4 @@
|
|||||||
"installerHeaderIcon": "assets/icon.ico"
|
"installerHeaderIcon": "assets/icon.ico"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-93
@@ -1,93 +0,0 @@
|
|||||||
# DashCaddy Deployment Script
|
|
||||||
# Deploys changes from Dev (E:) to Prod (C:)
|
|
||||||
|
|
||||||
$DevRoot = "E:\CaddyCerts\sites"
|
|
||||||
$ProdRoot = "C:\Caddy"
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
Write-Host "Deploying DashCaddy Changes..." -ForegroundColor Cyan
|
|
||||||
|
|
||||||
# 1. Pre-deploy validation - syntax check all JS files
|
|
||||||
Write-Host "Validating JavaScript syntax..." -ForegroundColor Yellow
|
|
||||||
$syntaxErrors = 0
|
|
||||||
Get-ChildItem "$DevRoot\dashcaddy-api" -Filter "*.js" | ForEach-Object {
|
|
||||||
$result = & node -c $_.FullName 2>&1
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
Write-Error "Syntax error in $($_.Name): $result"
|
|
||||||
$syntaxErrors++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Get-ChildItem "$DevRoot\dashcaddy-api\routes" -Filter "*.js" | ForEach-Object {
|
|
||||||
$result = & node -c $_.FullName 2>&1
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
Write-Error "Syntax error in routes/$($_.Name): $result"
|
|
||||||
$syntaxErrors++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($syntaxErrors -gt 0) {
|
|
||||||
Write-Error "Aborting deploy: $syntaxErrors syntax error(s) found."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
Write-Host " All files pass syntax check." -ForegroundColor Green
|
|
||||||
|
|
||||||
# 2. Update Frontend
|
|
||||||
Write-Host "Updating Dashboard UI..." -ForegroundColor Yellow
|
|
||||||
if (Test-Path "$ProdRoot\sites\status") {
|
|
||||||
# Build frontend bundles
|
|
||||||
Write-Host "Building frontend JavaScript..." -ForegroundColor Yellow
|
|
||||||
Set-Location "$DevRoot\status"
|
|
||||||
& npm install
|
|
||||||
& node build.js
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
Write-Error "Frontend build failed!"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
Write-Host " Frontend build complete." -ForegroundColor Green
|
|
||||||
|
|
||||||
# Copy all necessary files
|
|
||||||
Copy-Item "$DevRoot\status\index.html" "$ProdRoot\sites\status\index.html" -Force
|
|
||||||
Copy-Item "$DevRoot\status\dist\*" "$ProdRoot\sites\status\dist\" -Force
|
|
||||||
Set-Location $ProdRoot
|
|
||||||
} else {
|
|
||||||
Write-Warning "Target status folder not found. Skipping UI update."
|
|
||||||
}
|
|
||||||
|
|
||||||
# 3. Update Backend API
|
|
||||||
Write-Host "Updating API Server..." -ForegroundColor Yellow
|
|
||||||
if (Test-Path "$ProdRoot\sites\dashcaddy-api") {
|
|
||||||
# Copy all JS files, package files, API spec
|
|
||||||
Get-ChildItem "$DevRoot\dashcaddy-api" -Filter "*.js" | Copy-Item -Destination "$ProdRoot\sites\dashcaddy-api\" -Force
|
|
||||||
Copy-Item "$DevRoot\dashcaddy-api\package.json" "$ProdRoot\sites\dashcaddy-api\" -Force
|
|
||||||
Copy-Item "$DevRoot\dashcaddy-api\package-lock.json" "$ProdRoot\sites\dashcaddy-api\" -Force -ErrorAction SilentlyContinue
|
|
||||||
Copy-Item "$DevRoot\dashcaddy-api\openapi.yaml" "$ProdRoot\sites\dashcaddy-api\" -Force -ErrorAction SilentlyContinue
|
|
||||||
|
|
||||||
# Copy route modules
|
|
||||||
if (!(Test-Path "$ProdRoot\sites\dashcaddy-api\routes")) {
|
|
||||||
New-Item -ItemType Directory -Path "$ProdRoot\sites\dashcaddy-api\routes" | Out-Null
|
|
||||||
}
|
|
||||||
Copy-Item "$DevRoot\dashcaddy-api\routes\*" "$ProdRoot\sites\dashcaddy-api\routes\" -Force
|
|
||||||
|
|
||||||
# 4. Rebuild and Restart
|
|
||||||
Write-Host "Rebuilding API Container..." -ForegroundColor Yellow
|
|
||||||
Set-Location $ProdRoot
|
|
||||||
docker-compose up -d --build dashcaddy-api
|
|
||||||
|
|
||||||
# 5. Post-deploy health check
|
|
||||||
Write-Host "Waiting for container startup..." -ForegroundColor Yellow
|
|
||||||
Start-Sleep -Seconds 5
|
|
||||||
try {
|
|
||||||
$health = Invoke-RestMethod -Uri "http://localhost:3001/health" -TimeoutSec 10 -ErrorAction Stop
|
|
||||||
if ($health.status -eq 'ok') {
|
|
||||||
Write-Host " Health check passed." -ForegroundColor Green
|
|
||||||
} else {
|
|
||||||
Write-Warning "Health check returned unexpected status: $($health.status)"
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
Write-Warning "Health check failed: $_"
|
|
||||||
Write-Warning "Check logs with: docker logs --tail 30 dashcaddy-api"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Write-Warning "Target API folder not found. Skipping API update."
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Deployment Complete! Refresh your dashboard." -ForegroundColor Green
|
|
||||||
@@ -1,242 +0,0 @@
|
|||||||
# SAMI-CLOUD Status Dashboard API
|
|
||||||
|
|
||||||
Cross-platform Node.js API server for managing Caddy reverse proxy and DNS records via REST APIs.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Cross-Platform**: Works on Windows, Linux, and macOS
|
|
||||||
- **API-Based**: Uses Caddy Admin API and Technitium DNS API (no PowerShell required)
|
|
||||||
- **App Deployment**: Deploy apps by creating DNS records and Caddy reverse proxy routes
|
|
||||||
- **App Deletion**: Clean removal of DNS records and Caddy routes
|
|
||||||
- **Automatic Rollback**: If deployment fails, automatically rolls back changes
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
1. **Node.js** (v14 or higher)
|
|
||||||
2. **Caddy** with Admin API enabled
|
|
||||||
3. **Technitium DNS Server** (optional, for DNS management)
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd api
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
Set the following environment variables (or use defaults):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Caddy Admin API endpoint (default: http://localhost:2019)
|
|
||||||
export CADDY_ADMIN_API=http://localhost:2019
|
|
||||||
|
|
||||||
# Technitium DNS Server API endpoint (default: http://192.168.254.204:5380)
|
|
||||||
export DNS_SERVER_API=http://192.168.254.204:5380
|
|
||||||
|
|
||||||
# Technitium DNS API token (required for DNS operations)
|
|
||||||
export TECHNITIUM_API_TOKEN=your_api_token_here
|
|
||||||
```
|
|
||||||
|
|
||||||
### Windows (PowerShell)
|
|
||||||
```powershell
|
|
||||||
$env:CADDY_ADMIN_API="http://localhost:2019"
|
|
||||||
$env:DNS_SERVER_API="http://192.168.254.204:5380"
|
|
||||||
$env:TECHNITIUM_API_TOKEN="your_api_token_here"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Windows (Command Prompt)
|
|
||||||
```cmd
|
|
||||||
set CADDY_ADMIN_API=http://localhost:2019
|
|
||||||
set DNS_SERVER_API=http://192.168.254.204:5380
|
|
||||||
set TECHNITIUM_API_TOKEN=your_api_token_here
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running the Server
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm start
|
|
||||||
```
|
|
||||||
|
|
||||||
Or directly:
|
|
||||||
```bash
|
|
||||||
node caddy-api.js
|
|
||||||
```
|
|
||||||
|
|
||||||
The server will start on port 3001.
|
|
||||||
|
|
||||||
## API Endpoints
|
|
||||||
|
|
||||||
### Deploy an App
|
|
||||||
```http
|
|
||||||
POST /api/apps/deploy
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{
|
|
||||||
"appId": "myapp",
|
|
||||||
"config": {
|
|
||||||
"subdomain": "myapp",
|
|
||||||
"ip": "192.168.1.100",
|
|
||||||
"port": "8080",
|
|
||||||
"createDns": true,
|
|
||||||
"dnsType": "private",
|
|
||||||
"sslType": "internal"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"message": "App myapp deployed successfully",
|
|
||||||
"url": "https://myapp.sami",
|
|
||||||
"domain": "myapp.sami",
|
|
||||||
"ip": "192.168.1.100",
|
|
||||||
"port": "8080",
|
|
||||||
"dnsCreated": true,
|
|
||||||
"caddyConfigured": true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Delete an App
|
|
||||||
```http
|
|
||||||
POST /api/apps/delete
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{
|
|
||||||
"domain": "myapp.sami",
|
|
||||||
"ip": "192.168.1.100"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get Services List
|
|
||||||
```http
|
|
||||||
GET /api/services
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get Caddy Configuration
|
|
||||||
```http
|
|
||||||
GET /api/caddy/config
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test API
|
|
||||||
```http
|
|
||||||
GET /api/caddy/test
|
|
||||||
```
|
|
||||||
|
|
||||||
### Health Check
|
|
||||||
```http
|
|
||||||
GET /health
|
|
||||||
```
|
|
||||||
|
|
||||||
## Caddy Configuration Requirements
|
|
||||||
|
|
||||||
Your Caddyfile should have the Admin API enabled:
|
|
||||||
|
|
||||||
```caddyfile
|
|
||||||
{
|
|
||||||
admin localhost:2019 {
|
|
||||||
origins localhost localhost:2019
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For the status dashboard to proxy API requests, add this to your Caddyfile:
|
|
||||||
|
|
||||||
```caddyfile
|
|
||||||
status.sami {
|
|
||||||
tls internal
|
|
||||||
|
|
||||||
# API proxy to Node.js server
|
|
||||||
handle /api/* {
|
|
||||||
reverse_proxy localhost:3001
|
|
||||||
}
|
|
||||||
|
|
||||||
# Static site
|
|
||||||
root * /path/to/sites/status
|
|
||||||
file_server
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Getting Technitium DNS API Token
|
|
||||||
|
|
||||||
1. Open Technitium DNS web interface
|
|
||||||
2. Go to Settings → API
|
|
||||||
3. Create a new API token or copy existing one
|
|
||||||
4. Set it as the `TECHNITIUM_API_TOKEN` environment variable
|
|
||||||
|
|
||||||
## Deployment Flow
|
|
||||||
|
|
||||||
When deploying an app:
|
|
||||||
|
|
||||||
1. **Validate** - Checks required fields (appId, subdomain, ip)
|
|
||||||
2. **DNS Record** - Creates A record in DNS (if `createDns: true` and `dnsType: "private"`)
|
|
||||||
3. **Caddy Route** - Adds reverse proxy route via Caddy Admin API
|
|
||||||
4. **Rollback** - If Caddy configuration fails, removes DNS record
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Caddy Admin API not accessible
|
|
||||||
- Verify Caddy is running
|
|
||||||
- Check that admin API is enabled in your Caddyfile
|
|
||||||
- Confirm the CADDY_ADMIN_API URL is correct
|
|
||||||
|
|
||||||
### DNS operations failing
|
|
||||||
- Verify TECHNITIUM_API_TOKEN is set correctly
|
|
||||||
- Check DNS_SERVER_API URL is accessible
|
|
||||||
- Ensure the API token has permissions to manage zones
|
|
||||||
|
|
||||||
### Routes not appearing in Caddy
|
|
||||||
- Check Caddy logs: `caddy logs`
|
|
||||||
- Verify the route was added: `curl http://localhost:2019/config/`
|
|
||||||
- Ensure the domain resolves correctly in DNS
|
|
||||||
|
|
||||||
## Production Deployment
|
|
||||||
|
|
||||||
For production use:
|
|
||||||
|
|
||||||
1. Set up environment variables persistently
|
|
||||||
2. Use a process manager (PM2, systemd, etc.)
|
|
||||||
3. Configure proper logging
|
|
||||||
4. Set up SSL/TLS for the API if exposed externally
|
|
||||||
|
|
||||||
### Using PM2
|
|
||||||
```bash
|
|
||||||
npm install -g pm2
|
|
||||||
pm2 start caddy-api.js --name sami-api
|
|
||||||
pm2 save
|
|
||||||
pm2 startup
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using systemd (Linux)
|
|
||||||
Create `/etc/systemd/system/sami-api.service`:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[Unit]
|
|
||||||
Description=SAMI-CLOUD API Server
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=caddy
|
|
||||||
WorkingDirectory=/path/to/sites/status/api
|
|
||||||
Environment="CADDY_ADMIN_API=http://localhost:2019"
|
|
||||||
Environment="DNS_SERVER_API=http://192.168.254.204:5380"
|
|
||||||
Environment="TECHNITIUM_API_TOKEN=your_token"
|
|
||||||
ExecStart=/usr/bin/node caddy-api.js
|
|
||||||
Restart=on-failure
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
```
|
|
||||||
|
|
||||||
Then:
|
|
||||||
```bash
|
|
||||||
sudo systemctl enable sami-api
|
|
||||||
sudo systemctl start sami-api
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT
|
|
||||||
@@ -1,362 +0,0 @@
|
|||||||
// Cross-platform Node.js API server for Caddy management
|
|
||||||
// Uses Caddy Admin API and Technitium DNS API directly
|
|
||||||
// Run with: node caddy-api.js
|
|
||||||
|
|
||||||
const express = require('express');
|
|
||||||
const path = require('path');
|
|
||||||
const cors = require('cors');
|
|
||||||
const fs = require('fs');
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
const PORT = 3001;
|
|
||||||
|
|
||||||
// Middleware
|
|
||||||
app.use(cors());
|
|
||||||
app.use(express.json());
|
|
||||||
app.use(express.static('public'));
|
|
||||||
|
|
||||||
// Configuration
|
|
||||||
const CADDY_ADMIN_API = process.env.CADDY_ADMIN_API || 'http://localhost:2019';
|
|
||||||
const DNS_SERVER_API = process.env.DNS_SERVER_API || 'http://192.168.254.204:5380';
|
|
||||||
const DNS_API_TOKEN = process.env.TECHNITIUM_API_TOKEN || '';
|
|
||||||
|
|
||||||
// Helper function to make HTTP requests
|
|
||||||
async function makeRequest(url, options = {}) {
|
|
||||||
const https = url.startsWith('https:') ? require('https') : require('http');
|
|
||||||
const urlObj = new URL(url);
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const reqOptions = {
|
|
||||||
hostname: urlObj.hostname,
|
|
||||||
port: urlObj.port,
|
|
||||||
path: urlObj.pathname + urlObj.search,
|
|
||||||
method: options.method || 'GET',
|
|
||||||
headers: options.headers || {},
|
|
||||||
...options
|
|
||||||
};
|
|
||||||
|
|
||||||
const req = https.request(reqOptions, (res) => {
|
|
||||||
let data = '';
|
|
||||||
res.on('data', (chunk) => data += chunk);
|
|
||||||
res.on('end', () => {
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(data);
|
|
||||||
resolve({ status: res.statusCode, data: parsed });
|
|
||||||
} catch (e) {
|
|
||||||
resolve({ status: res.statusCode, data: data });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
req.on('error', reject);
|
|
||||||
|
|
||||||
if (options.body) {
|
|
||||||
req.write(typeof options.body === 'string' ? options.body : JSON.stringify(options.body));
|
|
||||||
}
|
|
||||||
|
|
||||||
req.end();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current Caddy configuration
|
|
||||||
app.get('/api/v1/caddy/config', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const response = await makeRequest(`${CADDY_ADMIN_API}/config/`);
|
|
||||||
|
|
||||||
if (response.status === 200) {
|
|
||||||
res.json({
|
|
||||||
status: 'success',
|
|
||||||
config: response.data
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res.status(response.status).json({
|
|
||||||
status: 'error',
|
|
||||||
message: 'Failed to get Caddy configuration',
|
|
||||||
details: response.data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error getting Caddy config:', error);
|
|
||||||
res.status(500).json({
|
|
||||||
status: 'error',
|
|
||||||
message: error.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get list of services (from apps.json + custom apps)
|
|
||||||
app.get('/api/v1/services', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const servicesPath = path.join(__dirname, '../apps.json');
|
|
||||||
|
|
||||||
if (fs.existsSync(servicesPath)) {
|
|
||||||
const servicesData = fs.readFileSync(servicesPath, 'utf8');
|
|
||||||
const services = JSON.parse(servicesData);
|
|
||||||
res.json({ status: 'success', services });
|
|
||||||
} else {
|
|
||||||
res.json({ status: 'success', services: [] });
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error reading services:', error);
|
|
||||||
res.status(500).json({
|
|
||||||
status: 'error',
|
|
||||||
message: error.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add DNS record via Technitium API
|
|
||||||
async function addDnsRecord(domain, ipAddress, ttl = 3600) {
|
|
||||||
if (!DNS_API_TOKEN) {
|
|
||||||
throw new Error('DNS API token not configured. Set TECHNITIUM_API_TOKEN environment variable.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = `${DNS_SERVER_API}/api/zones/records/add?token=${DNS_API_TOKEN}&domain=${domain}&type=A&ipAddress=${ipAddress}&ttl=${ttl}`;
|
|
||||||
|
|
||||||
console.log('Adding DNS record:', { domain, ipAddress, ttl });
|
|
||||||
const response = await makeRequest(url);
|
|
||||||
|
|
||||||
if (response.data.status === 'ok') {
|
|
||||||
return { success: true, message: 'DNS record added successfully' };
|
|
||||||
} else {
|
|
||||||
throw new Error(`DNS API error: ${response.data.message || 'Unknown error'}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete DNS record via Technitium API
|
|
||||||
async function deleteDnsRecord(domain, ipAddress) {
|
|
||||||
if (!DNS_API_TOKEN) {
|
|
||||||
console.warn('DNS API token not configured. Skipping DNS deletion.');
|
|
||||||
return { success: true, message: 'DNS deletion skipped (no token)' };
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = `${DNS_SERVER_API}/api/zones/records/delete?token=${DNS_API_TOKEN}&domain=${domain}&type=A&ipAddress=${ipAddress}`;
|
|
||||||
|
|
||||||
console.log('Deleting DNS record:', { domain, ipAddress });
|
|
||||||
const response = await makeRequest(url);
|
|
||||||
|
|
||||||
if (response.data.status === 'ok') {
|
|
||||||
return { success: true, message: 'DNS record deleted successfully' };
|
|
||||||
} else {
|
|
||||||
throw new Error(`DNS API error: ${response.data.message || 'Unknown error'}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add route to Caddy via Admin API
|
|
||||||
async function addCaddyRoute(domain, upstreamUrl, useTls = true) {
|
|
||||||
// Build Caddy route configuration
|
|
||||||
const routeConfig = {
|
|
||||||
"@id": domain,
|
|
||||||
"match": [{
|
|
||||||
"host": [domain]
|
|
||||||
}],
|
|
||||||
"handle": [{
|
|
||||||
"handler": "reverse_proxy",
|
|
||||||
"upstreams": [{
|
|
||||||
"dial": upstreamUrl.replace(/^https?:\/\//, '')
|
|
||||||
}]
|
|
||||||
}],
|
|
||||||
"terminal": true
|
|
||||||
};
|
|
||||||
|
|
||||||
// If using internal TLS, we need to add TLS configuration
|
|
||||||
if (useTls) {
|
|
||||||
// Caddy handles TLS automatically for matched domains
|
|
||||||
// Internal CA is configured in the global Caddyfile
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Adding Caddy route:', JSON.stringify(routeConfig, null, 2));
|
|
||||||
|
|
||||||
// Add the route to the HTTP server
|
|
||||||
const response = await makeRequest(`${CADDY_ADMIN_API}/config/apps/http/servers/srv0/routes/0`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(routeConfig)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.status === 200 || response.status === 201) {
|
|
||||||
return { success: true, message: 'Caddy route added successfully' };
|
|
||||||
} else {
|
|
||||||
throw new Error(`Caddy API error: ${JSON.stringify(response.data)}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deploy app endpoint - handles DNS and Caddy configuration via APIs
|
|
||||||
app.post('/api/v1/apps/deploy', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const { appId, config } = req.body;
|
|
||||||
const { subdomain, ip, createDns, port, sslType, dnsType } = config;
|
|
||||||
|
|
||||||
console.log('Deploying app:', { appId, config });
|
|
||||||
|
|
||||||
// Validate required fields
|
|
||||||
if (!appId || !subdomain || !ip) {
|
|
||||||
return res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
message: 'Missing required fields: appId, subdomain, ip'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the full domain
|
|
||||||
const domain = subdomain.includes('.') ? subdomain : `${subdomain}.sami`;
|
|
||||||
const finalPort = port || '80';
|
|
||||||
const upstreamUrl = `${ip}:${finalPort}`;
|
|
||||||
|
|
||||||
// Step 1: Add DNS record if requested (private DNS)
|
|
||||||
if (createDns && dnsType === 'private') {
|
|
||||||
try {
|
|
||||||
await addDnsRecord(domain, ip);
|
|
||||||
} catch (dnsError) {
|
|
||||||
console.error('DNS creation failed:', dnsError);
|
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: `DNS creation failed: ${dnsError.message}`,
|
|
||||||
step: 'dns'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 2: Add route to Caddy via Admin API
|
|
||||||
try {
|
|
||||||
const useTls = sslType === 'internal';
|
|
||||||
await addCaddyRoute(domain, upstreamUrl, useTls);
|
|
||||||
} catch (caddyError) {
|
|
||||||
console.error('Caddy route addition failed:', caddyError);
|
|
||||||
|
|
||||||
// Rollback DNS if it was created
|
|
||||||
if (createDns && dnsType === 'private') {
|
|
||||||
try {
|
|
||||||
await deleteDnsRecord(domain, ip);
|
|
||||||
} catch (rollbackError) {
|
|
||||||
console.error('DNS rollback failed:', rollbackError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: `Caddy configuration failed: ${caddyError.message}`,
|
|
||||||
step: 'caddy'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 3: Return success response
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
message: `App ${appId} deployed successfully`,
|
|
||||||
url: `https://${domain}`,
|
|
||||||
domain: domain,
|
|
||||||
ip: ip,
|
|
||||||
port: finalPort,
|
|
||||||
containerId: null,
|
|
||||||
dnsCreated: createDns && dnsType === 'private',
|
|
||||||
caddyConfigured: true
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Deployment error:', error);
|
|
||||||
res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: error.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Delete app endpoint - removes DNS and Caddy configuration
|
|
||||||
app.post('/api/v1/apps/delete', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const { domain, ip } = req.body;
|
|
||||||
|
|
||||||
if (!domain) {
|
|
||||||
return res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
message: 'Domain is required'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Deleting app:', { domain, ip });
|
|
||||||
|
|
||||||
// Step 1: Remove from Caddy
|
|
||||||
try {
|
|
||||||
const response = await makeRequest(`${CADDY_ADMIN_API}/id/${domain}`, {
|
|
||||||
method: 'DELETE'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.status !== 200) {
|
|
||||||
console.warn('Caddy route deletion warning:', response.data);
|
|
||||||
}
|
|
||||||
} catch (caddyError) {
|
|
||||||
console.error('Caddy route deletion failed:', caddyError);
|
|
||||||
// Continue anyway to try DNS deletion
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 2: Remove DNS record if IP provided
|
|
||||||
if (ip) {
|
|
||||||
try {
|
|
||||||
await deleteDnsRecord(domain, ip);
|
|
||||||
} catch (dnsError) {
|
|
||||||
console.error('DNS deletion failed:', dnsError);
|
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: `DNS deletion failed: ${dnsError.message}`,
|
|
||||||
caddyDeleted: true,
|
|
||||||
dnsDeleted: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
message: 'App deleted successfully',
|
|
||||||
domain: domain,
|
|
||||||
caddyDeleted: true,
|
|
||||||
dnsDeleted: !!ip
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Deletion error:', error);
|
|
||||||
res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: error.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test endpoint
|
|
||||||
app.get('/api/v1/caddy/test', (req, res) => {
|
|
||||||
res.json({
|
|
||||||
status: 'success',
|
|
||||||
message: 'Caddy API is running',
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
platform: process.platform,
|
|
||||||
caddyAdminApi: CADDY_ADMIN_API,
|
|
||||||
dnsServerApi: DNS_SERVER_API,
|
|
||||||
dnsTokenConfigured: !!DNS_API_TOKEN
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Health check
|
|
||||||
app.get('/health', (req, res) => {
|
|
||||||
res.json({ status: 'healthy', timestamp: new Date().toISOString() });
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start server
|
|
||||||
app.listen(PORT, () => {
|
|
||||||
console.log(`\n====================================`);
|
|
||||||
console.log(`Caddy API server running on http://localhost:${PORT}`);
|
|
||||||
console.log(`====================================`);
|
|
||||||
console.log(`Caddy Admin API: ${CADDY_ADMIN_API}`);
|
|
||||||
console.log(`DNS Server API: ${DNS_SERVER_API}`);
|
|
||||||
console.log(`DNS Token: ${DNS_API_TOKEN ? '✓ Configured' : '✗ Not configured'}`);
|
|
||||||
console.log(`\nEndpoints:`);
|
|
||||||
console.log(` POST /api/apps/deploy - Deploy an app (DNS + Caddy)`);
|
|
||||||
console.log(` POST /api/apps/delete - Delete an app (DNS + Caddy)`);
|
|
||||||
console.log(` GET /api/services - Get list of services`);
|
|
||||||
console.log(` GET /api/caddy/config - Get current Caddy configuration`);
|
|
||||||
console.log(` GET /api/caddy/test - Test API connectivity`);
|
|
||||||
console.log(` GET /health - Health check`);
|
|
||||||
console.log(`====================================\n`);
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = app;
|
|
||||||
@@ -1,206 +0,0 @@
|
|||||||
# Caddy Configuration Manager for Windows
|
|
||||||
# This script adds new service configurations to your Caddyfile
|
|
||||||
|
|
||||||
param(
|
|
||||||
[string]$Config,
|
|
||||||
[string]$Subdomain,
|
|
||||||
[string]$CaddyfilePath = "C:\caddy\Caddyfile",
|
|
||||||
[bool]$ReloadCaddy = $true
|
|
||||||
)
|
|
||||||
|
|
||||||
# Function to write JSON response
|
|
||||||
function Write-JsonResponse {
|
|
||||||
param($Status, $Message, $Data = $null)
|
|
||||||
|
|
||||||
$response = @{
|
|
||||||
status = $Status
|
|
||||||
message = $Message
|
|
||||||
timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($Data) {
|
|
||||||
$response.data = $Data
|
|
||||||
}
|
|
||||||
|
|
||||||
return $response | ConvertTo-Json
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to extract CA names from Caddyfile
|
|
||||||
function Get-CaddyfileCAs {
|
|
||||||
param([string]$CaddyfilePath)
|
|
||||||
|
|
||||||
try {
|
|
||||||
Write-Host "DEBUG: Checking file path: $CaddyfilePath"
|
|
||||||
|
|
||||||
if (-not (Test-Path $CaddyfilePath)) {
|
|
||||||
Write-Host "DEBUG: File not found"
|
|
||||||
return @()
|
|
||||||
}
|
|
||||||
|
|
||||||
$content = Get-Content $CaddyfilePath -Raw
|
|
||||||
Write-Host "DEBUG: File content length: $($content.Length)"
|
|
||||||
Write-Host "DEBUG: First 200 chars: $($content.Substring(0, [Math]::Min(200, $content.Length)))"
|
|
||||||
|
|
||||||
$caNames = @()
|
|
||||||
|
|
||||||
# Pattern 1: PKI block with CA definitions - pki { ca ca_name { name "Friendly Name" } }
|
|
||||||
$pkiPattern = 'pki\s*\{[^}]*?ca\s+([^\s\{]+)\s*\{([^}]*?)\}'
|
|
||||||
Write-Host "DEBUG: Searching for PKI pattern: $pkiPattern"
|
|
||||||
|
|
||||||
$pkiMatches = [regex]::Matches($content, $pkiPattern, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase -bor [System.Text.RegularExpressions.RegexOptions]::Singleline)
|
|
||||||
Write-Host "DEBUG: PKI matches found: $($pkiMatches.Count)"
|
|
||||||
|
|
||||||
foreach ($match in $pkiMatches) {
|
|
||||||
$caId = $match.Groups[1].Value
|
|
||||||
$caBlock = $match.Groups[2].Value
|
|
||||||
Write-Host "DEBUG: Found CA ID: $caId"
|
|
||||||
Write-Host "DEBUG: CA Block: $caBlock"
|
|
||||||
|
|
||||||
# Try to extract the friendly name from within the CA block
|
|
||||||
$nameMatch = [regex]::Match($caBlock, 'name\s+"([^"]+)"', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
|
|
||||||
if ($nameMatch.Success) {
|
|
||||||
$friendlyName = $nameMatch.Groups[1].Value
|
|
||||||
Write-Host "DEBUG: Found friendly name: $friendlyName"
|
|
||||||
$caNames += "$caId ($friendlyName)"
|
|
||||||
} else {
|
|
||||||
Write-Host "DEBUG: No friendly name found, using ID only"
|
|
||||||
$caNames += $caId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Pattern 2: tls { ca ca_name }
|
|
||||||
$matches1 = [regex]::Matches($content, 'tls\s*\{\s*ca\s+([^\s\}]+)', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
|
|
||||||
Write-Host "DEBUG: TLS block matches: $($matches1.Count)"
|
|
||||||
foreach ($match in $matches1) {
|
|
||||||
$caNames += $match.Groups[1].Value
|
|
||||||
}
|
|
||||||
|
|
||||||
# Pattern 3: tls ca_name (direct)
|
|
||||||
$matches2 = [regex]::Matches($content, 'tls\s+([^\s\{]+)', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
|
|
||||||
Write-Host "DEBUG: Direct TLS matches: $($matches2.Count)"
|
|
||||||
foreach ($match in $matches2) {
|
|
||||||
$caName = $match.Groups[1].Value
|
|
||||||
if ($caName -ne "internal" -and $caName -ne "off") {
|
|
||||||
$caNames += $caName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "DEBUG: Total CAs found: $($caNames.Count)"
|
|
||||||
Write-Host "DEBUG: CA list: $($caNames -join ', ')"
|
|
||||||
|
|
||||||
# Remove duplicates and return
|
|
||||||
return $caNames | Sort-Object | Get-Unique
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Host "DEBUG: Exception in Get-CaddyfileCAs: $($_.Exception.Message)"
|
|
||||||
Write-Host "Error reading CAs from Caddyfile: $($_.Exception.Message)"
|
|
||||||
return @()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Handle get-cas command
|
|
||||||
if ($args[0] -eq "get-cas") {
|
|
||||||
$CaddyfilePath = if ($args[1]) { $args[1] } else { "C:\caddy\Caddyfile" }
|
|
||||||
|
|
||||||
Write-Host "DEBUG: get-cas command received"
|
|
||||||
Write-Host "DEBUG: Caddyfile path: $CaddyfilePath"
|
|
||||||
Write-Host "DEBUG: File exists: $(Test-Path $CaddyfilePath)"
|
|
||||||
|
|
||||||
try {
|
|
||||||
$cas = Get-CaddyfileCAs -CaddyfilePath $CaddyfilePath
|
|
||||||
|
|
||||||
Write-Host "DEBUG: CAs found: $($cas -join ', ')"
|
|
||||||
|
|
||||||
$response = @{
|
|
||||||
status = "success"
|
|
||||||
message = "CAs retrieved successfully"
|
|
||||||
data = @{
|
|
||||||
cas = $cas
|
|
||||||
count = $cas.Count
|
|
||||||
caddyfilePath = $CaddyfilePath
|
|
||||||
}
|
|
||||||
timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Output ($response | ConvertTo-Json)
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Host "DEBUG: Error occurred: $($_.Exception.Message)"
|
|
||||||
|
|
||||||
$response = @{
|
|
||||||
status = "error"
|
|
||||||
message = "Failed to retrieve CAs: $($_.Exception.Message)"
|
|
||||||
timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Output ($response | ConvertTo-Json)
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main configuration addition logic
|
|
||||||
try {
|
|
||||||
# Validate required parameters for config addition
|
|
||||||
if (-not $Config -or -not $Subdomain) {
|
|
||||||
Write-Output (Write-JsonResponse "error" "Config and Subdomain parameters are required")
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if Caddyfile exists
|
|
||||||
if (-not (Test-Path $CaddyfilePath)) {
|
|
||||||
Write-Output (Write-JsonResponse "error" "Caddyfile not found at: $CaddyfilePath")
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Read existing Caddyfile
|
|
||||||
$existingConfig = Get-Content $CaddyfilePath -Raw -ErrorAction Stop
|
|
||||||
|
|
||||||
# Check if subdomain already exists
|
|
||||||
if ($existingConfig -match "$Subdomain\.sami\s*\{") {
|
|
||||||
Write-Output (Write-JsonResponse "error" "Subdomain '$Subdomain.sami' already exists in Caddyfile")
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create backup
|
|
||||||
$backupPath = "$CaddyfilePath.backup.$(Get-Date -Format 'yyyyMMdd-HHmmss')"
|
|
||||||
Copy-Item $CaddyfilePath $backupPath -ErrorAction Stop
|
|
||||||
Write-Host "Backup created: $backupPath"
|
|
||||||
|
|
||||||
# Append new configuration
|
|
||||||
$newContent = $existingConfig.TrimEnd() + "`n`n" + $Config.TrimEnd() + "`n"
|
|
||||||
Set-Content -Path $CaddyfilePath -Value $newContent -NoNewline -ErrorAction Stop
|
|
||||||
|
|
||||||
Write-Host "Configuration added successfully"
|
|
||||||
|
|
||||||
# Reload Caddy if requested
|
|
||||||
if ($ReloadCaddy) {
|
|
||||||
Write-Host "Reloading Caddy..."
|
|
||||||
|
|
||||||
# Try to reload Caddy
|
|
||||||
$reloadResult = & caddy reload --config $CaddyfilePath 2>&1
|
|
||||||
|
|
||||||
if ($LASTEXITCODE -eq 0) {
|
|
||||||
Write-Host "Caddy reloaded successfully"
|
|
||||||
Write-Output (Write-JsonResponse "success" "Configuration added and Caddy reloaded successfully" @{
|
|
||||||
backup = $backupPath
|
|
||||||
subdomain = "$Subdomain.sami"
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Write-Host "Caddy reload failed: $reloadResult"
|
|
||||||
# Restore backup if reload failed
|
|
||||||
Copy-Item $backupPath $CaddyfilePath -Force
|
|
||||||
Write-Output (Write-JsonResponse "error" "Caddy reload failed. Configuration rolled back. Error: $reloadResult")
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Write-Output (Write-JsonResponse "success" "Configuration added successfully (Caddy not reloaded)" @{
|
|
||||||
backup = $backupPath
|
|
||||||
subdomain = "$Subdomain.sami"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch {
|
|
||||||
Write-Output (Write-JsonResponse "error" "Error: $($_.Exception.Message)")
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
@echo off
|
|
||||||
title SAMI Caddy API Server
|
|
||||||
echo ========================================
|
|
||||||
echo Installing SAMI Caddy API Server...
|
|
||||||
echo ========================================
|
|
||||||
|
|
||||||
REM Check if Node.js is installed
|
|
||||||
echo Checking for Node.js...
|
|
||||||
node --version >nul 2>&1
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo.
|
|
||||||
echo ERROR: Node.js is not installed or not in PATH
|
|
||||||
echo Please install Node.js from https://nodejs.org/
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Node.js found:
|
|
||||||
node --version
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo Installing dependencies...
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Install npm dependencies
|
|
||||||
npm install
|
|
||||||
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo.
|
|
||||||
echo ERROR: Failed to install dependencies
|
|
||||||
echo Check the error messages above
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo Dependencies installed successfully!
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
echo Starting Caddy API server...
|
|
||||||
echo.
|
|
||||||
echo Server URL: http://localhost:3001
|
|
||||||
echo Test URL: http://localhost:3001/api/caddy/test
|
|
||||||
echo.
|
|
||||||
echo Press Ctrl+C to stop the server
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Start the server and keep window open on error
|
|
||||||
npm start
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo.
|
|
||||||
echo ERROR: Server failed to start
|
|
||||||
echo Check the error messages above
|
|
||||||
echo.
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo Server stopped.
|
|
||||||
pause
|
|
||||||
Generated
-1227
File diff suppressed because it is too large
Load Diff
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "sami-caddy-api",
|
|
||||||
"version": "2.0.0",
|
|
||||||
"description": "Cross-platform API server for managing Caddy and DNS via REST APIs",
|
|
||||||
"main": "caddy-api.js",
|
|
||||||
"scripts": {
|
|
||||||
"start": "node caddy-api.js",
|
|
||||||
"dev": "nodemon caddy-api.js",
|
|
||||||
"test": "node test-api.js"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"express": "^4.18.2",
|
|
||||||
"cors": "^2.8.5"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"nodemon": "^3.0.1"
|
|
||||||
},
|
|
||||||
"keywords": ["caddy", "api", "dns", "technitium", "reverse-proxy", "cross-platform", "sami-cloud"],
|
|
||||||
"author": "SAMI-CLOUD",
|
|
||||||
"license": "MIT"
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
@echo off
|
|
||||||
REM Quick start script for SAMI-CLOUD API Server (Windows)
|
|
||||||
|
|
||||||
echo ====================================
|
|
||||||
echo SAMI-CLOUD API Server
|
|
||||||
echo ====================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Check if Node.js is installed
|
|
||||||
where node >nul 2>nul
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo Error: Node.js is not installed or not in PATH
|
|
||||||
echo Please install Node.js from https://nodejs.org/
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Check if node_modules exists
|
|
||||||
if not exist "node_modules" (
|
|
||||||
echo Installing dependencies...
|
|
||||||
call npm install
|
|
||||||
echo.
|
|
||||||
)
|
|
||||||
|
|
||||||
REM Check environment variables
|
|
||||||
if "%CADDY_ADMIN_API%"=="" (
|
|
||||||
echo Warning: CADDY_ADMIN_API not set, using default: http://localhost:2019
|
|
||||||
set CADDY_ADMIN_API=http://localhost:2019
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%DNS_SERVER_API%"=="" (
|
|
||||||
echo Warning: DNS_SERVER_API not set, using default: http://192.168.254.204:5380
|
|
||||||
set DNS_SERVER_API=http://192.168.254.204:5380
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%TECHNITIUM_API_TOKEN%"=="" (
|
|
||||||
echo Warning: TECHNITIUM_API_TOKEN not set - DNS operations will fail
|
|
||||||
echo Set it with: set TECHNITIUM_API_TOKEN=your_token
|
|
||||||
echo.
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Starting API server...
|
|
||||||
echo.
|
|
||||||
node caddy-api.js
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Quick start script for SAMI-CLOUD API Server (Linux/macOS)
|
|
||||||
|
|
||||||
echo "===================================="
|
|
||||||
echo "SAMI-CLOUD API Server"
|
|
||||||
echo "===================================="
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Check if Node.js is installed
|
|
||||||
if ! command -v node &> /dev/null; then
|
|
||||||
echo "Error: Node.js is not installed or not in PATH"
|
|
||||||
echo "Please install Node.js from https://nodejs.org/"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if node_modules exists
|
|
||||||
if [ ! -d "node_modules" ]; then
|
|
||||||
echo "Installing dependencies..."
|
|
||||||
npm install
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check environment variables
|
|
||||||
if [ -z "$CADDY_ADMIN_API" ]; then
|
|
||||||
echo "Warning: CADDY_ADMIN_API not set, using default: http://localhost:2019"
|
|
||||||
export CADDY_ADMIN_API="http://localhost:2019"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$DNS_SERVER_API" ]; then
|
|
||||||
echo "Warning: DNS_SERVER_API not set, using default: http://192.168.254.204:5380"
|
|
||||||
export DNS_SERVER_API="http://192.168.254.204:5380"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$TECHNITIUM_API_TOKEN" ]; then
|
|
||||||
echo "Warning: TECHNITIUM_API_TOKEN not set - DNS operations will fail"
|
|
||||||
echo "Set it with: export TECHNITIUM_API_TOKEN=your_token"
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Starting API server..."
|
|
||||||
echo ""
|
|
||||||
node caddy-api.js
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
// Simple test script to verify API connectivity
|
|
||||||
const http = require('http');
|
|
||||||
|
|
||||||
const API_URL = 'http://localhost:3001';
|
|
||||||
|
|
||||||
function makeRequest(path) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
http.get(`${API_URL}${path}`, (res) => {
|
|
||||||
let data = '';
|
|
||||||
res.on('data', (chunk) => data += chunk);
|
|
||||||
res.on('end', () => {
|
|
||||||
try {
|
|
||||||
resolve({ status: res.statusCode, data: JSON.parse(data) });
|
|
||||||
} catch (e) {
|
|
||||||
resolve({ status: res.statusCode, data: data });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).on('error', reject);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runTests() {
|
|
||||||
console.log('Testing SAMI-CLOUD API...\n');
|
|
||||||
|
|
||||||
// Test 1: Health Check
|
|
||||||
console.log('1. Testing health endpoint...');
|
|
||||||
try {
|
|
||||||
const health = await makeRequest('/health');
|
|
||||||
if (health.status === 200) {
|
|
||||||
console.log(' ✓ Health check passed');
|
|
||||||
} else {
|
|
||||||
console.log(' ✗ Health check failed:', health.status);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(' ✗ Health check error:', error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 2: API Test Endpoint
|
|
||||||
console.log('\n2. Testing API test endpoint...');
|
|
||||||
try {
|
|
||||||
const test = await makeRequest('/api/v1/caddy/test');
|
|
||||||
if (test.status === 200) {
|
|
||||||
console.log(' ✓ API test passed');
|
|
||||||
console.log(' Platform:', test.data.platform);
|
|
||||||
console.log(' Caddy Admin API:', test.data.caddyAdminApi);
|
|
||||||
console.log(' DNS Server API:', test.data.dnsServerApi);
|
|
||||||
console.log(' DNS Token:', test.data.dnsTokenConfigured ? 'Configured' : 'Not configured');
|
|
||||||
} else {
|
|
||||||
console.log(' ✗ API test failed:', test.status);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(' ✗ API test error:', error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 3: Services Endpoint
|
|
||||||
console.log('\n3. Testing services endpoint...');
|
|
||||||
try {
|
|
||||||
const services = await makeRequest('/api/v1/services');
|
|
||||||
if (services.status === 200) {
|
|
||||||
console.log(' ✓ Services endpoint passed');
|
|
||||||
console.log(' Found', services.data.services.length, 'services');
|
|
||||||
} else {
|
|
||||||
console.log(' ✗ Services endpoint failed:', services.status);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(' ✗ Services endpoint error:', error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('\nTests complete!');
|
|
||||||
}
|
|
||||||
|
|
||||||
runTests().catch(console.error);
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
@echo off
|
|
||||||
title SAMI Caddy API Server - Debug Mode
|
|
||||||
echo ========================================
|
|
||||||
echo SAMI Caddy API Server - Debug Mode
|
|
||||||
echo ========================================
|
|
||||||
|
|
||||||
cd /d "%~dp0"
|
|
||||||
echo Current directory: %CD%
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo Checking Node.js...
|
|
||||||
node --version
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo ERROR: Node.js not found
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo Checking files...
|
|
||||||
if exist "caddy-api.js" (
|
|
||||||
echo ✓ caddy-api.js found
|
|
||||||
) else (
|
|
||||||
echo ✗ caddy-api.js NOT found
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
if exist "package.json" (
|
|
||||||
echo ✓ package.json found
|
|
||||||
) else (
|
|
||||||
echo ✗ package.json NOT found
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
if exist "caddy-manager.ps1" (
|
|
||||||
echo ✓ caddy-manager.ps1 found
|
|
||||||
) else (
|
|
||||||
echo ✗ caddy-manager.ps1 NOT found
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo Installing dependencies...
|
|
||||||
npm install
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo ERROR: npm install failed
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo Starting server with error capture...
|
|
||||||
echo ========================================
|
|
||||||
echo Server will run on: http://localhost:3001
|
|
||||||
echo Test endpoint: http://localhost:3001/api/caddy/test
|
|
||||||
echo.
|
|
||||||
echo If server starts successfully, you'll see "Caddy API server running..."
|
|
||||||
echo Press Ctrl+C to stop the server
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Capture both stdout and stderr
|
|
||||||
node caddy-api.js 2>&1
|
|
||||||
set SERVER_EXIT_CODE=%errorlevel%
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo Server exited with code: %SERVER_EXIT_CODE%
|
|
||||||
echo ========================================
|
|
||||||
|
|
||||||
if %SERVER_EXIT_CODE% neq 0 (
|
|
||||||
echo ERROR: Server failed to start or crashed
|
|
||||||
echo Check the error messages above
|
|
||||||
) else (
|
|
||||||
echo Server stopped normally
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo Press any key to close this window...
|
|
||||||
pause >nul
|
|
||||||
Reference in New Issue
Block a user