@powerhousedao/ph-cli 0.40.85-dev.0 → 0.40.85-dev.10

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/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@powerhousedao/ph-cli",
3
- "version": "0.40.85-dev.0",
3
+ "version": "0.40.85-dev.10",
4
4
  "description": "",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
@@ -17,6 +17,8 @@
17
17
  "generate-commands-md": "tsx scripts/generate-commands-md.ts",
18
18
  "generate-version": "tsx scripts/generate-version.ts",
19
19
  "prebuild": "npm run clean && npm run generate-commands-md && npm run generate-version",
20
+ "postbuild": "npm run copy-scripts",
21
+ "copy-scripts": "copyfiles scripts/* dist/",
20
22
  "build": "tsc --build",
21
23
  "dev": "concurrently -P 'pnpm -w run build:tsc --watch' 'nodemon --watch \"../..\" -e ts,tsx,js,json dist/src/cli.js -- {@}' --",
22
24
  "prepublishOnly": "npm run build",
@@ -32,6 +34,7 @@
32
34
  "devDependencies": {
33
35
  "@types/node": "^22.15.17",
34
36
  "concurrently": "^9.1.2",
37
+ "copyfiles": "^2.4.1",
35
38
  "nodemon": "^3.1.9",
36
39
  "vitest": "^3.1.2"
37
40
  },
