@pixelbyte-software/pixcode 1.51.5 → 1.51.6
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/assets/index-Bbmw_Gy1.css +32 -0
- package/dist/assets/{index-CpbSjzK7.js → index-DodOzOl5.js} +151 -150
- package/dist/index.html +2 -2
- package/dist-server/server/routes/platformization.js +17 -1
- package/dist-server/server/routes/platformization.js.map +1 -1
- package/dist-server/server/services/platformization.js +95 -1
- package/dist-server/server/services/platformization.js.map +1 -1
- package/package.json +1 -1
- package/server/routes/platformization.js +27 -9
- package/server/services/platformization.js +111 -12
- package/dist/assets/index-oDq76nSq.css +0 -32
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { execFile } from 'node:child_process';
|
|
4
|
+
import { execFile, spawn } from 'node:child_process';
|
|
5
5
|
import { promisify } from 'node:util';
|
|
6
6
|
|
|
7
7
|
import bcrypt from 'bcryptjs';
|
|
@@ -9,7 +9,7 @@ import bcrypt from 'bcryptjs';
|
|
|
9
9
|
import { appConfigDb, userDb } from '../database/db.js';
|
|
10
10
|
|
|
11
11
|
const CONFIG_KEY = 'platformization';
|
|
12
|
-
const execFileAsync = promisify(execFile);
|
|
12
|
+
const execFileAsync = promisify(execFile);
|
|
13
13
|
|
|
14
14
|
export const TEAM_ROLES = {
|
|
15
15
|
owner: [
|
|
@@ -803,9 +803,66 @@ export function exportAuditLog(format = 'json', filters = {}) {
|
|
|
803
803
|
return JSON.stringify(entries, null, 2);
|
|
804
804
|
}
|
|
805
805
|
|
|
806
|
-
function normalizeAccessMode(mode) {
|
|
807
|
-
return ['lan', 'tailscale', 'cloudflare_tunnel', 'custom_domain'].includes(mode) ? mode : 'lan';
|
|
808
|
-
}
|
|
806
|
+
function normalizeAccessMode(mode) {
|
|
807
|
+
return ['lan', 'tailscale', 'cloudflare_tunnel', 'custom_domain'].includes(mode) ? mode : 'lan';
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
function resolveTailscaleInstallPlan() {
|
|
811
|
+
const platform = os.platform();
|
|
812
|
+
if (platform === 'darwin') {
|
|
813
|
+
return {
|
|
814
|
+
platform,
|
|
815
|
+
command: 'brew',
|
|
816
|
+
args: ['install', 'tailscale'],
|
|
817
|
+
displayCommand: 'brew install tailscale',
|
|
818
|
+
docsUrl: 'https://tailscale.com/download/mac',
|
|
819
|
+
note: 'If Homebrew is not installed, open the download page and install the macOS app.',
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
if (platform === 'win32') {
|
|
823
|
+
return {
|
|
824
|
+
platform,
|
|
825
|
+
command: 'winget',
|
|
826
|
+
args: ['install', '--id', 'Tailscale.Tailscale', '-e', '--silent'],
|
|
827
|
+
displayCommand: 'winget install --id Tailscale.Tailscale -e --silent',
|
|
828
|
+
docsUrl: 'https://tailscale.com/download/windows',
|
|
829
|
+
note: 'If winget is unavailable, install from the Tailscale download page.',
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
return {
|
|
833
|
+
platform,
|
|
834
|
+
command: 'sh',
|
|
835
|
+
args: ['-c', 'curl -fsSL https://tailscale.com/install.sh | sh'],
|
|
836
|
+
displayCommand: 'curl -fsSL https://tailscale.com/install.sh | sh',
|
|
837
|
+
docsUrl: 'https://tailscale.com/download/linux',
|
|
838
|
+
note: 'Linux install may require root privileges. If this fails, run the command with sudo in a terminal.',
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function extractFirstUrl(text = '') {
|
|
843
|
+
return String(text).match(/https?:\/\/[^\s]+/i)?.[0] || null;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
function runTailscaleCommand(command, args, options = {}) {
|
|
847
|
+
return new Promise((resolve) => {
|
|
848
|
+
const child = spawn(command, args, {
|
|
849
|
+
shell: false,
|
|
850
|
+
windowsHide: true,
|
|
851
|
+
env: process.env,
|
|
852
|
+
...options,
|
|
853
|
+
});
|
|
854
|
+
let stdout = '';
|
|
855
|
+
let stderr = '';
|
|
856
|
+
child.stdout?.on('data', (chunk) => { stdout += chunk.toString(); });
|
|
857
|
+
child.stderr?.on('data', (chunk) => { stderr += chunk.toString(); });
|
|
858
|
+
child.on('error', (error) => {
|
|
859
|
+
resolve({ ok: false, code: null, stdout, stderr, error: error.message });
|
|
860
|
+
});
|
|
861
|
+
child.on('close', (code) => {
|
|
862
|
+
resolve({ ok: code === 0, code, stdout, stderr, error: code === 0 ? null : `${command} exited with code ${code}` });
|
|
863
|
+
});
|
|
864
|
+
});
|
|
865
|
+
}
|
|
809
866
|
|
|
810
867
|
function normalizePublicUrl(value) {
|
|
811
868
|
const raw = typeof value === 'string' ? value.trim() : '';
|
|
@@ -872,8 +929,9 @@ export function getRemoteAccessState() {
|
|
|
872
929
|
};
|
|
873
930
|
}
|
|
874
931
|
|
|
875
|
-
export async function detectTailscaleStatus() {
|
|
876
|
-
|
|
932
|
+
export async function detectTailscaleStatus() {
|
|
933
|
+
const installPlan = resolveTailscaleInstallPlan();
|
|
934
|
+
try {
|
|
877
935
|
const { stdout } = await execFileAsync('tailscale', ['status', '--json'], { timeout: 5000 });
|
|
878
936
|
const status = JSON.parse(stdout || '{}');
|
|
879
937
|
const self = status.Self || {};
|
|
@@ -886,7 +944,8 @@ export async function detectTailscaleStatus() {
|
|
|
886
944
|
magicDnsName: self.DNSName || null,
|
|
887
945
|
tailscaleIp: tailscaleIps[0] || null,
|
|
888
946
|
pixcodeUrl: tailscaleIps[0] ? `http://${tailscaleIps[0]}:${process.env.SERVER_PORT || 3001}` : null,
|
|
889
|
-
installUrl: 'https://tailscale.com/download',
|
|
947
|
+
installUrl: 'https://tailscale.com/download',
|
|
948
|
+
installPlan,
|
|
890
949
|
checkedAt: nowIso(),
|
|
891
950
|
message: tailscaleIps[0] ? 'Tailscale is ready for private Pixcode access.' : 'Tailscale CLI is installed but no device IP was detected.',
|
|
892
951
|
};
|
|
@@ -900,14 +959,54 @@ export async function detectTailscaleStatus() {
|
|
|
900
959
|
magicDnsName: null,
|
|
901
960
|
tailscaleIp: null,
|
|
902
961
|
pixcodeUrl: null,
|
|
903
|
-
installUrl: 'https://tailscale.com/download',
|
|
962
|
+
installUrl: 'https://tailscale.com/download',
|
|
963
|
+
installPlan,
|
|
904
964
|
checkedAt: nowIso(),
|
|
905
965
|
message: isMissing
|
|
906
966
|
? 'Tailscale is optional. Use the LAN links now, or install Tailscale from Settings > Access for private team access without a public domain.'
|
|
907
967
|
: (error?.message || 'Tailscale status could not be read.'),
|
|
908
|
-
};
|
|
909
|
-
}
|
|
910
|
-
}
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
export async function installTailscale(actorId = null) {
|
|
973
|
+
const plan = resolveTailscaleInstallPlan();
|
|
974
|
+
const result = await runTailscaleCommand(plan.command, plan.args);
|
|
975
|
+
const store = readStore();
|
|
976
|
+
addAudit(store, 'remote.access.tailscale.install', actorId, {
|
|
977
|
+
platform: plan.platform,
|
|
978
|
+
ok: result.ok,
|
|
979
|
+
command: plan.displayCommand,
|
|
980
|
+
});
|
|
981
|
+
writeStore(store);
|
|
982
|
+
return {
|
|
983
|
+
...result,
|
|
984
|
+
plan,
|
|
985
|
+
message: result.ok
|
|
986
|
+
? 'Tailscale install command completed. Run login/connect next.'
|
|
987
|
+
: `Install command failed. ${plan.note}`,
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
export async function loginTailscale(actorId = null) {
|
|
992
|
+
const result = await runTailscaleCommand('tailscale', ['up']);
|
|
993
|
+
const combinedOutput = `${result.stdout || ''}\n${result.stderr || ''}`;
|
|
994
|
+
const authUrl = extractFirstUrl(combinedOutput);
|
|
995
|
+
const store = readStore();
|
|
996
|
+
addAudit(store, 'remote.access.tailscale.login', actorId, {
|
|
997
|
+
ok: result.ok,
|
|
998
|
+
authUrl: Boolean(authUrl),
|
|
999
|
+
});
|
|
1000
|
+
writeStore(store);
|
|
1001
|
+
return {
|
|
1002
|
+
...result,
|
|
1003
|
+
authUrl,
|
|
1004
|
+
message: result.ok
|
|
1005
|
+
? 'Tailscale is connected.'
|
|
1006
|
+
: (authUrl ? 'Open the login URL to finish connecting this device.' : 'Tailscale login command failed.'),
|
|
1007
|
+
tailscale: await detectTailscaleStatus(),
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
911
1010
|
|
|
912
1011
|
export async function checkRemoteAccessHealth(input = {}, actorId = null) {
|
|
913
1012
|
const url = normalizePublicUrl(input.url || input.remoteUrl || '');
|