mango-cms 0.3.35 → 0.3.37

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { Command } from 'commander';
4
- import { execSync, spawn } from 'child_process';
4
+ import { execSync, spawn, spawnSync } from 'child_process';
5
5
  import path from 'path';
6
6
  import { fileURLToPath } from 'url';
7
7
  import inquirer from 'inquirer';
@@ -14,6 +14,22 @@ import os from 'os';
14
14
  const __filename = fileURLToPath(import.meta.url);
15
15
  const __dirname = path.dirname(__filename);
16
16
 
17
+ // When `mango` is invoked from a global install (npm i -g mango-cms) inside a
18
+ // project that has its own mango-cms in node_modules, defer to the project-local
19
+ // CLI so the version pinned in package.json always wins. This is what lets users
20
+ // type plain `mango dev` / `mango deploy` instead of prefixing with `yarn`/`npx`.
21
+ const localCli = path.join(process.cwd(), 'node_modules', 'mango-cms', 'cli.js');
22
+ if (fs.existsSync(localCli)) {
23
+ let isSelf = false;
24
+ try {
25
+ isSelf = fs.realpathSync(localCli) === fs.realpathSync(__filename);
26
+ } catch (e) { /* fall through and run this copy */ }
27
+ if (!isSelf) {
28
+ const result = spawnSync(process.execPath, [localCli, ...process.argv.slice(2)], { stdio: 'inherit' });
29
+ process.exit(result.status ?? 0);
30
+ }
31
+ }
32
+
17
33
  const program = new Command();
18
34
 
19
35
  // Helper function to download and extract versioned src zip file
@@ -521,16 +537,31 @@ async function detectWebServer() {
521
537
  }
522
538
  }
523
539
 
540
+ // Deploy-identity key for pm2 process names and web server config filenames.
541
+ // Defaults to `database` for backward compatibility (matches every existing
542
+ // deployed site today), but `database` is meant to be safely shareable across
543
+ // projects (e.g. a multi-tenant setup), while deploy identity must be unique
544
+ // per project on a given box. Set "deployId" in settings.json to override
545
+ // when a project intentionally shares its database with another deployed
546
+ // project — otherwise `mango deploy`/`mango launch` will silently skip
547
+ // creating a web server config for the second project (path already exists)
548
+ // and reload the FIRST project's live pm2 processes instead of starting its
549
+ // own (HAP-1990).
550
+ function deployId(settings) {
551
+ return (settings.deployId || settings.database).toLowerCase();
552
+ }
553
+
524
554
  // Helper function to generate nginx config