@@ -0,0 +1,84 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import { fileURLToPath } from "url";
4
+
5
+ /**
6
+ * Generate COMMANDS.md file from the help texts in help.ts
7
+ */
8
+ async function generateCommandsMd() {
9
+ try {
10
+ // Define paths for ES modules
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+ const rootDir = path.resolve(__dirname, "..");
14
+ const helpFilePath = path.join(rootDir, "src", "help.ts");
15
+ const outputPath = path.join(rootDir, "COMMANDS.md");
16
+
17
+ // Read the help.ts file
18
+ const helpFileContent = fs.readFileSync(helpFilePath, "utf8");
19
+
20
+ // Extract all help text constants using regex
21
+ const helpTextRegex = /export const (\w+)Help = `([\s\S]+?)`;/g;
22
+ const commands: { name: string; content: string }[] = [];
23
+
24
+ let match;
25
+ while ((match = helpTextRegex.exec(helpFileContent)) !== null) {
26
+ const commandName = match[1];
27
+ const helpContent = match[2];
28
+ commands.push({ name: commandName, content: helpContent });
29
+ }
30
+
31
+ // Sort commands alphabetically
32
+ commands.sort((a, b) => a.name.localeCompare(b.name));
33
+
34
+ // Generate the markdown content
35
+ let markdown = "# Powerhouse CLI Commands\n\n";
36
+ markdown +=
37
+ "This document provides detailed information about the available commands in the Powerhouse CLI.\n\n";
38
+ markdown += "## Table of Contents\n\n";
39
+
40
+ // Add table of contents
41
+ commands.forEach((command) => {
42
+ const displayName = formatCommandName(command.name);
43
+ const anchor = displayName.toLowerCase().replace(/\s+/g, "-");
44
+ markdown += `- [${displayName}](#${anchor})\n`;
45
+ });
46
+
47
+ markdown += "\n";
48
+
49
+ // Add command details
50
+ commands.forEach((command) => {
51
+ const displayName = formatCommandName(command.name);
52
+ markdown += `## ${displayName}\n\n`;
53
+ markdown += "```\n";
54
+ markdown += command.content.trim();
55
+ markdown += "\n```\n\n";
56
+ });
57
+
58
+ // Add footer
59
+ markdown += "---\n\n";
60
+ markdown +=
61
+ "*This document was automatically generated from the help text in the codebase.*\n";
62
+
63
+ // Write to COMMANDS.md
64
+ fs.writeFileSync(outputPath, markdown);
65
+
66
+ console.log(`✅ COMMANDS.md has been generated at ${outputPath}`);
67
+ } catch (error) {
68
+ console.error("Failed to generate COMMANDS.md:", error);
69
+ process.exit(1);
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Format command name for display (e.g., "setupGlobals" -> "Setup Globals")
75
+ */
76
+ function formatCommandName(commandName: string): string {
77
+ // Convert camelCase to separate words with spaces
78
+ const name = commandName.replace(/([A-Z])/g, " $1").trim();
79
+ // Capitalize first letter and convert the rest to lowercase
80
+ return name.charAt(0).toUpperCase() + name.slice(1);
81
+ }
82
+
83
+ // Run the script
84
+ generateCommandsMd();
@@ -0,0 +1,22 @@
1
+ import { readFileSync, writeFileSync } from "fs";
2
+ import { join } from "path";
3
+ import { fileURLToPath } from "url";
4
+
5
+ interface PackageJson {
6
+ version: string;
7
+ }
8
+
9
+ const __dirname = fileURLToPath(new URL(".", import.meta.url));
10
+
11
+ // Read package.json
12
+ const packageJson = JSON.parse(
13
+ readFileSync(join(__dirname, "../package.json"), "utf-8"),
14
+ ) as PackageJson;
15
+
16
+ // Generate version.ts content
17
+ const versionFileContent = `// This file is auto-generated. DO NOT EDIT.
18
+ export const version = "${packageJson.version}";
19
+ `;
20
+
21
+ // Write version.ts
22
+ writeFileSync(join(__dirname, "../src/version.ts"), versionFileContent);
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # =============================================================================
4
+ # Configuration
5
+ # =============================================================================
6
+ PROJECT_NAME=${1:-"global"}
7
+ ACTION=${2:-"status"}
8
+
9
+ # =============================================================================
10
+ # OS Detection and Windows Handling
11
+ # =============================================================================
12
+ if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then
13
+ if [ -f "$0.ps1" ]; then
14
+ powershell -ExecutionPolicy Bypass -File "$0.ps1" -PROJECT_NAME "$PROJECT_NAME" -ACTION "$ACTION"
15
+ else
16
+ echo "Error: Windows management script (manage-environment.ps1) not found"
17
+ exit 1
18
+ fi
19
+ else
20
+ # =============================================================================
21
+ # Service Management
22
+ # =============================================================================
23
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
24
+ echo " Managing project: $PROJECT_NAME"
25
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
26
+
27
+ # Function to check if service is properly set up
28
+ check_setup() {
29
+ local project_name=$1
30
+ local error=0
31
+
32
+ # Check if .env file exists
33
+ if [ ! -f ".env" ]; then
34
+ echo "Error: .env file not found in project directory"
35
+ error=1
36
+ fi
37
+
38
+ # Check if Nginx configuration exists
39
+ if [ ! -f "/etc/nginx/sites-available/$project_name" ]; then
40
+ echo "Error: Nginx configuration not found"
41
+ error=1
42
+ fi
43
+
44
+ # Check if database is configured
45
+ if ! grep -q "DATABASE_URL" ".env"; then
46
+ echo "Error: Database configuration not found in .env file"
47
+ error=1
48
+ fi
49
+
50
+ if [ $error -eq 1 ]; then
51
+ echo "Please run 'ph setup-environment' first to set up the service"
52
+ exit 1
53
+ fi
54
+ }
55
+
56
+ # Function to enable/disable Nginx site
57
+ manage_nginx_site() {
58
+ local action=$1
59
+ local site_path="/etc/nginx/sites-available/$PROJECT_NAME"
60
+ local enabled_path="/etc/nginx/sites-enabled/$PROJECT_NAME"
61
+
62
+ if [ ! -f "$site_path" ]; then
63
+ echo "Error: Nginx site configuration for $PROJECT_NAME not found"
64
+ return 1
65
+ fi
66
+
67
+ case "$action" in
68
+ "enable")
69
+ if [ ! -L "$enabled_path" ]; then
70
+ sudo ln -sf "$site_path" "$enabled_path"
71
+ sudo nginx -t && sudo nginx -s reload
72
+ fi
73
+ ;;
74
+ "disable")
75
+ if [ -L "$enabled_path" ]; then
76
+ sudo rm -f "$enabled_path"
77
+ sudo nginx -t && sudo nginx -s reload
78
+ fi
79
+ ;;
80
+ esac
81
+ }
82
+
83
+ case "$ACTION" in
84
+ "start")
85
+ check_setup "$PROJECT_NAME"
86
+ echo "Starting services..."
87
+ # Build Connect
88
+ echo "Building Connect..."
89
+ ph connect build
90
+
91
+ # Enable Nginx site
92
+ manage_nginx_site "enable"
93
+
94
+ # Start Switchboard via PM2
95
+ if ! pm2 list | grep -q "switchboard_${PROJECT_NAME}"; then
96
+ cd $PROJECT_NAME
97
+ pm2 start "pnpm switchboard" --name "switchboard_${PROJECT_NAME}"
98
+ pm2 save
99
+ else
100
+ pm2 start "switchboard_${PROJECT_NAME}"
101
+ fi
102
+ ;;
103
+
104
+ "stop")
105
+ check_setup "$PROJECT_NAME"
106
+ echo "Stopping services..."
107
+ # Stop Switchboard via PM2
108
+ if pm2 list | grep -q "switchboard_${PROJECT_NAME}"; then
109
+ pm2 stop "switchboard_${PROJECT_NAME}"
110
+ fi
111
+
112
+ # Disable Nginx site
113
+ manage_nginx_site "disable"
114
+ ;;
115
+
116
+ "restart")
117
+ check_setup "$PROJECT_NAME"
118
+ echo "Restarting services..."
119
+ # Build Connect
120
+ echo "Building Connect..."
121
+ ph connect build
122
+
123
+ # Restart Nginx site
124
+ manage_nginx_site "disable"
125
+ manage_nginx_site "enable"
126
+
127
+ # Restart Switchboard via PM2
128
+ if pm2 list | grep -q "switchboard_${PROJECT_NAME}"; then
129
+ pm2 restart "switchboard_${PROJECT_NAME}"
130
+ else
131
+ cd $PROJECT_NAME
132
+ pm2 start "pnpm switchboard" --name "switchboard_${PROJECT_NAME}"
133
+ pm2 save
134
+ fi
135
+ ;;
136
+
137
+ "status")
138
+ check_setup "$PROJECT_NAME"
139
+ echo "Checking service status..."
140
+ echo "Nginx site status:"
141
+ if [ -L "/etc/nginx/sites-enabled/$PROJECT_NAME" ]; then
142
+ echo "Site is enabled"
143
+ else
144
+ echo "Site is disabled"
145
+ fi
146
+
147
+ echo -e "\nSwitchboard status:"
148
+ pm2 list | grep "switchboard_${PROJECT_NAME}"
149
+ ;;
150
+
151
+ *)
152
+ echo "Usage: $0 [project_name] {start|stop|restart|status}"
153
+ echo "Default project_name: global"
154
+ echo "Default action: status"
155
+ exit 1
156
+ ;;
157
+ esac
158
+ fi
@@ -0,0 +1,378 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # =============================================================================
4
+ # Configuration
5
+ # =============================================================================
6
+ TARGET_TAG=${1:-"latest"}
7
+ PROJECT_NAME=${2:-"global"}
8
+
9
+ # =============================================================================
10
+ # OS Detection and Windows Handling
11
+ # =============================================================================
12
+ if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then
13
+ if [ -f "$0.ps1" ]; then
14
+ powershell -ExecutionPolicy Bypass -File "$0.ps1" -TARGET_TAG "$TARGET_TAG"
15
+ else
16
+ echo "Error: Windows setup script (setup-environment.ps1) not found"
17
+ exit 1
18
+ fi
19
+ else
20
+ # =============================================================================
21
+ # Package Installation
22
+ # =============================================================================
23
+ sudo apt install -y postgresql postgresql-contrib nginx
24
+
25
+ # =============================================================================
26
+ # Interactive Package Installation
27
+ # =============================================================================
28
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
29
+ echo " Package Installation"
30
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
31
+ while true; do
32
+ read -p "Enter package name to install (or press Enter to skip): " package_name
33
+ if [ -z "$package_name" ]; then
34
+ break
35
+ fi
36
+ ph install "$package_name"
37
+ done
38
+
39
+ # =============================================================================
40
+ # Connect Build
41
+ # =============================================================================
42
+ ph connect build
43
+
44
+ # =============================================================================
45
+ # Database Configuration
46
+ # =============================================================================
47
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
48
+ echo " Database Configuration"
49
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
50
+ echo "Choose database type:"
51
+ echo "1) Local PostgreSQL database"
52
+ echo "2) Remote PostgreSQL database"
53
+ read -p "Enter your choice (1 or 2): " db_choice
54
+
55
+ if [ "$db_choice" = "1" ]; then
56
+ echo "Setting up local PostgreSQL database..."
57
+
58
+ # Generate database credentials
59
+ DB_PASSWORD="powerhouse"
60
+ DB_USER="powerhouse"
61
+ DB_NAME="powerhouse_${PROJECT_NAME}"
62
+
63
+ # Check if database already exists
64
+ if sudo -u postgres psql -lqt | cut -d \| -f 1 | grep -qw $DB_NAME; then
65
+ echo "Database $DB_NAME already exists"
66
+ read -p "Do you want to recreate it? (y/n): " recreate_db
67
+ if [ "$recreate_db" = "y" ]; then
68
+ sudo -u postgres psql -c "DROP DATABASE $DB_NAME;"
69
+ else
70
+ echo "Using existing database"
71
+ fi
72
+ fi
73
+
74
+ # Create database and user if they don't exist
75
+ sudo -u postgres psql << EOF
76
+ DO
77
+ \$do\$
78
+ BEGIN
79
+ IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = '$DB_USER') THEN
80
+ CREATE USER $DB_USER WITH PASSWORD '$DB_PASSWORD';
81
+ END IF;
82
+ END
83
+ \$do\$;
84
+
85
+ CREATE DATABASE $DB_NAME OWNER $DB_USER;
86
+ GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;
87
+ EOF
88
+
89
+ # Configure PostgreSQL
90
+ sudo sed -i "s/#listen_addresses = 'localhost'/listen_addresses = 'localhost'/" /etc/postgresql/*/main/postgresql.conf
91
+
92
+ # Set DATABASE_URL for local database
93
+ DATABASE_URL="postgresql://$DB_USER:$DB_PASSWORD@localhost:5432/$DB_NAME"
94
+
95
+ echo "Local database configured successfully!"
96
+ echo "Database URL: $DATABASE_URL"
97
+ echo "Please save these credentials securely!"
98
+ else
99
+ echo "Enter remote PostgreSQL URL (format: postgresql://user:password@host:port/db)"
100
+ echo "Example: postgresql://powerhouse:password@db.example.com:5432/powerhouse"
101
+ read -p "DATABASE_URL: " DATABASE_URL
102
+ fi
103
+
104
+ # Save DATABASE_URL to .env file
105
+ echo "DATABASE_URL=$DATABASE_URL" | sudo tee -a .env
106
+
107
+ # =============================================================================
108
+ # SSL Configuration
109
+ # =============================================================================
110
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
111
+ echo " SSL Configuration"
112
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
113
+ echo "Choose SSL configuration:"
114
+ echo "1) Let's Encrypt certificates for domains"
115
+ echo "2) Self-signed certificate for machine hostname"
116
+ read -p "Enter your choice (1 or 2): " ssl_choice
117
+
118
+ if [ "$ssl_choice" = "1" ]; then
119
+ # Install certbot
120
+ sudo apt install -y certbot python3-certbot-nginx
121
+
122
+ # =============================================================================
123
+ # Domain Setup
124
+ # =============================================================================
125
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
126
+ echo " Domain Setup"
127
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
128
+ read -p "Enter Connect domain (e.g. connect.google.com): " connect_domain
129
+ read -p "Enter Switchboard domain (e.g. switchboard.google.com): " switchboard_domain
130
+
131
+ echo "Using domains:"
132
+ echo "Connect: $connect_domain"
133
+ echo "Switchboard: $switchboard_domain"
134
+
135
+ # Generate temporary SSL certificates
136
+ echo "Generating temporary SSL certificates..."
137
+ sudo mkdir -p /etc/nginx/ssl
138
+ sudo openssl req -x509 -nodes -days 1 -newkey rsa:2048 \
139
+ -keyout /etc/nginx/ssl/temp.key \
140
+ -out /etc/nginx/ssl/temp.crt \
141
+ -subj "/CN=$connect_domain" \
142
+ -addext "subjectAltName = DNS:$connect_domain,DNS:$switchboard_domain"
143
+
144
+ # Check if Nginx configuration already exists
145
+ if [ -f "/etc/nginx/sites-available/$PROJECT_NAME" ]; then
146
+ echo "Nginx configuration for $PROJECT_NAME already exists"
147
+ read -p "Do you want to overwrite it? (y/n): " overwrite_nginx
148
+ if [ "$overwrite_nginx" != "y" ]; then
149
+ echo "Keeping existing Nginx configuration"
150
+ else
151
+ # Create Nginx configuration for domains
152
+ echo "Creating Nginx configuration..."
153
+ sudo tee /etc/nginx/sites-available/$PROJECT_NAME > /dev/null << EOF
154
+ # Security headers
155
+ add_header Strict-Transport-Security "max-age=63072000" always;
156
+ add_header X-Frame-Options DENY;
157
+ add_header X-Content-Type-Options nosniff;
158
+ add_header X-XSS-Protection "1; mode=block";
159
+
160
+ server {
161
+ listen 80;
162
+ server_name $connect_domain $switchboard_domain;
163
+ return 301 https://\$host\$request_uri;
164
+ }
165
+
166
+ server {
167
+ listen 443 ssl http2;
168
+ server_name $connect_domain;
169
+
170
+ ssl_certificate /etc/nginx/ssl/temp.crt;
171
+ ssl_certificate_key /etc/nginx/ssl/temp.key;
172
+
173
+ # SSL configuration
174
+ ssl_protocols TLSv1.2 TLSv1.3;
175
+ ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
176
+ ssl_prefer_server_ciphers off;
177
+ ssl_session_timeout 1d;
178
+ ssl_session_cache shared:SSL:50m;
179
+ ssl_session_tickets off;
180
+ ssl_stapling on;
181
+ ssl_stapling_verify on;
182
+
183
+ if (\$http_x_forwarded_proto = "http") {
184
+ return 301 https://\$server_name\$request_uri;
185
+ }
186
+
187
+ location / {
188
+ root $PWD/.ph/connect-build/dist;
189
+ try_files \$uri \$uri/ /index.html;
190
+ add_header Cache-Control "no-cache";
191
+ add_header X-Forwarded-Proto \$scheme;
192
+ add_header X-Forwarded-Host \$host;
193
+ add_header X-Forwarded-Port \$server_port;
194
+ }
195
+ }
196
+
197
+ server {
198
+ listen 443 ssl http2;
199
+ server_name $switchboard_domain;
200
+
201
+ ssl_certificate /etc/nginx/ssl/temp.crt;
202
+ ssl_certificate_key /etc/nginx/ssl/temp.key;
203
+
204
+ location / {
205
+ proxy_pass http://localhost:4001;
206
+ proxy_http_version 1.1;
207
+ proxy_set_header Upgrade \$http_upgrade;
208
+ proxy_set_header Connection 'upgrade';
209
+ proxy_set_header Host \$host;
210
+ proxy_cache_bypass \$http_upgrade;
211
+ proxy_set_header X-Real-IP \$remote_addr;
212
+ proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
213
+ proxy_set_header X-Forwarded-Proto \$scheme;
214
+ }
215
+ }
216
+ EOF
217
+ fi
218
+ else
219
+ # Create Nginx configuration for domains
220
+ echo "Creating Nginx configuration..."
221
+ sudo tee /etc/nginx/sites-available/$PROJECT_NAME > /dev/null << EOF
222
+ # Security headers
223
+ add_header Strict-Transport-Security "max-age=63072000" always;
224
+ add_header X-Frame-Options DENY;
225
+ add_header X-Content-Type-Options nosniff;
226
+ add_header X-XSS-Protection "1; mode=block";
227
+
228
+ server {
229
+ listen 80;
230
+ server_name $connect_domain $switchboard_domain;
231
+ return 301 https://\$host\$request_uri;
232
+ }
233
+
234
+ server {
235
+ listen 443 ssl http2;
236
+ server_name $connect_domain;
237
+
238
+ ssl_certificate /etc/nginx/ssl/temp.crt;
239
+ ssl_certificate_key /etc/nginx/ssl/temp.key;
240
+
241
+ # SSL configuration
242
+ ssl_protocols TLSv1.2 TLSv1.3;
243
+ ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
244
+ ssl_prefer_server_ciphers off;
245
+ ssl_session_timeout 1d;
246
+ ssl_session_cache shared:SSL:50m;
247
+ ssl_session_tickets off;
248
+ ssl_stapling on;
249
+ ssl_stapling_verify on;
250
+
251
+ if (\$http_x_forwarded_proto = "http") {
252
+ return 301 https://\$server_name\$request_uri;
253
+ }
254
+
255
+ location / {
256
+ root $PWD/.ph/connect-build/dist;
257
+ try_files \$uri \$uri/ /index.html;
258
+ add_header Cache-Control "no-cache";
259
+ add_header X-Forwarded-Proto \$scheme;
260
+ add_header X-Forwarded-Host \$host;
261
+ add_header X-Forwarded-Port \$server_port;
262
+ }
263
+ }
264
+
265
+ server {
266
+ listen 443 ssl http2;
267
+ server_name $switchboard_domain;
268
+
269
+ ssl_certificate /etc/nginx/ssl/temp.crt;
270
+ ssl_certificate_key /etc/nginx/ssl/temp.key;
271
+
272
+ location / {
273
+ proxy_pass http://localhost:4001;
274
+ proxy_http_version 1.1;
275
+ proxy_set_header Upgrade \$http_upgrade;
276
+ proxy_set_header Connection 'upgrade';
277
+ proxy_set_header Host \$host;
278
+ proxy_cache_bypass \$http_upgrade;
279
+ proxy_set_header X-Real-IP \$remote_addr;
280
+ proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
281
+ proxy_set_header X-Forwarded-Proto \$scheme;
282
+ }
283
+ }
284
+ EOF
285
+ fi
286
+
287
+ # Enable the site
288
+ sudo ln -sf /etc/nginx/sites-available/$PROJECT_NAME /etc/nginx/sites-enabled/
289
+ sudo rm -f /etc/nginx/sites-enabled/default
290
+
291
+ # Test Nginx configuration
292
+ sudo nginx -t
293
+
294
+ # Obtain SSL certificates
295
+ echo "Obtaining SSL certificates..."
296
+ sudo certbot --nginx -d $connect_domain -d $switchboard_domain --non-interactive --agree-tos --email admin@$connect_domain
297
+
298
+ # Remove temporary certificates
299
+ sudo rm -f /etc/nginx/ssl/temp.*
300
+
301
+ else
302
+ # Get machine hostname
303
+ hostname=$(hostname)
304
+
305
+ # Generate self-signed certificate
306
+ echo "Generating self-signed certificate for $hostname..."
307
+ sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
308
+ -keyout /etc/ssl/private/$hostname.key \
309
+ -out /etc/ssl/certs/$hostname.crt \
310
+ -subj "/CN=$hostname" \
311
+ -addext "subjectAltName = DNS:$hostname"
312
+
313
+ # Create Nginx configuration for self-signed
314
+ echo "Creating Nginx configuration..."
315
+ sudo tee /etc/nginx/sites-available/$PROJECT_NAME > /dev/null << EOF
316
+ # Security headers
317
+ add_header Strict-Transport-Security "max-age=63072000" always;
318
+ add_header X-Frame-Options DENY;
319
+ add_header X-Content-Type-Options nosniff;
320
+ add_header X-XSS-Protection "1; mode=block";
321
+
322
+ server {
323
+ listen 80;
324
+ server_name $hostname;
325
+ return 301 https://\$host\$request_uri;
326
+ }
327
+
328
+ server {
329
+ listen 443 ssl http2;
330
+ server_name $hostname;
331
+
332
+ ssl_certificate /etc/ssl/certs/$hostname.crt;
333
+ ssl_certificate_key /etc/ssl/private/$hostname.key;
334
+
335
+ location /connect {
336
+ proxy_pass http://localhost:3000;
337
+ proxy_http_version 1.1;
338
+ proxy_set_header Upgrade \$http_upgrade;
339
+ proxy_set_header Connection 'upgrade';
340
+ proxy_set_header Host \$host;
341
+ proxy_cache_bypass \$http_upgrade;
342
+ proxy_set_header X-Real-IP \$remote_addr;
343
+ proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
344
+ proxy_set_header X-Forwarded-Proto \$scheme;
345
+ }
346
+
347
+ location /switchboard {
348
+ proxy_pass http://localhost:4001;
349
+ proxy_http_version 1.1;
350
+ proxy_set_header Upgrade \$http_upgrade;
351
+ proxy_set_header Connection 'upgrade';
352
+ proxy_set_header Host \$host;
353
+ proxy_cache_bypass \$http_upgrade;
354
+ proxy_set_header X-Real-IP \$remote_addr;
355
+ proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
356
+ proxy_set_header X-Forwarded-Proto \$scheme;
357
+ }
358
+ }
359
+ EOF
360
+
361
+ # Enable the site
362
+ sudo ln -sf /etc/nginx/sites-available/$PROJECT_NAME /etc/nginx/sites-enabled/
363
+ sudo rm -f /etc/nginx/sites-enabled/default
364
+
365
+ # Test Nginx configuration
366
+ sudo nginx -t
367
+ fi
368
+
369
+ # =============================================================================
370
+ # Database Schema Setup
371
+ # =============================================================================
372
+ pnpm prisma db push --schema node_modules/document-drive/dist/prisma/schema.prisma
373
+
374
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
375
+ echo " Environment setup complete!"
376
+ echo " Use 'ph service start' to start services"
377
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
378
+ fi