feat: daemon - machine scoped sessions added
This commit is contained in:
parent
4f0b4fecaf
commit
c37d749d7a
@ -13,7 +13,8 @@
|
|||||||
"migrate": "dotenv -e .env.example -- prisma migrate dev",
|
"migrate": "dotenv -e .env.example -- prisma migrate dev",
|
||||||
"generate": "prisma generate",
|
"generate": "prisma generate",
|
||||||
"postinstall": "prisma generate",
|
"postinstall": "prisma generate",
|
||||||
"db": "docker run -d -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=handy -v $(pwd)/.pgdata:/var/lib/postgresql/data -p 5432:5432 postgres"
|
"db": "docker run -d -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=handy -v $(pwd)/.pgdata:/var/lib/postgresql/data -p 5432:5432 postgres",
|
||||||
|
"redis": "docker run -d -p 6379:6379 redis"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chalk": "^2.2.0",
|
"@types/chalk": "^2.2.0",
|
||||||
@ -57,4 +58,4 @@
|
|||||||
"zod": "^3.24.2",
|
"zod": "^3.24.2",
|
||||||
"zod-to-json-schema": "^3.24.3"
|
"zod-to-json-schema": "^3.24.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -26,7 +26,14 @@ interface UserScopedConnection {
|
|||||||
userId: string;
|
userId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClientConnection = SessionScopedConnection | UserScopedConnection;
|
interface MachineScopedConnection {
|
||||||
|
connectionType: 'machine-scoped';
|
||||||
|
socket: Socket;
|
||||||
|
userId: string;
|
||||||
|
machineId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientConnection = SessionScopedConnection | UserScopedConnection | MachineScopedConnection;
|
||||||
|
|
||||||
declare module 'fastify' {
|
declare module 'fastify' {
|
||||||
interface FastifyRequest {
|
interface FastifyRequest {
|
||||||
@ -132,6 +139,12 @@ export async function startApi(): Promise<{ app: FastifyInstance; io: Server }>
|
|||||||
connection.socket.emit(event, payload);
|
connection.socket.emit(event, payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send to all machine-scoped connections - they get all user updates
|
||||||
|
if (connection.connectionType === 'machine-scoped') {
|
||||||
|
log({ module: 'websocket' }, `Sending ${event} to machine-scoped connection ${connection.socket.id}`);
|
||||||
|
connection.socket.emit(event, payload);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -854,8 +867,9 @@ export async function startApi(): Promise<{ app: FastifyInstance; io: Server }>
|
|||||||
io.on("connection", async (socket) => {
|
io.on("connection", async (socket) => {
|
||||||
log({ module: 'websocket' }, `New connection attempt from socket: ${socket.id}`);
|
log({ module: 'websocket' }, `New connection attempt from socket: ${socket.id}`);
|
||||||
const token = socket.handshake.auth.token as string;
|
const token = socket.handshake.auth.token as string;
|
||||||
const clientType = socket.handshake.auth.clientType as 'session-scoped' | 'user-scoped' | undefined;
|
const clientType = socket.handshake.auth.clientType as 'session-scoped' | 'user-scoped' | 'machine-scoped' | undefined;
|
||||||
const sessionId = socket.handshake.auth.sessionId as string | undefined;
|
const sessionId = socket.handshake.auth.sessionId as string | undefined;
|
||||||
|
const machineId = socket.handshake.auth.machineId as string | undefined;
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
log({ module: 'websocket' }, `No token provided`);
|
log({ module: 'websocket' }, `No token provided`);
|
||||||
@ -871,6 +885,14 @@ export async function startApi(): Promise<{ app: FastifyInstance; io: Server }>
|
|||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate machine-scoped clients have machineId
|
||||||
|
if (clientType === 'machine-scoped' && !machineId) {
|
||||||
|
log({ module: 'websocket' }, `Machine-scoped client missing machineId`);
|
||||||
|
socket.emit('error', { message: 'Machine ID required for machine-scoped clients' });
|
||||||
|
socket.disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const verified = await tokenVerifier.verify(token);
|
const verified = await tokenVerifier.verify(token);
|
||||||
if (!verified) {
|
if (!verified) {
|
||||||
@ -881,10 +903,10 @@ export async function startApi(): Promise<{ app: FastifyInstance; io: Server }>
|
|||||||
}
|
}
|
||||||
|
|
||||||
const userId = verified.user as string;
|
const userId = verified.user as string;
|
||||||
log({ module: 'websocket' }, `Token verified: ${userId}, clientType: ${clientType || 'user-scoped'}, sessionId: ${sessionId || 'none'}, socketId: ${socket.id}`);
|
log({ module: 'websocket' }, `Token verified: ${userId}, clientType: ${clientType || 'user-scoped'}, sessionId: ${sessionId || 'none'}, machineId: ${machineId || 'none'}, socketId: ${socket.id}`);
|
||||||
|
|
||||||
// Store connection based on type
|
// Store connection based on type
|
||||||
const metadata = { clientType: clientType || 'user-scoped', sessionId };
|
const metadata = { clientType: clientType || 'user-scoped', sessionId, machineId };
|
||||||
let connection: ClientConnection;
|
let connection: ClientConnection;
|
||||||
if (metadata.clientType === 'session-scoped' && sessionId) {
|
if (metadata.clientType === 'session-scoped' && sessionId) {
|
||||||
connection = {
|
connection = {
|
||||||
@ -893,6 +915,13 @@ export async function startApi(): Promise<{ app: FastifyInstance; io: Server }>
|
|||||||
userId,
|
userId,
|
||||||
sessionId
|
sessionId
|
||||||
};
|
};
|
||||||
|
} else if (metadata.clientType === 'machine-scoped' && machineId) {
|
||||||
|
connection = {
|
||||||
|
connectionType: 'machine-scoped',
|
||||||
|
socket,
|
||||||
|
userId,
|
||||||
|
machineId
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
connection = {
|
connection = {
|
||||||
connectionType: 'user-scoped',
|
connectionType: 'user-scoped',
|
||||||
@ -904,6 +933,22 @@ export async function startApi(): Promise<{ app: FastifyInstance; io: Server }>
|
|||||||
userIdToClientConnections.set(userId, new Set());
|
userIdToClientConnections.set(userId, new Set());
|
||||||
}
|
}
|
||||||
userIdToClientConnections.get(userId)!.add(connection);
|
userIdToClientConnections.get(userId)!.add(connection);
|
||||||
|
|
||||||
|
// Broadcast daemon online status
|
||||||
|
if (connection.connectionType === 'machine-scoped') {
|
||||||
|
// Broadcast daemon online
|
||||||
|
emitUpdateToInterestedClients({
|
||||||
|
event: 'ephemeral',
|
||||||
|
userId,
|
||||||
|
sessionId: '', // No specific session
|
||||||
|
payload: {
|
||||||
|
type: 'daemon-status',
|
||||||
|
machineId,
|
||||||
|
status: 'online',
|
||||||
|
timestamp: Date.now()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Lock
|
// Lock
|
||||||
const receiveMessageLock = new AsyncLock();
|
const receiveMessageLock = new AsyncLock();
|
||||||
@ -942,6 +987,21 @@ export async function startApi(): Promise<{ app: FastifyInstance; io: Server }>
|
|||||||
}
|
}
|
||||||
|
|
||||||
log({ module: 'websocket' }, `User disconnected: ${userId}`);
|
log({ module: 'websocket' }, `User disconnected: ${userId}`);
|
||||||
|
|
||||||
|
// Broadcast daemon offline status
|
||||||
|
if (connection.connectionType === 'machine-scoped') {
|
||||||
|
emitUpdateToInterestedClients({
|
||||||
|
event: 'ephemeral',
|
||||||
|
userId,
|
||||||
|
sessionId: '', // No specific session
|
||||||
|
payload: {
|
||||||
|
type: 'daemon-status',
|
||||||
|
machineId: connection.machineId,
|
||||||
|
status: 'offline',
|
||||||
|
timestamp: Date.now()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('session-alive', async (data: any) => {
|
socket.on('session-alive', async (data: any) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user