525
- function generateNginxConfig(settings, buildPath) {
555
+ function generateNginxConfig(settings, buildPath, deployUi = false) {
526
556
  const domain = settings.mangoDomain;
527
557
  const port = settings.port || 3002;
528
558
  const frontPort = settings.frontPort || 3001;
529
559
  const uiDomain = settings.uiDomain;
530
560
  const uiPort = settings.uiPort || 3001;
531
561
 
532
- // Reverse-proxy the Mango UI (Nuxt SSR server) on its own subdomain, when configured.
533
- const uiServerBlock = uiDomain ? `
562
+ // Reverse-proxy the Mango UI (Nuxt SSR server) on its own subdomain,
563
+ // only when the UI is part of this deploy and a uiDomain is configured.
564
+ const uiServerBlock = (deployUi && uiDomain) ? `
534
565
  server {
535
566
  server_name ${uiDomain};
536
567
 
@@ -617,15 +648,16 @@ ${uiServerBlock}`;
617
648
  }
618
649
 
619
650
  // Helper function to generate apache config
620
- function generateApacheConfig(settings, buildPath) {
651
+ function generateApacheConfig(settings, buildPath, deployUi = false) {
621
652
  const domain = settings.mangoDomain;
622
653
  const port = settings.port || 3002;
623
654
  const frontPort = settings.frontPort || 3001;
624
655
  const uiDomain = settings.uiDomain;
625
656
  const uiPort = settings.uiPort || 3001;
626
657
 
627
- // Reverse-proxy the Mango UI (Nuxt SSR server) on its own subdomain, when configured.
628
- const uiVirtualHost = uiDomain ? `
658
+ // Reverse-proxy the Mango UI (Nuxt SSR server) on its own subdomain,
659
+ // only when the UI is part of this deploy and a uiDomain is configured.
660
+ const uiVirtualHost = (deployUi && uiDomain) ? `
629
661
  <VirtualHost *:80>
630
662
  ServerName ${uiDomain}
631
663
 
@@ -697,7 +729,7 @@ ${uiVirtualHost}`;
697
729
  }
698
730
 
699
731
  // Helper function to configure web server
700
- async function configureWebServer(settings, buildPath) {
732
+ async function configureWebServer(settings, buildPath, deployUi = false) {
701
733
  let webServer = await detectWebServer();
702
734
 
703
735
  if (!webServer) {
@@ -713,12 +745,12 @@ async function configureWebServer(settings, buildPath) {
713
745
  }
714
746
 
715
747
  const config = webServer === 'nginx' ?
716
- generateNginxConfig(settings, buildPath) :
717
- generateApacheConfig(settings, buildPath);
748
+ generateNginxConfig(settings, buildPath, deployUi) :
749
+ generateApacheConfig(settings, buildPath, deployUi);
718
750
 
719
751
  const configPath = webServer === 'nginx' ?
720
- `/etc/nginx/sites-available/${settings.database.toLowerCase()}` :
721
- `/etc/apache2/sites-available/${settings.database.toLowerCase()}.conf`;
752
+ `/etc/nginx/sites-available/${deployId(settings)}` :
753
+ `/etc/apache2/sites-available/${deployId(settings)}.conf`;
722
754
 
723
755
  try {
724
756
  // Check if config already exists
@@ -731,7 +763,7 @@ async function configureWebServer(settings, buildPath) {
731
763
 
732
764
  if (webServer === 'nginx') {
733
765
  // Create symlink if it doesn't exist
734
- const enabledPath = `/etc/nginx/sites-enabled/${settings.database.toLowerCase()}`;
766
+ const enabledPath = `/etc/nginx/sites-enabled/${deployId(settings)}`;
735
767
  if (!fs.existsSync(enabledPath)) {
736
768
  execSync(`sudo ln -s ${configPath} ${enabledPath}`);
737
769
  }
@@ -739,7 +771,7 @@ async function configureWebServer(settings, buildPath) {
739
771
  execSync('sudo systemctl reload nginx');
740
772
  } else {
741
773
  // Enable apache site
742
- execSync(`sudo a2ensite ${settings.database.toLowerCase()}`);
774
+ execSync(`sudo a2ensite ${deployId(settings)}`);
743
775
  execSync('sudo systemctl reload apache2');
744
776
  }
745
777
  console.log(`${webServer} configuration has been updated and service reloaded`);
@@ -764,8 +796,8 @@ async function checkPM2() {
764
796
 
765
797
  // Helper function to manage PM2 process
766
798
  async function managePM2Process(settings) {
767
- const processName = `${settings.database.toLowerCase()}-mango`;
768
- const name = settings.database.toLowerCase()
799
+ const processName = `${deployId(settings)}-mango`;
800
+ const name = deployId(settings)
769
801
 
770
802
  try {
771
803
  // Check if process exists
@@ -796,7 +828,9 @@ async function managePM2Process(settings) {
796
828
  }
797
829
  }
798
830
 
799
- // Helper function to build and (re)start the Mango UI (Nuxt) under PM2
831
+ // Helper function to build and (re)start the Mango UI (Nuxt) under PM2.
832
+ // Only called when the UI deploy is explicitly enabled (--ui flag or
833
+ // "deployUi": true in settings.json) — the admin UI is opt-in on deploy.
800
834
  async function manageUiPM2Process(settings, mangoCmsRoot, userProjectRoot) {
801
835
  if (!settings.uiDomain) {
802
836
  console.log('No uiDomain configured, skipping Mango UI deploy.');
@@ -805,7 +839,7 @@ async function manageUiPM2Process(settings, mangoCmsRoot, userProjectRoot) {
805
839
 
806
840
  const { dir: uiDir, ejected } = resolveUiDir(mangoCmsRoot, userProjectRoot);
807
841
  const uiPort = settings.uiPort || 3001;
808
- const processName = `${settings.database.toLowerCase()}-ui`;
842
+ const processName = `${deployId(settings)}-ui`;
809
843
 
810
844
  // Install deps + build the Nuxt app (reads this project's mango/config).
811
845
  if (!fs.existsSync(path.join(uiDir, 'node_modules'))) {
@@ -843,29 +877,39 @@ async function manageUiPM2Process(settings, mangoCmsRoot, userProjectRoot) {
843
877
  program
844
878
  .command('deploy')
845
879
  .description('Build and deploy Mango CMS using PM2')
846
- .action(async () => {
880
+ .option('--ui', 'also build and deploy the Mango admin UI (off by default; or set "deployUi": true in settings.json)')
881
+ .action(async (options) => {
847
882
  try {
848
883
  const configPath = path.join(process.cwd(), 'mango/config/settings.json');
849
884
  const settings = JSON.parse(fs.readFileSync(configPath, 'utf8'));
850
885
  const mangoCmsRoot = path.resolve(__dirname);
851
886
  const userProjectRoot = process.cwd();
852
887
 
888
+ // The admin UI is opt-in: deployed only with --ui or "deployUi": true.
889
+ const deployUi = Boolean(options.ui || settings.deployUi);
890
+
853
891
  // Run build command
854
892
  console.log('Building Mango CMS...');
855
893
  execSync('npm run mango build', { stdio: 'inherit' });
856
894
 
857
- // Make sure a UI bundle is available for deploy.
858
- await ensureUiExists(mangoCmsRoot);
895
+ // Make sure a UI bundle is available for deploy (only when deploying it).
896
+ if (deployUi) {
897
+ await ensureUiExists(mangoCmsRoot);
898
+ }
859
899
 
860
900
  // Check if PM2 is installed
861
901
  if (await checkPM2()) {
862
902
  await managePM2Process(settings);
863
- await manageUiPM2Process(settings, mangoCmsRoot, userProjectRoot);
903
+ if (deployUi) {
904
+ await manageUiPM2Process(settings, mangoCmsRoot, userProjectRoot);
905
+ } else {
906
+ console.log('Skipping Mango UI deploy (opt in with "mango deploy --ui" or "deployUi": true in settings.json).');
907
+ }
864
908
 
865
909
  // Configure web server
866
910
  const buildPath = path.join(process.cwd(), 'build');
867
911
  try {
868
- await configureWebServer(settings, buildPath);
912
+ await configureWebServer(settings, buildPath, deployUi);
869
913
  } catch (error) {
870
914
  console.warn('Warning: Could not configure web server:', error.message);
871
915
  console.warn('You may need to configure your web server manually.');
@@ -881,8 +925,9 @@ program
881
925
 
882
926
  program
883
927
  .command('launch')
884
- .description('One-shot server deploy: git pull, install, build the site, and deploy backend + UI')
885
- .action(async () => {
928
+ .description('One-shot server deploy: git pull, install, build the site, and deploy the backend (add --ui for the admin UI)')
929
+ .option('--ui', 'also build and deploy the Mango admin UI (off by default; or set "deployUi": true in settings.json)')
930
+ .action(async (options) => {
886
931
  try {
887
932
  const cwd = process.cwd();
888
933
  const useYarn = fs.existsSync(path.join(cwd, 'yarn.lock'));
@@ -909,12 +954,14 @@ program
909
954
  console.log('\n→ Skipping site build (no "build" script found)');
910
955
  }
911
956
 
912
- // 4. Deploy backend + UI + web server config via the freshly installed mango.
913
- // `mango deploy` builds the backend, (re)starts PM2 for {db}-mango, {db}
914
- // and {db}-ui, and writes the nginx/apache blocks for all three domains.
915
- run('Deploying backend + UI', 'npx mango deploy');
957
+ // 4. Deploy backend (+ UI when opted in) + web server config via the
958
+ // freshly installed mango. `mango deploy` builds the backend and
959
+ // (re)starts PM2 for {db}-mango and {db}; with --ui it also builds
960
+ // the admin UI, starts {db}-ui, and writes its web server block.
961
+ const deployCmd = options.ui ? 'npx mango deploy --ui' : 'npx mango deploy';
962
+ run(options.ui ? 'Deploying backend + UI' : 'Deploying backend', deployCmd);
916
963
 
917
- console.log('\n✨ Launch complete! Backend, site, and UI are deployed.');
964
+ console.log(`\n✨ Launch complete! Backend and site are deployed${options.ui ? ', along with the admin UI' : ''}.`);
918
965
  } catch (error) {
919
966
  console.error('\nLaunch failed:', error.message);
920
967
  process.exit(1);
@@ -24,7 +24,7 @@
24
24
  "dayjs": "^1.10.7",
25
25
  "express": "^4.18.1",
26
26
  "google-maps": "^4.3.3",
27
- "mango-cms": "^0.3.31",
27
+ "mango-cms": "^0.3.36",
28
28
  "mapbox-gl": "^2.7.0",
29
29
  "vite": "^6.2.2",
30
30
  "vue": "^3.2.37",
@@ -54,6 +54,19 @@ let appPlaceholder = '<!--SSR-->'
54
54
 
55
55
  let serve = async function (req, res) {
56
56
 
57
+ // Check for a short url redirect
58
+ let shortUrl = req.path.split('/').slice(1).join('/').split('?')[0]
59
+ if (shortUrl) {
60
+ let redirect = (await axios.get(`http://localhost:${settings.port}/shortUrls/?search={"short":"${shortUrl}"}`))?.data?.response?.[0]
61
+ if (redirect?.target) {
62
+ let target = redirect.target.includes('http') ? redirect.target : `https://${settings.siteDomain}/${redirect.target}`
63
+ let redirectUrl = redirect.referralSource ? `${target}?referral=${redirect.referralSource}&referralType=Page&utm_source=${redirect.referralSource}` : target
64
+ // Increment the visit count
65
+ await axios.put(`http://localhost:${settings.port}/shortUrls/${redirect.id}`, { visits: redirect.visits + 1 })
66
+ return res.redirect(redirectUrl)
67
+ }
68
+ }
69
+
57
70
  let index = fs.readFileSync('./index.html', 'utf8')
58
71
 
59
72
  // Cookies
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mango-cms",
3
- "version": "0.3.35",
3
+ "version": "0.3.37",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "exports": {