mango-cms 0.2.48 → 0.2.50
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/cli.js +128 -1
- package/default/gitignore +1 -0
- package/default/package.json +1 -1
- package/default/src/helpers/mango.js +8 -2
- package/default/src/main.js +1 -0
- package/default/vite.config.js +5 -0
- package/lib/dev-proxy.js +79 -0
- package/lib/hosts-manager.js +53 -0
- package/package.json +3 -1
package/cli.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const { Command } = require('commander');
|
|
4
|
-
const { execSync } = require('child_process');
|
|
4
|
+
const { execSync, spawn } = require('child_process');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const inquirer = require('inquirer');
|
|
7
7
|
const fs = require('fs-extra');
|
|
8
8
|
const axios = require('axios');
|
|
9
9
|
const AdmZip = require('adm-zip');
|
|
10
|
+
const { addHosts, removeHosts } = require('./lib/hosts-manager');
|
|
10
11
|
|
|
11
12
|
const program = new Command();
|
|
12
13
|
|
|
@@ -783,4 +784,130 @@ program
|
|
|
783
784
|
}
|
|
784
785
|
});
|
|
785
786
|
|
|
787
|
+
// --- Dev Proxy Commands ---
|
|
788
|
+
const proxyCmd = program
|
|
789
|
+
.command('proxy <action>')
|
|
790
|
+
.description('Manage local dev proxy domains (start, stop, status)')
|
|
791
|
+
.action(async (action) => {
|
|
792
|
+
const allowed = ['start', 'stop', 'status'];
|
|
793
|
+
if (!allowed.includes(action)) {
|
|
794
|
+
console.error(`Unknown proxy action: ${action}. Use: ${allowed.join(', ')}`);
|
|
795
|
+
process.exit(1);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const pidPath = path.join(process.cwd(), '.mango-proxy.pid');
|
|
799
|
+
const settingsPath = path.join(process.cwd(), 'mango/config/settings.json');
|
|
800
|
+
|
|
801
|
+
if (action === 'start') {
|
|
802
|
+
if (!fs.existsSync(settingsPath)) {
|
|
803
|
+
console.error('Error: mango/config/settings.json not found. Run this from your project root.');
|
|
804
|
+
process.exit(1);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
808
|
+
const { siteName } = settings;
|
|
809
|
+
|
|
810
|
+
if (!siteName) {
|
|
811
|
+
console.error('Error: siteName must be set in settings.json');
|
|
812
|
+
process.exit(1);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// Derive dev proxy domains from siteName
|
|
816
|
+
const slug = siteName.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
817
|
+
const frontDomain = `${slug}.mango`;
|
|
818
|
+
const apiDomain = `api.${slug}.mango`;
|
|
819
|
+
|
|
820
|
+
// Kill existing proxy if running
|
|
821
|
+
if (fs.existsSync(pidPath)) {
|
|
822
|
+
try {
|
|
823
|
+
const oldPid = fs.readFileSync(pidPath, 'utf8').trim();
|
|
824
|
+
execSync(`sudo kill ${oldPid} 2>/dev/null || true`);
|
|
825
|
+
} catch (e) { /* ignore */ }
|
|
826
|
+
fs.unlinkSync(pidPath);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// Add /etc/hosts entries
|
|
830
|
+
addHosts([frontDomain, apiDomain]);
|
|
831
|
+
|
|
832
|
+
const frontendPort = settings.frontPort || 5173;
|
|
833
|
+
const backendPort = settings.port || 3002;
|
|
834
|
+
|
|
835
|
+
// Start the proxy server as a detached background process
|
|
836
|
+
const proxyScript = path.join(__dirname, 'lib/dev-proxy.js');
|
|
837
|
+
const logPath = path.join(process.cwd(), '.mango-proxy.log');
|
|
838
|
+
|
|
839
|
+
// Use shell to redirect output to a log file and background the process
|
|
840
|
+
// This keeps sudo in the current TTY so the password prompt works
|
|
841
|
+
execSync(
|
|
842
|
+
`sudo node "${proxyScript}" ${frontDomain} ${apiDomain} ${frontendPort} ${backendPort} "${pidPath}" > "${logPath}" 2>&1 &`,
|
|
843
|
+
{ stdio: 'inherit', shell: true }
|
|
844
|
+
);
|
|
845
|
+
|
|
846
|
+
// Wait briefly for the proxy to start and write its PID
|
|
847
|
+
let started = false;
|
|
848
|
+
for (let i = 0; i < 10; i++) {
|
|
849
|
+
await new Promise(r => setTimeout(r, 300));
|
|
850
|
+
if (fs.existsSync(pidPath)) {
|
|
851
|
+
const pid = fs.readFileSync(pidPath, 'utf8').trim();
|
|
852
|
+
try {
|
|
853
|
+
execSync(`kill -0 ${pid} 2>/dev/null`);
|
|
854
|
+
started = true;
|
|
855
|
+
break;
|
|
856
|
+
} catch (e) { /* not running yet */ }
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
if (!started) {
|
|
861
|
+
console.error('\nFailed to start proxy server.');
|
|
862
|
+
if (fs.existsSync(logPath)) {
|
|
863
|
+
console.error('Log output:\n' + fs.readFileSync(logPath, 'utf8'));
|
|
864
|
+
}
|
|
865
|
+
process.exit(1);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
console.log(`
|
|
869
|
+
Mango Dev Proxy started.
|
|
870
|
+
|
|
871
|
+
http://${frontDomain} -> localhost:${frontendPort} (frontend)
|
|
872
|
+
http://${apiDomain} -> localhost:${backendPort} (backend)
|
|
873
|
+
|
|
874
|
+
Start your dev servers with proxy support:
|
|
875
|
+
mango start (backend on port ${backendPort})
|
|
876
|
+
VITE_DEV_PROXY=${apiDomain} mango ui dev (frontend on port ${frontendPort})
|
|
877
|
+
|
|
878
|
+
Proxy log: ${logPath}
|
|
879
|
+
To stop: mango proxy stop
|
|
880
|
+
`);
|
|
881
|
+
} else if (action === 'stop') {
|
|
882
|
+
if (fs.existsSync(pidPath)) {
|
|
883
|
+
try {
|
|
884
|
+
const pid = fs.readFileSync(pidPath, 'utf8').trim();
|
|
885
|
+
execSync(`sudo kill ${pid}`);
|
|
886
|
+
console.log('Proxy server stopped.');
|
|
887
|
+
} catch (e) {
|
|
888
|
+
console.log('Proxy process not found (may have already exited).');
|
|
889
|
+
}
|
|
890
|
+
fs.unlinkSync(pidPath);
|
|
891
|
+
} else {
|
|
892
|
+
console.log('No proxy PID file found. Proxy may not be running.');
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
removeHosts();
|
|
896
|
+
console.log('Dev proxy cleaned up.');
|
|
897
|
+
|
|
898
|
+
} else if (action === 'status') {
|
|
899
|
+
if (fs.existsSync(pidPath)) {
|
|
900
|
+
const pid = fs.readFileSync(pidPath, 'utf8').trim();
|
|
901
|
+
try {
|
|
902
|
+
execSync(`kill -0 ${pid} 2>/dev/null`);
|
|
903
|
+
console.log(`Proxy is running (PID: ${pid}).`);
|
|
904
|
+
} catch (e) {
|
|
905
|
+
console.log('Proxy PID file exists but process is not running.');
|
|
906
|
+
}
|
|
907
|
+
} else {
|
|
908
|
+
console.log('Proxy is not running.');
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
|
|
786
913
|
program.parse(process.argv);
|
package/default/gitignore
CHANGED
package/default/package.json
CHANGED
|
@@ -21,8 +21,14 @@ let api = `https://${mangoDomain}`
|
|
|
21
21
|
let ws = `wss://${mangoDomain}/graphql`
|
|
22
22
|
|
|
23
23
|
if (process.env.NODE_ENV != 'production' && useDevAPI) {
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
if (import.meta.env.VITE_DEV_PROXY) {
|
|
25
|
+
const proxyApi = import.meta.env.VITE_DEV_PROXY
|
|
26
|
+
api = `http://${proxyApi}`
|
|
27
|
+
ws = `ws://${proxyApi}/graphql`
|
|
28
|
+
} else {
|
|
29
|
+
api = `http://localhost:${port}`
|
|
30
|
+
ws = `ws://localhost:${port}/graphql`
|
|
31
|
+
}
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
function getQuery(params) {
|
package/default/src/main.js
CHANGED
|
@@ -69,6 +69,7 @@ app.provide('darkMode', darkMode().darkMode)
|
|
|
69
69
|
const user = initUser()
|
|
70
70
|
|
|
71
71
|
let api = useDevAPI ? `http://localhost:${port}` : `https://${mangoDomain}`
|
|
72
|
+
if (isDev && import.meta.env.VITE_DEV_PROXY) api = `http://${import.meta.env.VITE_DEV_PROXY}`
|
|
72
73
|
if (!isDev) api = `https://${mangoDomain}`
|
|
73
74
|
|
|
74
75
|
const store = reactive({
|
package/default/vite.config.js
CHANGED
|
@@ -57,6 +57,11 @@ export default defineConfig({
|
|
|
57
57
|
],
|
|
58
58
|
server: {
|
|
59
59
|
host: '0.0.0.0',
|
|
60
|
+
// When running behind mango proxy, configure HMR to use the proxy domain
|
|
61
|
+
// VITE_DEV_PROXY is set to api.{slug}.mango; derive frontend domain by stripping "api." prefix
|
|
62
|
+
...(process.env.VITE_DEV_PROXY?.startsWith('api.') ? {
|
|
63
|
+
hmr: { host: process.env.VITE_DEV_PROXY.replace(/^api\./, '') }
|
|
64
|
+
} : {}),
|
|
60
65
|
},
|
|
61
66
|
resolve: {
|
|
62
67
|
alias: {
|
package/lib/dev-proxy.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mango Dev Proxy Server
|
|
5
|
+
*
|
|
6
|
+
* Runs on port 80, routes requests by hostname to the correct local dev server.
|
|
7
|
+
* Supports WebSocket upgrade for Vite HMR and Socket.io.
|
|
8
|
+
*
|
|
9
|
+
* Usage: node lib/dev-proxy.js <frontDomain> <apiDomain> <frontPort> <backendPort> [pidPath]
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const http = require('http');
|
|
13
|
+
const httpProxy = require('http-proxy');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
|
|
16
|
+
const frontDomain = process.argv[2];
|
|
17
|
+
const apiDomain = process.argv[3];
|
|
18
|
+
const frontendPort = parseInt(process.argv[4], 10);
|
|
19
|
+
const backendPort = parseInt(process.argv[5], 10);
|
|
20
|
+
const pidPath = process.argv[6];
|
|
21
|
+
|
|
22
|
+
if (!frontDomain || !apiDomain || !frontendPort || !backendPort) {
|
|
23
|
+
console.error('Usage: node lib/dev-proxy.js <frontDomain> <apiDomain> <frontPort> <backendPort> [pidPath]');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const proxy = httpProxy.createProxyServer({
|
|
28
|
+
ws: true,
|
|
29
|
+
xfwd: true,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
proxy.on('error', (err, req, res) => {
|
|
33
|
+
console.error(`Proxy error for ${req.headers.host}${req.url}:`, err.message);
|
|
34
|
+
if (res.writeHead) {
|
|
35
|
+
res.writeHead(502);
|
|
36
|
+
res.end('Bad Gateway - is your dev server running?');
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
function getTarget(host) {
|
|
41
|
+
const hostname = host?.split(':')[0];
|
|
42
|
+
if (hostname === apiDomain) {
|
|
43
|
+
return `http://127.0.0.1:${backendPort}`;
|
|
44
|
+
}
|
|
45
|
+
if (hostname === frontDomain) {
|
|
46
|
+
return `http://127.0.0.1:${frontendPort}`;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const server = http.createServer((req, res) => {
|
|
52
|
+
const target = getTarget(req.headers.host);
|
|
53
|
+
if (!target) {
|
|
54
|
+
res.writeHead(404);
|
|
55
|
+
res.end(`Unknown host: ${req.headers.host}`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
proxy.web(req, res, { target });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
server.on('upgrade', (req, socket, head) => {
|
|
62
|
+
const target = getTarget(req.headers.host);
|
|
63
|
+
if (target) {
|
|
64
|
+
proxy.ws(req, socket, head, { target });
|
|
65
|
+
} else {
|
|
66
|
+
socket.destroy();
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
server.listen(80, '0.0.0.0', () => {
|
|
71
|
+
console.log('Mango Dev Proxy running on port 80');
|
|
72
|
+
console.log(` ${frontDomain} -> http://127.0.0.1:${frontendPort} (frontend)`);
|
|
73
|
+
console.log(` ${apiDomain} -> http://127.0.0.1:${backendPort} (backend)`);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Write PID for cleanup
|
|
77
|
+
if (pidPath) {
|
|
78
|
+
fs.writeFileSync(pidPath, String(process.pid));
|
|
79
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages /etc/hosts entries for Mango dev proxy domains.
|
|
3
|
+
* Lines are tagged with "# mango-proxy" for easy cleanup.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
|
|
9
|
+
const HOSTS_FILE = '/etc/hosts';
|
|
10
|
+
const TAG = '# mango-proxy';
|
|
11
|
+
|
|
12
|
+
function addHosts(domains) {
|
|
13
|
+
const hostsContent = fs.readFileSync(HOSTS_FILE, 'utf8');
|
|
14
|
+
const newDomains = domains.filter(d => !hostsContent.includes(d));
|
|
15
|
+
|
|
16
|
+
if (newDomains.length === 0) {
|
|
17
|
+
console.log('Hosts entries already exist, skipping.');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const entry = `127.0.0.1 ${newDomains.join(' ')} ${TAG}`;
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
execSync(`echo '${entry}' | sudo tee -a ${HOSTS_FILE} > /dev/null`, { stdio: 'inherit' });
|
|
25
|
+
console.log(`Added to /etc/hosts: ${newDomains.join(', ')}`);
|
|
26
|
+
} catch (err) {
|
|
27
|
+
console.error('Failed to update /etc/hosts. You may need to add this line manually:');
|
|
28
|
+
console.error(` ${entry}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function removeHosts() {
|
|
33
|
+
try {
|
|
34
|
+
const hostsContent = fs.readFileSync(HOSTS_FILE, 'utf8');
|
|
35
|
+
const lines = hostsContent.split('\n');
|
|
36
|
+
const filtered = lines.filter(line => !line.includes(TAG));
|
|
37
|
+
|
|
38
|
+
if (lines.length === filtered.length) {
|
|
39
|
+
console.log('No mango-proxy entries found in /etc/hosts.');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const tmpFile = '/tmp/mango-hosts-clean';
|
|
44
|
+
fs.writeFileSync(tmpFile, filtered.join('\n'));
|
|
45
|
+
execSync(`sudo cp ${tmpFile} ${HOSTS_FILE}`, { stdio: 'inherit' });
|
|
46
|
+
fs.unlinkSync(tmpFile);
|
|
47
|
+
console.log('Removed mango-proxy entries from /etc/hosts.');
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.error('Failed to clean /etc/hosts:', err.message);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = { addHosts, removeHosts };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mango-cms",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.50",
|
|
4
4
|
"main": "./index.js",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.js",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"index.js",
|
|
11
11
|
"cli.js",
|
|
12
12
|
"webpack.config.js",
|
|
13
|
+
"lib/**/*",
|
|
13
14
|
"default/**/*",
|
|
14
15
|
"!default/node_modules/**"
|
|
15
16
|
],
|
|
@@ -70,6 +71,7 @@
|
|
|
70
71
|
"graphql-type-datetime": "^0.2.4",
|
|
71
72
|
"graphql-ws": "^5.9.0",
|
|
72
73
|
"html-to-text": "^8.0.0",
|
|
74
|
+
"http-proxy": "^1.18.1",
|
|
73
75
|
"inquirer": "^8.2.4",
|
|
74
76
|
"json-fn": "^1.1.1",
|
|
75
77
|
"lodash": "^4.17.21",
|