@saluzi/saluzi-edu 0.3.3 → 0.3.5
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/cli.js +114 -114
- package/package.json +1 -1
- package/packages/remote-control-server/src/__tests__/auth-security.test.ts +5 -7
- package/packages/remote-control-server/src/__tests__/design-gaps.test.ts +10 -2
- package/packages/remote-control-server/src/__tests__/low-priority-fixes.test.ts +6 -2
- package/packages/remote-control-server/src/nodeServer.ts +15 -0
package/package.json
CHANGED
|
@@ -105,13 +105,11 @@ describe('Security: /setup concurrent protection', () => {
|
|
|
105
105
|
app = createApp()
|
|
106
106
|
})
|
|
107
107
|
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
// this test only validates under non-isolate mode.
|
|
114
|
-
test.skip('concurrent setup requests only create one admin', async () => {
|
|
108
|
+
// freshDb() resets the db/sqlite.ts singleton and points it at a unique
|
|
109
|
+
// temp file per test, so the route handler's getDb() resolves to the same
|
|
110
|
+
// connection as this test. The production INSERT ... WHERE NOT EXISTS is
|
|
111
|
+
// atomic, so exactly one of two concurrent setup requests succeeds.
|
|
112
|
+
test('concurrent setup requests only create one admin', async () => {
|
|
115
113
|
// Fire two setup requests concurrently
|
|
116
114
|
const [res1, res2] = await Promise.all([
|
|
117
115
|
app.request('/web/auth/setup', {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { describe, test, expect, beforeEach, mock } from 'bun:test'
|
|
2
2
|
import { randomUUID } from 'node:crypto'
|
|
3
|
+
import { tmpdir } from 'node:os'
|
|
4
|
+
import { join } from 'node:path'
|
|
3
5
|
import { mockConfigModule } from './helpers/mock-config'
|
|
4
6
|
|
|
5
7
|
mock.module('../config', () => mockConfigModule())
|
|
@@ -37,7 +39,10 @@ function freshDb(): Database {
|
|
|
37
39
|
try {
|
|
38
40
|
resetDbSingleton()
|
|
39
41
|
} catch {}
|
|
40
|
-
const uniquePath =
|
|
42
|
+
const uniquePath = join(
|
|
43
|
+
tmpdir(),
|
|
44
|
+
`rcs-design-gap-${randomUUID().replace(/-/g, '').slice(0, 8)}.db`,
|
|
45
|
+
)
|
|
41
46
|
return initDatabase(uniquePath)
|
|
42
47
|
}
|
|
43
48
|
|
|
@@ -330,7 +335,10 @@ describe('Gap 2: resolveOwnedWebSessionId removes auto-bind', () => {
|
|
|
330
335
|
resetDbSingleton()
|
|
331
336
|
} catch {}
|
|
332
337
|
initDatabase(
|
|
333
|
-
|
|
338
|
+
join(
|
|
339
|
+
tmpdir(),
|
|
340
|
+
`rcs-gap2-${randomUUID().replace(/-/g, '').slice(0, 8)}.db`,
|
|
341
|
+
),
|
|
334
342
|
)
|
|
335
343
|
})
|
|
336
344
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, test, expect, beforeEach, mock } from 'bun:test'
|
|
2
2
|
import { readFileSync } from 'node:fs'
|
|
3
|
-
import {
|
|
3
|
+
import { tmpdir } from 'node:os'
|
|
4
|
+
import { join, resolve } from 'node:path'
|
|
4
5
|
import { randomUUID } from 'node:crypto'
|
|
5
6
|
import { mockConfigModule } from './helpers/mock-config'
|
|
6
7
|
|
|
@@ -34,7 +35,10 @@ function freshDb() {
|
|
|
34
35
|
} catch {
|
|
35
36
|
// ignore
|
|
36
37
|
}
|
|
37
|
-
const uniquePath =
|
|
38
|
+
const uniquePath = join(
|
|
39
|
+
tmpdir(),
|
|
40
|
+
`rcs-low-fixes-${randomUUID().replace(/-/g, '').slice(0, 8)}.db`,
|
|
41
|
+
)
|
|
38
42
|
return initDatabase(uniquePath)
|
|
39
43
|
}
|
|
40
44
|
|
|
@@ -162,6 +162,21 @@ export async function startRcsServer(
|
|
|
162
162
|
// eslint-disable-next-line custom-rules/no-process-exit
|
|
163
163
|
process.exit(1)
|
|
164
164
|
}
|
|
165
|
+
if (opts.singleUser && err.code === 'EADDRINUSE') {
|
|
166
|
+
// Single-user embedded mode (`slz rcs one` / `/rcs one`) breaks
|
|
167
|
+
// silently when another RCS instance owns the port: the one-time
|
|
168
|
+
// login codes and the worker-session poll live in THIS process's
|
|
169
|
+
// memory, while the browser talks to the OTHER server — its /otp/
|
|
170
|
+
// route doesn't know the code and bounces the user to the login
|
|
171
|
+
// page, defeating the auto-open-into-session flow. Fail loudly.
|
|
172
|
+
console.error(
|
|
173
|
+
'[RCS] Single-user mode needs this process to own the port ' +
|
|
174
|
+
'(one-time login codes are in-memory). ' +
|
|
175
|
+
'Stop the other RCS instance or set RCS_PORT.',
|
|
176
|
+
)
|
|
177
|
+
// eslint-disable-next-line custom-rules/no-process-exit
|
|
178
|
+
process.exit(1)
|
|
179
|
+
}
|
|
165
180
|
})
|
|
166
181
|
|
|
167
182
|
// Inject WebSocket support for Node.js
|