migrate from github
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
const Y = require('yjs');
|
||||
const { keyCollabDocUpdates } = require('../key');
|
||||
|
||||
function findDocInMemory(docs, docId) {
|
||||
if (!docs) {
|
||||
return null;
|
||||
}
|
||||
return docs.get(`doc-${docId}`) || docs.get(docId) || null;
|
||||
}
|
||||
|
||||
async function buildDocFromRedis(redis, docId) {
|
||||
const redisKey = keyCollabDocUpdates(docId);
|
||||
const updates = await redis.getListBuffers(redisKey);
|
||||
if (!updates || updates.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const doc = new Y.Doc();
|
||||
for (const update of updates) {
|
||||
if (update && update.length > 0) {
|
||||
Y.applyUpdate(doc, update);
|
||||
}
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
async function loadDocForRead(docs, redis, docId) {
|
||||
const inMemoryDoc = findDocInMemory(docs, docId);
|
||||
if (inMemoryDoc) {
|
||||
return inMemoryDoc;
|
||||
}
|
||||
return buildDocFromRedis(redis, docId);
|
||||
}
|
||||
|
||||
const XML_FRAGMENT_DOC_TYPES = new Set(['yoresee_rich_text']);
|
||||
|
||||
function encodeSnapshotResponse(doc, docType) {
|
||||
const update = Y.encodeStateAsUpdate(doc);
|
||||
let content = '';
|
||||
|
||||
if (XML_FRAGMENT_DOC_TYPES.has(docType)) {
|
||||
const fragment = doc.getXmlFragment('content');
|
||||
content = fragment ? fragment.toString() : '';
|
||||
} else {
|
||||
const ytext = doc.getText('content');
|
||||
content = ytext ? ytext.toString() : '';
|
||||
}
|
||||
|
||||
return {
|
||||
state: Buffer.from(update).toString('base64'),
|
||||
content
|
||||
};
|
||||
}
|
||||
|
||||
function encodeBinaryUpdate(doc) {
|
||||
return Buffer.from(Y.encodeStateAsUpdate(doc));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
loadDocForRead,
|
||||
encodeSnapshotResponse,
|
||||
encodeBinaryUpdate
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
function writeJson(res, status, payload) {
|
||||
res.writeHead(status, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify(payload));
|
||||
}
|
||||
|
||||
function writeText(res, status, body) {
|
||||
res.writeHead(status);
|
||||
res.end(body);
|
||||
}
|
||||
|
||||
function getRequestURL(req) {
|
||||
const host = req.headers && req.headers.host ? req.headers.host : 'localhost';
|
||||
return new URL(req.url || '/', `http://${host}`);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
writeJson,
|
||||
writeText,
|
||||
getRequestURL
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
const { writeJson } = require('./helpers');
|
||||
|
||||
function buildReadinessState(isDraining, redisClient) {
|
||||
const ready = !isDraining && !!redisClient;
|
||||
return {
|
||||
ready,
|
||||
redisState: redisClient ? 'connected' : 'disconnected',
|
||||
detail: isDraining ? 'server is draining' : undefined
|
||||
};
|
||||
}
|
||||
|
||||
function handleProbeRequest(pathname, res, { isDraining, redisClient }) {
|
||||
if (pathname === '/livez') {
|
||||
writeJson(res, 200, { status: 'ok' });
|
||||
return true;
|
||||
}
|
||||
|
||||
const state = buildReadinessState(isDraining, redisClient);
|
||||
|
||||
if (pathname === '/readyz') {
|
||||
writeJson(res, state.ready ? 200 : 503, {
|
||||
status: state.ready ? 'ok' : 'not_ready',
|
||||
redis: state.redisState,
|
||||
detail: state.detail
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pathname === '/health') {
|
||||
writeJson(res, 200, {
|
||||
status: 'ok',
|
||||
redis: state.redisState,
|
||||
persistence: 'custom',
|
||||
ready: state.ready ? 'true' : 'false',
|
||||
detail: state.detail
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
handleProbeRequest
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
const { writeJson, writeText, getRequestURL } = require('./helpers');
|
||||
const { handleProbeRequest } = require('./probes');
|
||||
const { loadDocForRead, encodeSnapshotResponse, encodeBinaryUpdate } = require('./docs');
|
||||
|
||||
function decodeDocID(pathname, prefix) {
|
||||
try {
|
||||
return decodeURIComponent(pathname.slice(prefix.length));
|
||||
} catch (_err) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function createRequestHandler({ redis, wsUtils, getIsDraining }) {
|
||||
const docs = wsUtils.docs;
|
||||
|
||||
return async function handleRequest(req, res) {
|
||||
try {
|
||||
const url = getRequestURL(req);
|
||||
const pathname = url.pathname;
|
||||
|
||||
if (handleProbeRequest(pathname, res, { isDraining: getIsDraining(), redisClient: redis.redisClient })) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname === '/api/active-rooms') {
|
||||
const rooms = await redis.getActiveRooms();
|
||||
writeJson(res, 200, { rooms, count: rooms.length });
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/internal/yjs/doc-snapshot/')) {
|
||||
const docId = decodeDocID(pathname, '/internal/yjs/doc-snapshot/');
|
||||
if (!docId) {
|
||||
writeText(res, 400, 'doc id required');
|
||||
return;
|
||||
}
|
||||
|
||||
const docType = url.searchParams.get('type') || '';
|
||||
const doc = await loadDocForRead(docs, redis, docId);
|
||||
if (!doc) {
|
||||
writeText(res, 404, 'doc not loaded');
|
||||
return;
|
||||
}
|
||||
|
||||
writeJson(res, 200, encodeSnapshotResponse(doc, docType));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/internal/yjs/doc/')) {
|
||||
const docId = decodeDocID(pathname, '/internal/yjs/doc/');
|
||||
if (!docId) {
|
||||
writeText(res, 400, 'doc id required');
|
||||
return;
|
||||
}
|
||||
|
||||
const doc = await loadDocForRead(docs, redis, docId);
|
||||
if (!doc) {
|
||||
writeText(res, 404, 'doc not loaded');
|
||||
return;
|
||||
}
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'application/octet-stream' });
|
||||
res.end(encodeBinaryUpdate(doc));
|
||||
return;
|
||||
}
|
||||
|
||||
writeText(res, 404, '');
|
||||
} catch (err) {
|
||||
console.error('Request handling failed:', err);
|
||||
writeText(res, 500, 'internal error');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createRequestHandler
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
const http = require('http');
|
||||
const { createRequestHandler } = require('./router');
|
||||
|
||||
function createHTTPServer({ redis, wsUtils, getIsDraining }) {
|
||||
const requestHandler = createRequestHandler({
|
||||
redis,
|
||||
wsUtils,
|
||||
getIsDraining
|
||||
});
|
||||
|
||||
return http.createServer((req, res) => {
|
||||
void requestHandler(req, res);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createHTTPServer
|
||||
};
|
||||
Reference in New Issue
Block a user