Compare commits
6
Commits
p7-wizard-wiring
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1fa5ba8baa | ||
|
|
0fd1bbd80b | ||
|
|
c430e02117 | ||
|
|
776cf3c4ca | ||
|
|
5b3e637423 | ||
|
|
ecdb2fd8ee |
No files matched your search
@@ -82,6 +82,26 @@ describe('network-people-store: CRUD', () => {
|
|||||||
expect(list).toHaveLength(1); // merged, not duplicated
|
expect(list).toHaveLength(1); // merged, not duplicated
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('upsertPerson merges by providerUserId when id is omitted (wizard after provider sync)', async () => {
|
||||||
|
const synced = await store.upsertPerson({
|
||||||
|
displayName: 'SamiAhmed7777',
|
||||||
|
providerUserId: 'ughVnuLDu621CNTRL',
|
||||||
|
});
|
||||||
|
expect(synced.groups).toEqual(['Admin']); // bootstrap identity
|
||||||
|
|
||||||
|
const fromWizard = await store.upsertPerson({
|
||||||
|
displayName: 'Sami Ahmed',
|
||||||
|
providerUserId: 'ughVnuLDu621CNTRL',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(fromWizard.id).toBe(synced.id);
|
||||||
|
expect(fromWizard.displayName).toBe('Sami Ahmed');
|
||||||
|
expect(fromWizard.groups).toEqual(['Admin']);
|
||||||
|
expect(fromWizard.protectedAt).toBe(synced.protectedAt);
|
||||||
|
const list = await store.listPeople();
|
||||||
|
expect(list).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
test('updatePerson patches only provided fields', async () => {
|
test('updatePerson patches only provided fields', async () => {
|
||||||
const p = await store.upsertPerson({ displayName: 'Bob', providerUserId: 'ts-2', groups: ['G'] });
|
const p = await store.upsertPerson({ displayName: 'Bob', providerUserId: 'ts-2', groups: ['G'] });
|
||||||
const u = await store.updatePerson(p.id, { displayName: 'Bobby' });
|
const u = await store.updatePerson(p.id, { displayName: 'Bobby' });
|
||||||
|
|||||||
@@ -779,7 +779,29 @@ async function createApp() {
|
|||||||
// Private Network wizard (DC-152). Dogfood finding 2026-10-04: the wizard
|
// Private Network wizard (DC-152). Dogfood finding 2026-10-04: the wizard
|
||||||
// factory needs INSTANCES keyed by mode id; without them every mode
|
// factory needs INSTANCES keyed by mode id; without them every mode
|
||||||
// selection 503s provider_unavailable (unit tests inject their own map).
|
// selection 503s provider_unavailable (unit tests inject their own map).
|
||||||
const p7TailscaleProvider = require('./network/providers/tailscale').createTailscaleProvider(); // eslint-disable-line global-require
|
// Coord client wiring (dogfood finding #2, 2026-10-04): the provider is
|
||||||
|
// built with coordClient=null and nothing ever called attachCoordClient()
|
||||||
|
// in prod (the promised routes/network.js wiring never landed), so wizard
|
||||||
|
// credential validation always failed not_configured. Delegate every coord
|
||||||
|
// method to a FRESH client per call — the sanctioned context pattern
|
||||||
|
// (ctx.tailscaleCoord.getClient re-reads the token from credentialManager
|
||||||
|
// each call, so wizard-stored tokens take effect without a restart).
|
||||||
|
const p7CoordProxy = new Proxy({}, {
|
||||||
|
get(_t, prop) {
|
||||||
|
if (typeof prop !== 'string') return undefined;
|
||||||
|
return async (...args) => {
|
||||||
|
const client = await ctx.tailscaleCoord.getClient();
|
||||||
|
const fn = client ? client[prop] : null;
|
||||||
|
if (typeof fn !== 'function') {
|
||||||
|
throw new Error('coord client method unavailable: ' + String(prop));
|
||||||
|
}
|
||||||
|
return fn.apply(client, args);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const p7TailscaleProvider = require('./network/providers/tailscale').createTailscaleProvider({ // eslint-disable-line global-require
|
||||||
|
coordClient: p7CoordProxy,
|
||||||
|
});
|
||||||
const p7WireGuardProvider = require('./network/providers/wireguard').createWireGuardProvider(); // eslint-disable-line global-require
|
const p7WireGuardProvider = require('./network/providers/wireguard').createWireGuardProvider(); // eslint-disable-line global-require
|
||||||
apiRouter.use('/network/wizard', networkWizardRoutes({
|
apiRouter.use('/network/wizard', networkWizardRoutes({
|
||||||
registry: { getActiveProvider },
|
registry: { getActiveProvider },
|
||||||
|
|||||||
@@ -180,7 +180,17 @@ function createPeopleStore(opts = {}) {
|
|||||||
function upsertPerson(input = {}) {
|
function upsertPerson(input = {}) {
|
||||||
return _enqueue(() => {
|
return _enqueue(() => {
|
||||||
const data = _load();
|
const data = _load();
|
||||||
const existing = input.id ? data.people[input.id] : null;
|
// Resolve the existing record by id when given, else by providerUserId
|
||||||
|
// (dogfood finding 2026-10-04: the wizard upserts a person AFTER a
|
||||||
|
// provider sync already created one for the same Tailscale user with no
|
||||||
|
// id — without this lookup the same providerUserId was duplicated).
|
||||||
|
const existing = input.id
|
||||||
|
? data.people[input.id]
|
||||||
|
: (input.providerUserId
|
||||||
|
? Object.values(data.people).find(
|
||||||
|
(p) => p.providerUserId && p.providerUserId === input.providerUserId
|
||||||
|
) || null
|
||||||
|
: null);
|
||||||
const isFirst = Object.keys(data.people).length === 0;
|
const isFirst = Object.keys(data.people).length === 0;
|
||||||
|
|
||||||
const id = existing ? existing.id : (input.id || crypto.randomUUID());
|
const id = existing ? existing.id : (input.id || crypto.randomUUID());
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ readonly API_DIR="${SITES_DIR}/dashcaddy-api"
|
|||||||
readonly DASHBOARD_DIR="${SITES_DIR}/status"
|
readonly DASHBOARD_DIR="${SITES_DIR}/status"
|
||||||
readonly CONTAINER_NAME="dashcaddy-api"
|
readonly CONTAINER_NAME="dashcaddy-api"
|
||||||
readonly CADDY_ADMIN_PORT=2019
|
readonly CADDY_ADMIN_PORT=2019
|
||||||
readonly BACKUP_DIR="${BACKUP_DIR:-${INSTALL_DIR}/backups}"
|
|
||||||
readonly DEFAULT_MAX_STORAGE_BYTES=""
|
readonly DEFAULT_MAX_STORAGE_BYTES=""
|
||||||
|
|
||||||
# ---- Tunables (overridable via flags) --------------------------------------
|
# ---- Tunables (overridable via flags) --------------------------------------
|
||||||
@@ -1045,6 +1044,9 @@ parse_args() {
|
|||||||
|
|
||||||
# Normalize TLD
|
# Normalize TLD
|
||||||
if [[ -n "$TLD" && "$TLD" != .* ]]; then TLD=".$TLD"; fi
|
if [[ -n "$TLD" && "$TLD" != .* ]]; then TLD=".$TLD"; fi
|
||||||
|
|
||||||
|
# Resolve BACKUP_DIR default when not set by the --backup-dir flag.
|
||||||
|
BACKUP_DIR="${BACKUP_DIR:-${INSTALL_DIR}/backups}"
|
||||||
}
|
}
|
||||||
|
|
||||||
print_help() {
|
print_help() {
|
||||||
|
|||||||
Reference in new issue
Block a user