Improve router encoding
This commit is contained in:
@@ -5,6 +5,7 @@ import type { StoreSQLite } from '../services/invitation-store.js';
|
||||
import { parseInvitation } from '../utils/invitation-parser.js';
|
||||
|
||||
import Z from 'zod';
|
||||
import { encodeExtendedJson } from '../utils/ext-json.js';
|
||||
|
||||
export class InvitationsRoute {
|
||||
constructor(
|
||||
@@ -27,6 +28,12 @@ export class InvitationsRoute {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an invitation, and if the text/event-stream header is present, subscribe the client to the SSE stream.
|
||||
* @param request - The request.
|
||||
* @param reply - The reply.
|
||||
* @returns The invitation.
|
||||
*/
|
||||
async getInvitation(request: FastifyRequest, reply: FastifyReply) {
|
||||
// Get the invitation identifier from the query
|
||||
const { invitationIdentifier } = request.query as { invitationIdentifier?: string };
|
||||
@@ -39,8 +46,12 @@ export class InvitationsRoute {
|
||||
// Get the invitation from the store
|
||||
const storedInvitation = await this.invitationStore.get(invitationIdentifier);
|
||||
|
||||
if (request.headers['accept'] === 'text/event-stream') {
|
||||
// Subscribe the client to the SSE stream.
|
||||
// If the client is not subscribing to the SSE stream, return the invitation.
|
||||
if (request.headers['accept'] !== 'text/event-stream') {
|
||||
return encodeExtendedJson(storedInvitation || {});
|
||||
}
|
||||
|
||||
// Its an SSE request, so we need to subscribe the client to the SSE stream.
|
||||
await this.sseBroadcaster.subscribe(request, reply);
|
||||
|
||||
// If the invitation doesn't exist, don't send anything.
|
||||
@@ -50,19 +61,14 @@ export class InvitationsRoute {
|
||||
|
||||
// Send the invitation to the client as if it was a get request.
|
||||
this.sseBroadcaster.sendEvent(reply, 'invitation-updated', storedInvitation);
|
||||
|
||||
// Return early
|
||||
return;
|
||||
}
|
||||
|
||||
if(!storedInvitation) {
|
||||
return reply.status(200).send({});
|
||||
}
|
||||
|
||||
// If the client is not subscribing to the SSE stream, return the invitation.
|
||||
return reply.status(200).send(storedInvitation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the invitation.
|
||||
* @param request - The request.
|
||||
* @param reply - The reply.
|
||||
* @returns The merged invitation.
|
||||
*/
|
||||
async updateInvitation(request: FastifyRequest, reply: FastifyReply) {
|
||||
// Parse the invitation
|
||||
const invitation = parseInvitation.parse(request.body);
|
||||
@@ -79,7 +85,7 @@ export class InvitationsRoute {
|
||||
// Broadcast the invitation update (We send down the whole invitation. Clients will have to compare commitIds)
|
||||
await this.sseBroadcaster.broadcast(invitation.invitationIdentifier, 'invitation-updated', invitation);
|
||||
|
||||
return reply.status(200).send(invitation);
|
||||
return invitation;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@ import { z } from "zod";
|
||||
import {
|
||||
decodeExtendedJsonObject,
|
||||
encodeExtendedJsonObject,
|
||||
} from "../utils/ext-json";
|
||||
} from "../utils/ext-json.js";
|
||||
|
||||
// Interface to add to our route classes so that we can register them.
|
||||
// NOTE: I hate this pattern. But ExpressJS is odd in that it is structured as a singleton that still needs registration.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import sqlite3, { type Database } from 'better-sqlite3';
|
||||
import { pack, unpack } from 'msgpackr';
|
||||
import { encodeExtendedJsonObject, decodeExtendedJsonObject } from '../utils/ext-json.js';
|
||||
import { binToHex, hexToBin } from '@bitauth/libauth';
|
||||
|
||||
export interface SQLiteOptions {
|
||||
wal: boolean;
|
||||
@@ -107,25 +109,34 @@ export class StoreSQLite<T> {
|
||||
async get(key: string): Promise<T | undefined> {
|
||||
const result = this.db
|
||||
.prepare(`SELECT value FROM "${this.storeName}" WHERE key = ?`)
|
||||
.get(key) as { value: Buffer } | undefined;
|
||||
.get(key) as { value: string } | undefined;
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Deserialize using msgpackr for consistency with other implementations
|
||||
return unpack(result.value) as T;
|
||||
const binValue = hexToBin(result.value);
|
||||
|
||||
const unpackedValue = unpack(binValue);
|
||||
|
||||
const decodedValue = decodeExtendedJsonObject(unpackedValue);
|
||||
|
||||
return decodedValue as T;
|
||||
}
|
||||
|
||||
async set(key: string, value: T): Promise<void> {
|
||||
const encodedValue = encodeExtendedJsonObject(value);
|
||||
|
||||
// Serialize using msgpackr for consistency with other implementations
|
||||
const serializedValue = pack(value);
|
||||
const packedValue = pack(encodedValue);
|
||||
|
||||
const serializedValue = binToHex(packedValue);
|
||||
|
||||
this.db
|
||||
.prepare(
|
||||
`INSERT OR REPLACE INTO "${this.storeName}" (key, value) VALUES (?, ?)`,
|
||||
)
|
||||
.run(key, Buffer.from(serializedValue));
|
||||
.run(key, serializedValue);
|
||||
}
|
||||
|
||||
async delete(key: string): Promise<void> {
|
||||
|
||||
@@ -2,6 +2,8 @@ import type { FastifyReply, FastifyRequest } from "fastify";
|
||||
|
||||
import debug, { type Debugger } from "debug";
|
||||
|
||||
import { encodeExtendedJson } from "../utils/ext-json.js";
|
||||
|
||||
/**
|
||||
* Represents an event stored in the history buffer.
|
||||
* Used for replaying missed events to reconnecting clients.
|
||||
@@ -87,7 +89,7 @@ export class SSEBroadcaster {
|
||||
const timestamp = Date.now();
|
||||
client.raw.write(`id: ${timestamp}\n`);
|
||||
client.raw.write(`event: ${topic}\n`);
|
||||
client.raw.write(`data: ${JSON.stringify(data)}\n\n`);
|
||||
client.raw.write(`data: ${encodeExtendedJson(data)}\n\n`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user