From 4f94927e38228c9087eace08ffc7656735288551 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 03:35:52 +0000 Subject: [PATCH 1/3] Initial plan From 7a31917550cc2fd956eeab15df87d4c5ed761eb3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 17:07:06 +0000 Subject: [PATCH 2/3] feat: add code submission service with Azure Service Bus queue Agent-Logs-Url: https://github.com/nitinrawat111/DisCode/sessions/d124a7cf-68b4-4060-b70f-427ffdd2c387 --- packages/backend/.env.example | 6 + packages/backend/package.json | 1 + .../src/controllers/submission.controller.ts | 78 +++++ packages/backend/src/db/db.ts | 2 + ...775235625000_submissions_table_creation.ts | 49 +++ packages/backend/src/dtos/submission.dto.ts | 49 +++ .../backend/src/models/submission.model.ts | 61 ++++ .../src/routes/api/v1/submission.router.ts | 235 +++++++++++++++ .../backend/src/routes/api/v1/v1.router.ts | 2 + packages/backend/src/server.ts | 2 + .../services/code-execution-queue.service.ts | 58 ++++ .../src/services/submission.service.ts | 123 ++++++++ packages/backend/src/types/global.d.ts | 2 + pnpm-lock.yaml | 283 ++++++++++++++++++ 14 files changed, 951 insertions(+) create mode 100644 packages/backend/src/controllers/submission.controller.ts create mode 100644 packages/backend/src/db/migrations/1775235625000_submissions_table_creation.ts create mode 100644 packages/backend/src/dtos/submission.dto.ts create mode 100644 packages/backend/src/models/submission.model.ts create mode 100644 packages/backend/src/routes/api/v1/submission.router.ts create mode 100644 packages/backend/src/services/code-execution-queue.service.ts create mode 100644 packages/backend/src/services/submission.service.ts diff --git a/packages/backend/.env.example b/packages/backend/.env.example index e68a487..b194448 100644 --- a/packages/backend/.env.example +++ b/packages/backend/.env.example @@ -17,3 +17,9 @@ POSTGRES_PASSWORD=postgres ############################ AZURE_ACCOUNT_NAME= AZURE_ACCOUNT_KEY= + +############################ +# Azure Service Bus +############################ +AZURE_SERVICE_BUS_CONNECTION_STRING= +AZURE_SERVICE_BUS_QUEUE_NAME=code-execution diff --git a/packages/backend/package.json b/packages/backend/package.json index 6938c45..e171fa7 100755 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -37,6 +37,7 @@ "typescript-eslint": "^8.57.1" }, "dependencies": { + "@azure/service-bus": "^7.9.5", "@azure/storage-blob": "^12.20.0", "@dotenvx/dotenvx": "^1.57.0", "bcrypt": "^6.0.0", diff --git a/packages/backend/src/controllers/submission.controller.ts b/packages/backend/src/controllers/submission.controller.ts new file mode 100644 index 0000000..6e8b052 --- /dev/null +++ b/packages/backend/src/controllers/submission.controller.ts @@ -0,0 +1,78 @@ +import { RequestHandler } from "express"; +import { ApiResponse } from "../utils/ApiResponse"; +import { SubmissionServiceInstance } from "../services/submission.service"; +import { Submission } from "../models/submission.model"; +import { UserJWTPayload } from "../types"; +import { + CreateSubmissionRequest, + GetSubmissionsQuery, + UpdateSubmissionRequest, +} from "../dtos/submission.dto"; + +export type SubmissionIdParam = { + submissionId: string; +}; + +class SubmissionController { + createSubmission: RequestHandler< + unknown, + ApiResponse, + CreateSubmissionRequest, + unknown, + UserJWTPayload + > = async (req, res) => { + const submission = await SubmissionServiceInstance.createSubmission( + req.body, + res.locals.userId, + ); + res + .status(201) + .json(new ApiResponse(201, "Submission created successfully", submission)); + }; + + getSubmissionById: RequestHandler< + SubmissionIdParam, + ApiResponse + > = async (req, res) => { + const submissionId = parseInt(req.params.submissionId, 10); + const submission = + await SubmissionServiceInstance.getSubmissionById(submissionId); + res + .status(200) + .json( + new ApiResponse(200, "Submission fetched successfully", submission), + ); + }; + + getSubmissions: RequestHandler< + unknown, + ApiResponse<{ + submissions: Submission[]; + totalSubmissions: number; + totalPages: number; + currentPage: number; + }>, + unknown, + GetSubmissionsQuery + > = async (req, res) => { + const result = await SubmissionServiceInstance.getSubmissions(req.query); + res + .status(200) + .json( + new ApiResponse(200, "Submissions fetched successfully", result), + ); + }; + + updateSubmission: RequestHandler< + unknown, + ApiResponse, + UpdateSubmissionRequest + > = async (req, res) => { + await SubmissionServiceInstance.updateSubmission(req.body); + res + .status(200) + .json(new ApiResponse(200, "Submission updated successfully")); + }; +} + +export const SubmissionControllerInstance = new SubmissionController(); diff --git a/packages/backend/src/db/db.ts b/packages/backend/src/db/db.ts index 712b21f..792e8c8 100755 --- a/packages/backend/src/db/db.ts +++ b/packages/backend/src/db/db.ts @@ -3,6 +3,7 @@ import { Kysely, PostgresDialect } from "kysely"; import { Logger } from "../utils/logger"; import { UserTable } from "../models/user.model"; import { ProblemTable } from "../models/problem.model"; +import { SubmissionTable } from "../models/submission.model"; // Create the Postgres dialect for Kysely // Reference: https://kysely.dev/docs/getting-started#instantiation @@ -23,6 +24,7 @@ const dialect = new PostgresDialect({ export interface Database { users: UserTable; problems: ProblemTable; + submissions: SubmissionTable; } // Instantiate Database diff --git a/packages/backend/src/db/migrations/1775235625000_submissions_table_creation.ts b/packages/backend/src/db/migrations/1775235625000_submissions_table_creation.ts new file mode 100644 index 0000000..b62542a --- /dev/null +++ b/packages/backend/src/db/migrations/1775235625000_submissions_table_creation.ts @@ -0,0 +1,49 @@ +import { sql, type Kysely } from "kysely"; + +// `any`(or unknown) is required here since migrations should be frozen in time +// Migrations should never depend on the current code of your app +// because they need to work even when the app changes +// For more info, see: https://kysely.dev/docs/migrations +export async function up(db: Kysely): Promise { + await sql`CREATE TYPE submission_status AS ENUM ( + 'Queued', + 'Compile Error', + 'Runtime Error', + 'Time Limit Error', + 'Wrong Answer', + 'Successful', + 'Server Error' + )`.execute(db); + + await db.schema + .createTable("submissions") + .addColumn("submission_id", "serial", (col) => col.primaryKey()) + .addColumn("user_id", "integer", (col) => + col.notNull().references("users.user_id").onDelete("cascade"), + ) + .addColumn("problem_id", "text", (col) => col.notNull()) + .addColumn("language", "varchar(50)", (col) => col.notNull()) + .addColumn("status", sql`submission_status`, (col) => + col.notNull().defaultTo(sql`'Queued'::submission_status`), + ) + .addColumn("runtime", "float8") + .addColumn("memory_used", "float8") + .addColumn("test_cases_passed", "integer") + .addColumn("total_test_cases", "integer") + .addColumn("error_message", "text") + .addColumn("submission_key", "text", (col) => col.notNull()) + .addColumn("created_at", "timestamptz", (col) => + col.notNull().defaultTo(sql`now()`), + ) + .addColumn("executed_at", "timestamptz") + .execute(); +} + +// `any`(or unknown) is required here since migrations should be frozen in time +// Migrations should never depend on the current code of your app +// because they need to work even when the app changes +// For more info, see: https://kysely.dev/docs/migrations +export async function down(db: Kysely): Promise { + await db.schema.dropTable("submissions").execute(); + await sql`DROP TYPE submission_status`.execute(db); +} diff --git a/packages/backend/src/dtos/submission.dto.ts b/packages/backend/src/dtos/submission.dto.ts new file mode 100644 index 0000000..acb0b2d --- /dev/null +++ b/packages/backend/src/dtos/submission.dto.ts @@ -0,0 +1,49 @@ +import { z } from "zod"; +import { Language, SubmissionStatus } from "../models/submission.model"; +import { UserIdDto } from "./user.dto"; + +//////////////////////////////////////////// +// Common Dtos +//////////////////////////////////////////// +export const SubmissionIdDto = z.number().int().positive(); +export const ProblemIdDto = z.string().uuid(); +export const LanguageDto = z.enum(Language); +export const SubmissionStatusDto = z.enum(SubmissionStatus); +export const SubmissionKeyDto = z.string().min(1); + +//////////////////////////////////////////// +// Create Submission Request Dto +//////////////////////////////////////////// +export const CreateSubmissionRequestDto = z.object({ + problem_id: ProblemIdDto, + language: LanguageDto, + submission_key: SubmissionKeyDto, +}); +export type CreateSubmissionRequest = z.infer; + +//////////////////////////////////////////// +// Update Submission Request Dto +// Called by the code execution worker after running the submission +//////////////////////////////////////////// +export const UpdateSubmissionRequestDto = z.object({ + submission_id: SubmissionIdDto, + status: SubmissionStatusDto, + runtime: z.number().nonnegative().nullish(), + memory_used: z.number().nonnegative().nullish(), + test_cases_passed: z.number().int().nonnegative().nullish(), + total_test_cases: z.number().int().nonnegative().nullish(), + error_message: z.string().nullish(), + executed_at: z.string().datetime().nullish(), +}); +export type UpdateSubmissionRequest = z.infer; + +//////////////////////////////////////////// +// Get Submissions Query Dto +//////////////////////////////////////////// +export const GetSubmissionsQueryDto = z.object({ + user_id: UserIdDto.nullish(), + problem_id: ProblemIdDto.nullish(), + page: z.coerce.number().int().positive().default(1), + limit: z.coerce.number().int().positive().max(50).default(10), +}); +export type GetSubmissionsQuery = z.infer; diff --git a/packages/backend/src/models/submission.model.ts b/packages/backend/src/models/submission.model.ts new file mode 100644 index 0000000..6507f1d --- /dev/null +++ b/packages/backend/src/models/submission.model.ts @@ -0,0 +1,61 @@ +import { + ColumnType, + Generated, + Insertable, + Selectable, + Updateable, +} from "kysely"; +import { UserTable } from "./user.model"; + +//////////////////////////////////////////// +// Submission Status +//////////////////////////////////////////// +export const SubmissionStatus = { + Queued: "Queued", + CompileError: "Compile Error", + RuntimeError: "Runtime Error", + TimeLimitError: "Time Limit Error", + WrongAnswer: "Wrong Answer", + Successful: "Successful", + ServerError: "Server Error", +} as const; +export type SubmissionStatus = + (typeof SubmissionStatus)[keyof typeof SubmissionStatus]; + +//////////////////////////////////////////// +// Programming Languages +//////////////////////////////////////////// +export const Language = { + Python: "Python", + Cpp: "Cpp", +} as const; +export type Language = (typeof Language)[keyof typeof Language]; + +//////////////////////////////////////////// +// Submission Table Definition +//////////////////////////////////////////// +export interface SubmissionTable { + submission_id: Generated; + user_id: UserTable["user_id"]; + /** + * UUID of the problem being submitted + */ + problem_id: string; + language: Language; + status: ColumnType; + runtime: number | null; + memory_used: number | null; + test_cases_passed: number | null; + total_test_cases: number | null; + error_message: string | null; + /** + * Azure Blob Storage key for the submitted code file + */ + submission_key: string; + created_at: ColumnType; + executed_at: ColumnType; +} + +export type Submission = Selectable; +export type NewSubmission = Insertable; +export type SubmissionUpdate = Updateable; diff --git a/packages/backend/src/routes/api/v1/submission.router.ts b/packages/backend/src/routes/api/v1/submission.router.ts new file mode 100644 index 0000000..b41ef82 --- /dev/null +++ b/packages/backend/src/routes/api/v1/submission.router.ts @@ -0,0 +1,235 @@ +import { Router } from "express"; +import { SubmissionControllerInstance } from "../../../controllers/submission.controller"; +import { parseUserHeaders } from "../../../middlewares/parseUserHeaders.middleware"; +import { + getBodyValidationMiddleware, + getQueryValidationMiddleware, +} from "../../../middlewares/validation.middleware"; +import { + CreateSubmissionRequestDto, + GetSubmissionsQueryDto, + UpdateSubmissionRequestDto, +} from "../../../dtos/submission.dto"; + +export const SubmissionRouter: Router = Router(); + +/** + * @swagger + * /api/v1/submissions: + * post: + * summary: "Create a new code submission" + * tags: [Submissions] + * security: + * - bearerAuth: [] + * consumes: + * - "application/json" + * produces: + * - "application/json" + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * problem_id: + * type: string + * format: uuid + * description: "UUID of the problem being submitted" + * language: + * type: string + * enum: [Python, Cpp] + * description: "Programming language used" + * submission_key: + * type: string + * description: "Azure Blob Storage key for the submitted code file" + * required: + * - problem_id + * - language + * - submission_key + * responses: + * 201: + * description: "Submission created and queued for execution successfully" + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: integer + * example: 201 + * message: + * type: string + * example: "Submission created successfully" + * data: + * type: object + * properties: + * submission_id: + * type: integer + * status: + * type: string + * enum: [Queued, Compile Error, Runtime Error, Time Limit Error, Wrong Answer, Successful, Server Error] + * example: "Queued" + * 400: + * description: "Invalid request body (validation error)" + * 401: + * description: "Unauthorized (missing or invalid access token)" + */ +SubmissionRouter.post( + "/", + parseUserHeaders, + getBodyValidationMiddleware(CreateSubmissionRequestDto), + SubmissionControllerInstance.createSubmission, +); + +/** + * @swagger + * /api/v1/submissions: + * get: + * summary: "Get submissions with optional filters and pagination" + * tags: [Submissions] + * parameters: + * - in: query + * name: user_id + * schema: + * type: integer + * description: "Filter by user ID" + * - in: query + * name: problem_id + * schema: + * type: string + * format: uuid + * description: "Filter by problem ID" + * - in: query + * name: page + * schema: + * type: integer + * minimum: 1 + * default: 1 + * description: "Page number for pagination" + * - in: query + * name: limit + * schema: + * type: integer + * minimum: 1 + * maximum: 50 + * default: 10 + * description: "Number of submissions per page" + * produces: + * - "application/json" + * responses: + * 200: + * description: "Submissions fetched successfully" + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: integer + * example: 200 + * message: + * type: string + * example: "Submissions fetched successfully" + * data: + * type: object + * properties: + * submissions: + * type: array + * items: + * type: object + * totalSubmissions: + * type: integer + * totalPages: + * type: integer + * currentPage: + * type: integer + */ +SubmissionRouter.get( + "/", + getQueryValidationMiddleware(GetSubmissionsQueryDto), + SubmissionControllerInstance.getSubmissions, +); + +/** + * @swagger + * /api/v1/submissions/{submissionId}: + * get: + * summary: "Get a submission by ID" + * tags: [Submissions] + * parameters: + * - in: path + * name: submissionId + * required: true + * schema: + * type: integer + * description: "ID of the submission to fetch" + * produces: + * - "application/json" + * responses: + * 200: + * description: "Submission fetched successfully" + * 404: + * description: "Submission not found" + */ +SubmissionRouter.get("/:submissionId", SubmissionControllerInstance.getSubmissionById); + +/** + * @swagger + * /api/v1/submissions: + * patch: + * summary: "Update a submission result (called by execution worker)" + * tags: [Submissions] + * consumes: + * - "application/json" + * produces: + * - "application/json" + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * submission_id: + * type: integer + * description: "ID of the submission to update" + * status: + * type: string + * enum: [Queued, Compile Error, Runtime Error, Time Limit Error, Wrong Answer, Successful, Server Error] + * description: "Execution result status" + * runtime: + * type: number + * description: "Runtime in milliseconds" + * memory_used: + * type: number + * description: "Memory used in megabytes" + * test_cases_passed: + * type: integer + * description: "Number of test cases passed" + * total_test_cases: + * type: integer + * description: "Total number of test cases" + * error_message: + * type: string + * description: "Error message if execution failed" + * executed_at: + * type: string + * format: date-time + * description: "Execution timestamp" + * required: + * - submission_id + * - status + * responses: + * 200: + * description: "Submission updated successfully" + * 400: + * description: "Invalid request body (validation error)" + * 404: + * description: "Submission not found" + */ +SubmissionRouter.patch( + "/", + getBodyValidationMiddleware(UpdateSubmissionRequestDto), + SubmissionControllerInstance.updateSubmission, +); diff --git a/packages/backend/src/routes/api/v1/v1.router.ts b/packages/backend/src/routes/api/v1/v1.router.ts index 6462b78..d019c37 100755 --- a/packages/backend/src/routes/api/v1/v1.router.ts +++ b/packages/backend/src/routes/api/v1/v1.router.ts @@ -2,8 +2,10 @@ import { Router } from "express"; import { UserRouter } from "./user.router"; import { JWKSRouter } from "./jwks.router"; import { ProblemRouter } from "./problem.router"; +import { SubmissionRouter } from "./submission.router"; export const V1Router: Router = Router(); V1Router.use("/users", UserRouter); V1Router.use("/jwks", JWKSRouter); V1Router.use("/problems", ProblemRouter); +V1Router.use("/submissions", SubmissionRouter); diff --git a/packages/backend/src/server.ts b/packages/backend/src/server.ts index b137d3b..c22db7a 100755 --- a/packages/backend/src/server.ts +++ b/packages/backend/src/server.ts @@ -8,6 +8,7 @@ import { morganMiddleware } from "./middlewares/morgan.middleware"; import express, { NextFunction, Request, Response } from "express"; import { z, ZodError } from "zod"; import { JwksServiceInstance } from "./services/jwks.service"; +import { CodeExecutionQueueServiceInstance } from "./services/code-execution-queue.service"; import type { Express } from "express"; //////////////////////////////////////////////////////////// @@ -73,6 +74,7 @@ app.use((err: Error, _req: Request, res: Response, next: NextFunction) => { const PORT = parseInt(process.env.PORT); async function init() { await JwksServiceInstance.waitForInit(); + await CodeExecutionQueueServiceInstance.waitForInit(); app.listen(PORT, () => { Logger.info(`Server started on PORT: ${PORT}`); }); diff --git a/packages/backend/src/services/code-execution-queue.service.ts b/packages/backend/src/services/code-execution-queue.service.ts new file mode 100644 index 0000000..39ae0b6 --- /dev/null +++ b/packages/backend/src/services/code-execution-queue.service.ts @@ -0,0 +1,58 @@ +import { ServiceBusClient, ServiceBusSender } from "@azure/service-bus"; +import { Logger } from "../utils/logger"; +import { CreateSubmissionRequest } from "../dtos/submission.dto"; + +export interface SubmissionQueueMessage extends CreateSubmissionRequest { + submission_id: number; +} + +export class CodeExecutionQueueService { + private client!: ServiceBusClient; + private sender!: ServiceBusSender; + private initPromise: Promise | null; + + constructor() { + this.initPromise = this.init(); + } + + private async init() { + const connectionString = process.env.AZURE_SERVICE_BUS_CONNECTION_STRING; + const queueName = process.env.AZURE_SERVICE_BUS_QUEUE_NAME; + + if (!connectionString) { + throw new Error( + "Missing required environment variable: AZURE_SERVICE_BUS_CONNECTION_STRING", + ); + } + if (!queueName) { + throw new Error( + "Missing required environment variable: AZURE_SERVICE_BUS_QUEUE_NAME", + ); + } + + this.client = new ServiceBusClient(connectionString); + this.sender = this.client.createSender(queueName); + Logger.info("Azure Service Bus queue initialized"); + this.initPromise = null; + } + + async waitForInit() { + return this.initPromise; + } + + async queueSubmissionForExecution(message: SubmissionQueueMessage) { + await this.waitForInit(); + await this.sender.sendMessages({ + body: message, + contentType: "application/json", + }); + } + + async close() { + await this.sender?.close(); + await this.client?.close(); + } +} + +export const CodeExecutionQueueServiceInstance = + new CodeExecutionQueueService(); diff --git a/packages/backend/src/services/submission.service.ts b/packages/backend/src/services/submission.service.ts new file mode 100644 index 0000000..78fc7af --- /dev/null +++ b/packages/backend/src/services/submission.service.ts @@ -0,0 +1,123 @@ +import { sql } from "kysely"; +import { DB } from "../db/db"; +import { Submission } from "../models/submission.model"; +import { User } from "../models/user.model"; +import { + CreateSubmissionRequest, + GetSubmissionsQuery, + UpdateSubmissionRequest, +} from "../dtos/submission.dto"; +import { ApiError } from "../utils/ApiError"; +import { CodeExecutionQueueServiceInstance } from "./code-execution-queue.service"; + +export class SubmissionService { + async createSubmission( + submissionRequest: CreateSubmissionRequest, + userId: User["user_id"], + ): Promise { + const inserted = await DB.insertInto("submissions") + .values({ + user_id: userId, + problem_id: submissionRequest.problem_id, + language: submissionRequest.language, + submission_key: submissionRequest.submission_key, + }) + .returningAll() + .executeTakeFirst(); + + if (inserted === undefined) { + throw new ApiError(500, "Failed to create submission"); + } + + // After creating submission in DB, queue it for execution + await CodeExecutionQueueServiceInstance.queueSubmissionForExecution({ + submission_id: inserted.submission_id, + problem_id: inserted.problem_id, + language: inserted.language, + submission_key: inserted.submission_key, + }); + + return inserted; + } + + async getSubmissionById( + submissionId: Submission["submission_id"], + ): Promise { + const submission = await DB.selectFrom("submissions") + .selectAll() + .where("submission_id", "=", submissionId) + .executeTakeFirst(); + + if (submission === undefined) { + throw new ApiError(404, "Submission not found"); + } + + return submission; + } + + async getSubmissions(query: GetSubmissionsQuery): Promise<{ + submissions: Submission[]; + totalSubmissions: number; + totalPages: number; + currentPage: number; + }> { + let baseQuery = DB.selectFrom("submissions").selectAll(); + + if (typeof query.user_id === "number") { + baseQuery = baseQuery.where("user_id", "=", query.user_id); + } + + if (typeof query.problem_id === "string") { + baseQuery = baseQuery.where("problem_id", "=", query.problem_id); + } + + // Get total count for pagination + const countQuery = baseQuery + .select(sql`COUNT(*)::int`.as("total")) + .executeTakeFirstOrThrow(); + + // Apply pagination and ordering + const offset = (query.page - 1) * query.limit; + const submissionsQuery = baseQuery + .orderBy("created_at", "desc") + .limit(query.limit) + .offset(offset) + .execute(); + + const [countResult, submissions] = await Promise.all([ + countQuery, + submissionsQuery, + ]); + + const totalSubmissions = countResult.total; + const totalPages = Math.ceil(totalSubmissions / query.limit); + + return { + submissions, + totalSubmissions, + totalPages, + currentPage: query.page, + }; + } + + async updateSubmission(updates: UpdateSubmissionRequest): Promise { + const result = await DB.updateTable("submissions") + .set({ + status: updates.status, + runtime: updates.runtime ?? undefined, + memory_used: updates.memory_used ?? undefined, + test_cases_passed: updates.test_cases_passed ?? undefined, + total_test_cases: updates.total_test_cases ?? undefined, + error_message: updates.error_message ?? undefined, + executed_at: updates.executed_at ?? undefined, + }) + .where("submission_id", "=", updates.submission_id) + .executeTakeFirst(); + + if (result.numUpdatedRows === 0n) { + throw new ApiError(404, "Submission not found"); + } + } +} + +export const SubmissionServiceInstance = new SubmissionService(); diff --git a/packages/backend/src/types/global.d.ts b/packages/backend/src/types/global.d.ts index d55718c..548868e 100644 --- a/packages/backend/src/types/global.d.ts +++ b/packages/backend/src/types/global.d.ts @@ -11,5 +11,7 @@ declare namespace NodeJS { POSTGRES_PASSWORD: string; AZURE_ACCOUNT_NAME: string; AZURE_ACCOUNT_KEY: string; + AZURE_SERVICE_BUS_CONNECTION_STRING: string; + AZURE_SERVICE_BUS_QUEUE_NAME: string; } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 26e8e04..83b0c87 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: packages/backend: dependencies: + '@azure/service-bus': + specifier: ^7.9.5 + version: 7.9.5 '@azure/storage-blob': specifier: ^12.20.0 version: 12.31.0 @@ -138,10 +141,18 @@ packages: peerDependencies: openapi-types: '>=7' + '@azure/abort-controller@1.1.0': + resolution: {integrity: sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==} + engines: {node: '>=12.0.0'} + '@azure/abort-controller@2.1.2': resolution: {integrity: sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==} engines: {node: '>=18.0.0'} + '@azure/core-amqp@4.4.1': + resolution: {integrity: sha512-eiVwGOMpHWPS6YsX0kjW4rfH+f0Pb5L2EKNDbuXldVkuFKSEfROdl81xHLsMAl5PP5wiiTjErcMcKsJqwyaRqw==} + engines: {node: '>=20.0.0'} + '@azure/core-auth@1.10.1': resolution: {integrity: sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==} engines: {node: '>=20.0.0'} @@ -185,6 +196,10 @@ packages: resolution: {integrity: sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==} engines: {node: '>=20.0.0'} + '@azure/service-bus@7.9.5': + resolution: {integrity: sha512-R5Af+4jtZZII2snLomaddMyElFtTCBRZp2qERPlP8PuISLU87eFYFM7xWzxjNd0yeiyQUBkamx/ZhOC8eWhCHA==} + engines: {node: '>=18.0.0'} + '@azure/storage-blob@12.31.0': resolution: {integrity: sha512-DBgNv10aCSxopt92DkTDD0o9xScXeBqPKGmR50FPZQaEcH4JLQ+GEOGEDv19V5BMkB7kxr+m4h6il/cCDPvmHg==} engines: {node: '>=20.0.0'} @@ -369,6 +384,9 @@ packages: '@types/http-errors@2.0.5': resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + '@types/is-buffer@2.0.2': + resolution: {integrity: sha512-G6OXy83Va+xEo8XgqAJYOuvOMxeey9xM5XKkvwJNmN8rVdcB+r15HvHsG86hl86JvU0y1aa7Z2ERkNFYWw9ySg==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -502,6 +520,10 @@ packages: async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -509,6 +531,9 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + basic-auth@2.0.1: resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} engines: {node: '>= 0.8'} @@ -528,6 +553,9 @@ packages: resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} engines: {node: 18 || 20 || >=22} + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -544,6 +572,10 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + call-bound@1.0.4: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} @@ -646,6 +678,10 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} @@ -819,6 +855,10 @@ packages: fn.name@1.1.0: resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -833,6 +873,10 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -865,10 +909,17 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -898,6 +949,9 @@ packages: resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -921,10 +975,26 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + is-arguments@1.2.0: + resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} + engines: {node: '>= 0.4'} + + is-buffer@2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -932,10 +1002,18 @@ packages: is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -963,6 +1041,9 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + jssha@3.3.1: + resolution: {integrity: sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1013,6 +1094,9 @@ packages: resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} engines: {node: '>= 12.0.0'} + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -1294,6 +1378,10 @@ packages: pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + postgres-array@2.0.0: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} @@ -1319,6 +1407,10 @@ packages: engines: {node: '>=14'} hasBin: true + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -1350,6 +1442,12 @@ packages: resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} engines: {node: '>= 20.19.0'} + rhea-promise@3.0.3: + resolution: {integrity: sha512-a875P5YcMkePSTEWMsnmCQS7Y4v/XvIw7ZoMtJxqtQRZsqSA6PsZxuz4vktyRykPuUgdNsA6F84dS3iEXZoYnQ==} + + rhea@3.0.4: + resolution: {integrity: sha512-n3kw8syCdrsfJ72w3rohpoHHlmv/RZZEP9VY5BVjjo0sEGIt4YSKypBgaiA+OUSgJAzLjOECYecsclG5xbYtZw==} + router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} @@ -1360,6 +1458,10 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + safe-stable-stringify@2.5.0: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} @@ -1380,6 +1482,10 @@ packages: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -1544,6 +1650,9 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + uuid@13.0.0: resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} hasBin: true @@ -1559,6 +1668,10 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1637,10 +1750,30 @@ snapshots: openapi-types: 12.1.3 z-schema: 5.0.5 + '@azure/abort-controller@1.1.0': + dependencies: + tslib: 2.8.1 + '@azure/abort-controller@2.1.2': dependencies: tslib: 2.8.1 + '@azure/core-amqp@4.4.1': + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-auth': 1.10.1 + '@azure/core-util': 1.13.1 + '@azure/logger': 1.3.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + rhea: 3.0.4 + rhea-promise: 3.0.3 + tslib: 2.8.1 + util: 0.12.5 + transitivePeerDependencies: + - supports-color + '@azure/core-auth@1.10.1': dependencies: '@azure/abort-controller': 2.1.2 @@ -1716,6 +1849,29 @@ snapshots: transitivePeerDependencies: - supports-color + '@azure/service-bus@7.9.5': + dependencies: + '@azure/abort-controller': 1.1.0 + '@azure/core-amqp': 4.4.1 + '@azure/core-auth': 1.10.1 + '@azure/core-client': 1.10.1 + '@azure/core-paging': 1.6.2 + '@azure/core-rest-pipeline': 1.23.0 + '@azure/core-tracing': 1.3.1 + '@azure/core-util': 1.13.1 + '@azure/core-xml': 1.5.0 + '@azure/logger': 1.3.0 + '@types/is-buffer': 2.0.2 + buffer: 6.0.3 + is-buffer: 2.0.5 + jssha: 3.3.1 + long: 5.3.2 + process: 0.11.10 + rhea-promise: 3.0.3 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + '@azure/storage-blob@12.31.0': dependencies: '@azure/abort-controller': 2.1.2 @@ -1911,6 +2067,10 @@ snapshots: '@types/http-errors@2.0.5': {} + '@types/is-buffer@2.0.2': + dependencies: + '@types/node': 25.5.0 + '@types/json-schema@7.0.15': {} '@types/morgan@1.9.10': @@ -2080,10 +2240,16 @@ snapshots: async@3.2.6: {} + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + balanced-match@1.0.2: {} balanced-match@4.0.4: {} + base64-js@1.5.1: {} + basic-auth@2.0.1: dependencies: safe-buffer: 5.1.2 @@ -2116,6 +2282,11 @@ snapshots: dependencies: balanced-match: 4.0.4 + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + bytes@3.1.2: {} c12@3.3.3: @@ -2138,6 +2309,13 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + call-bound@1.0.4: dependencies: call-bind-apply-helpers: 1.0.2 @@ -2214,6 +2392,12 @@ snapshots: deep-is@0.1.4: {} + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + defu@6.1.4: {} depd@2.0.0: {} @@ -2431,6 +2615,10 @@ snapshots: fn.name@1.1.0: {} + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + forwarded@0.2.0: {} fresh@2.0.0: {} @@ -2439,6 +2627,8 @@ snapshots: function-bind@1.1.2: {} + generator-function@2.0.1: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -2485,8 +2675,16 @@ snapshots: gopd@1.2.0: {} + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + has-symbols@1.1.0: {} + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -2521,6 +2719,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + ieee754@1.2.1: {} + ignore@5.3.2: {} ignore@7.0.5: {} @@ -2536,16 +2736,44 @@ snapshots: ipaddr.js@1.9.1: {} + is-arguments@1.2.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-buffer@2.0.5: {} + + is-callable@1.2.7: {} + is-extglob@2.1.1: {} + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 is-promise@4.0.0: {} + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + is-stream@2.0.1: {} + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + isexe@2.0.0: {} isexe@3.1.5: {} @@ -2564,6 +2792,8 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + jssha@3.3.1: {} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -2614,6 +2844,8 @@ snapshots: safe-stable-stringify: 2.5.0 triple-beam: 1.4.1 + long@5.3.2: {} + make-error@1.3.6: {} math-intrinsics@1.1.0: {} @@ -2796,6 +3028,8 @@ snapshots: exsolve: 1.0.8 pathe: 2.0.3 + possible-typed-array-names@1.1.0: {} + postgres-array@2.0.0: {} postgres-bytea@1.0.1: {} @@ -2810,6 +3044,8 @@ snapshots: prettier@3.8.1: {} + process@0.11.10: {} + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -2843,6 +3079,20 @@ snapshots: readdirp@5.0.0: {} + rhea-promise@3.0.3: + dependencies: + debug: 4.4.3 + rhea: 3.0.4 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + rhea@3.0.4: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + router@2.2.0: dependencies: debug: 4.4.3 @@ -2857,6 +3107,12 @@ snapshots: safe-buffer@5.2.1: {} + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + safe-stable-stringify@2.5.0: {} safer-buffer@2.1.2: {} @@ -2888,6 +3144,15 @@ snapshots: transitivePeerDependencies: - supports-color + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + setprototypeof@1.2.0: {} shebang-command@2.0.0: @@ -3053,6 +3318,14 @@ snapshots: util-deprecate@1.0.2: {} + util@0.12.5: + dependencies: + inherits: 2.0.4 + is-arguments: 1.2.0 + is-generator-function: 1.1.2 + is-typed-array: 1.1.15 + which-typed-array: 1.1.20 + uuid@13.0.0: {} v8-compile-cache-lib@3.0.1: {} @@ -3061,6 +3334,16 @@ snapshots: vary@1.1.2: {} + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + which@2.0.2: dependencies: isexe: 2.0.0 From 4979c1922742e333a8ddbf40fe200102edbe5906 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 17:08:15 +0000 Subject: [PATCH 3/3] feat: add code submission service with Azure Service Bus queue Agent-Logs-Url: https://github.com/nitinrawat111/DisCode/sessions/d124a7cf-68b4-4060-b70f-427ffdd2c387 --- .../backend/src/controllers/submission.controller.ts | 8 ++++---- packages/backend/src/dtos/submission.dto.ts | 8 ++++++-- packages/backend/src/models/submission.model.ts | 12 ++++++++++-- .../backend/src/routes/api/v1/submission.router.ts | 5 ++++- packages/backend/src/services/media.service.ts | 2 +- 5 files changed, 25 insertions(+), 10 deletions(-) diff --git a/packages/backend/src/controllers/submission.controller.ts b/packages/backend/src/controllers/submission.controller.ts index 6e8b052..f45887d 100644 --- a/packages/backend/src/controllers/submission.controller.ts +++ b/packages/backend/src/controllers/submission.controller.ts @@ -27,7 +27,9 @@ class SubmissionController { ); res .status(201) - .json(new ApiResponse(201, "Submission created successfully", submission)); + .json( + new ApiResponse(201, "Submission created successfully", submission), + ); }; getSubmissionById: RequestHandler< @@ -58,9 +60,7 @@ class SubmissionController { const result = await SubmissionServiceInstance.getSubmissions(req.query); res .status(200) - .json( - new ApiResponse(200, "Submissions fetched successfully", result), - ); + .json(new ApiResponse(200, "Submissions fetched successfully", result)); }; updateSubmission: RequestHandler< diff --git a/packages/backend/src/dtos/submission.dto.ts b/packages/backend/src/dtos/submission.dto.ts index acb0b2d..5ceb372 100644 --- a/packages/backend/src/dtos/submission.dto.ts +++ b/packages/backend/src/dtos/submission.dto.ts @@ -19,7 +19,9 @@ export const CreateSubmissionRequestDto = z.object({ language: LanguageDto, submission_key: SubmissionKeyDto, }); -export type CreateSubmissionRequest = z.infer; +export type CreateSubmissionRequest = z.infer< + typeof CreateSubmissionRequestDto +>; //////////////////////////////////////////// // Update Submission Request Dto @@ -35,7 +37,9 @@ export const UpdateSubmissionRequestDto = z.object({ error_message: z.string().nullish(), executed_at: z.string().datetime().nullish(), }); -export type UpdateSubmissionRequest = z.infer; +export type UpdateSubmissionRequest = z.infer< + typeof UpdateSubmissionRequestDto +>; //////////////////////////////////////////// // Get Submissions Query Dto diff --git a/packages/backend/src/models/submission.model.ts b/packages/backend/src/models/submission.model.ts index 6507f1d..28d0fcc 100644 --- a/packages/backend/src/models/submission.model.ts +++ b/packages/backend/src/models/submission.model.ts @@ -42,7 +42,11 @@ export interface SubmissionTable { */ problem_id: string; language: Language; - status: ColumnType; + status: ColumnType< + SubmissionStatus, + SubmissionStatus | undefined, + SubmissionStatus + >; runtime: number | null; memory_used: number | null; test_cases_passed: number | null; @@ -53,7 +57,11 @@ export interface SubmissionTable { */ submission_key: string; created_at: ColumnType; - executed_at: ColumnType; + executed_at: ColumnType< + Date | null, + string | null | undefined, + string | null | undefined + >; } export type Submission = Selectable; diff --git a/packages/backend/src/routes/api/v1/submission.router.ts b/packages/backend/src/routes/api/v1/submission.router.ts index b41ef82..5d5a8c4 100644 --- a/packages/backend/src/routes/api/v1/submission.router.ts +++ b/packages/backend/src/routes/api/v1/submission.router.ts @@ -172,7 +172,10 @@ SubmissionRouter.get( * 404: * description: "Submission not found" */ -SubmissionRouter.get("/:submissionId", SubmissionControllerInstance.getSubmissionById); +SubmissionRouter.get( + "/:submissionId", + SubmissionControllerInstance.getSubmissionById, +); /** * @swagger diff --git a/packages/backend/src/services/media.service.ts b/packages/backend/src/services/media.service.ts index fa2e260..319cf50 100644 --- a/packages/backend/src/services/media.service.ts +++ b/packages/backend/src/services/media.service.ts @@ -46,7 +46,7 @@ export class MediaService { // If-None-Match condition to prevent overwriting existing blobs // Reference: https://learn.microsoft.com/en-us/rest/api/storageservices/specifying-conditional-headers-for-blob-service-operations#Subheading1 ifNoneMatch: "*", - } + }, }); }