@nubisco/openbridge 0.32.4 → 0.34.0
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/__tests__/avatar-claim.test.d.ts +2 -0
- package/dist/__tests__/avatar-claim.test.d.ts.map +1 -0
- package/dist/__tests__/avatar-claim.test.js +47 -0
- package/dist/__tests__/avatar-claim.test.js.map +1 -0
- package/dist/__tests__/updater.test.d.ts +2 -0
- package/dist/__tests__/updater.test.d.ts.map +1 -0
- package/dist/__tests__/updater.test.js +67 -0
- package/dist/__tests__/updater.test.js.map +1 -0
- package/dist/auth.d.ts +16 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +41 -2
- package/dist/auth.js.map +1 -1
- package/dist/platform-client.d.ts +5 -0
- package/dist/platform-client.d.ts.map +1 -1
- package/dist/platform-client.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +52 -8
- package/dist/server.js.map +1 -1
- package/dist/updater.d.ts +69 -5
- package/dist/updater.d.ts.map +1 -1
- package/dist/updater.js +207 -5
- package/dist/updater.js.map +1 -1
- package/package.json +5 -5
- package/ui-dist/assets/{index-CdTRF5cq.css → index-BAbIn_Li.css} +2 -2
- package/ui-dist/assets/index-CgvH8KsO.js +106 -0
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/index-CHDfUhDg.js +0 -106
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"avatar-claim.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/avatar-claim.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { acceptPictureClaim } from '../auth.js';
|
|
3
|
+
const ISSUER = 'https://platform.nubisco.io';
|
|
4
|
+
const GOOD = `${ISSUER}/api/avatars/3f9c0a1e5b7d4c2a8e6f1b0d9c3a7e52`;
|
|
5
|
+
describe('acceptPictureClaim', () => {
|
|
6
|
+
it('keeps an avatar served by the issuer', () => {
|
|
7
|
+
expect(acceptPictureClaim(ISSUER, GOOD)).toBe(GOOD);
|
|
8
|
+
});
|
|
9
|
+
it('keeps it when the issuer was configured with a trailing slash', () => {
|
|
10
|
+
expect(acceptPictureClaim(`${ISSUER}/`, GOOD)).toBe(GOOD);
|
|
11
|
+
});
|
|
12
|
+
it('refuses a URL on another origin', () => {
|
|
13
|
+
// The value becomes an <img src> on every page, so a foreign host would
|
|
14
|
+
// have this app announce the viewer to whoever controls it.
|
|
15
|
+
expect(acceptPictureClaim(ISSUER, 'https://evil.example/api/avatars/abc')).toBeUndefined();
|
|
16
|
+
expect(acceptPictureClaim(ISSUER, 'http://platform.nubisco.io/api/avatars/abc')).toBeUndefined();
|
|
17
|
+
expect(acceptPictureClaim(ISSUER, 'https://platform.nubisco.io.evil.example/api/avatars/abc')).toBeUndefined();
|
|
18
|
+
});
|
|
19
|
+
it('refuses a path outside the avatar service', () => {
|
|
20
|
+
expect(acceptPictureClaim(ISSUER, `${ISSUER}/api/tokens/abc`)).toBeUndefined();
|
|
21
|
+
expect(acceptPictureClaim(ISSUER, `${ISSUER}/`)).toBeUndefined();
|
|
22
|
+
expect(acceptPictureClaim(ISSUER, `${ISSUER}/api/avatars`)).toBeUndefined();
|
|
23
|
+
});
|
|
24
|
+
it('refuses credentials in the URL', () => {
|
|
25
|
+
// No business in an image source, and a sign the value was not built by
|
|
26
|
+
// the platform.
|
|
27
|
+
expect(acceptPictureClaim(ISSUER, 'https://user:pw@platform.nubisco.io/api/avatars/abc')).toBeUndefined();
|
|
28
|
+
expect(acceptPictureClaim(ISSUER, 'https://user@platform.nubisco.io/api/avatars/abc')).toBeUndefined();
|
|
29
|
+
});
|
|
30
|
+
it('treats an absent or empty claim as no avatar', () => {
|
|
31
|
+
// The platform omits the claim entirely for someone with no avatar, so
|
|
32
|
+
// absent means "no avatar" and must clear anything stored, never be read
|
|
33
|
+
// as "unchanged".
|
|
34
|
+
expect(acceptPictureClaim(ISSUER, undefined)).toBeUndefined();
|
|
35
|
+
expect(acceptPictureClaim(ISSUER, '')).toBeUndefined();
|
|
36
|
+
expect(acceptPictureClaim(ISSUER, null)).toBeUndefined();
|
|
37
|
+
expect(acceptPictureClaim(ISSUER, 42)).toBeUndefined();
|
|
38
|
+
});
|
|
39
|
+
it('refuses anything when no issuer is configured', () => {
|
|
40
|
+
expect(acceptPictureClaim(null, GOOD)).toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
it('refuses a value that is not a URL at all', () => {
|
|
43
|
+
expect(acceptPictureClaim(ISSUER, '/api/avatars/abc')).toBeUndefined();
|
|
44
|
+
expect(acceptPictureClaim(ISSUER, 'not a url')).toBeUndefined();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
//# sourceMappingURL=avatar-claim.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"avatar-claim.test.js","sourceRoot":"","sources":["../../src/__tests__/avatar-claim.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updater.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/updater.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { detectInstall, detectRestartStrategy, globalPrefix, PACKAGE_ROOT } from '../updater.js';
|
|
3
|
+
const saved = { ...process.env };
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
delete process.env.INVOCATION_ID;
|
|
6
|
+
delete process.env.OPENBRIDGE_INSTALL;
|
|
7
|
+
delete process.env.OPENBRIDGE_VERSION_SPEC;
|
|
8
|
+
});
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
process.env = { ...saved };
|
|
11
|
+
});
|
|
12
|
+
describe('detectRestartStrategy', () => {
|
|
13
|
+
it('trusts systemd, which stamps every service it starts', () => {
|
|
14
|
+
process.env.INVOCATION_ID = 'a8f3c2';
|
|
15
|
+
expect(detectRestartStrategy()).toBe('supervisor');
|
|
16
|
+
});
|
|
17
|
+
it('trusts the container restart policy', () => {
|
|
18
|
+
process.env.OPENBRIDGE_INSTALL = 'docker';
|
|
19
|
+
expect(detectRestartStrategy()).toBe('supervisor');
|
|
20
|
+
});
|
|
21
|
+
it('recognises a supervisor name the kernel truncated', () => {
|
|
22
|
+
// /proc/<pid>/comm is capped at fifteen characters and OpenRC's
|
|
23
|
+
// supervise-daemon is sixteen, so it arrives as "supervise-daemo". An exact
|
|
24
|
+
// comparison misses it, the daemon concludes nothing will restart it, and
|
|
25
|
+
// it leaves a process behind to start a replacement while the supervisor
|
|
26
|
+
// starts one too. The second dies on the port the first holds.
|
|
27
|
+
const truncated = 'supervise-daemo';
|
|
28
|
+
const supervisors = ['supervise-daemon', 'systemd', 'runsv', 's6-supervise', 'runit', 'launchd'];
|
|
29
|
+
expect(supervisors.some((n) => n === truncated || n.startsWith(truncated))).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
it('assumes nothing will restart it when it recognises no supervisor', () => {
|
|
32
|
+
// The optimistic answer leaves a stopped daemon and someone locked out, so
|
|
33
|
+
// an unrecognised environment gets the strategy that starts its own
|
|
34
|
+
// replacement rather than the benefit of the doubt.
|
|
35
|
+
expect(detectRestartStrategy()).toBe('spawn');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe('detectInstall', () => {
|
|
39
|
+
it('refuses to replace a source checkout, and says why', () => {
|
|
40
|
+
// Tests run from the monorepo, so this is the source case: somebody's
|
|
41
|
+
// working tree, which an update would discard.
|
|
42
|
+
const info = detectInstall();
|
|
43
|
+
expect(info.method).toBe('source');
|
|
44
|
+
expect(info.canSelfUpdate).toBe(false);
|
|
45
|
+
expect(info.blockedBy).toMatch(/source checkout/);
|
|
46
|
+
});
|
|
47
|
+
it('withholds self-update from a pinned deployment, and says why', () => {
|
|
48
|
+
process.env.OPENBRIDGE_INSTALL = 'docker';
|
|
49
|
+
process.env.OPENBRIDGE_VERSION_SPEC = '0.31.1';
|
|
50
|
+
const info = detectInstall();
|
|
51
|
+
expect(info.canSelfUpdate).toBe(false);
|
|
52
|
+
expect(info.blockedBy).toMatch(/pinned to 0\.31\.1/);
|
|
53
|
+
});
|
|
54
|
+
it('allows it for an unpinned container', () => {
|
|
55
|
+
process.env.OPENBRIDGE_INSTALL = 'docker';
|
|
56
|
+
expect(detectInstall().canSelfUpdate).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('globalPrefix', () => {
|
|
60
|
+
it('is null when not installed under a node_modules', () => {
|
|
61
|
+
// A source checkout has no prefix to install into, which is exactly why
|
|
62
|
+
// detectInstall refuses it.
|
|
63
|
+
expect(PACKAGE_ROOT).not.toContain('/node_modules/');
|
|
64
|
+
expect(globalPrefix()).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=updater.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updater.test.js","sourceRoot":"","sources":["../../src/__tests__/updater.test.ts"],"names":[],"mappings":"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"}
|
package/dist/auth.d.ts
CHANGED
|
@@ -18,6 +18,22 @@ export interface TAuthConfig {
|
|
|
18
18
|
appId: string | null;
|
|
19
19
|
secret: Buffer;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Keep a `picture` claim only when it is an avatar served by our own issuer.
|
|
23
|
+
*
|
|
24
|
+
* The value becomes an `<img src>` on every page, so a token that carried a
|
|
25
|
+
* URL anywhere else would have this app fetch it, announcing the viewer to
|
|
26
|
+
* whoever controls it. Restricting it to the issuer's origin under
|
|
27
|
+
* `/api/avatars/` means the only thing it can point at is the avatar service
|
|
28
|
+
* the token came from. Credentials in the URL are refused outright: they have
|
|
29
|
+
* no business in an image source and are a sign the value was built by
|
|
30
|
+
* something other than the platform.
|
|
31
|
+
*
|
|
32
|
+
* An absent or unusable claim returns undefined, which clears any stored value.
|
|
33
|
+
* The platform omits `picture` entirely for someone with no avatar, so absent
|
|
34
|
+
* means "no avatar" rather than "unchanged".
|
|
35
|
+
*/
|
|
36
|
+
export declare function acceptPictureClaim(issuer: string | null, picture: unknown): string | undefined;
|
|
21
37
|
export declare function loadAuthConfig(): TAuthConfig;
|
|
22
38
|
export declare function registerAuthRoutes(app: FastifyInstance, config: TAuthConfig): Promise<void>;
|
|
23
39
|
//# sourceMappingURL=auth.d.ts.map
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAA;AAS9D,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;CACf;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAA;AAS9D,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;CACf;AAUD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAc9F;AA2BD,wBAAgB,cAAc,IAAI,WAAW,CAS5C;AAkCD,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,eAAe,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAyFjG"}
|
package/dist/auth.js
CHANGED
|
@@ -17,6 +17,41 @@ import os from 'os';
|
|
|
17
17
|
import { PlatformClient } from './platform-client.js';
|
|
18
18
|
const COOKIE_NAME = 'openbridge_session';
|
|
19
19
|
const SESSION_TTL_SEC = 7 * 24 * 60 * 60; // 7 days
|
|
20
|
+
/**
|
|
21
|
+
* Keep a `picture` claim only when it is an avatar served by our own issuer.
|
|
22
|
+
*
|
|
23
|
+
* The value becomes an `<img src>` on every page, so a token that carried a
|
|
24
|
+
* URL anywhere else would have this app fetch it, announcing the viewer to
|
|
25
|
+
* whoever controls it. Restricting it to the issuer's origin under
|
|
26
|
+
* `/api/avatars/` means the only thing it can point at is the avatar service
|
|
27
|
+
* the token came from. Credentials in the URL are refused outright: they have
|
|
28
|
+
* no business in an image source and are a sign the value was built by
|
|
29
|
+
* something other than the platform.
|
|
30
|
+
*
|
|
31
|
+
* An absent or unusable claim returns undefined, which clears any stored value.
|
|
32
|
+
* The platform omits `picture` entirely for someone with no avatar, so absent
|
|
33
|
+
* means "no avatar" rather than "unchanged".
|
|
34
|
+
*/
|
|
35
|
+
export function acceptPictureClaim(issuer, picture) {
|
|
36
|
+
if (!issuer || typeof picture !== 'string' || picture === '')
|
|
37
|
+
return undefined;
|
|
38
|
+
let url;
|
|
39
|
+
let base;
|
|
40
|
+
try {
|
|
41
|
+
url = new URL(picture);
|
|
42
|
+
base = new URL(issuer);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
if (url.origin !== base.origin)
|
|
48
|
+
return undefined;
|
|
49
|
+
if (!url.pathname.startsWith('/api/avatars/'))
|
|
50
|
+
return undefined;
|
|
51
|
+
if (url.username !== '' || url.password !== '')
|
|
52
|
+
return undefined;
|
|
53
|
+
return url.toString();
|
|
54
|
+
}
|
|
20
55
|
function getConfigDir() {
|
|
21
56
|
// The Docker image mounts /root/.openbridge as a persistent volume.
|
|
22
57
|
// Fall back to the user's home dir for `pnpm dev` runs.
|
|
@@ -122,9 +157,13 @@ export async function registerAuthRoutes(app, config) {
|
|
|
122
157
|
if (expectedSub && claims.sub !== expectedSub) {
|
|
123
158
|
return reply.code(409).send({ error: 'subject_mismatch' });
|
|
124
159
|
}
|
|
160
|
+
// Re-read at every sign-in, so a changed avatar is picked up and a removed
|
|
161
|
+
// one disappears. This session is the only user record OpenBridge keeps.
|
|
162
|
+
const picture = acceptPictureClaim(config.issuer, claims.picture);
|
|
125
163
|
const payload = {
|
|
126
164
|
userId: claims.sub,
|
|
127
165
|
email: claims.email,
|
|
166
|
+
...(picture ? { picture } : {}),
|
|
128
167
|
exp: Math.floor(Date.now() / 1000) + SESSION_TTL_SEC,
|
|
129
168
|
};
|
|
130
169
|
const cookieValue = signSession(config.secret, payload);
|
|
@@ -132,7 +171,7 @@ export async function registerAuthRoutes(app, config) {
|
|
|
132
171
|
if (isProductionLike())
|
|
133
172
|
parts.push('Secure');
|
|
134
173
|
reply.header('Set-Cookie', parts.join('; '));
|
|
135
|
-
return { ok: true, user: { id: claims.sub, email: claims.email } };
|
|
174
|
+
return { ok: true, user: { id: claims.sub, email: claims.email, picture } };
|
|
136
175
|
});
|
|
137
176
|
app.get('/auth/me', async (req, reply) => {
|
|
138
177
|
const cookie = getCookie(req, COOKIE_NAME);
|
|
@@ -141,7 +180,7 @@ export async function registerAuthRoutes(app, config) {
|
|
|
141
180
|
const session = verifySession(config.secret, cookie);
|
|
142
181
|
if (!session)
|
|
143
182
|
return reply.code(401).send({ error: 'unauthenticated' });
|
|
144
|
-
return { user: { id: session.userId, email: session.email } };
|
|
183
|
+
return { user: { id: session.userId, email: session.email, picture: session.picture } };
|
|
145
184
|
});
|
|
146
185
|
app.post('/auth/logout', async (_req, reply) => {
|
|
147
186
|
const parts = [`${COOKIE_NAME}=`, 'HttpOnly', 'SameSite=Lax', 'Path=/', 'Max-Age=0'];
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAChD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAA;AAClF,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,EAAE,cAAc,EAAmB,MAAM,sBAAsB,CAAA;AAEtE,MAAM,WAAW,GAAG,oBAAoB,CAAA;AACxC,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA,CAAC,SAAS;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,6 +16,11 @@ export interface TPlatformClientOptions {
|
|
|
16
16
|
export interface TJwtClaims {
|
|
17
17
|
sub: string;
|
|
18
18
|
email: string;
|
|
19
|
+
/**
|
|
20
|
+
* The platform's avatar URL. Absent, never null or empty, when the person
|
|
21
|
+
* has no avatar, so an absent claim means "no avatar" rather than "unchanged".
|
|
22
|
+
*/
|
|
23
|
+
picture?: string;
|
|
19
24
|
name?: string;
|
|
20
25
|
role: string;
|
|
21
26
|
/** App id (uuid) the token was issued to. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-client.d.ts","sourceRoot":"","sources":["../src/platform-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,sBAAsB;IACrC,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAA;IACd,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oFAAoF;IACpF,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;CACZ;AA6BD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,SAAS,CAAsD;IACvE,OAAO,CAAC,YAAY,CAAoC;gBAE5C,OAAO,EAAE,sBAAsB;YAK7B,SAAS;YAaT,YAAY;YAgBZ,SAAS;IAQjB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CAsBjD"}
|
|
1
|
+
{"version":3,"file":"platform-client.d.ts","sourceRoot":"","sources":["../src/platform-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,sBAAsB;IACrC,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAA;IACd,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oFAAoF;IACpF,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;CACZ;AA6BD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,SAAS,CAAsD;IACvE,OAAO,CAAC,YAAY,CAAoC;gBAE5C,OAAO,EAAE,sBAAsB;YAK7B,SAAS;YAaT,YAAY;YAgBZ,SAAS;IAQjB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CAsBjD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-client.js","sourceRoot":"","sources":["../src/platform-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"platform-client.js","sourceRoot":"","sources":["../src/platform-client.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAA;AAa7B,OAAO,KAAK,EAAE,cAAc,EAA4B,MAAM,0BAA0B,CAAA;AAGxF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAA;AASjF,OAAO,EAA4B,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAC3E,OAAO,EAAE,kBAAkB,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAA;AAa7B,OAAO,KAAK,EAAE,cAAc,EAA4B,MAAM,0BAA0B,CAAA;AAGxF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAA;AASjF,OAAO,EAA4B,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAC3E,OAAO,EAAE,kBAAkB,EAAE,CAAA;AAyC7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAChE,OAAO,EAGL,KAAK,mBAAmB,EAEzB,MAAM,2BAA2B,CAAA;AAElC,MAAM,WAAW,OAAO;IACtB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAWD,wBAAsB,YAAY,CAChC,QAAQ,EAAE,cAAc,EACxB,MAAM,GAAE,aAAa,GAAG,IAAW,EACnC,OAAO,GAAE,OAAO,GAAG,IAAW,EAC9B,kBAAkB,GAAE,MAAM,EAAO,EACjC,mBAAmB,GAAE,GAAG,CAAC,MAAM,CAAa,EAC5C,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAa,EAC3E,kBAAkB,GAAE,GAAG,CAAC,MAAM,CAAa,EAC3C,iBAAiB,GAAE,iBAAiB,GAAG,IAAW,EAClD,mBAAmB,GAAE,mBAAmB,GAAG,IAAW;AACtD;;;;GAIG;AACH,sBAAsB,GAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,CAAC,GAAG,IAAW;AAC5E,4EAA4E;AAC5E,mBAAmB,GAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,IAAW,sSA0tDzE"}
|
package/dist/server.js
CHANGED
|
@@ -17,7 +17,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
17
17
|
// Version: resolved in version.ts so the CLI can read it without loading this module.
|
|
18
18
|
import { APP_VOLUME, VERSION_FILE, OPENBRIDGE_VERSION } from './version.js';
|
|
19
19
|
export { OPENBRIDGE_VERSION };
|
|
20
|
-
import { CURRENT_DIR, PACKAGE_NAME, PREVIOUS_DIR, STAGING_DIR, detectInstall, fetchLatestVersion, fetchReleaseNotes, installedVersionAt, npmInstall, releaseUrl, } from './updater.js';
|
|
20
|
+
import { CURRENT_DIR, PACKAGE_NAME, PREVIOUS_DIR, STAGING_DIR, detectInstall, fetchLatestVersion, fetchReleaseNotes, globalPrefix, installedVersionAt, npmInstall, npmInstallGlobal, releaseUrl, restartDaemon, versionOnDisk, } from './updater.js';
|
|
21
21
|
let updateProgress = { stage: 'idle' };
|
|
22
22
|
const updateListeners = new Set();
|
|
23
23
|
// Resolve UI dist: env override → npm layout (../ui-dist) → monorepo dev layout
|
|
@@ -97,6 +97,8 @@ nativeAccessoryUuid = null) {
|
|
|
97
97
|
// What to run when we cannot do it ourselves, matched to how this
|
|
98
98
|
// particular deployment was installed.
|
|
99
99
|
updateCommand: install.command,
|
|
100
|
+
/** Why self-update is unavailable, so the UI can say more than "not available". */
|
|
101
|
+
updateBlockedBy: install.blockedBy ?? null,
|
|
100
102
|
installMethod: install.method,
|
|
101
103
|
pinnedTo: install.pinnedTo,
|
|
102
104
|
releaseUrl: latest ? releaseUrl(latest) : null,
|
|
@@ -133,21 +135,63 @@ nativeAccessoryUuid = null) {
|
|
|
133
135
|
throw {
|
|
134
136
|
statusCode: 503,
|
|
135
137
|
message: install.pinnedTo !== null
|
|
136
|
-
? `This deployment is pinned to ${install.pinnedTo}. Change the pin
|
|
137
|
-
: `This ${install.method} install cannot update itself. Run: ${install.command}`,
|
|
138
|
+
? `This deployment is pinned to ${install.pinnedTo}. Change the pin to move version.`
|
|
139
|
+
: `This ${install.method} install cannot update itself (${install.blockedBy}). Run: ${install.command}`,
|
|
138
140
|
};
|
|
139
141
|
}
|
|
142
|
+
function broadcast(msg) {
|
|
143
|
+
updateProgress = msg;
|
|
144
|
+
for (const listener of updateListeners)
|
|
145
|
+
listener(msg);
|
|
146
|
+
}
|
|
147
|
+
// An npm install replaces itself in place. There is no staging tree to swap,
|
|
148
|
+
// because the package directory is the only copy and overwriting it while
|
|
149
|
+
// the daemon runs is safe: what it is executing is already in memory.
|
|
150
|
+
if (install.method === 'npm') {
|
|
151
|
+
const prefix = globalPrefix();
|
|
152
|
+
if (!prefix)
|
|
153
|
+
throw { statusCode: 503, message: 'Could not determine the npm prefix to install into' };
|
|
154
|
+
(async () => {
|
|
155
|
+
try {
|
|
156
|
+
broadcast({ stage: 'downloading', progress: 0, message: 'Checking npm for the latest version...' });
|
|
157
|
+
const version = await fetchLatestVersion();
|
|
158
|
+
if (!version)
|
|
159
|
+
throw new Error('Could not resolve the latest version from the npm registry');
|
|
160
|
+
if (version === OPENBRIDGE_VERSION) {
|
|
161
|
+
broadcast({ stage: 'idle', message: `Already on v${version}` });
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
log.info(`Self-update: installing ${PACKAGE_NAME}@${version} into ${prefix}...`);
|
|
165
|
+
broadcast({ stage: 'downloading', message: `Installing v${version} from npm...`, version });
|
|
166
|
+
await npmInstallGlobal(prefix, version, (line) => {
|
|
167
|
+
if (line)
|
|
168
|
+
log.debug(`npm: ${line}`);
|
|
169
|
+
});
|
|
170
|
+
// Only believe it once the new version is on disk. A failed install
|
|
171
|
+
// must leave the daemon running the old code, not exit into nothing.
|
|
172
|
+
const onDisk = versionOnDisk();
|
|
173
|
+
if (onDisk !== version) {
|
|
174
|
+
throw new Error(`Install finished but the package reports ${onDisk ?? 'no version'}, expected ${version}`);
|
|
175
|
+
}
|
|
176
|
+
log.info(`Update to v${version} installed: restarting to pick it up...`);
|
|
177
|
+
broadcast({ stage: 'restarting', message: `Restarting with v${version}...`, version });
|
|
178
|
+
restartDaemon(install.restart ?? 'spawn', (m) => log.info(m));
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
// Nothing was swapped, so the running daemon is untouched and the old
|
|
182
|
+
// version stays installed.
|
|
183
|
+
log.error(`Self-update failed: ${err.message}`);
|
|
184
|
+
broadcast({ stage: 'error', message: err.message });
|
|
185
|
+
}
|
|
186
|
+
})();
|
|
187
|
+
return { updating: true };
|
|
188
|
+
}
|
|
140
189
|
try {
|
|
141
190
|
mkdirSync(STAGING_DIR, { recursive: true });
|
|
142
191
|
}
|
|
143
192
|
catch {
|
|
144
193
|
throw { statusCode: 503, message: 'Update volume not available. Mount /opt/openbridge as a Docker volume.' };
|
|
145
194
|
}
|
|
146
|
-
function broadcast(msg) {
|
|
147
|
-
updateProgress = msg;
|
|
148
|
-
for (const listener of updateListeners)
|
|
149
|
-
listener(msg);
|
|
150
|
-
}
|
|
151
195
|
// Run update async: respond immediately
|
|
152
196
|
;
|
|
153
197
|
(async () => {
|