@walkhi/code-relax 0.1.0-beta.30 → 0.1.0-beta.31
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/README.md +95 -89
- package/dist/bin/cleanup-accounts.mjs +63 -63
- package/dist/bin/codex-remote.mjs +374 -367
- package/dist/bin/manage-relay-admin.mjs +20 -20
- package/dist/bin/migrate-relay-db.mjs +109 -109
- package/dist/bin/publish-android-release.mjs +62 -58
- package/dist/bin/self-relay-admin.mjs +8 -8
- package/dist/bin/self-relay-demo.mjs +33 -33
- package/dist/bin/self-relay-server.mjs +66 -66
- package/dist/build-platform.json +1 -1
- package/dist/get-lan-networks.ps1 +40 -40
- package/dist/get-shared-service-url.ps1 +6 -6
- package/dist/shared/app-server-events.cjs +422 -412
- package/dist/shared/code-browser.cjs +159 -159
- package/dist/shared/file-changes.cjs +31 -31
- package/dist/shared/file-citations.cjs +117 -117
- package/dist/shared/file-mentions.cjs +92 -92
- package/dist/shared/message-delivery.cjs +54 -54
- package/dist/shared/p2p-data-channel.cjs +72 -72
- package/dist/shared/p2p-diagnostics.cjs +28 -28
- package/dist/shared/p2p-file-channel.cjs +74 -74
- package/dist/shared/plan-pricing.cjs +32 -32
- package/dist/shared/question-attention.cjs +40 -0
- package/dist/shared/server-requests.cjs +219 -219
- package/dist/shared/timeline-sync.cjs +173 -173
- package/dist/skills/code-relax/SKILL.md +22 -22
- package/dist/src/archived-threads.mjs +22 -0
- package/dist/src/background-process.mjs +18 -18
- package/dist/src/bounded-process-log.mjs +63 -63
- package/dist/src/celsius-client.mjs +107 -107
- package/dist/src/code-browser.mjs +100 -100
- package/dist/src/desktop-launcher.mjs +46 -46
- package/dist/src/dots-approval-timeline.mjs +99 -99
- package/dist/src/dots-avatar.mjs +42 -42
- package/dist/src/dots-chat-projection.mjs +50 -50
- package/dist/src/dots-chat-server.mjs +461 -461
- package/dist/src/dots-message-images.mjs +319 -319
- package/dist/src/dots-profile.mjs +77 -77
- package/dist/src/dots-room-activity.mjs +94 -94
- package/dist/src/dots-room-client.mjs +135 -135
- package/dist/src/dots-room-projection.mjs +94 -94
- package/dist/src/dots-schedules.mjs +66 -66
- package/dist/src/dots-summary-tasks.mjs +32 -32
- package/dist/src/dots-task-catalog.mjs +430 -430
- package/dist/src/dots-task-files.mjs +52 -52
- package/dist/src/execution-health.mjs +26 -26
- package/dist/src/file-transfer-range.mjs +22 -22
- package/dist/src/file-upload-session.mjs +130 -130
- package/dist/src/hosted-history-app-server.mjs +103 -103
- package/dist/src/hosted-history-cache.mjs +113 -113
- package/dist/src/lan-socket-server.mjs +265 -263
- package/dist/src/managed-app-server.mjs +244 -244
- package/dist/src/message-images.mjs +9 -9
- package/dist/src/message-upload-files.mjs +74 -74
- package/dist/src/onboarding.mjs +12 -12
- package/dist/src/plan-pricing.mjs +47 -47
- package/dist/src/platform/README.md +12 -12
- package/dist/src/platform/codex-protocol.mjs +46 -46
- package/dist/src/platform/codex-runtime.mjs +3 -3
- package/dist/src/platform/credential-store.mjs +3 -3
- package/dist/src/platform/desktop-host.mjs +3 -3
- package/dist/src/platform/desktop-project-registration.mjs +3 -3
- package/dist/src/platform/desktop-project-store.mjs +208 -149
- package/dist/src/platform/file-opener.mjs +9 -0
- package/dist/src/platform/linux/README.md +3 -3
- package/dist/src/platform/linux/codex-runtime.mjs +56 -0
- package/dist/src/platform/linux/credential-store.mjs +28 -0
- package/dist/src/platform/linux/desktop-host.mjs +83 -0
- package/dist/src/platform/linux/desktop-installation.mjs +36 -0
- package/dist/src/platform/linux/desktop-project-registration.mjs +11 -0
- package/dist/src/platform/linux/network-info.mjs +11 -0
- package/dist/src/platform/linux/process.mjs +42 -0
- package/dist/src/platform/linux/project-locations.mjs +17 -0
- package/dist/src/platform/linux/resident-startup.mjs +52 -0
- package/dist/src/platform/linux/retained-runtime.mjs +76 -0
- package/dist/src/platform/linux/service-host.mjs +18 -0
- package/dist/src/platform/linux/system.mjs +49 -0
- package/dist/src/platform/linux/user-environment.mjs +7 -0
- package/dist/src/platform/macos/README.md +3 -3
- package/dist/src/platform/macos/codex-runtime.mjs +125 -125
- package/dist/src/platform/macos/credential-store.mjs +10 -10
- package/dist/src/platform/macos/desktop-host.mjs +82 -82
- package/dist/src/platform/macos/desktop-project-registration.mjs +11 -10
- package/dist/src/platform/macos/network-info.mjs +8 -28
- package/dist/src/platform/macos/project-locations.mjs +8 -8
- package/dist/src/platform/macos/resident-startup.mjs +84 -84
- package/dist/src/platform/macos/retained-runtime.mjs +127 -127
- package/dist/src/platform/macos/service-host.mjs +18 -18
- package/dist/src/platform/macos/system.mjs +29 -29
- package/dist/src/platform/macos/user-environment.mjs +7 -7
- package/dist/src/platform/native-package.mjs +58 -46
- package/dist/src/platform/network-info.mjs +3 -3
- package/dist/src/platform/path-identity.mjs +5 -5
- package/dist/src/platform/posix/network-candidates.mjs +23 -0
- package/dist/src/platform/posix/runtime-paths.mjs +25 -25
- package/dist/src/platform/project-locations.mjs +3 -3
- package/dist/src/platform/runtime-paths.mjs +8 -8
- package/dist/src/platform/select.mjs +5 -5
- package/dist/src/platform/service-host.mjs +3 -3
- package/dist/src/platform/user-environment.mjs +3 -3
- package/dist/src/platform/windows/IsolatedProcess.cs +79 -79
- package/dist/src/platform/windows/activate-desktop.ps1 +48 -48
- package/dist/src/platform/windows/background-process.mjs +30 -30
- package/dist/src/platform/windows/codex-runtime.mjs +215 -215
- package/dist/src/platform/windows/credential-store.mjs +27 -27
- package/dist/src/platform/windows/credential-store.ps1 +49 -49
- package/dist/src/platform/windows/desktop-helper.mjs +179 -179
- package/dist/src/platform/windows/desktop-host.mjs +58 -58
- package/dist/src/platform/windows/desktop-info.ps1 +14 -14
- package/dist/src/platform/windows/desktop-project-registration.mjs +4 -4
- package/dist/src/platform/windows/desktop-runtime.ps1 +10 -10
- package/dist/src/platform/windows/desktop-shortcuts.ps1 +21 -21
- package/dist/src/platform/windows/dots-profile.mjs +49 -49
- package/dist/src/platform/windows/launch-worker.mjs +41 -41
- package/dist/src/platform/windows/network-info.mjs +34 -34
- package/dist/src/platform/windows/project-locations.mjs +16 -16
- package/dist/src/platform/windows/resident-start.js +20 -20
- package/dist/src/platform/windows/resident-start.ps1 +7 -7
- package/dist/src/platform/windows/resident-startup.ps1 +83 -83
- package/dist/src/platform/windows/resolve-state-root.ps1 +14 -14
- package/dist/src/platform/windows/runtime-paths.mjs +20 -20
- package/dist/src/platform/windows/rustdesk-video-probe.mjs +167 -167
- package/dist/src/platform/windows/service-host.mjs +31 -31
- package/dist/src/platform/windows/start-hidden-console.ps1 +49 -49
- package/dist/src/platform/windows/stop-desktop.ps1 +22 -22
- package/dist/src/platform/windows/user-environment.mjs +50 -50
- package/dist/src/platform/windows/user-environment.ps1 +41 -41
- package/dist/src/project-archive.mjs +30 -0
- package/dist/src/project-create.mjs +121 -119
- package/dist/src/project-management.mjs +59 -69
- package/dist/src/project-remove.mjs +55 -0
- package/dist/src/project-update.mjs +56 -0
- package/dist/src/runtime-discovery.mjs +47 -47
- package/dist/src/self-relay/account-cleanup.mjs +30 -30
- package/dist/src/self-relay/account-connections.mjs +52 -52
- package/dist/src/self-relay/admin-cloud.mjs +101 -101
- package/dist/src/self-relay/admin-page.mjs +80 -80
- package/dist/src/self-relay/admin-server.mjs +74 -74
- package/dist/src/self-relay/admin-state.mjs +127 -127
- package/dist/src/self-relay/admin-style.mjs +14 -14
- package/dist/src/self-relay/android-releases.mjs +61 -61
- package/dist/src/self-relay/client.mjs +61 -61
- package/dist/src/self-relay/connector.mjs +439 -438
- package/dist/src/self-relay/debug-allowlist.mjs +33 -33
- package/dist/src/self-relay/demo.mjs +37 -37
- package/dist/src/self-relay/device-info.mjs +27 -0
- package/dist/src/self-relay/device-store.mjs +92 -84
- package/dist/src/self-relay/diagnostics.mjs +54 -54
- package/dist/src/self-relay/email-accounts.mjs +183 -183
- package/dist/src/self-relay/file-wire.mjs +13 -13
- package/dist/src/self-relay/lan-authorizations.mjs +71 -42
- package/dist/src/self-relay/lifecycle.mjs +264 -250
- package/dist/src/self-relay/long-pairing.mjs +40 -40
- package/dist/src/self-relay/mysql-state.mjs +118 -105
- package/dist/src/self-relay/p2p-probe.mjs +159 -159
- package/dist/src/self-relay/privacy-page.mjs +26 -26
- package/dist/src/self-relay/public-releases.mjs +34 -0
- package/dist/src/self-relay/rtc-file-bridge.mjs +41 -41
- package/dist/src/self-relay/server.mjs +1006 -932
- package/dist/src/self-relay/usage-daily.mjs +55 -55
- package/dist/src/self-relay/usage-meter.mjs +141 -141
- package/dist/src/self-relay/windows-route-helper.mjs +75 -75
- package/dist/src/self-relay/wire.mjs +36 -36
- package/dist/src/server.mjs +1861 -1778
- package/dist/src/service-control.mjs +82 -82
- package/dist/src/service-doctor.mjs +110 -110
- package/dist/src/service-health.mjs +33 -33
- package/dist/src/service-install.mjs +91 -91
- package/dist/src/service-lifecycle.mjs +138 -138
- package/dist/src/service-restart.mjs +81 -81
- package/dist/src/shared-app-server.mjs +615 -611
- package/dist/src/shared-catalog.mjs +132 -132
- package/dist/src/shared-recovery.mjs +146 -146
- package/dist/src/shared-thread-stream.mjs +164 -164
- package/dist/src/skill-install.mjs +51 -51
- package/dist/src/thread-title-generation.mjs +150 -150
- package/dist/src/turn-model-history.mjs +47 -47
- package/dist/src/usage-reset.mjs +17 -17
- package/dist/src/workstation-downloads.mjs +120 -120
- package/dist/tools/rtc-route-helper.ps1 +99 -99
- package/dist/tools/start-rtc-route-helper.ps1 +15 -15
- package/dist/web/vendor/markdown-it.LICENSE.txt +22 -22
- package/dist/web/vendor/markdown-it.umd.min.js +13 -13
- package/package.json +7 -6
- package/tools/postinstall.mjs +127 -127
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { validId } from './device-store.mjs';
|
|
4
|
-
import { digest, matches, secret } from './wire.mjs';
|
|
5
|
-
|
|
6
|
-
// Operator-owned file, separate from device state. Only hashes are retained here.
|
|
7
|
-
export function longPairing(file) {
|
|
8
|
-
function read() {
|
|
9
|
-
if (!file || !fs.existsSync(file)) return [];
|
|
10
|
-
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
11
|
-
if (data.version !== 1 || !Array.isArray(data.invitations) || data.invitations.some(item =>
|
|
12
|
-
!validId(item.id) || !validId(item.room) || !/^[a-f0-9]{64}$/.test(item.hash) ||
|
|
13
|
-
!Number.isSafeInteger(item.expires))) throw new Error('Invalid long pairing invitations');
|
|
14
|
-
return data.invitations;
|
|
15
|
-
}
|
|
16
|
-
function write(invitations) {
|
|
17
|
-
if (!file) throw new Error('Long pairing file is not configured');
|
|
18
|
-
const temporary = `${file}.${randomUUID()}.tmp`;
|
|
19
|
-
try {
|
|
20
|
-
fs.writeFileSync(temporary, JSON.stringify({ version: 1, invitations }), { mode: 0o600, flag: 'wx' });
|
|
21
|
-
fs.renameSync(temporary, file);
|
|
22
|
-
} finally { fs.rmSync(temporary, { force: true }); }
|
|
23
|
-
}
|
|
24
|
-
return {
|
|
25
|
-
list: () => read().map(({ hash, ...item }) => item),
|
|
26
|
-
find: code => read().find(item => Date.now() < item.expires && matches(code, item.hash)),
|
|
27
|
-
create(room, expires) {
|
|
28
|
-
if (!validId(room) || !Number.isSafeInteger(expires) || expires <= Date.now()) throw new Error('Invalid invitation room or expiry');
|
|
29
|
-
const invitations = read(), code = secret();
|
|
30
|
-
const item = { id: randomUUID(), room, expires, hash: digest(code) };
|
|
31
|
-
write([...invitations, item]);
|
|
32
|
-
return { id: item.id, room, expires, code };
|
|
33
|
-
},
|
|
34
|
-
revoke(id) {
|
|
35
|
-
const invitations = read();
|
|
36
|
-
if (!invitations.some(item => item.id === id)) throw new Error('Invitation not found');
|
|
37
|
-
write(invitations.filter(item => item.id !== id));
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
}
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { validId } from './device-store.mjs';
|
|
4
|
+
import { digest, matches, secret } from './wire.mjs';
|
|
5
|
+
|
|
6
|
+
// Operator-owned file, separate from device state. Only hashes are retained here.
|
|
7
|
+
export function longPairing(file) {
|
|
8
|
+
function read() {
|
|
9
|
+
if (!file || !fs.existsSync(file)) return [];
|
|
10
|
+
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
11
|
+
if (data.version !== 1 || !Array.isArray(data.invitations) || data.invitations.some(item =>
|
|
12
|
+
!validId(item.id) || !validId(item.room) || !/^[a-f0-9]{64}$/.test(item.hash) ||
|
|
13
|
+
!Number.isSafeInteger(item.expires))) throw new Error('Invalid long pairing invitations');
|
|
14
|
+
return data.invitations;
|
|
15
|
+
}
|
|
16
|
+
function write(invitations) {
|
|
17
|
+
if (!file) throw new Error('Long pairing file is not configured');
|
|
18
|
+
const temporary = `${file}.${randomUUID()}.tmp`;
|
|
19
|
+
try {
|
|
20
|
+
fs.writeFileSync(temporary, JSON.stringify({ version: 1, invitations }), { mode: 0o600, flag: 'wx' });
|
|
21
|
+
fs.renameSync(temporary, file);
|
|
22
|
+
} finally { fs.rmSync(temporary, { force: true }); }
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
list: () => read().map(({ hash, ...item }) => item),
|
|
26
|
+
find: code => read().find(item => Date.now() < item.expires && matches(code, item.hash)),
|
|
27
|
+
create(room, expires) {
|
|
28
|
+
if (!validId(room) || !Number.isSafeInteger(expires) || expires <= Date.now()) throw new Error('Invalid invitation room or expiry');
|
|
29
|
+
const invitations = read(), code = secret();
|
|
30
|
+
const item = { id: randomUUID(), room, expires, hash: digest(code) };
|
|
31
|
+
write([...invitations, item]);
|
|
32
|
+
return { id: item.id, room, expires, code };
|
|
33
|
+
},
|
|
34
|
+
revoke(id) {
|
|
35
|
+
const invitations = read();
|
|
36
|
+
if (!invitations.some(item => item.id === id)) throw new Error('Invitation not found');
|
|
37
|
+
write(invitations.filter(item => item.id !== id));
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -1,105 +1,118 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import mysql from 'mysql2/promise';
|
|
5
|
-
import { deviceStore } from './device-store.mjs';
|
|
6
|
-
import { relayAdminState } from './admin-state.mjs';
|
|
7
|
-
import { relaySchemaVersions } from '../../database/versions.mjs';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const [
|
|
30
|
-
const [
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
record
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
[
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import mysql from 'mysql2/promise';
|
|
5
|
+
import { deviceStore } from './device-store.mjs';
|
|
6
|
+
import { relayAdminState } from './admin-state.mjs';
|
|
7
|
+
import { relaySchemaVersions } from '../../database/versions.mjs';
|
|
8
|
+
import { connectionInfo } from './device-info.mjs';
|
|
9
|
+
|
|
10
|
+
const sqlTime = value => value.replace('T', ' ').replace(/Z$/, '');
|
|
11
|
+
const isoTime = value => value.replace(' ', 'T') + 'Z';
|
|
12
|
+
|
|
13
|
+
export async function openRelayMysql(configFile, initialMaxRelaySessions) {
|
|
14
|
+
const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
|
|
15
|
+
if (!config.database || !config.user || !config.password || !(config.socketPath || config.host)) {
|
|
16
|
+
throw new Error('Incomplete relay MySQL configuration');
|
|
17
|
+
}
|
|
18
|
+
const pool = mysql.createPool({ ...config, waitForConnections: true, connectionLimit: 4,
|
|
19
|
+
namedPlaceholders: false, dateStrings: true, timezone: 'Z', multipleStatements: false });
|
|
20
|
+
try {
|
|
21
|
+
const [versions] = await pool.query('SELECT version, sha256 FROM schema_migrations');
|
|
22
|
+
const knownVersions = relaySchemaVersions;
|
|
23
|
+
if (!versions.some(row => row.version === knownVersions[0]) || versions.length > knownVersions.length) throw new Error('Unexpected relay database schema');
|
|
24
|
+
for (const row of versions) {
|
|
25
|
+
if (!knownVersions.includes(row.version)) throw new Error('Unexpected relay database schema');
|
|
26
|
+
const source = fs.readFileSync(fileURLToPath(new URL(`../../database/${row.version}.sql`, import.meta.url)));
|
|
27
|
+
if (row.sha256 !== createHash('sha256').update(source).digest('hex')) throw new Error(`Unexpected relay database schema: ${row.version}`);
|
|
28
|
+
}
|
|
29
|
+
const [computers] = await pool.query('SELECT id, host_hash, status, revision, created_at, updated_at FROM computers');
|
|
30
|
+
const [clients] = await pool.query('SELECT computer_id, client_id, credential_hash, created_at, updated_at, last_connected_at, platform, device_name FROM mobile_authorizations ORDER BY created_at');
|
|
31
|
+
const [official] = await pool.query('SELECT computer_id, environment_id, name, hostname, updated_at FROM official_computers');
|
|
32
|
+
const records = new Map(computers.map(row => [row.id, { id: row.id, hostHash: row.host_hash, status: row.status,
|
|
33
|
+
revision: Number(row.revision), createdAt: isoTime(row.created_at), updatedAt: isoTime(row.updated_at), clients: [] }]));
|
|
34
|
+
for (const row of clients) {
|
|
35
|
+
const record = records.get(row.computer_id);
|
|
36
|
+
if (!record) throw new Error('Orphan mobile authorization');
|
|
37
|
+
record.clients.push({ id: row.client_id, hash: row.credential_hash,
|
|
38
|
+
createdAt: isoTime(row.created_at), updatedAt: isoTime(row.updated_at),
|
|
39
|
+
...(row.last_connected_at ? { lastConnectedAt: isoTime(row.last_connected_at) } : {}),
|
|
40
|
+
...(row.platform ? { platform: row.platform } : {}), ...(row.device_name ? { deviceName: row.device_name } : {}) });
|
|
41
|
+
}
|
|
42
|
+
for (const row of official) {
|
|
43
|
+
const record = records.get(row.computer_id);
|
|
44
|
+
if (!record) throw new Error('Orphan official computer');
|
|
45
|
+
record.officialComputer = { environmentId: row.environment_id, name: row.name,
|
|
46
|
+
hostname: row.hostname, updatedAt: isoTime(row.updated_at) };
|
|
47
|
+
}
|
|
48
|
+
const validator = deviceStore();
|
|
49
|
+
for (const record of records.values()) validator.save(record);
|
|
50
|
+
|
|
51
|
+
const [settings] = await pool.query('SELECT max_relay_sessions FROM relay_settings WHERE id = 1');
|
|
52
|
+
if (settings.length !== 1) throw new Error('Missing relay settings');
|
|
53
|
+
const [rows] = await pool.query('SELECT id, title, message, status, created_at, updated_at, published_at FROM announcements ORDER BY created_at DESC');
|
|
54
|
+
const announcements = rows.map(row => ({ id: row.id, title: row.title, message: row.message, status: row.status,
|
|
55
|
+
createdAt: isoTime(row.created_at), updatedAt: isoTime(row.updated_at),
|
|
56
|
+
publishedAt: row.published_at ? isoTime(row.published_at) : null }));
|
|
57
|
+
const active = announcements.find(item => item.status === 'active');
|
|
58
|
+
const initial = { version: 3, maxRelaySessions: settings[0].max_relay_sessions, announcements,
|
|
59
|
+
announcement: active ? { id: active.id, title: active.title, message: active.message, publishedAt: active.publishedAt } : null };
|
|
60
|
+
const admin = relayAdminState(undefined, initialMaxRelaySessions, { initial, persist: async next => {
|
|
61
|
+
const connection = await pool.getConnection();
|
|
62
|
+
try {
|
|
63
|
+
await connection.beginTransaction();
|
|
64
|
+
await connection.execute('UPDATE relay_settings SET max_relay_sessions = ?, updated_at = UTC_TIMESTAMP(3) WHERE id = 1', [next.maxRelaySessions]);
|
|
65
|
+
await connection.query('DELETE FROM announcements');
|
|
66
|
+
for (const item of next.announcements) await connection.execute(
|
|
67
|
+
'INSERT INTO announcements (id,title,message,status,created_at,updated_at,published_at) VALUES (?,?,?,?,?,?,?)',
|
|
68
|
+
[item.id, item.title, item.message, item.status, sqlTime(item.createdAt), sqlTime(item.updatedAt),
|
|
69
|
+
item.publishedAt ? sqlTime(item.publishedAt) : null]);
|
|
70
|
+
await connection.commit();
|
|
71
|
+
} catch (error) { await connection.rollback(); throw error; }
|
|
72
|
+
finally { connection.release(); }
|
|
73
|
+
} });
|
|
74
|
+
return {
|
|
75
|
+
pool,
|
|
76
|
+
schemaVersions: versions.map(row => row.version),
|
|
77
|
+
devices: {
|
|
78
|
+
entries: () => [...records.values()],
|
|
79
|
+
async recordConnection(id, clientId, at, info) {
|
|
80
|
+
const record = records.get(id), client = record?.clients.find(item => item.id === clientId);
|
|
81
|
+
if (!client) return;
|
|
82
|
+
const updated = connectionInfo(client, at, info);
|
|
83
|
+
await pool.execute('UPDATE mobile_authorizations SET last_connected_at=?,platform=?,device_name=? WHERE computer_id=? AND client_id=?',
|
|
84
|
+
[sqlTime(updated.lastConnectedAt), updated.platform || null, updated.deviceName || null, id, clientId]);
|
|
85
|
+
records.set(id, { ...record, clients: record.clients.map(item => item.id === clientId ? updated : item) });
|
|
86
|
+
return updated;
|
|
87
|
+
},
|
|
88
|
+
async save(record) {
|
|
89
|
+
validator.save(record);
|
|
90
|
+
const connection = await pool.getConnection();
|
|
91
|
+
try {
|
|
92
|
+
await connection.beginTransaction();
|
|
93
|
+
await connection.execute(`INSERT INTO computers (id,host_hash,status,revision,created_at,updated_at)
|
|
94
|
+
VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE host_hash=VALUES(host_hash),status=VALUES(status),
|
|
95
|
+
revision=VALUES(revision),updated_at=VALUES(updated_at)`,
|
|
96
|
+
[record.id, record.hostHash, record.status, record.revision, sqlTime(record.createdAt), sqlTime(record.updatedAt)]);
|
|
97
|
+
await connection.execute('DELETE FROM mobile_authorizations WHERE computer_id = ?', [record.id]);
|
|
98
|
+
for (const client of record.clients) await connection.execute(
|
|
99
|
+
'INSERT INTO mobile_authorizations (computer_id,client_id,credential_hash,created_at,updated_at,last_connected_at,platform,device_name) VALUES (?,?,?,?,?,?,?,?)',
|
|
100
|
+
[record.id, client.id, client.hash, sqlTime(client.createdAt), sqlTime(client.updatedAt),
|
|
101
|
+
client.lastConnectedAt ? sqlTime(client.lastConnectedAt) : null, client.platform || null, client.deviceName || null]);
|
|
102
|
+
if (record.officialComputer) {
|
|
103
|
+
const item = record.officialComputer;
|
|
104
|
+
await connection.execute(`INSERT INTO official_computers (computer_id,environment_id,name,hostname,updated_at)
|
|
105
|
+
VALUES (?,?,?,?,?) ON DUPLICATE KEY UPDATE environment_id=VALUES(environment_id),name=VALUES(name),
|
|
106
|
+
hostname=VALUES(hostname),updated_at=VALUES(updated_at)`,
|
|
107
|
+
[record.id, item.environmentId, item.name, item.hostname, sqlTime(item.updatedAt)]);
|
|
108
|
+
}
|
|
109
|
+
await connection.commit(); records.set(record.id, record);
|
|
110
|
+
} catch (error) { await connection.rollback(); throw error; }
|
|
111
|
+
finally { connection.release(); }
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
admin,
|
|
115
|
+
close: () => pool.end(),
|
|
116
|
+
};
|
|
117
|
+
} catch (error) { await pool.end(); throw error; }
|
|
118
|
+
}
|