Compare commits
3 Commits
cdb44655b7
...
642e075721
Author | SHA1 | Date |
---|---|---|
Alie | 642e075721 | |
Alie | 1aacac9c41 | |
Alie | e472fe48b4 |
|
@ -10,7 +10,7 @@ services:
|
|||
MONGO_INITDB_DATABASE: bot
|
||||
volumes:
|
||||
- mongodb_data:/data/db
|
||||
|
||||
|
||||
bot-api:
|
||||
image: git.fai.st/fedi-image-bot/bot-api:latest
|
||||
container_name: bot-api
|
||||
|
@ -38,8 +38,8 @@ services:
|
|||
environment:
|
||||
PORT: 8081
|
||||
BOT_API_URI: "http://bot-api:8080"
|
||||
GELBOORU_IMAGES_PER_REQUEST: 100 # Number of images per request, maximum 100
|
||||
GELBOORU_TAGS: "2girls sleeping" # Tags of the images. The images will have all of these tags
|
||||
GELBOORU_IMAGES_PER_REQUEST: 100 # Number of images per request, maximum 100
|
||||
GELBOORU_TAGS: "2girls sleeping" # Tags of the images. The images will have all of these tags
|
||||
volumes:
|
||||
- ./:/usr/src/app:ro
|
||||
|
||||
|
|
|
@ -16,11 +16,12 @@
|
|||
"dependencies": {
|
||||
"compression": "^1.7.4",
|
||||
"express": "^4.19.2",
|
||||
"express-list-endpoints": "^6.0.0"
|
||||
"express-list-endpoints": "^6.0.0",
|
||||
"winston": "^3.13.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "bun run src/index.ts",
|
||||
"dev": "docker compose down -v && docker compose up",
|
||||
"test": "docker compose down -v && docker compose run fe-middleware 'sleep 5 && bun test'"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@ app.use(express.json());
|
|||
app.use(compression());
|
||||
|
||||
app.get("/", (_, res) => {
|
||||
const endpoints = listEndpoints(app);
|
||||
res.json({ endpoints });
|
||||
const endpoints = listEndpoints(app);
|
||||
res.json({ endpoints });
|
||||
});
|
||||
|
||||
app.get("/image", ImageController.get);
|
||||
|
||||
export default app;
|
||||
export default app;
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
import { Request, Response } from "express";
|
||||
import logger from "src/logger";
|
||||
import ImageService from "src/services/ImageService";
|
||||
|
||||
class ImageController {
|
||||
async get(req: Request, res: Response) {
|
||||
try {
|
||||
const image = await ImageService.get();
|
||||
res.json(image);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ "error": `Internal server error: ${error}` });
|
||||
}
|
||||
async get(_: Request, res: Response) {
|
||||
try {
|
||||
const image = await ImageService.get();
|
||||
res.json(image);
|
||||
} catch (error: any) {
|
||||
logger.error(error);
|
||||
res.status(500).json({ error: `Internal server error: ${error}` });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new ImageController();
|
||||
export default new ImageController();
|
||||
|
|
13
src/index.ts
13
src/index.ts
|
@ -1,15 +1,16 @@
|
|||
import { env } from "bun";
|
||||
import app from "src/app";
|
||||
import logger from "src/logger";
|
||||
|
||||
const PORT = env.PORT;
|
||||
|
||||
const server = app.listen(PORT, () =>
|
||||
console.log(`Express server listening on port ${PORT}`)
|
||||
logger.info(`Express server listening on port ${PORT}`)
|
||||
);
|
||||
|
||||
process.on("SIGTERM", () => {
|
||||
server.close(() => {
|
||||
console.log('Server closed.');
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
server.close(() => {
|
||||
logger.info("Server closed.");
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import winston, { format } from "winston";
|
||||
|
||||
const logger = winston.createLogger({
|
||||
format: format.combine(
|
||||
format.timestamp(),
|
||||
format.align(),
|
||||
format.colorize({ all: true }),
|
||||
format.printf((info) => `${info.timestamp} ${info.level}:${info.message}`)
|
||||
),
|
||||
});
|
||||
|
||||
const files = new winston.transports.File({ filename: "/tmp/express.log" });
|
||||
const console = new winston.transports.Console();
|
||||
|
||||
logger.clear().add(files).add(console);
|
||||
|
||||
export default logger;
|
|
@ -1,22 +1,22 @@
|
|||
import { env } from "bun";
|
||||
import logger from "src/logger";
|
||||
import { BotApiResponse } from "src/types/BotApiResponse";
|
||||
|
||||
class BotApiService {
|
||||
readonly BOT_API_URI = env.BOT_API_URI;
|
||||
|
||||
async getAll(): Promise<BotApiResponse> {
|
||||
const get_url = `${this.BOT_API_URI}/images`;
|
||||
const response: BotApiResponse = await fetch(get_url)
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
throw new Error("Error fetching images");
|
||||
} else {
|
||||
return res.json();
|
||||
}
|
||||
}) as BotApiResponse;
|
||||
return response;
|
||||
}
|
||||
readonly BOT_API_URI = env.BOT_API_URI;
|
||||
|
||||
async getAll(): Promise<BotApiResponse> {
|
||||
const get_url = `${this.BOT_API_URI}/images`;
|
||||
const response: BotApiResponse = (await fetch(get_url).then(async (res) => {
|
||||
if (!res.ok) {
|
||||
logger.error(`${res.status}: ${res.statusText}, ${await res.text()}`);
|
||||
throw new Error("Error fetching images");
|
||||
} else {
|
||||
return res.json();
|
||||
}
|
||||
})) as BotApiResponse;
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
export default new BotApiService();
|
||||
export default new BotApiService();
|
||||
|
|
|
@ -1,24 +1,32 @@
|
|||
import { env } from "bun";
|
||||
import logger from "src/logger";
|
||||
import GelbooruApiResponse from "src/types/GelbooruApiResponse";
|
||||
import GelbooruServiceResponse from "src/types/GelbooruServiceResponse";
|
||||
|
||||
class GelbooruApiService {
|
||||
async get(): Promise<GelbooruServiceResponse> {
|
||||
const LIMIT = env.GELBOORU_IMAGES_PER_REQUEST || 100;
|
||||
const TAGS = encodeURIComponent(env.GELBOORU_TAGS || "");
|
||||
const url: string = `https://gelbooru.com/index.php?page=dapi&s=post&q=index&limit=${LIMIT}&json=1&tags=${TAGS}`;
|
||||
async get(): Promise<GelbooruServiceResponse> {
|
||||
const LIMIT = env.GELBOORU_IMAGES_PER_REQUEST || 100;
|
||||
const TAGS = encodeURIComponent(env.GELBOORU_TAGS || "");
|
||||
const url: string = `https://gelbooru.com/index.php?page=dapi&s=post&q=index&limit=${LIMIT}&json=1&tags=${TAGS}`;
|
||||
|
||||
const response: GelbooruApiResponse = await fetch(url)
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
throw new Error("Error fetching images");
|
||||
} else {
|
||||
return res.json();
|
||||
}
|
||||
}) as GelbooruApiResponse;
|
||||
const response: GelbooruApiResponse = (await fetch(url).then(
|
||||
async (res) => {
|
||||
if (!res.ok) {
|
||||
logger.error(`${res.status}: ${res.statusText}, ${await res.text()}`);
|
||||
throw new Error("Error fetching images");
|
||||
} else {
|
||||
return res.json();
|
||||
}
|
||||
}
|
||||
)) as GelbooruApiResponse;
|
||||
|
||||
return { posts: response.post.map(post => ({ url: post.file_url, tags: post.tags.split(" ") })) };
|
||||
}
|
||||
return {
|
||||
posts: response.post.map((post) => ({
|
||||
url: post.file_url,
|
||||
tags: post.tags.split(" "),
|
||||
})),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default new GelbooruApiService();
|
||||
export default new GelbooruApiService();
|
||||
|
|
|
@ -2,33 +2,38 @@ import GelbooruServiceResponse from "src/types/GelbooruServiceResponse";
|
|||
import Image from "src/types/Image";
|
||||
import BotApiService from "src/services/BotApiService";
|
||||
import GelbooruApiService from "src/services/GelbooruApiService";
|
||||
import logger from "src/logger";
|
||||
|
||||
class ImageService {
|
||||
postsQueue: Image[] = [];
|
||||
postsQueue: Image[] = [];
|
||||
|
||||
async get(): Promise<Image> {
|
||||
while (this.postsQueue.length === 0) {
|
||||
const validPosts = await this.getNewValidImages();
|
||||
this.postsQueue = validPosts;
|
||||
}
|
||||
return this.postsQueue.pop() as Image;
|
||||
async get(): Promise<Image> {
|
||||
while (this.postsQueue.length === 0) {
|
||||
const validPosts = await this.getNewValidImages();
|
||||
this.postsQueue = validPosts;
|
||||
logger.info(`Got ${validPosts.length} images from remote`);
|
||||
}
|
||||
return this.postsQueue.pop() as Image;
|
||||
}
|
||||
|
||||
private async getNewValidImages(): Promise<Image[]> {
|
||||
const gelbooruResponse: GelbooruServiceResponse = await GelbooruApiService.get();
|
||||
const posts = gelbooruResponse.posts;
|
||||
private async getNewValidImages(): Promise<Image[]> {
|
||||
const gelbooruResponse: GelbooruServiceResponse =
|
||||
await GelbooruApiService.get();
|
||||
const posts = gelbooruResponse.posts;
|
||||
|
||||
const botResponse = await BotApiService.getAll();
|
||||
const imagesUrls = botResponse.images.map(image => image.url);
|
||||
const botResponse = await BotApiService.getAll();
|
||||
const imagesUrls = botResponse.images.map((image) => image.url);
|
||||
|
||||
const validPosts = Promise.all(posts
|
||||
.filter(post => !imagesUrls.some(url => url === post.url))
|
||||
.map((post): Image => {
|
||||
return { url: post.url, tags: post.tags };
|
||||
}));
|
||||
|
||||
return validPosts;
|
||||
}
|
||||
const validPosts = Promise.all(
|
||||
posts
|
||||
.filter((post) => !imagesUrls.some((url) => url === post.url))
|
||||
.map((post): Image => {
|
||||
return { url: post.url, tags: post.tags };
|
||||
})
|
||||
);
|
||||
|
||||
return validPosts;
|
||||
}
|
||||
}
|
||||
|
||||
export default new ImageService();
|
||||
export default new ImageService();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import BotImage from "src/types/BotImage";
|
||||
|
||||
export interface BotApiResponse {
|
||||
images: BotImage[]
|
||||
}
|
||||
images: BotImage[];
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export default interface BotImage {
|
||||
_id: string
|
||||
url: string
|
||||
status: string
|
||||
tags: string[]
|
||||
__v: number
|
||||
_id: string;
|
||||
url: string;
|
||||
status: string;
|
||||
tags: string[];
|
||||
__v: number;
|
||||
}
|
||||
|
|
|
@ -1,42 +1,42 @@
|
|||
export default interface GelbooruApiResponse {
|
||||
"@attributes": Attributes
|
||||
post: Post[]
|
||||
"@attributes": Attributes;
|
||||
post: Post[];
|
||||
}
|
||||
|
||||
|
||||
export interface Attributes {
|
||||
limit: number
|
||||
offset: number
|
||||
count: number
|
||||
limit: number;
|
||||
offset: number;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface Post {
|
||||
id: number
|
||||
created_at: string
|
||||
score: number
|
||||
width: number
|
||||
height: number
|
||||
md5: string
|
||||
directory: string
|
||||
image: string
|
||||
rating: string
|
||||
source: string
|
||||
change: number
|
||||
owner: string
|
||||
creator_id: number
|
||||
parent_id: number
|
||||
sample: number
|
||||
preview_height: number
|
||||
preview_width: number
|
||||
tags: string
|
||||
title: string
|
||||
has_notes: string
|
||||
has_comments: string
|
||||
file_url: string
|
||||
preview_url: string
|
||||
sample_url: string
|
||||
sample_height: number
|
||||
sample_width: number
|
||||
status: string
|
||||
post_locked: number
|
||||
has_children: string
|
||||
id: number;
|
||||
created_at: string;
|
||||
score: number;
|
||||
width: number;
|
||||
height: number;
|
||||
md5: string;
|
||||
directory: string;
|
||||
image: string;
|
||||
rating: string;
|
||||
source: string;
|
||||
change: number;
|
||||
owner: string;
|
||||
creator_id: number;
|
||||
parent_id: number;
|
||||
sample: number;
|
||||
preview_height: number;
|
||||
preview_width: number;
|
||||
tags: string;
|
||||
title: string;
|
||||
has_notes: string;
|
||||
has_comments: string;
|
||||
file_url: string;
|
||||
preview_url: string;
|
||||
sample_url: string;
|
||||
sample_height: number;
|
||||
sample_width: number;
|
||||
status: string;
|
||||
post_locked: number;
|
||||
has_children: string;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default interface GelbooruPost {
|
||||
url: string;
|
||||
tags: string[];
|
||||
}
|
||||
url: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import GelbooruPost from "src/types/GelbooruPost";
|
||||
|
||||
export default interface GelbooruServiceResponse {
|
||||
posts: GelbooruPost[];
|
||||
}
|
||||
posts: GelbooruPost[];
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default interface Image {
|
||||
url: string;
|
||||
tags: string[];
|
||||
}
|
||||
url: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
|
|
@ -1,41 +1,36 @@
|
|||
import { afterEach, describe, expect, it, mock, jest } from "bun:test";
|
||||
import Image from "src/types/Image";
|
||||
import ImageService from "src/services/ImageService";
|
||||
import GelbooruApiResponse from "src/types/GelbooruServiceResponse";
|
||||
import { BotApiResponse } from "src/types/BotApiResponse";
|
||||
import fs from "node:fs";
|
||||
import ImageController from "src/controllers/ImageController";
|
||||
import { afterEach, describe, expect, it, mock } from "bun:test";
|
||||
import app from "src/app";
|
||||
import ImageService from "src/services/ImageService";
|
||||
import request from "supertest";
|
||||
|
||||
const imageServiceOriginal = ImageService;
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
mock.module("src/services/ImageService", () => ({
|
||||
default: imageServiceOriginal,
|
||||
}));
|
||||
})
|
||||
mock.restore();
|
||||
mock.module("src/services/ImageService", () => ({
|
||||
default: imageServiceOriginal,
|
||||
}));
|
||||
});
|
||||
|
||||
describe("endpoint returns the correct status codes", () => {
|
||||
it("should return 200 if successful", async () => {
|
||||
// Can't mock ImageService partially because of bun incomplete implementation of testing :(
|
||||
// It goes to the network directly to test
|
||||
const res = await request(app).get("/image");
|
||||
expect(res.status).toBe(200);
|
||||
});
|
||||
it("should return 200 if successful", async () => {
|
||||
// Can't mock ImageService partially because of bun incomplete implementation of testing :(
|
||||
// It goes to the network directly to test
|
||||
const res = await request(app).get("/image");
|
||||
expect(res.status).toBe(200);
|
||||
});
|
||||
|
||||
it("should return 500 if any error happens", async () => {
|
||||
mock.module("src/services/ImageService", () => {
|
||||
return {
|
||||
default: {
|
||||
get: () => {
|
||||
throw new Error("Controlled error");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
const res = await request(app).get("/image");
|
||||
expect(res.status).toBe(500);
|
||||
it("should return 500 if any error happens", async () => {
|
||||
mock.module("src/services/ImageService", () => {
|
||||
return {
|
||||
default: {
|
||||
get: () => {
|
||||
throw new Error("Controlled error");
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
})
|
||||
const res = await request(app).get("/image");
|
||||
expect(res.status).toBe(500);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,61 +1,68 @@
|
|||
import { afterEach, describe, expect, it, mock, spyOn } from "bun:test";
|
||||
import Image from "src/types/Image";
|
||||
import ImageService from "src/services/ImageService";
|
||||
import GelbooruApiResponse from "src/types/GelbooruServiceResponse";
|
||||
import { BotApiResponse } from "src/types/BotApiResponse";
|
||||
import GelbooruApiService from "src/services/GelbooruApiService";
|
||||
import { afterEach, describe, expect, it, mock } from "bun:test";
|
||||
import BotApiService from "src/services/BotApiService";
|
||||
import fs from "node:fs";
|
||||
import GelbooruApiService from "src/services/GelbooruApiService";
|
||||
import ImageService from "src/services/ImageService";
|
||||
import { BotApiResponse } from "src/types/BotApiResponse";
|
||||
import GelbooruApiResponse from "src/types/GelbooruServiceResponse";
|
||||
import Image from "src/types/Image";
|
||||
|
||||
const imageServiceOriginal = ImageService;
|
||||
const gelbooruApiServiceOriginal = GelbooruApiService;
|
||||
const botApiServiceOriginal = BotApiService;
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
mock.module("src/services/ImageService", () => ({
|
||||
default: imageServiceOriginal,
|
||||
}));
|
||||
mock.module("src/services/GelbooruApiService", () => ({
|
||||
default: gelbooruApiServiceOriginal,
|
||||
}));
|
||||
mock.module("src/services/BotApiService", () => ({
|
||||
default: botApiServiceOriginal
|
||||
}));
|
||||
})
|
||||
mock.restore();
|
||||
mock.module("src/services/ImageService", () => ({
|
||||
default: imageServiceOriginal,
|
||||
}));
|
||||
mock.module("src/services/GelbooruApiService", () => ({
|
||||
default: gelbooruApiServiceOriginal,
|
||||
}));
|
||||
mock.module("src/services/BotApiService", () => ({
|
||||
default: botApiServiceOriginal,
|
||||
}));
|
||||
});
|
||||
|
||||
describe("endpoint gets a non repeated image", () => {
|
||||
it("should return an image that is not in the response of the /images endpoint of the bot API", async () => {
|
||||
const REPEATED_URL = "https://fastly.picsum.photos/id/1/10/20.jpg?hmac=gY6PvUXFacKfYpBpTTVcNLxumpyMmoCamM-J5DOPwNc";
|
||||
const UNIQUE_URL = "https://fastly.picsum.photos/id/2/10/20.jpg?hmac=zy6lz21CuRIstr9ETx9h5AuoH50s_L2uIEct3dROpY8";
|
||||
it("should return an image that is not in the response of the /images endpoint of the bot API", async () => {
|
||||
const REPEATED_URL =
|
||||
"https://fastly.picsum.photos/id/1/10/20.jpg?hmac=gY6PvUXFacKfYpBpTTVcNLxumpyMmoCamM-J5DOPwNc";
|
||||
const UNIQUE_URL =
|
||||
"https://fastly.picsum.photos/id/2/10/20.jpg?hmac=zy6lz21CuRIstr9ETx9h5AuoH50s_L2uIEct3dROpY8";
|
||||
|
||||
mock.module("src/services/GelbooruApiService", () => {
|
||||
let alreadyCalled = false;
|
||||
return {
|
||||
default: {
|
||||
get: (): GelbooruApiResponse => {
|
||||
if (alreadyCalled) {
|
||||
return { posts: [{ url: UNIQUE_URL, tags: [] }] };
|
||||
} else {
|
||||
alreadyCalled = true;
|
||||
return { posts: [{ url: REPEATED_URL, tags: [] }] };
|
||||
}
|
||||
}
|
||||
}
|
||||
mock.module("src/services/GelbooruApiService", () => {
|
||||
let alreadyCalled = false;
|
||||
return {
|
||||
default: {
|
||||
get: (): GelbooruApiResponse => {
|
||||
if (alreadyCalled) {
|
||||
return { posts: [{ url: UNIQUE_URL, tags: [] }] };
|
||||
} else {
|
||||
alreadyCalled = true;
|
||||
return { posts: [{ url: REPEATED_URL, tags: [] }] };
|
||||
}
|
||||
});
|
||||
|
||||
mock.module("src/services/BotApiService", () => ({
|
||||
default: {
|
||||
getAll: (): BotApiResponse => ({
|
||||
images: [
|
||||
{ _id: "0", url: REPEATED_URL, status: "consumed", tags: ["pokemon", "computer"], "__v": 0 }
|
||||
]
|
||||
})
|
||||
}
|
||||
}));
|
||||
|
||||
const image: Image = await ImageService.get();
|
||||
expect(image.url).not.toBe(REPEATED_URL);
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
mock.module("src/services/BotApiService", () => ({
|
||||
default: {
|
||||
getAll: (): BotApiResponse => ({
|
||||
images: [
|
||||
{
|
||||
_id: "0",
|
||||
url: REPEATED_URL,
|
||||
status: "consumed",
|
||||
tags: ["pokemon", "computer"],
|
||||
__v: 0,
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
const image: Image = await ImageService.get();
|
||||
expect(image.url).not.toBe(REPEATED_URL);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,6 +18,6 @@
|
|||
"types": [
|
||||
"bun-types" // add Bun global
|
||||
],
|
||||
"baseUrl": "./",
|
||||
"baseUrl": "./"
|
||||
}
|
||||
}
|
||||
|
|
196
yarn.lock
196
yarn.lock
|
@ -1,8 +1,22 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
# bun ./bun.lockb --hash: D2F4EB28F4D7A9AB-c66ee1379fa18f54-F2EFEC486AB909C5-0f692bf48fab37c2
|
||||
# bun ./bun.lockb --hash: 2D4E664D3091262E-6e6ac083a9fdd796-C67F3D33680268C0-c674fd6e89aeea1f
|
||||
|
||||
|
||||
"@colors/colors@1.6.0", "@colors/colors@^1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz"
|
||||
integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==
|
||||
|
||||
"@dabh/diagnostics@^2.0.2":
|
||||
version "2.0.3"
|
||||
resolved "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz"
|
||||
integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==
|
||||
dependencies:
|
||||
colorspace "1.1.x"
|
||||
enabled "2.0.x"
|
||||
kuler "^2.0.0"
|
||||
|
||||
"@types/body-parser@*":
|
||||
version "1.19.5"
|
||||
resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz"
|
||||
|
@ -128,6 +142,11 @@
|
|||
"@types/methods" "^1.1.4"
|
||||
"@types/superagent" "^8.1.0"
|
||||
|
||||
"@types/triple-beam@^1.3.2":
|
||||
version "1.3.5"
|
||||
resolved "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz"
|
||||
integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==
|
||||
|
||||
"@types/ws@~8.5.10":
|
||||
version "8.5.10"
|
||||
resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz"
|
||||
|
@ -153,6 +172,11 @@ asap@^2.0.0:
|
|||
resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz"
|
||||
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
|
||||
|
||||
async@^3.2.3:
|
||||
version "3.2.5"
|
||||
resolved "https://registry.npmjs.org/async/-/async-3.2.5.tgz"
|
||||
integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz"
|
||||
|
@ -205,6 +229,47 @@ call-bind@^1.0.7:
|
|||
get-intrinsic "^1.2.4"
|
||||
set-function-length "^1.2.1"
|
||||
|
||||
color@^3.1.3:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.npmjs.org/color/-/color-3.2.1.tgz"
|
||||
integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==
|
||||
dependencies:
|
||||
color-convert "^1.9.3"
|
||||
color-string "^1.6.0"
|
||||
|
||||
color-convert@^1.9.3:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
|
||||
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
|
||||
dependencies:
|
||||
color-name "1.1.3"
|
||||
|
||||
color-name@1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
|
||||
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
|
||||
|
||||
color-name@^1.0.0:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
color-string@^1.6.0:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz"
|
||||
integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==
|
||||
dependencies:
|
||||
color-name "^1.0.0"
|
||||
simple-swizzle "^0.2.2"
|
||||
|
||||
colorspace@1.1.x:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz"
|
||||
integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==
|
||||
dependencies:
|
||||
color "^3.1.3"
|
||||
text-hex "1.0.x"
|
||||
|
||||
combined-stream@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz"
|
||||
|
@ -315,6 +380,11 @@ ee-first@1.1.1:
|
|||
resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz"
|
||||
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
|
||||
|
||||
enabled@2.0.x:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz"
|
||||
integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==
|
||||
|
||||
encodeurl@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz"
|
||||
|
@ -389,6 +459,11 @@ fast-safe-stringify@^2.1.1:
|
|||
resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz"
|
||||
integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==
|
||||
|
||||
fecha@^4.2.0:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz"
|
||||
integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==
|
||||
|
||||
finalhandler@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz"
|
||||
|
@ -402,6 +477,11 @@ finalhandler@1.2.0:
|
|||
statuses "2.0.1"
|
||||
unpipe "~1.0.0"
|
||||
|
||||
fn.name@1.x.x:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz"
|
||||
integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==
|
||||
|
||||
form-data@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz"
|
||||
|
@ -501,7 +581,7 @@ iconv-lite@0.4.24:
|
|||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
inherits@2.0.4:
|
||||
inherits@2.0.4, inherits@^2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
@ -511,6 +591,33 @@ ipaddr.js@1.9.1:
|
|||
resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz"
|
||||
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
|
||||
|
||||
is-arrayish@^0.3.1:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz"
|
||||
integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
|
||||
|
||||
is-stream@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz"
|
||||
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
|
||||
|
||||
kuler@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz"
|
||||
integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==
|
||||
|
||||
logform@^2.3.2, logform@^2.4.0:
|
||||
version "2.6.0"
|
||||
resolved "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz"
|
||||
integrity sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==
|
||||
dependencies:
|
||||
"@colors/colors" "1.6.0"
|
||||
"@types/triple-beam" "^1.3.2"
|
||||
fecha "^4.2.0"
|
||||
ms "^2.1.1"
|
||||
safe-stable-stringify "^2.3.1"
|
||||
triple-beam "^1.3.0"
|
||||
|
||||
lru-cache@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz"
|
||||
|
@ -565,7 +672,7 @@ ms@2.1.2:
|
|||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
ms@2.1.3:
|
||||
ms@2.1.3, ms@^2.1.1:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
@ -599,6 +706,13 @@ once@^1.4.0:
|
|||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
one-time@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz"
|
||||
integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==
|
||||
dependencies:
|
||||
fn.name "1.x.x"
|
||||
|
||||
parseurl@~1.3.3:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz"
|
||||
|
@ -639,16 +753,30 @@ raw-body@2.5.2:
|
|||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
readable-stream@^3.4.0, readable-stream@^3.6.0:
|
||||
version "3.6.2"
|
||||
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz"
|
||||
integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
|
||||
dependencies:
|
||||
inherits "^2.0.3"
|
||||
string_decoder "^1.1.1"
|
||||
util-deprecate "^1.0.1"
|
||||
|
||||
safe-buffer@5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
safe-buffer@5.2.1:
|
||||
safe-buffer@5.2.1, safe-buffer@~5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
safe-stable-stringify@^2.3.1:
|
||||
version "2.4.3"
|
||||
resolved "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz"
|
||||
integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==
|
||||
|
||||
"safer-buffer@>= 2.1.2 < 3":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"
|
||||
|
@ -717,11 +845,30 @@ side-channel@^1.0.4:
|
|||
get-intrinsic "^1.2.4"
|
||||
object-inspect "^1.13.1"
|
||||
|
||||
simple-swizzle@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz"
|
||||
integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==
|
||||
dependencies:
|
||||
is-arrayish "^0.3.1"
|
||||
|
||||
stack-trace@0.0.x:
|
||||
version "0.0.10"
|
||||
resolved "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz"
|
||||
integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==
|
||||
|
||||
statuses@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz"
|
||||
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
|
||||
|
||||
string_decoder@^1.1.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
|
||||
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
|
||||
dependencies:
|
||||
safe-buffer "~5.2.0"
|
||||
|
||||
superagent@^8.1.2:
|
||||
version "8.1.2"
|
||||
resolved "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz"
|
||||
|
@ -746,11 +893,21 @@ supertest@^6.3.4:
|
|||
methods "^1.1.2"
|
||||
superagent "^8.1.2"
|
||||
|
||||
text-hex@1.0.x:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz"
|
||||
integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==
|
||||
|
||||
toidentifier@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz"
|
||||
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
|
||||
|
||||
triple-beam@^1.3.0:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz"
|
||||
integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==
|
||||
|
||||
type-is@~1.6.18:
|
||||
version "1.6.18"
|
||||
resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz"
|
||||
|
@ -774,6 +931,11 @@ unpipe@1.0.0, unpipe@~1.0.0:
|
|||
resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
|
||||
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
|
||||
|
||||
util-deprecate@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
|
||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||
|
||||
utils-merge@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz"
|
||||
|
@ -784,6 +946,32 @@ vary@~1.1.2:
|
|||
resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz"
|
||||
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
|
||||
|
||||
winston@^3.13.0:
|
||||
version "3.13.0"
|
||||
resolved "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz"
|
||||
integrity sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==
|
||||
dependencies:
|
||||
"@dabh/diagnostics" "^2.0.2"
|
||||
"@colors/colors" "^1.6.0"
|
||||
async "^3.2.3"
|
||||
is-stream "^2.0.0"
|
||||
logform "^2.4.0"
|
||||
one-time "^1.0.0"
|
||||
readable-stream "^3.4.0"
|
||||
safe-stable-stringify "^2.3.1"
|
||||
stack-trace "0.0.x"
|
||||
triple-beam "^1.3.0"
|
||||
winston-transport "^4.7.0"
|
||||
|
||||
winston-transport@^4.7.0:
|
||||
version "4.7.0"
|
||||
resolved "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz"
|
||||
integrity sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==
|
||||
dependencies:
|
||||
logform "^2.3.2"
|
||||
readable-stream "^3.6.0"
|
||||
triple-beam "^1.3.0"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
|
||||
|
|
Loading…
Reference in New Issue