diff --git a/.env.example b/.env.example index 4b3cbbc2a..87837f063 100755 --- a/.env.example +++ b/.env.example @@ -24,6 +24,18 @@ VITE_PORT=5173 # Uncomment the following line if you have a custom claude cli path other than the default "claude" # CLAUDE_CLI_PATH=claude +# ============================================================================= +# SSL/TLS CONFIGURATION (Optional - for HTTPS) +# ============================================================================= + +# To enable HTTPS, provide paths to your SSL certificate and private key. +# When both are set and files exist, the server will use HTTPS instead of HTTP. +# SSL_CERT=/path/to/your/certificate.crt +# SSL_KEY=/path/to/your/private.key + +# Optional: Specify a different port for HTTPS (defaults to PORT if not set) +# SSL_PORT=443 + # ============================================================================= # DATABASE CONFIGURATION # ============================================================================= diff --git a/server/index.js b/server/index.js index bac8e0bc2..a9921ac6c 100755 --- a/server/index.js +++ b/server/index.js @@ -35,6 +35,7 @@ import express from 'express'; import { WebSocketServer, WebSocket } from 'ws'; import os from 'os'; import http from 'http'; +import https from 'https'; import cors from 'cors'; import { promises as fsPromises } from 'fs'; import { spawn } from 'child_process'; @@ -206,7 +207,29 @@ async function setupProjectsWatcher() { const app = express(); -const server = http.createServer(app); + +// Create HTTP or HTTPS server based on SSL configuration +let server; +let useHttps = false; +const SSL_CERT = process.env.SSL_CERT; +const SSL_KEY = process.env.SSL_KEY; + +if (SSL_CERT && SSL_KEY && fs.existsSync(SSL_CERT) && fs.existsSync(SSL_KEY)) { + try { + const sslOptions = { + cert: fs.readFileSync(SSL_CERT), + key: fs.readFileSync(SSL_KEY) + }; + server = https.createServer(sslOptions, app); + useHttps = true; + console.log('[INFO] HTTPS enabled with SSL certificates'); + } catch (err) { + console.warn('[WARN] Failed to enable HTTPS, falling back to HTTP:', err.message); + server = http.createServer(app); + } +} else { + server = http.createServer(app); +} const ptySessionsMap = new Map(); const PTY_SESSION_TIMEOUT = 30 * 60 * 1000; @@ -1889,6 +1912,7 @@ async function getFileTree(dirPath, maxDepth = 3, currentDepth = 0, showHidden = } const PORT = process.env.PORT || 3001; +const SSL_PORT = process.env.SSL_PORT || PORT; // Initialize database and start server async function startServer() { @@ -1908,7 +1932,8 @@ async function startServer() { console.log(`${c.warn('[WARN]')} Note: Requests will be proxied to Vite dev server at ${c.dim('http://localhost:' + (process.env.VITE_PORT || 5173))}`); } - server.listen(PORT, '0.0.0.0', async () => { + const listenPort = useHttps ? SSL_PORT : PORT; + server.listen(listenPort, '0.0.0.0', async () => { const appInstallPath = path.join(__dirname, '..'); console.log(''); @@ -1916,7 +1941,8 @@ async function startServer() { console.log(` ${c.bright('Claude Code UI Server - Ready')}`); console.log(c.dim('═'.repeat(63))); console.log(''); - console.log(`${c.info('[INFO]')} Server URL: ${c.bright('http://0.0.0.0:' + PORT)}`); + const protocol = useHttps ? 'https' : 'http'; + console.log(`${c.info('[INFO]')} Server URL: ${c.bright(protocol + '://0.0.0.0:' + listenPort)}`); console.log(`${c.info('[INFO]')} Installed at: ${c.dim(appInstallPath)}`); console.log(`${c.tip('[TIP]')} Run "cloudcli status" for full configuration details`); console.log('');