mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
feat(server): publish a stable installation identity for client connections
The iOS app lets an account reach one server through several addresses, such as a home network IP and a public name. Before it sends credentials to an alternative address it needs to know the address leads to the same installation, so expose a public, credential-free GET /server-identity returning a persisted UUID, and add the same optional instanceId to app-info with the server-connection-identity feature flag. The UUID lives in a singleton server_identity row shared by every replica, created on first use and cached per process. An unreadable identity omits instanceId instead of failing app-info, which clients rely on for feature detection. Existing routes and bodies are unchanged and the web client does not call the new route; the iOS app does. The migration uses IF NOT EXISTS because an unreleased build created the same table as 0109.
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import { ServerIdentityRepository } from './server-identity.repository';
|
||||
import { ServerIdentityService } from './server-identity.service';
|
||||
|
||||
describe('ServerIdentityService', () => {
|
||||
it('shares the durable identity across concurrent requests', async () => {
|
||||
const getOrCreate = vi.fn().mockResolvedValue('538a27ca-87ef-47bc-a195-13c8597c3529');
|
||||
const service = new ServerIdentityService({ getOrCreate } as unknown as ServerIdentityRepository);
|
||||
const [first, second] = await Promise.all([service.getIdentity(), service.getIdentity()]);
|
||||
expect(first).toEqual({ product: 'bookorbit', protocolVersion: 1, instanceId: '538a27ca-87ef-47bc-a195-13c8597c3529' });
|
||||
expect(second).toEqual(first);
|
||||
expect(getOrCreate).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('retries after a failed database read', async () => {
|
||||
const getOrCreate = vi.fn().mockRejectedValueOnce(new Error('unavailable')).mockResolvedValue('restored');
|
||||
const service = new ServerIdentityService({ getOrCreate } as unknown as ServerIdentityRepository);
|
||||
await expect(service.getIdentity()).rejects.toThrow('unavailable');
|
||||
expect((await service.getIdentity()).instanceId).toBe('restored');
|
||||
expect(getOrCreate).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user