add: tokenID added to JWT in endpoint GET /captcha/:id/result
This commit is contained in:
parent
1633e56b1e
commit
b18b45078f
@ -1,11 +1,14 @@
|
|||||||
import { Context } from "hono";
|
import { Context } from "hono";
|
||||||
import { Bindings, BlankEnv, BlankInput } from "hono/types";
|
import { Bindings, BlankEnv } from "hono/types";
|
||||||
import { ErrorResponse } from "src/schema";
|
import { ErrorResponse } from "src/schema";
|
||||||
import { createHandlers } from "src/utils.ts";
|
import { createHandlers } from "src/utils.ts";
|
||||||
import * as jose from "jose";
|
import * as jose from "jose";
|
||||||
|
import { generateRandomId } from "@core/lib/randomID.ts";
|
||||||
|
import { lockManager } from "@core/mq/lockManager.ts";
|
||||||
|
|
||||||
interface CaptchaResponse {
|
interface CaptchaResponse {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
difficulty?: number;
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -13,7 +16,7 @@ const getChallengeVerificationResult = async (id: string, ans: string) => {
|
|||||||
const baseURL = process.env["UCAPTCHA_URL"];
|
const baseURL = process.env["UCAPTCHA_URL"];
|
||||||
const url = new URL(baseURL);
|
const url = new URL(baseURL);
|
||||||
url.pathname = `/challenge/${id}/validation`;
|
url.pathname = `/challenge/${id}/validation`;
|
||||||
const res = await fetch(url.toString(), {
|
return await fetch(url.toString(), {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
@ -22,11 +25,10 @@ const getChallengeVerificationResult = async (id: string, ans: string) => {
|
|||||||
y: ans
|
y: ans
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
return res;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const verifyChallengeHandler = createHandlers(
|
export const verifyChallengeHandler = createHandlers(
|
||||||
async (c: Context<BlankEnv & { Bindings: Bindings }, "/captcha/:id/result", BlankInput>) => {
|
async (c: Context<BlankEnv & { Bindings: Bindings }, "/captcha/:id/result">) => {
|
||||||
const id = c.req.param("id");
|
const id = c.req.param("id");
|
||||||
const ans = c.req.query("ans");
|
const ans = c.req.query("ans");
|
||||||
if (!ans) {
|
if (!ans) {
|
||||||
@ -76,7 +78,11 @@ export const verifyChallengeHandler = createHandlers(
|
|||||||
const jwtSecret = new TextEncoder().encode(secret);
|
const jwtSecret = new TextEncoder().encode(secret);
|
||||||
const alg = "HS256";
|
const alg = "HS256";
|
||||||
|
|
||||||
const jwt = await new jose.SignJWT()
|
|
||||||
|
const tokenID = generateRandomId(10);
|
||||||
|
const EXPIRE_FIVE_MINUTES = 300;
|
||||||
|
await lockManager.acquireLock(tokenID, EXPIRE_FIVE_MINUTES);
|
||||||
|
const jwt = await new jose.SignJWT({ difficulty: data.difficulty!, id: tokenID })
|
||||||
.setProtectedHeader({ alg })
|
.setProtectedHeader({ alg })
|
||||||
.setIssuedAt()
|
.setIssuedAt()
|
||||||
.sign(jwtSecret);
|
.sign(jwtSecret);
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@cvsa/core",
|
|
||||||
"exports": "./main.ts",
|
|
||||||
"imports": {
|
|
||||||
"ioredis": "npm:ioredis",
|
|
||||||
"log/": "./log/",
|
|
||||||
"db/": "./db/",
|
|
||||||
"$std/": "https://deno.land/std@0.216.0/",
|
|
||||||
"mq/": "./mq/",
|
|
||||||
"chalk": "npm:chalk",
|
|
||||||
"winston": "npm:winston",
|
|
||||||
"logform": "npm:logform",
|
|
||||||
"@core/": "./",
|
|
||||||
"child_process": "node:child_process",
|
|
||||||
"util": "node:util"
|
|
||||||
}
|
|
||||||
}
|
|
142
packages/core/lib/randomID.ts
Normal file
142
packages/core/lib/randomID.ts
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
const getSecureRandomInt = (max: number): number => {
|
||||||
|
const array = new Uint32Array(1);
|
||||||
|
crypto.getRandomValues(array);
|
||||||
|
// Using modulo bias is technically present, but negligible here because the space (56) is tiny compared to 2^32.
|
||||||
|
return array[0] % max;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random ID with characteristics similar to UUIDv7,
|
||||||
|
* incorporating a timestamp prefix for sortability and a random suffix,
|
||||||
|
* using a customizable length and a specific character set.
|
||||||
|
*
|
||||||
|
* This function aims for sortability by placing a time-based component at the beginning,
|
||||||
|
* similar to UUIDv7, while allowing a variable total length and using a character set
|
||||||
|
* designed to avoid visually similar characters.
|
||||||
|
*
|
||||||
|
* The character set includes uppercase and lowercase letters and numbers,
|
||||||
|
* excluding visually similar characters (0, O, I, l, 1).
|
||||||
|
*
|
||||||
|
* **Length Reference**:
|
||||||
|
*
|
||||||
|
* With a collision probability of **0.1%**,
|
||||||
|
* the maximum ID generation rate per millisecond for the following lengths is:
|
||||||
|
* - **10**: 1.8 IDs / ms or 1,844 QPS
|
||||||
|
* - **12**: 27 IDs / ms or 26,998 QPS
|
||||||
|
* - **16**: 5784 IDs / ms or 5,784,295 QPS
|
||||||
|
*
|
||||||
|
* With a collision probability of **0.001%**,
|
||||||
|
* the maximum ID generation rate per millisecond for the following lengths is:
|
||||||
|
* - **11**: 1.5 IDs / ms or 1,520 QPS
|
||||||
|
* - **14**: 85 IDs / ms or 85,124 QPS
|
||||||
|
* - **16**: 1246 IDs / ms or 1,245,983 QPS
|
||||||
|
*
|
||||||
|
* With a collision probability of **0.00001%**,
|
||||||
|
* the maximum ID generation rate per millisecond for the following lengths is:
|
||||||
|
* - **14**: 18 IDs / ms or 18,339 QPS
|
||||||
|
* - **15**: 70 IDs / ms or 70,164 QPS
|
||||||
|
* - **16**: 1246 IDs / ms or 268,438 QPS
|
||||||
|
*
|
||||||
|
* The formula: max_qps = 1000 * (2 * (56**(length - 8)) * -log(1 - prob))**(1/3)
|
||||||
|
*
|
||||||
|
* @param length The desired total length of the ID. Must be at least 8.
|
||||||
|
* @returns A sortable random ID string of the specified length.
|
||||||
|
* @throws Error if the requested length is less than the minimum required for the timestamp prefix (8).
|
||||||
|
*/
|
||||||
|
export function generateRandomId(length: number): string {
|
||||||
|
// Character set excluding 0, O, I, l, 1
|
||||||
|
const allowedChars = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"; // 56 characters
|
||||||
|
const base = allowedChars.length; // 56
|
||||||
|
const TIMESTAMP_PREFIX_LENGTH = 8; // Fixed length for the timestamp part to ensure sortability
|
||||||
|
|
||||||
|
if (length < TIMESTAMP_PREFIX_LENGTH) {
|
||||||
|
throw new Error(`Length must be at least ${TIMESTAMP_PREFIX_LENGTH} to include the timestamp prefix.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Generate Timestamp Prefix ---
|
||||||
|
const timestamp = Date.now(); // Milliseconds since epoch (Unix time)
|
||||||
|
let timestampBaseString = "";
|
||||||
|
let tempTimestamp = timestamp;
|
||||||
|
const firstChar = allowedChars[0]; // Character for padding ('a')
|
||||||
|
|
||||||
|
// Convert timestamp to a base-56 string
|
||||||
|
// We process from the least significant "digit" to the most significant
|
||||||
|
while (tempTimestamp > 0) {
|
||||||
|
timestampBaseString = allowedChars[tempTimestamp % base] + timestampBaseString;
|
||||||
|
tempTimestamp = Math.floor(tempTimestamp / base);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pad the timestamp string at the beginning to ensure a fixed length.
|
||||||
|
// This is crucial for chronological sortability of the generated IDs.
|
||||||
|
while (timestampBaseString.length < TIMESTAMP_PREFIX_LENGTH) {
|
||||||
|
timestampBaseString = firstChar + timestampBaseString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Although highly unlikely with an 8-character prefix using base 56 for current timestamps,
|
||||||
|
// this would truncate if the timestamp string somehow exceeded the prefix length.
|
||||||
|
if (timestampBaseString.length > TIMESTAMP_PREFIX_LENGTH) {
|
||||||
|
timestampBaseString = timestampBaseString.substring(timestampBaseString.length - TIMESTAMP_PREFIX_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Generate Random Suffix ---
|
||||||
|
const randomLength = length - TIMESTAMP_PREFIX_LENGTH;
|
||||||
|
let randomSuffix = "";
|
||||||
|
const allowedCharsLength = allowedChars.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < randomLength; i++) {
|
||||||
|
const randomIndex = getSecureRandomInt(allowedCharsLength);
|
||||||
|
randomSuffix += allowedChars[randomIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Concatenate and Return ---
|
||||||
|
return timestampBaseString + randomSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes the timestamp (in milliseconds since epoch) from an ID
|
||||||
|
* generated by a function that uses a fixed-length timestamp prefix
|
||||||
|
* encoded in a specific base and character set (like the previous example).
|
||||||
|
*
|
||||||
|
* It extracts the timestamp prefix and converts it back from the
|
||||||
|
* custom base-56 encoding to a number.
|
||||||
|
*
|
||||||
|
* @param id The ID string containing the timestamp prefix.
|
||||||
|
* @returns The timestamp in milliseconds since epoch.
|
||||||
|
* @throws Error if the ID is too short or contains invalid characters in the timestamp prefix.
|
||||||
|
*/
|
||||||
|
export function decodeTimestampFromId(id: string): number {
|
||||||
|
// Character set must match the encoding function used to generate the ID
|
||||||
|
const allowedChars = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"; // 56 characters
|
||||||
|
const base = allowedChars.length; // 56
|
||||||
|
const TIMESTAMP_PREFIX_LENGTH = 8; // Fixed length for the timestamp part
|
||||||
|
|
||||||
|
if (id.length < TIMESTAMP_PREFIX_LENGTH) {
|
||||||
|
throw new Error(
|
||||||
|
`ID must be at least ${TIMESTAMP_PREFIX_LENGTH} characters long to contain a timestamp prefix.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the timestamp prefix from the beginning of the ID string
|
||||||
|
const timestampPrefix = id.substring(0, TIMESTAMP_PREFIX_LENGTH);
|
||||||
|
let timestamp = 0;
|
||||||
|
|
||||||
|
// Convert the base-56 timestamp string back to a number
|
||||||
|
// Iterate through the prefix characters from left to right (most significant 'digit')
|
||||||
|
for (let i = 0; i < timestampPrefix.length; i++) {
|
||||||
|
const char = timestampPrefix[i];
|
||||||
|
// Find the index (value) of the character in the allowed character set
|
||||||
|
const charIndex = allowedChars.indexOf(char);
|
||||||
|
|
||||||
|
if (charIndex === -1) {
|
||||||
|
// If a character is not in the allowed set, the ID is likely invalid
|
||||||
|
throw new Error(`Invalid character "${char}" found in timestamp prefix.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard base conversion: accumulate the value
|
||||||
|
// For each digit, multiply the current total by the base and add the digit's value
|
||||||
|
timestamp = timestamp * base + charIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return timestamp;
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import { Redis } from "ioredis";
|
import { Redis } from "ioredis";
|
||||||
import { redis } from "../../core/db/redis.ts";
|
import { redis } from "@core/db/redis.ts";
|
||||||
|
|
||||||
class LockManager {
|
class LockManager {
|
||||||
private redis: Redis;
|
private redis: Redis;
|
@ -1,8 +1,7 @@
|
|||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { RateLimiter, type RateLimiterConfig } from "mq/rateLimiter.ts";
|
import { RateLimiter, type RateLimiterConfig } from "mq/rateLimiter.ts";
|
||||||
import { redis } from "db/redis.ts";
|
|
||||||
import { ReplyError } from "ioredis";
|
import { ReplyError } from "ioredis";
|
||||||
import { SECOND } from "../const/time.ts";
|
import { SECOND } from "@core/const/time.ts";
|
||||||
import { spawn, SpawnOptions } from "child_process";
|
import { spawn, SpawnOptions } from "child_process";
|
||||||
|
|
||||||
export function spawnPromise(
|
export function spawnPromise(
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "core",
|
"name": "core",
|
||||||
"dependencies": {
|
"scripts": {
|
||||||
"chalk": "^5.4.1",
|
"test": "bun --env-file=.env.test run vitest"
|
||||||
"ioredis": "^5.6.1",
|
},
|
||||||
"logform": "^2.7.0",
|
"dependencies": {
|
||||||
"postgres": "^3.4.5",
|
"chalk": "^5.4.1",
|
||||||
"winston": "^3.17.0"
|
"ioredis": "^5.6.1",
|
||||||
},
|
"logform": "^2.7.0",
|
||||||
"devDependencies": {
|
"postgres": "^3.4.5",
|
||||||
"@types/ioredis": "^5.0.0"
|
"winston": "^3.17.0"
|
||||||
}
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/ioredis": "^5.0.0"
|
||||||
|
}
|
||||||
}
|
}
|
80
packages/core/test/lib/randomID.test.ts
Normal file
80
packages/core/test/lib/randomID.test.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { generateRandomId, decodeTimestampFromId } from "@core/lib/randomID.ts";
|
||||||
|
|
||||||
|
describe("generateRandomId", () => {
|
||||||
|
it("should throw an error if the requested length is less than 8", () => {
|
||||||
|
expect(() => generateRandomId(7)).toThrowError("Length must be at least 8 to include the timestamp prefix.");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should generate an ID of the specified length", () => {
|
||||||
|
const length = 15;
|
||||||
|
const id = generateRandomId(length);
|
||||||
|
expect(id).toHaveLength(length);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should generate an ID with a timestamp prefix of length 8", () => {
|
||||||
|
const id = generateRandomId(12);
|
||||||
|
expect(id).toHaveProperty("substring");
|
||||||
|
expect(id).toHaveProperty("length");
|
||||||
|
expect(id.length).toBeGreaterThanOrEqual(8);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should generate an ID containing only allowed characters", () => {
|
||||||
|
const allowedChars = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
||||||
|
const id = generateRandomId(20);
|
||||||
|
for (const char of id) {
|
||||||
|
expect(allowedChars).toContain(char);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should generate IDs that are sortable by creation time", () => {
|
||||||
|
const id1 = generateRandomId(10);
|
||||||
|
// Simulate a slight delay to ensure different timestamps
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
const id2 = generateRandomId(10);
|
||||||
|
expect(id2 >= id1).toBe(true);
|
||||||
|
resolve(null);
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("decodeTimestampFromId", () => {
|
||||||
|
it("should throw an error if the ID length is less than 8", () => {
|
||||||
|
expect(() => decodeTimestampFromId("abcdefg")).toThrowError(
|
||||||
|
"ID must be at least 8 characters long to contain a timestamp prefix."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw an error if the timestamp prefix contains invalid characters", () => {
|
||||||
|
const invalidId = "0bcdefghijk";
|
||||||
|
expect(() => decodeTimestampFromId(invalidId)).toThrowError('Invalid character "0" found in timestamp prefix.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should correctly decode the timestamp from a generated ID", () => {
|
||||||
|
const now = Date.now();
|
||||||
|
// Mock Date.now to control the timestamp for testing
|
||||||
|
const originalDateNow = Date.now;
|
||||||
|
global.Date.now = () => now;
|
||||||
|
const id = generateRandomId(16);
|
||||||
|
global.Date.now = originalDateNow; // Restore original Date.now
|
||||||
|
|
||||||
|
const decodedTimestamp = decodeTimestampFromId(id);
|
||||||
|
// Allow a small margin for potential timing differences in test execution
|
||||||
|
expect(decodedTimestamp).toBeGreaterThanOrEqual(now - 1);
|
||||||
|
expect(decodedTimestamp).toBeLessThanOrEqual(now + 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should correctly decode the timestamp even with a longer ID", () => {
|
||||||
|
const now = Date.now();
|
||||||
|
const originalDateNow = Date.now;
|
||||||
|
global.Date.now = () => now;
|
||||||
|
const id = generateRandomId(20);
|
||||||
|
global.Date.now = originalDateNow;
|
||||||
|
|
||||||
|
const decodedTimestamp = decodeTimestampFromId(id);
|
||||||
|
expect(decodedTimestamp).toBeGreaterThanOrEqual(now - 1);
|
||||||
|
expect(decodedTimestamp).toBeLessThanOrEqual(now + 1);
|
||||||
|
});
|
||||||
|
});
|
@ -1,7 +1,7 @@
|
|||||||
import { Job } from "bullmq";
|
import { Job } from "bullmq";
|
||||||
import { getAllVideosWithoutActiveSnapshotSchedule, scheduleSnapshot } from "db/snapshotSchedule.ts";
|
import { getAllVideosWithoutActiveSnapshotSchedule, scheduleSnapshot } from "db/snapshotSchedule.ts";
|
||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { lockManager } from "mq/lockManager.ts";
|
import { lockManager } from "@core/mq/lockManager.ts";
|
||||||
import { getLatestVideoSnapshot } from "db/snapshot.ts";
|
import { getLatestVideoSnapshot } from "db/snapshot.ts";
|
||||||
import { HOUR, MINUTE } from "@core/const/time.ts";
|
import { HOUR, MINUTE } from "@core/const/time.ts";
|
||||||
import { sql } from "@core/db/dbNew";
|
import { sql } from "@core/db/dbNew";
|
||||||
|
@ -3,7 +3,7 @@ import { getUnlabelledVideos, getVideoInfoFromAllData, insertVideoLabel } from "
|
|||||||
import Akari from "ml/akari.ts";
|
import Akari from "ml/akari.ts";
|
||||||
import { ClassifyVideoQueue } from "mq/index.ts";
|
import { ClassifyVideoQueue } from "mq/index.ts";
|
||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { lockManager } from "mq/lockManager.ts";
|
import { lockManager } from "@core/mq/lockManager.ts";
|
||||||
import { aidExistsInSongs } from "db/songs.ts";
|
import { aidExistsInSongs } from "db/songs.ts";
|
||||||
import { insertIntoSongs } from "mq/task/collectSongs.ts";
|
import { insertIntoSongs } from "mq/task/collectSongs.ts";
|
||||||
import { scheduleSnapshot } from "db/snapshotSchedule.ts";
|
import { scheduleSnapshot } from "db/snapshotSchedule.ts";
|
||||||
|
@ -4,7 +4,7 @@ import { truncate } from "utils/truncate.ts";
|
|||||||
import { getVideosWithoutActiveSnapshotSchedule, scheduleSnapshot } from "db/snapshotSchedule.ts";
|
import { getVideosWithoutActiveSnapshotSchedule, scheduleSnapshot } from "db/snapshotSchedule.ts";
|
||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { HOUR, MINUTE, WEEK } from "@core/const/time.ts";
|
import { HOUR, MINUTE, WEEK } from "@core/const/time.ts";
|
||||||
import { lockManager } from "mq/lockManager.ts";
|
import { lockManager } from "@core/mq/lockManager.ts";
|
||||||
import { getRegularSnapshotInterval } from "mq/task/regularSnapshotInterval.ts";
|
import { getRegularSnapshotInterval } from "mq/task/regularSnapshotInterval.ts";
|
||||||
import { sql } from "@core/db/dbNew.ts";
|
import { sql } from "@core/db/dbNew.ts";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import { Job } from "bullmq";
|
|||||||
import { scheduleSnapshot, setSnapshotStatus, snapshotScheduleExists } from "db/snapshotSchedule.ts";
|
import { scheduleSnapshot, setSnapshotStatus, snapshotScheduleExists } from "db/snapshotSchedule.ts";
|
||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { HOUR, MINUTE, SECOND } from "@core/const/time.ts";
|
import { HOUR, MINUTE, SECOND } from "@core/const/time.ts";
|
||||||
import { lockManager } from "mq/lockManager.ts";
|
import { lockManager } from "@core/mq/lockManager.ts";
|
||||||
import { getBiliVideoStatus, setBiliVideoStatus } from "../../db/bilibili_metadata.ts";
|
import { getBiliVideoStatus, setBiliVideoStatus } from "../../db/bilibili_metadata.ts";
|
||||||
import { insertVideoSnapshot } from "mq/task/getVideoStats.ts";
|
import { insertVideoSnapshot } from "mq/task/getVideoStats.ts";
|
||||||
import { getSongsPublihsedAt } from "db/songs.ts";
|
import { getSongsPublihsedAt } from "db/songs.ts";
|
||||||
|
@ -3,7 +3,7 @@ import { redis } from "@core/db/redis.ts";
|
|||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { classifyVideosWorker, classifyVideoWorker } from "mq/exec/classifyVideo.ts";
|
import { classifyVideosWorker, classifyVideoWorker } from "mq/exec/classifyVideo.ts";
|
||||||
import { WorkerError } from "mq/schema.ts";
|
import { WorkerError } from "mq/schema.ts";
|
||||||
import { lockManager } from "mq/lockManager.ts";
|
import { lockManager } from "@core/mq/lockManager.ts";
|
||||||
import Akari from "ml/akari.ts";
|
import Akari from "ml/akari.ts";
|
||||||
|
|
||||||
const shutdown = async (signal: string) => {
|
const shutdown = async (signal: string) => {
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
} from "mq/exec/executors.ts";
|
} from "mq/exec/executors.ts";
|
||||||
import { redis } from "@core/db/redis.ts";
|
import { redis } from "@core/db/redis.ts";
|
||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { lockManager } from "mq/lockManager.ts";
|
import { lockManager } from "@core/mq/lockManager.ts";
|
||||||
import { WorkerError } from "mq/schema.ts";
|
import { WorkerError } from "mq/schema.ts";
|
||||||
|
|
||||||
const releaseLockForJob = async (name: string) => {
|
const releaseLockForJob = async (name: string) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user