update olm and client routes

This commit is contained in:
miloschwartz
2025-11-06 20:12:54 -08:00
parent 999fb2fff1
commit 2274a3525b
14 changed files with 495 additions and 186 deletions

View File

@@ -1,46 +1,58 @@
import { NextFunction, Request, Response } from "express";
import { db } from "@server/db";
import { hash } from "@node-rs/argon2";
import HttpCode from "@server/types/HttpCode";
import { z } from "zod";
import { olms } from "@server/db";
import createHttpError from "http-errors";
import response from "@server/lib/response";
import { SqliteError } from "better-sqlite3";
import moment from "moment";
import { generateId, generateSessionToken } from "@server/auth/sessions/app";
import { createOlmSession } from "@server/auth/sessions/olm";
import { generateId } from "@server/auth/sessions/app";
import { fromError } from "zod-validation-error";
import { hashPassword } from "@server/auth/password";
import { OpenAPITags, registry } from "@server/openApi";
export const createOlmBodySchema = z.object({});
export type CreateOlmBody = z.infer<typeof createOlmBodySchema>;
export type CreateOlmResponse = {
// token: string;
olmId: string;
secret: string;
};
const createOlmSchema = z
const bodySchema = z
.object({
name: z.string().min(1).max(255)
})
.strict();
const createOlmParamsSchema = z
.object({
userId: z.string().optional()
});
const paramsSchema = z.object({
userId: z.string()
});
export async function createOlm(
export type CreateOlmBody = z.infer<typeof bodySchema>;
export type CreateOlmResponse = {
olmId: string;
secret: string;
};
registry.registerPath({
method: "put",
path: "/user/{userId}/olm",
description: "Create a new olm for a user.",
tags: [OpenAPITags.User, OpenAPITags.Client],
request: {
body: {
content: {
"application/json": {
schema: bodySchema
}
}
},
params: paramsSchema
},
responses: {}
});
export async function createUserOlm(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const parsedBody = createOlmSchema.safeParse(req.body);
const parsedBody = bodySchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
@@ -52,7 +64,7 @@ export async function createOlm(
const { name } = parsedBody.data;
const parsedParams = createOlmParamsSchema.safeParse(req.params);
const parsedParams = paramsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
@@ -63,20 +75,6 @@ export async function createOlm(
}
const { userId } = parsedParams.data;
let userIdFinal = userId;
if (req.user) { // overwrite the user with the one calling because we want to assign the olm to the user creating it
userIdFinal = req.user.userId;
}
if (!userIdFinal) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Either userId must be provided or request must be authenticated"
)
);
}
const olmId = generateId(15);
const secret = generateId(48);
@@ -85,20 +83,16 @@ export async function createOlm(
await db.insert(olms).values({
olmId: olmId,
userId: userIdFinal,
userId,
name,
secretHash,
dateCreated: moment().toISOString()
});
// const token = generateSessionToken();
// await createOlmSession(token, olmId);
return response<CreateOlmResponse>(res, {
data: {
olmId,
secret
// token,
},
success: true,
error: false,

View File

@@ -8,28 +8,33 @@ import response from "@server/lib/response";
import { z } from "zod";
import { fromError } from "zod-validation-error";
import logger from "@server/logger";
import { OpenAPITags, registry } from "@server/openApi";
const deleteOlmParamsSchema = z
const paramsSchema = z
.object({
userId: z.string(),
olmId: z.string()
})
.strict();
export async function deleteOlm(
registry.registerPath({
method: "delete",
path: "/user/{userId}/olm/{olmId}",
description: "Delete an olm for a user.",
tags: [OpenAPITags.User, OpenAPITags.Client],
request: {
params: paramsSchema
},
responses: {}
});
export async function deleteUserOlm(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const userId = req.user?.userId;
if (!userId) {
return next(
createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated")
);
}
const parsedParams = deleteOlmParamsSchema.safeParse(req.params);
const parsedParams = paramsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
@@ -41,31 +46,6 @@ export async function deleteOlm(
const { olmId } = parsedParams.data;
// Verify the OLM belongs to the current user
const [existingOlm] = await db
.select()
.from(olms)
.where(eq(olms.olmId, olmId))
.limit(1);
if (!existingOlm) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Olm with ID ${olmId} not found`
)
);
}
if (existingOlm.userId !== userId) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
"You do not have permission to delete this device"
)
);
}
// Delete associated clients and the OLM in a transaction
await db.transaction(async (trx) => {
// Find all clients associated with this OLM
@@ -83,9 +63,7 @@ export async function deleteOlm(
// Delete all associated clients
if (associatedClients.length > 0) {
await trx
.delete(clients)
.where(eq(clients.olmId, olmId));
await trx.delete(clients).where(eq(clients.olmId, olmId));
}
// Finally, delete the OLM itself
@@ -109,4 +87,3 @@ export async function deleteOlm(
);
}
}

View File

@@ -1,7 +1,8 @@
export * from "./handleOlmRegisterMessage";
export * from "./getOlmToken";
export * from "./createOlm";
export * from "./createUserOlm";
export * from "./handleOlmRelayMessage";
export * from "./handleOlmPingMessage";
export * from "./listOlms";
export * from "./deleteOlm";
export * from "./deleteUserOlm";
export * from "./listUserOlms";
export * from "./deleteUserOlm";

View File

@@ -8,8 +8,9 @@ import response from "@server/lib/response";
import { z } from "zod";
import { fromError } from "zod-validation-error";
import logger from "@server/logger";
import { OpenAPITags, registry } from "@server/openApi";
const listOlmsSchema = z.object({
const querySchema = z.object({
limit: z
.string()
.optional()
@@ -24,7 +25,25 @@ const listOlmsSchema = z.object({
.pipe(z.number().int().nonnegative())
});
export type ListOlmsResponse = {
const paramsSchema = z
.object({
userId: z.string()
})
.strict();
registry.registerPath({
method: "delete",
path: "/user/{userId}/olms",
description: "List all olms for a user.",
tags: [OpenAPITags.User, OpenAPITags.Client],
request: {
query: querySchema,
params: paramsSchema
},
responses: {}
});
export type ListUserOlmsResponse = {
olms: Array<{
olmId: string;
dateCreated: string;
@@ -40,21 +59,13 @@ export type ListOlmsResponse = {
};
};
export async function listOlms(
export async function listUserOlms(
req: Request,
res: Response,
next: NextFunction
): Promise<any> {
try {
const userId = req.user?.userId;
if (!userId) {
return next(
createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated")
);
}
const parsedQuery = listOlmsSchema.safeParse(req.query);
const parsedQuery = querySchema.safeParse(req.query);
if (!parsedQuery.success) {
return next(
createHttpError(
@@ -66,6 +77,18 @@ export async function listOlms(
const { limit, offset } = parsedQuery.data;
const parsedParams = paramsSchema.safeParse(req.params);
if (!parsedParams.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedParams.error).toString()
)
);
}
const { userId } = parsedParams.data;
// Get total count
const [totalCountResult] = await db
.select({ count: count() })
@@ -90,7 +113,7 @@ export async function listOlms(
.limit(limit)
.offset(offset);
return response<ListOlmsResponse>(res, {
return response<ListUserOlmsResponse>(res, {
data: {
olms: userOlms,
pagination: {
@@ -101,7 +124,7 @@ export async function listOlms(
},
success: true,
error: false,
message: "OLMs retrieved successfully",
message: "Olms retrieved successfully",
status: HttpCode.OK
});
} catch (error) {
@@ -114,4 +137,3 @@ export async function listOlms(
);
}
}