diff --git a/packages/backend/.dockerignore b/packages/backend/.dockerignore new file mode 100644 index 00000000..93f13619 --- /dev/null +++ b/packages/backend/.dockerignore @@ -0,0 +1,2 @@ +node_modules +npm-debug.log diff --git a/packages/backend/.gitignore b/packages/backend/.gitignore new file mode 100644 index 00000000..0c91abe0 --- /dev/null +++ b/packages/backend/.gitignore @@ -0,0 +1,7 @@ +.bin +node_modules +build +dist +bin/Logs +release +types diff --git a/packages/backend/.prettierignore b/packages/backend/.prettierignore new file mode 100644 index 00000000..a7c47537 --- /dev/null +++ b/packages/backend/.prettierignore @@ -0,0 +1 @@ +types/ diff --git a/packages/backend/.prettierrc.json b/packages/backend/.prettierrc.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/backend/.prettierrc.json @@ -0,0 +1 @@ +{} diff --git a/packages/backend/Dockerfile b/packages/backend/Dockerfile new file mode 100644 index 00000000..84188f64 --- /dev/null +++ b/packages/backend/Dockerfile @@ -0,0 +1,22 @@ +# 使用官方 Node.js 镜像作为基础镜像 +FROM m.daocloud.io/docker.io/library/node:20 + +# 设置工作目录 +WORKDIR /app + +# 复制 package.json 和 package-lock.json +COPY package*.json ./ + +# 安装依赖 +RUN npm install --registry=https://registry.npmmirror.com + +# 复制项目文件 +COPY . . + +RUN npm run build + +# 暴露应用运行的端口 +EXPOSE 3000 + +# 启动应用 +CMD ["node", "dist/inedx.js"] diff --git a/packages/backend/docker-compose.yml b/packages/backend/docker-compose.yml new file mode 100644 index 00000000..38244af6 --- /dev/null +++ b/packages/backend/docker-compose.yml @@ -0,0 +1,30 @@ +version: '3' +services: + app: + build: . + ports: + - "3000:3000" + volumes: + - .:/app + - /app/node_modules + environment: + NODE_ENV: development + MYSQL_HOST: mysql + MYSQL_USER: root + MYSQL_PASSWORD: 123456 + MYSQL_DATABASE: mediago + depends_on: + - mysql + + mysql: + image: m.daocloud.io/docker.io/library/mysql:5.7 + ports: + - "3306:3306" + environment: + MYSQL_ROOT_PASSWORD: 123456 + MYSQL_DATABASE: mediago + volumes: + - mysql-data:/var/lib/mysql + +volumes: + mysql-data: diff --git a/packages/backend/eslint.config.js b/packages/backend/eslint.config.js new file mode 100644 index 00000000..7a25179e --- /dev/null +++ b/packages/backend/eslint.config.js @@ -0,0 +1,16 @@ +import globals from "globals"; +import pluginJs from "@eslint/js"; +import tseslint from "typescript-eslint"; + +export default [ + { + languageOptions: { globals: globals.node }, + }, + pluginJs.configs.recommended, + ...tseslint.configs.recommended, + { + rules: { + "@typescript-eslint/no-explicit-any": "warn", + }, + }, +]; diff --git a/packages/backend/gulpfile.ts b/packages/backend/gulpfile.ts new file mode 100644 index 00000000..8c94c6ca --- /dev/null +++ b/packages/backend/gulpfile.ts @@ -0,0 +1 @@ +export { dev, build } from "./scripts"; diff --git a/packages/backend/package.json b/packages/backend/package.json new file mode 100644 index 00000000..c12853da --- /dev/null +++ b/packages/backend/package.json @@ -0,0 +1,72 @@ +{ + "name": "backend", + "version": "0.1.0", + "description": "在线视频下载器", + "type": "commonjs", + "main": "dist/index.js", + "scripts": { + "start": "nodemon dist/index.js", + "dev": "cross-env NODE_ENV=development gulp dev", + "build": "cross-env NODE_ENV=production gulp build", + "mysql": "docker-compose up -d mysql", + "mysql:stop": "docker-compose stop mysql", + "types": "tsc", + "types:watch": "tsc -w", + "lint-staged": "lint-staged" + }, + "lint-staged": { + "*.{js,jsx,ts,tsx}": "eslint" + }, + "author": "", + "license": "MIT", + "devDependencies": { + "@eslint/js": "^8.57.0", + "@types/cors": "^2.8.17", + "@types/express": "^4.17.21", + "@types/fs-extra": "^11.0.4", + "@types/glob": "^8.1.0", + "@types/gulp": "^4.0.17", + "@types/koa": "^2.15.0", + "@types/koa__router": "^12.0.4", + "@types/lodash": "^4.17.4", + "@types/mime-types": "^2.1.4", + "@types/node": "^20.12.12", + "@types/node-fetch": "^2.6.11", + "@types/semver": "^7.5.8", + "@types/serve-handler": "^6.1.4", + "@types/undertaker": "^1.2.11", + "consola": "^3.2.3", + "cross-env": "^7.0.3", + "del": "^7.1.0", + "dotenv": "^16.4.5", + "esbuild": "^0.21.4", + "esbuild-register": "^3.5.0", + "eslint": "^8.57.0", + "glob": "^10.4.1", + "globals": "^15.3.0", + "gulp": "^5.0.0", + "nodemon": "^3.1.4", + "semver": "^7.6.2", + "typescript": "^5.4.5", + "typescript-eslint": "^7.10.0" + }, + "dependencies": { + "@koa/router": "^12.0.1", + "axios": "^1.7.2", + "cheerio": "1.0.0-rc.12", + "cors": "^2.8.5", + "dayjs": "^1.11.11", + "execa": "^9.1.0", + "fs-extra": "^11.2.0", + "inversify": "^6.0.2", + "koa": "^2.15.3", + "lint-staged": "^15.2.5", + "lodash": "^4.17.21", + "mime-types": "^2.1.35", + "mysql2": "^3.11.0", + "nanoid": "^5.0.7", + "reflect-metadata": "^0.2.2", + "typeorm": "0.3.20" + }, + "keywords": [] +} diff --git a/packages/backend/scripts/config.ts b/packages/backend/scripts/config.ts new file mode 100644 index 00000000..e82f3e41 --- /dev/null +++ b/packages/backend/scripts/config.ts @@ -0,0 +1,57 @@ +import { Env, isDev, mainResolve } from "./utils"; +import esbuild from "esbuild"; + +const external = [ + "electron", + "nock", + "aws-sdk", + "mock-aws-s3", + "@cliqz/adblocker-electron-preload", + "node-pty", +]; + +function getConfig(): esbuild.BuildOptions { + const getDefine = (): Record => { + if (isDev) { + return { + __bin__: `"${mainResolve("app/bin").replace(/\\/g, "\\\\")}"`, + }; + } + + return { + ...Env.getInstance().loadDotEnvDefined(), + "process.env.NODE_ENV": '"production"', + }; + }; + + return { + bundle: true, + sourcemap: process.env.NODE_ENV === "development", + external, + define: getDefine(), + outdir: mainResolve("dist"), + loader: { ".png": "file" }, + minify: process.env.NODE_ENV === "production", + }; +} + +function buildOptions( + entry: string, + platform: esbuild.Platform, + target: string, +): esbuild.BuildOptions { + return { + ...getConfig(), + entryPoints: [mainResolve(entry)], + platform: platform, + target: [target], + }; +} + +export function browserOptions(entry: string): esbuild.BuildOptions { + return buildOptions(entry, "browser", "chrome89"); +} + +export function nodeOptions(entry: string): esbuild.BuildOptions { + return buildOptions(entry, "node", "node16.13"); +} diff --git a/packages/backend/scripts/index.ts b/packages/backend/scripts/index.ts new file mode 100644 index 00000000..102178bb --- /dev/null +++ b/packages/backend/scripts/index.ts @@ -0,0 +1,49 @@ +import { deleteSync } from "del"; +import { mainResolve, Env } from "./utils"; +import gulp from "gulp"; +import * as esbuild from "esbuild"; +import consola from "consola"; +import { nodeOptions } from "./config"; +// import fs from "fs"; + +const env = Env.getInstance(); +env.loadDotEnvRuntime(); + +async function clean() { + return deleteSync([mainResolve("app/dist")]); +} + +// async function copyBin() { +// const source = mainResolve("bin", process.platform); +// const target = mainResolve("app/bin"); +// fs.cpSync(source, target, { recursive: true }); +// } + +// const copy = gulp.parallel(copyBin); + +async function watchTask() { + const main = await esbuild.context(nodeOptions("src/index.ts")); + + const watcher = gulp.watch(["./src"]); + watcher + .on("ready", async () => { + await main.rebuild(); + }) + .on("change", async () => { + await main.rebuild(); + }) + .on("error", (error) => { + consola.error(error); + }); + return Promise.resolve(); +} + +async function buildTask() { + await esbuild.build(nodeOptions("src/index.ts")); +} + +// 开发环境 +// TODO 暂时不拷贝 bin 文件夹 +export const dev = gulp.series(watchTask); +// 构建打包 +export const build = gulp.series(clean, buildTask); diff --git a/packages/backend/scripts/utils.ts b/packages/backend/scripts/utils.ts new file mode 100644 index 00000000..f2c49a9d --- /dev/null +++ b/packages/backend/scripts/utils.ts @@ -0,0 +1,71 @@ +import dotenv from "dotenv"; +import { resolve } from "path"; +import fs from "fs-extra"; + +const baseResolve = (...r: any[]) => resolve(__dirname, ...r); +export const mainResolve = (...r: string[]) => baseResolve("..", ...r); +export const rootResolve = (...r: string[]) => baseResolve("../../..", ...r); + +export const isDev = process.env.NODE_ENV === "development"; + +export const isLinux = process.platform === "linux"; +export const isMac = process.platform === "darwin"; +export const isWin = process.platform === "win32"; + +export class Env { + env: Record = {}; + nodeEnv = ""; + static instance: Env; + + private constructor(nodeEnv = process.env.NODE_ENV) { + const env = this.parseEnv(rootResolve(".env")); + const modeEnv = this.parseEnv(rootResolve(`.env.${nodeEnv}`)); + const localEnv = this.parseEnv(rootResolve(`.env.${nodeEnv}.local`)); + + this.nodeEnv = nodeEnv || "development"; + this.env = { ...env, ...modeEnv, ...localEnv }; + } + + static getInstance() { + if (!Env.instance) { + Env.instance = new Env(); + } + + return Env.instance; + } + + private parseEnv(path: string) { + if (!fs.existsSync(path)) { + return null; + } + + const parsed = dotenv.parse(fs.readFileSync(path)); + if (!parsed) { + return null; + } + + return Object.keys(parsed).reduce((prev: any, curr) => { + prev[curr] = parsed[curr]; + return prev; + }, {}); + } + + loadDotEnvRuntime() { + Object.keys(this.env).forEach((key) => { + if (process.env[key] != null || !this.env[key]) return; + process.env[key] = this.env[key]; + }); + } + + loadDotEnvDefined() { + return Object.keys(this.env).reduce>((prev, cur) => { + if (!cur.startsWith("APP_")) return prev; + prev[`process.env.${[cur]}`] = JSON.stringify(this.env[cur]); + return prev; + }, {}); + } + + get isDev() { + return this.nodeEnv === "development"; + } +} diff --git a/packages/backend/src/app.ts b/packages/backend/src/app.ts new file mode 100644 index 00000000..c2b76b43 --- /dev/null +++ b/packages/backend/src/app.ts @@ -0,0 +1,34 @@ +import { inject, injectable } from "inversify"; +import { TYPES } from "./types.ts"; +import TypeORM from "./vendor/TypeORM.ts"; +import RouterHandlerService from "./core/router.ts"; +import Koa from "koa"; + +@injectable() +export default class ElectronApp extends Koa { + constructor( + @inject(TYPES.RouterHandlerService) + private readonly router: RouterHandlerService, + @inject(TYPES.TypeORM) + private readonly db: TypeORM, + ) { + super(); + } + + private async vendorInit() { + await this.db.init(); + } + + async init(): Promise { + this.router.init(); + + // vendor + await this.vendorInit(); + + this.use(this.router.routes()).use(this.router.allowedMethods()); + + this.listen(3000, () => { + console.log("Server running on port 3000"); + }); + } +} diff --git a/packages/backend/src/controller/HomeController.ts b/packages/backend/src/controller/HomeController.ts new file mode 100644 index 00000000..4905f68c --- /dev/null +++ b/packages/backend/src/controller/HomeController.ts @@ -0,0 +1,18 @@ +import { inject, injectable } from "inversify"; +import { type Controller } from "../interfaces.ts"; +import { TYPES } from "../types.ts"; +import FavoriteRepository from "../repository/FavoriteRepository.ts"; +import { get } from "../helper/decorator.ts"; + +@injectable() +export default class HomeController implements Controller { + constructor( + @inject(TYPES.FavoriteRepository) + private readonly favoriteRepository: FavoriteRepository, + ) {} + + @get("/") + async getFavorites() { + return false; + } +} diff --git a/packages/backend/src/core/router.ts b/packages/backend/src/core/router.ts new file mode 100644 index 00000000..98bb68a9 --- /dev/null +++ b/packages/backend/src/core/router.ts @@ -0,0 +1,64 @@ +import { injectable, multiInject } from "inversify"; +import { Controller } from "../interfaces.ts"; +import { TYPES } from "../types.ts"; +import Router from "@koa/router"; +import { success } from "../helper/utils.ts"; +import { error } from "console"; +// import { error, success } from "../helper/utils.ts"; + +@injectable() +export default class RouterHandlerService extends Router { + constructor( + @multiInject(TYPES.Controller) + private readonly controllers: Controller[], + ) { + super(); + } + + private registerIpc( + controller: Controller, + propertyKey: string | symbol, + ): void { + const property = controller[propertyKey]; + if (typeof property !== "function") return; + + const httpMethod: "get" | "post" = Reflect.getMetadata( + "http-method", + controller, + propertyKey, + ); + if (!httpMethod) return; + + const routerPath = Reflect.getMetadata( + "router-path", + controller, + propertyKey, + ); + if (!routerPath) return; + + this[httpMethod](routerPath, async (context, next) => { + try { + let res = property.call(controller, context, next); + if (res.then) { + res = await res; + } + context.body = success(res); + } catch (e: unknown) { + if (e instanceof Error) { + context.body = error(e.message); + } else { + context.body = error(String(e)); + } + } + }); + } + + init(): void { + for (const controller of this.controllers) { + const Class = Object.getPrototypeOf(controller); + Object.getOwnPropertyNames(Class).forEach((propertyKey) => + this.registerIpc(controller, propertyKey), + ); + } + } +} diff --git a/packages/backend/src/core/vendor.ts b/packages/backend/src/core/vendor.ts new file mode 100644 index 00000000..c521a497 --- /dev/null +++ b/packages/backend/src/core/vendor.ts @@ -0,0 +1,3 @@ +export interface Vendor { + init(): Promise; +} diff --git a/packages/backend/src/entity/Favorite.ts b/packages/backend/src/entity/Favorite.ts new file mode 100644 index 00000000..6990d6a4 --- /dev/null +++ b/packages/backend/src/entity/Favorite.ts @@ -0,0 +1,39 @@ +import { + Column, + CreateDateColumn, + Entity, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from "typeorm"; + +@Entity({ + name: "favorite", +}) +export class Favorite { + @PrimaryGeneratedColumn() + id: number; + + @Column({ + type: "text", + nullable: false, + }) + title: string; + + @Column({ + type: "text", + nullable: false, + }) + url: string; + + @Column({ + type: "text", + nullable: true, + }) + icon?: string; + + @CreateDateColumn() + createdDate: Date; + + @UpdateDateColumn() + updatedDate: Date; +} diff --git a/packages/backend/src/global.d.ts b/packages/backend/src/global.d.ts new file mode 100644 index 00000000..a518dba6 --- /dev/null +++ b/packages/backend/src/global.d.ts @@ -0,0 +1,6 @@ +/* eslint-disable no-var */ +declare global { + var __bin__: string; +} + +export {}; diff --git a/packages/backend/src/helper/decorator.ts b/packages/backend/src/helper/decorator.ts new file mode 100644 index 00000000..543a85bf --- /dev/null +++ b/packages/backend/src/helper/decorator.ts @@ -0,0 +1,13 @@ +export const get = (route: string) => { + return (target: any, propertyKey: string): void => { + Reflect.defineMetadata("http-method", "get", target, propertyKey); + Reflect.defineMetadata("router-path", route, target, propertyKey); + }; +}; + +export const post = (route: string) => { + return (target: any, propertyName: string): void => { + Reflect.defineMetadata("http-method", "post", target, propertyName); + Reflect.defineMetadata("router-path", route, target, propertyName); + }; +}; diff --git a/packages/backend/src/helper/ffmpeg.ts b/packages/backend/src/helper/ffmpeg.ts new file mode 100644 index 00000000..309fc03d --- /dev/null +++ b/packages/backend/src/helper/ffmpeg.ts @@ -0,0 +1,39 @@ +import { spawn } from "child_process"; +import { ffmpegPath } from "./variables.ts"; + +export const convertToAudio = async ( + input: string, + output: string, +): Promise => { + return new Promise((resolve, reject) => { + const ffmpeg = spawn(ffmpegPath, [ + "-y", + "-v", + "error", + "-i", + input, + "-acodec", + "mp3", + "-format", + "mp3", + output, + ]); + let errData = ""; + + ffmpeg.stderr.on("data", (data) => { + errData += String(data); + }); + + ffmpeg.on("error", (err) => { + reject(err); + }); + + ffmpeg.on("close", (code) => { + if (code === 0) { + resolve(); + } else { + reject(new Error(errData)); + } + }); + }); +}; diff --git a/packages/backend/src/helper/index.ts b/packages/backend/src/helper/index.ts new file mode 100644 index 00000000..d2694564 --- /dev/null +++ b/packages/backend/src/helper/index.ts @@ -0,0 +1,41 @@ +import os from "os"; +import fetch from "node-fetch"; +import https from "https"; + +export function getLocalIP() { + const interfaces = os.networkInterfaces(); + let localIP = ""; + + // 遍历网络接口 + for (const key in interfaces) { + const iface = interfaces[key]; + if (!iface) continue; + + // 过滤出 IPv4 地址且非回环地址 + const filteredIface = iface.filter( + (details) => details.family === "IPv4" && !details.internal, + ); + + if (filteredIface.length > 0) { + localIP = filteredIface[0].address; + break; + } + } + + return localIP; +} + +function fetchWrapper(url: string) { + const options = { + agent: new https.Agent({ + rejectUnauthorized: false, + }), + }; + return fetch(url, options); +} + +export { sleep, formatHeaders } from "./utils.ts"; +export * from "./variables.ts"; +export { on, handle } from "./decorator.ts"; +export { convertToAudio } from "./ffmpeg.ts"; +export { fetchWrapper as fetch }; diff --git a/packages/backend/src/helper/utils.ts b/packages/backend/src/helper/utils.ts new file mode 100644 index 00000000..5021867a --- /dev/null +++ b/packages/backend/src/helper/utils.ts @@ -0,0 +1,37 @@ +import EventEmitter from "events"; + +export async function sleep(second = 1): Promise { + return new Promise((resolve) => setTimeout(resolve, second * 1000)); +} + +export function formatHeaders(headers: Record): string { + if (!headers) return ""; + const formatted = Object.entries(headers) + .map(([key, value]) => `${key}:${value}`) + .join("\n"); + return formatted; +} + +export const event = new EventEmitter(); + +export interface IpcResponse { + code: number; + message: string; + data: Record | null; +} + +export function success(data: Record): IpcResponse { + return { + code: 0, + message: "success", + data, + }; +} + +export function error(message = "fail"): IpcResponse { + return { + code: -1, + message, + data: null, + }; +} diff --git a/packages/backend/src/helper/variables.ts b/packages/backend/src/helper/variables.ts new file mode 100644 index 00000000..3785eb14 --- /dev/null +++ b/packages/backend/src/helper/variables.ts @@ -0,0 +1,35 @@ +import { resolve } from "path"; + +export enum Platform { + Windows = "win32", + MacOS = "darwin", + Linux = "linux", +} + +export const isMac = process.platform === Platform.MacOS; +export const isWin = process.platform === Platform.Windows; +export const isLinux = process.platform === Platform.Linux; + +export function resolveBin(path: string) { + if (isWin) { + path += ".exe"; + } + return resolve(__bin__, path); +} + +export const appName = process.env.APP_NAME || "mediago"; +export const defaultScheme = "mediago"; +export const PERSIST_MEDIAGO = "persist:mediago"; +export const PERSIST_WEBVIEW = "persist:webview"; +export const PRIVACY_WEBVIEW = "webview"; + +// bin path +export const ffmpegPath = resolveBin("ffmpeg"); +export const biliDownloaderBin = resolveBin("BBDown"); +export const m3u8DownloaderBin = resolveBin("N_m3u8DL-RE"); + +// user agent +export const pcUA = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"; +export const mobileUA = + "Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36"; diff --git a/packages/backend/src/images.d.ts b/packages/backend/src/images.d.ts new file mode 100644 index 00000000..a58c5d8a --- /dev/null +++ b/packages/backend/src/images.d.ts @@ -0,0 +1,7 @@ +declare module "*.svg"; +declare module "*.png"; +declare module "*.jpg"; +declare module "*.jpeg"; +declare module "*.gif"; +declare module "*.bmp"; +declare module "*.tiff"; diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts new file mode 100644 index 00000000..d50613f5 --- /dev/null +++ b/packages/backend/src/index.ts @@ -0,0 +1,7 @@ +import "reflect-metadata"; +import { container } from "./inversify.config.ts"; +import { TYPES } from "./types.ts"; +import ElectronApp from "./app.ts"; + +const mediago = container.get(TYPES.ElectronApp); +mediago.init(); diff --git a/packages/backend/src/interfaces.ts b/packages/backend/src/interfaces.ts new file mode 100644 index 00000000..53fc706f --- /dev/null +++ b/packages/backend/src/interfaces.ts @@ -0,0 +1,78 @@ +import { Conversion } from "./entity/Conversion.ts"; + +export type Controller = Record; + +export interface DownloadItem { + id: number; + type: DownloadType; + name: string; + url: string; + headers?: string; + status?: DownloadStatus; + isLive?: boolean; +} + +export enum DownloadFilter { + list = "list", + done = "done", +} + +export interface DownloadItemPagination { + current?: number; + pageSize?: number; + filter?: DownloadFilter; +} + +export interface ConversionPagination { + current?: number; + pageSize?: number; +} + +export interface VideoResponse { + total: number; + list: DownloadItem[]; +} + +export interface ConversionResponse { + total: number; + list: Conversion[]; +} + +export enum DownloadStatus { + Ready = "ready", + Watting = "watting", + Downloading = "downloading", + Stopped = "stopped", + Success = "success", + Failed = "failed", +} + +export type Task = { + id: number; + params: Omit; +}; + +export interface DownloadProgress { + id: number; + type: string; + percent: string; + speed: string; + isLive: boolean; +} + +export enum DownloadType { + m3u8 = "m3u8", + bilibili = "bilibili", +} +export interface DownloadParams { + id: number; + type: DownloadType; + url: string; + local: string; + name: string; + headers?: string; + abortSignal: AbortController; + proxy?: string; + deleteSegments?: boolean; + callback: (progress: DownloadProgress) => void; +} diff --git a/packages/backend/src/inversify.config.ts b/packages/backend/src/inversify.config.ts new file mode 100644 index 00000000..415912be --- /dev/null +++ b/packages/backend/src/inversify.config.ts @@ -0,0 +1,42 @@ +import HomeController from "./controller/HomeController.ts"; +import { Container } from "inversify"; +import FavoriteRepository from "./repository/FavoriteRepository.ts"; +import ElectronApp from "./app.ts"; +import { Controller } from "./interfaces.ts"; +import { TYPES } from "./types.ts"; +import TypeORM from "./vendor/TypeORM.ts"; +import RouterHandler from "./core/router.ts"; +import HomeService from "./services/HomeService.ts"; + +const container = new Container({ + skipBaseClassChecks: true, + defaultScope: "Singleton", + autoBindInjectable: true, +}); + +container + .bind(TYPES.ElectronApp) + .to(ElectronApp) + .inSingletonScope(); + +// services +container.bind(TYPES.HomeService).to(HomeService); + +// controller +container.bind(TYPES.Controller).to(HomeController); + +// repository +container + .bind(TYPES.FavoriteRepository) + .to(FavoriteRepository); + +// vendor +container.bind(TYPES.TypeORM).to(TypeORM); + +// core +container + .bind(TYPES.RouterHandlerService) + .to(RouterHandler) + .inSingletonScope(); + +export { container }; diff --git a/packages/backend/src/main.d.ts b/packages/backend/src/main.d.ts new file mode 100644 index 00000000..3d003a2b --- /dev/null +++ b/packages/backend/src/main.d.ts @@ -0,0 +1,66 @@ +import { Rectangle } from "electron"; +import { type DownloadType } from "interfaces"; +import { AppLanguage, AppTheme } from "./types"; + +declare interface EnvPath { + binPath: string; + dbPath: string; + workspace: string; + platform: string; + local: string; +} + +declare interface BrowserWindowInitialVal { + url?: string; + sourceList?: WebSource[]; +} + +declare interface WebSource { + url: string; + type: DownloadType; + name: string; + headers?: string; +} + +declare interface AppStore { + // 本地存储地址 + local: string; + // 下载完成提示音 + promptTone: boolean; + // 代理地址 + proxy: string; + // 是否开启代理 + useProxy: boolean; + // 下载完成后删除原始文件 + deleteSegments: boolean; + // 新窗口打开浏览器 + openInNewWindow: boolean; + mainBounds?: Rectangle; + browserBounds?: Rectangle; + blockAds: boolean; + // 主题 + theme: AppTheme; + // 使用浏览器插件 + useExtension: boolean; + // 是否使用手机UA + isMobile: boolean; + // 最大同时下载数 + maxRunner: number; + // 语言 + language: AppLanguage; + // 是否显示终端 + showTerminal: boolean; + // 隐私模式 + privacy: boolean; + // 机器id + machineId: string; + // 下载代理设置 + downloadProxySwitch: boolean; + // 自动更新 + autoUpgrade: boolean; +} + +declare interface BrowserStore { + url: string; + sourceList: WebSource[]; +} diff --git a/packages/backend/src/repository/FavoriteRepository.ts b/packages/backend/src/repository/FavoriteRepository.ts new file mode 100644 index 00000000..0fefe7bd --- /dev/null +++ b/packages/backend/src/repository/FavoriteRepository.ts @@ -0,0 +1,42 @@ +import { inject, injectable } from "inversify"; +import { TYPES } from "../types.ts"; +import { Favorite } from "../entity/Favorite.ts"; +import TypeORM from "../vendor/TypeORM.ts"; + +@injectable() +export default class FavoriteRepository { + constructor( + @inject(TYPES.TypeORM) + private readonly db: TypeORM, + ) {} + + async findFavorites(): Promise { + return await this.db.manager.find(Favorite, { + order: { + createdDate: "desc", + }, + }); + } + + async addFavorite(favorite: Favorite): Promise { + const exist = await this.db.manager.findOne(Favorite, { + where: { + url: favorite.url, + }, + }); + + if (exist) { + throw new Error("网址已经存在"); + } + + const item = new Favorite(); + item.title = favorite.title; + item.url = favorite.url; + item.icon = favorite.icon; + return await this.db.manager.save(item); + } + + async removeFavorite(id: number): Promise { + await this.db.manager.getRepository(Favorite).delete(id); + } +} diff --git a/packages/backend/src/services/HomeService.ts b/packages/backend/src/services/HomeService.ts new file mode 100644 index 00000000..8b85d7e1 --- /dev/null +++ b/packages/backend/src/services/HomeService.ts @@ -0,0 +1,9 @@ +import EventEmitter from "events"; +import { injectable } from "inversify"; + +@injectable() +export default class HomeService extends EventEmitter { + constructor() { + super(); + } +} diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts new file mode 100644 index 00000000..03fee47b --- /dev/null +++ b/packages/backend/src/types.ts @@ -0,0 +1,34 @@ +export const TYPES = { + ElectronApp: Symbol.for("ElectronApp"), + RouterHandlerService: Symbol.for("RouterHandlerService"), + Controller: Symbol.for("Controller"), + // repository + VideoRepository: Symbol.for("VideoRepository"), + FavoriteRepository: Symbol.for("FavoriteRepository"), + ConversionRepository: Symbol.for("ConversionRepository"), + // windows + BrowserWindow: Symbol.for("BrowserWindow"), + MainWindow: Symbol.for("MainWindow"), + PlayerWindow: Symbol.for("PlayerWindow"), + // services + HomeService: Symbol.for("HomeService"), + DownloadService: Symbol.for("DownloadService"), + SessionService: Symbol.for("SessionService"), + ProtocolService: Symbol.for("ProtocolService"), + VideoService: Symbol.for("VideoService"), + SniffingHelper: Symbol.for("SniffingHelper"), + // vendor + TypeORM: Symbol.for("TypeORM"), +}; + +export enum AppTheme { + System = "system", + Light = "light", + Dark = "dark", +} + +export enum AppLanguage { + System = "system", + ZH = "zh", + EN = "en", +} diff --git a/packages/backend/src/vendor/TypeORM.ts b/packages/backend/src/vendor/TypeORM.ts new file mode 100644 index 00000000..b6142252 --- /dev/null +++ b/packages/backend/src/vendor/TypeORM.ts @@ -0,0 +1,33 @@ +import { injectable } from "inversify"; +import { DataSource, EntityManager } from "typeorm"; +import { Favorite } from "../entity/Favorite.ts"; +import { Vendor } from "../core/vendor.ts"; + +@injectable() +export default class DatabaseService implements Vendor { + appDataSource: DataSource; + + constructor() { + this.appDataSource = new DataSource({ + type: "mysql", + host: "127.0.0.1", + port: 3306, + username: "root", + password: "123456", + database: "mediago", + synchronize: true, + logging: false, + entities: [Favorite], + migrations: [], + subscribers: [], + }); + } + + async init() { + await this.appDataSource.initialize(); + } + + get manager(): EntityManager { + return this.appDataSource.manager; + } +} diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json new file mode 100644 index 00000000..52374e7e --- /dev/null +++ b/packages/backend/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2015", + "module": "NodeNext", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "moduleResolution": "NodeNext", + "strictPropertyInitialization": false, + "typeRoots": ["node_modules/@types"], + "declaration": true, + "emitDeclarationOnly": true, + "declarationDir": "./types", + "outDir": "app/output/build/main", + "allowImportingTsExtensions": true + }, + "include": ["src"], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} diff --git a/packages/backend/tsconfig.node.json b/packages/backend/tsconfig.node.json new file mode 100644 index 00000000..516ff9f5 --- /dev/null +++ b/packages/backend/tsconfig.node.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "strict": true, + "composite": true, + "forceConsistentCasingInFileNames": true, + "module": "ES2015", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "typeRoots": ["node_modules/@types"] + }, + "include": ["gulpfile.ts", "scripts"] +} diff --git a/packages/main/package.json b/packages/main/package.json index 214a54f3..688673e9 100644 --- a/packages/main/package.json +++ b/packages/main/package.json @@ -34,6 +34,7 @@ "@types/node-fetch": "^2.6.11", "@types/semver": "^7.5.8", "@types/serve-handler": "^6.1.4", + "@types/undertaker": "^1.2.11", "consola": "^3.2.3", "cross-env": "^7.0.3", "del": "^7.1.0", diff --git a/packages/main/scripts/utils.ts b/packages/main/scripts/utils.ts index 553a99b9..f54cedf4 100644 --- a/packages/main/scripts/utils.ts +++ b/packages/main/scripts/utils.ts @@ -5,7 +5,7 @@ import consola from "consola"; import fs from "fs-extra"; import electron from "electron"; -const baseResolve = (...r) => resolve(__dirname, ...r); +const baseResolve = (...r: any[]) => resolve(__dirname, ...r); export const mainResolve = (...r: string[]) => baseResolve("..", ...r); export const rootResolve = (...r: string[]) => baseResolve("../../..", ...r); @@ -81,7 +81,7 @@ export class Env { return null; } - return Object.keys(parsed).reduce((prev, curr) => { + return Object.keys(parsed).reduce((prev: any, curr) => { prev[curr] = parsed[curr]; return prev; }, {}); diff --git a/packages/main/tsconfig.node.json b/packages/main/tsconfig.node.json index 5e6ce44c..52138e16 100644 --- a/packages/main/tsconfig.node.json +++ b/packages/main/tsconfig.node.json @@ -9,5 +9,5 @@ "esModuleInterop": true, "resolveJsonModule": true }, - "include": ["script", "app/package.json"] + "include": ["scripts", "gulpfile.ts"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc3416ac..22a72925 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -71,6 +71,145 @@ importers: specifier: ^8.1.1 version: 8.1.1 + packages/backend: + dependencies: + '@koa/router': + specifier: ^12.0.1 + version: 12.0.1 + axios: + specifier: ^1.7.2 + version: 1.7.2 + cheerio: + specifier: 1.0.0-rc.12 + version: 1.0.0-rc.12 + cors: + specifier: ^2.8.5 + version: 2.8.5 + dayjs: + specifier: ^1.11.11 + version: 1.11.11 + execa: + specifier: ^9.1.0 + version: 9.1.0 + fs-extra: + specifier: ^11.2.0 + version: 11.2.0 + inversify: + specifier: ^6.0.2 + version: 6.0.2 + koa: + specifier: ^2.15.3 + version: 2.15.3 + lint-staged: + specifier: ^15.2.5 + version: 15.2.5 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + mime-types: + specifier: ^2.1.35 + version: 2.1.35 + mysql2: + specifier: ^3.11.0 + version: 3.11.0 + nanoid: + specifier: ^5.0.7 + version: 5.0.7 + reflect-metadata: + specifier: ^0.2.2 + version: 0.2.2 + typeorm: + specifier: 0.3.20 + version: 0.3.20(better-sqlite3@9.6.0)(mysql2@3.11.0) + devDependencies: + '@eslint/js': + specifier: ^8.57.0 + version: 8.57.0 + '@types/cors': + specifier: ^2.8.17 + version: 2.8.17 + '@types/express': + specifier: ^4.17.21 + version: 4.17.21 + '@types/fs-extra': + specifier: ^11.0.4 + version: 11.0.4 + '@types/glob': + specifier: ^8.1.0 + version: 8.1.0 + '@types/gulp': + specifier: ^4.0.17 + version: 4.0.17 + '@types/koa': + specifier: ^2.15.0 + version: 2.15.0 + '@types/koa__router': + specifier: ^12.0.4 + version: 12.0.4 + '@types/lodash': + specifier: ^4.17.4 + version: 4.17.4 + '@types/mime-types': + specifier: ^2.1.4 + version: 2.1.4 + '@types/node': + specifier: ^20.12.12 + version: 20.12.12 + '@types/node-fetch': + specifier: ^2.6.11 + version: 2.6.11 + '@types/semver': + specifier: ^7.5.8 + version: 7.5.8 + '@types/serve-handler': + specifier: ^6.1.4 + version: 6.1.4 + '@types/undertaker': + specifier: ^1.2.11 + version: 1.2.11 + consola: + specifier: ^3.2.3 + version: 3.2.3 + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + del: + specifier: ^7.1.0 + version: 7.1.0 + dotenv: + specifier: ^16.4.5 + version: 16.4.5 + esbuild: + specifier: ^0.21.4 + version: 0.21.4 + esbuild-register: + specifier: ^3.5.0 + version: 3.5.0(esbuild@0.21.4) + eslint: + specifier: ^8.57.0 + version: 8.57.0 + glob: + specifier: ^10.4.1 + version: 10.4.1 + globals: + specifier: ^15.3.0 + version: 15.3.0 + gulp: + specifier: ^5.0.0 + version: 5.0.0 + nodemon: + specifier: ^3.1.4 + version: 3.1.4 + semver: + specifier: ^7.6.2 + version: 7.6.2 + typescript: + specifier: ^5.4.5 + version: 5.4.5 + typescript-eslint: + specifier: ^7.10.0 + version: 7.10.0(eslint@8.57.0)(typescript@5.4.5) + packages/main: dependencies: '@cliqz/adblocker-electron': @@ -150,7 +289,7 @@ importers: version: 7.1.0 typeorm: specifier: 0.3.20 - version: 0.3.20(better-sqlite3@9.6.0) + version: 0.3.20(better-sqlite3@9.6.0)(mysql2@3.11.0) devDependencies: '@electron/rebuild': specifier: ^3.6.0 @@ -194,6 +333,9 @@ importers: '@types/serve-handler': specifier: ^6.1.4 version: 6.1.4 + '@types/undertaker': + specifier: ^1.2.11 + version: 1.2.11 consola: specifier: ^3.2.3 version: 3.2.3 @@ -1460,6 +1602,10 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@koa/router@12.0.1': + resolution: {integrity: sha512-ribfPYfHb+Uw3b27Eiw6NPqjhIhTpVFzEWLwyc/1Xp+DCdwRRyIlAUODX+9bPARF6aQtUu1+/PHzdNvRzcs/+Q==} + engines: {node: '>= 12'} + '@lit-labs/ssr-dom-shim@1.2.0': resolution: {integrity: sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==} @@ -2004,6 +2150,9 @@ packages: resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} + '@types/accepts@1.3.7': + resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} + '@types/body-parser@1.19.5': resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} @@ -2016,9 +2165,15 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/content-disposition@0.5.8': + resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==} + '@types/conventional-commits-parser@5.0.0': resolution: {integrity: sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==} + '@types/cookies@0.9.0': + resolution: {integrity: sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==} + '@types/cors@2.8.17': resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} @@ -2070,6 +2225,9 @@ packages: '@types/hoist-non-react-statics@3.3.5': resolution: {integrity: sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==} + '@types/http-assert@1.5.5': + resolution: {integrity: sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==} + '@types/http-cache-semantics@4.0.4': resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} @@ -2082,9 +2240,21 @@ packages: '@types/jsonfile@6.1.4': resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} + '@types/keygrip@1.0.6': + resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} + '@types/keyv@3.1.4': resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + '@types/koa-compose@3.2.8': + resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==} + + '@types/koa@2.15.0': + resolution: {integrity: sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==} + + '@types/koa__router@12.0.4': + resolution: {integrity: sha512-Y7YBbSmfXZpa/m5UGGzb7XadJIRBRnwNY9cdAojZGp65Cpe5MAP3mOZE7e3bImt8dfKS4UFcR16SLH8L/z7PBw==} + '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} @@ -2718,6 +2888,10 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + aws-ssl-profiles@1.1.1: + resolution: {integrity: sha512-+H+kuK34PfMaI9PNU/NSjBKL5hh/KDM9J72kwYeYEm0A8B1AC4fuCy3qsjnA7lxklgyXsB68yn8Z2xoZEjgwCQ==} + engines: {node: '>= 6.0.0'} + axios@1.7.2: resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==} @@ -2834,6 +3008,10 @@ packages: resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + cache-content-type@1.0.1: + resolution: {integrity: sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==} + engines: {node: '>= 6.0.0'} + cacheable-lookup@5.0.4: resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} engines: {node: '>=10.6.0'} @@ -2981,6 +3159,10 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -3101,6 +3283,10 @@ packages: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} + cookies@0.9.1: + resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==} + engines: {node: '>= 0.8'} + copy-props@4.0.0: resolution: {integrity: sha512-bVWtw1wQLzzKiYROtvNlbJgxgBYt2bMJpkCbKmXM3xyijvcjjWXEk5nyrrT3bgJ7ODb19ZohE2T0Y3FgNPyoTw==} engines: {node: '>= 10.13.0'} @@ -3295,6 +3481,9 @@ packages: dedent@0.7.0: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} + deep-equal@1.0.1: + resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==} + deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -3339,6 +3528,14 @@ packages: delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + + depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -3951,6 +4148,9 @@ packages: resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + generate-function@2.3.1: + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + gensequence@7.0.0: resolution: {integrity: sha512-47Frx13aZh01afHJTB3zTtKIlFI6vWY+MYCN9Qpew6i52rfKjnhCF/l1YlC8UmEMvvntZZ6z4PiCcmyuedR2aQ==} engines: {node: '>=18'} @@ -4178,9 +4378,17 @@ packages: htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + http-assert@1.5.0: + resolution: {integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==} + engines: {node: '>= 0.8'} + http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + http-errors@1.8.1: + resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} + engines: {node: '>= 0.6'} + http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -4235,6 +4443,9 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + ignore@5.3.1: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} @@ -4444,6 +4655,9 @@ packages: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} + is-property@1.0.2: + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -4624,6 +4838,10 @@ packages: jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + keygrip@1.1.0: + resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} + engines: {node: '>= 0.6'} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -4637,6 +4855,17 @@ packages: known-css-properties@0.31.0: resolution: {integrity: sha512-sBPIUGTNF0czz0mwGGUoKKJC8Q7On1GPbCSFPfyEsfHb2DyBG0Y4QtV+EVWpINSaiGKZblDNuF5AezxSgOhesQ==} + koa-compose@4.1.0: + resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==} + + koa-convert@2.0.0: + resolution: {integrity: sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==} + engines: {node: '>= 10'} + + koa@2.15.3: + resolution: {integrity: sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg==} + engines: {node: ^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4} + last-run@2.0.0: resolution: {integrity: sha512-j+y6WhTLN4Itnf9j5ZQos1BGPCS8DAwmgMroR3OzfxAsBxam0hMw7J8M3KqZl0pLQJ1jNnwIexg5DYpC/ctwEQ==} engines: {node: '>= 10.13.0'} @@ -4768,6 +4997,9 @@ packages: resolution: {integrity: sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==} engines: {node: '>=18'} + long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + longest@2.0.1: resolution: {integrity: sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==} engines: {node: '>=0.10.0'} @@ -4792,6 +5024,10 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} + lru-cache@8.0.5: + resolution: {integrity: sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==} + engines: {node: '>=16.14'} + lucide-react@0.400.0: resolution: {integrity: sha512-rpp7pFHh3Xd93KHixNgB0SqThMHpYNzsGUu69UaQbSZ75Q/J3m5t6EhKyMT3m4w2WOxmJ2mY0tD3vebnXqQryQ==} peerDependencies: @@ -5008,9 +5244,17 @@ packages: mute-stream@0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + mysql2@3.11.0: + resolution: {integrity: sha512-J9phbsXGvTOcRVPR95YedzVSxJecpW5A5+cQ57rhHIFXteTP10HCs+VBjS7DHIKfEaI1zQ5tlVrquCd64A6YvA==} + engines: {node: '>= 8.0'} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + named-placeholders@1.1.3: + resolution: {integrity: sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==} + engines: {node: '>=12.0.0'} + nan@2.19.0: resolution: {integrity: sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==} @@ -5072,6 +5316,11 @@ packages: node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + nodemon@3.1.4: + resolution: {integrity: sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==} + engines: {node: '>=10'} + hasBin: true + nopt@6.0.0: resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -5170,6 +5419,9 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} + only@0.0.2: + resolution: {integrity: sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==} + open@8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} @@ -5304,6 +5556,9 @@ packages: path-to-regexp@2.2.1: resolution: {integrity: sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==} + path-to-regexp@6.2.2: + resolution: {integrity: sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -5515,6 +5770,9 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + pump@3.0.0: resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} @@ -6122,6 +6380,9 @@ packages: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} engines: {node: '>= 0.8.0'} + seq-queue@0.0.5: + resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} + serialize-error@7.0.1: resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} engines: {node: '>=10'} @@ -6259,6 +6520,10 @@ packages: sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + sqlstring@2.3.3: + resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} + engines: {node: '>= 0.6'} + ssri@9.0.1: resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -6272,6 +6537,10 @@ packages: resolution: {integrity: sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==} engines: {node: '>= 6'} + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -6543,6 +6812,10 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + truncate-utf8-bytes@1.0.2: resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==} @@ -6558,6 +6831,10 @@ packages: tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + tsscmp@1.0.6: + resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} + engines: {node: '>=0.6.x'} + tsx@4.11.0: resolution: {integrity: sha512-vzGGELOgAupsNVssAmZjbUDfdm/pWP4R+Kg8TVdsonxbXk0bEpE1qh0yV6/QxUVXaVlNemgcPajGdJJ82n3stg==} engines: {node: '>=18.0.0'} @@ -6697,6 +6974,9 @@ packages: resolution: {integrity: sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==} engines: {node: '>=0.10.0'} + undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + undertaker-registry@2.0.0: resolution: {integrity: sha512-+hhVICbnp+rlzZMgxXenpvTxpuvA67Bfgtt+O9WOE5jo7w/dyiF1VmoZVIHvP2EkUjsyKyTwYKlLhA+j47m1Ew==} engines: {node: '>= 10.13.0'} @@ -7048,6 +7328,10 @@ packages: yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + ylru@1.4.0: + resolution: {integrity: sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==} + engines: {node: '>= 4.0.0'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -7656,7 +7940,7 @@ snapshots: '@electron/get@2.0.3': dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) env-paths: 2.2.1 fs-extra: 8.1.0 got: 11.8.6 @@ -7670,7 +7954,7 @@ snapshots: '@electron/notarize@2.2.1': dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) fs-extra: 9.1.0 promise-retry: 2.0.1 transitivePeerDependencies: @@ -7679,7 +7963,7 @@ snapshots: '@electron/osx-sign@1.0.5': dependencies: compare-version: 0.1.2 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) fs-extra: 10.1.0 isbinaryfile: 4.0.10 minimist: 1.2.8 @@ -7691,7 +7975,7 @@ snapshots: dependencies: '@malept/cross-spawn-promise': 2.0.0 chalk: 4.1.2 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) detect-libc: 2.0.3 fs-extra: 10.1.0 got: 11.8.6 @@ -7711,7 +7995,7 @@ snapshots: dependencies: '@electron/asar': 3.2.10 '@malept/cross-spawn-promise': 1.1.1 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) dir-compare: 3.3.0 fs-extra: 9.1.0 minimatch: 3.1.2 @@ -7950,7 +8234,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) espree: 9.6.1 globals: 13.24.0 ignore: 5.3.1 @@ -7985,7 +8269,7 @@ snapshots: '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -8020,6 +8304,16 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 + '@koa/router@12.0.1': + dependencies: + debug: 4.3.4(supports-color@5.5.0) + http-errors: 2.0.0 + koa-compose: 4.1.0 + methods: 1.1.2 + path-to-regexp: 6.2.2 + transitivePeerDependencies: + - supports-color + '@lit-labs/ssr-dom-shim@1.2.0': {} '@lit/reactive-element@2.0.4': @@ -8036,7 +8330,7 @@ snapshots: '@malept/flatpak-bundler@0.4.0': dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) fs-extra: 9.1.0 lodash: 4.17.21 tmp-promise: 3.0.3 @@ -8477,6 +8771,10 @@ snapshots: '@tootallnate/once@2.0.0': {} + '@types/accepts@1.3.7': + dependencies: + '@types/node': 20.12.12 + '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 @@ -8498,10 +8796,19 @@ snapshots: dependencies: '@types/node': 20.12.12 + '@types/content-disposition@0.5.8': {} + '@types/conventional-commits-parser@5.0.0': dependencies: '@types/node': 20.12.12 + '@types/cookies@0.9.0': + dependencies: + '@types/connect': 3.4.38 + '@types/express': 4.17.21 + '@types/keygrip': 1.0.6 + '@types/node': 20.12.12 + '@types/cors@2.8.17': dependencies: '@types/node': 20.12.12 @@ -8572,6 +8879,8 @@ snapshots: '@types/react': 18.3.3 hoist-non-react-statics: 3.3.2 + '@types/http-assert@1.5.5': {} + '@types/http-cache-semantics@4.0.4': {} '@types/http-errors@2.0.4': {} @@ -8584,10 +8893,31 @@ snapshots: dependencies: '@types/node': 20.12.12 + '@types/keygrip@1.0.6': {} + '@types/keyv@3.1.4': dependencies: '@types/node': 20.12.12 + '@types/koa-compose@3.2.8': + dependencies: + '@types/koa': 2.15.0 + + '@types/koa@2.15.0': + dependencies: + '@types/accepts': 1.3.7 + '@types/content-disposition': 0.5.8 + '@types/cookies': 0.9.0 + '@types/http-assert': 1.5.5 + '@types/http-errors': 2.0.4 + '@types/keygrip': 1.0.6 + '@types/koa-compose': 3.2.8 + '@types/node': 20.12.12 + + '@types/koa__router@12.0.4': + dependencies: + '@types/koa': 2.15.0 + '@types/linkify-it@5.0.0': {} '@types/lodash@4.17.4': {} @@ -8752,7 +9082,7 @@ snapshots: '@typescript-eslint/types': 7.10.0 '@typescript-eslint/typescript-estree': 7.10.0(typescript@5.4.5) '@typescript-eslint/visitor-keys': 7.10.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) eslint: 8.57.0 optionalDependencies: typescript: 5.4.5 @@ -8765,7 +9095,7 @@ snapshots: '@typescript-eslint/types': 7.15.0 '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.4.5) '@typescript-eslint/visitor-keys': 7.15.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) eslint: 8.57.0 optionalDependencies: typescript: 5.4.5 @@ -8786,7 +9116,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 7.10.0(typescript@5.4.5) '@typescript-eslint/utils': 7.10.0(eslint@8.57.0)(typescript@5.4.5) - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) eslint: 8.57.0 ts-api-utils: 1.3.0(typescript@5.4.5) optionalDependencies: @@ -8798,7 +9128,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.4.5) '@typescript-eslint/utils': 7.15.0(eslint@8.57.0)(typescript@5.4.5) - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) eslint: 8.57.0 ts-api-utils: 1.3.0(typescript@5.4.5) optionalDependencies: @@ -8814,7 +9144,7 @@ snapshots: dependencies: '@typescript-eslint/types': 7.10.0 '@typescript-eslint/visitor-keys': 7.10.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.4 @@ -8829,7 +9159,7 @@ snapshots: dependencies: '@typescript-eslint/types': 7.15.0 '@typescript-eslint/visitor-keys': 7.15.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.4 @@ -9047,7 +9377,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -9262,7 +9592,7 @@ snapshots: builder-util: 24.13.1 builder-util-runtime: 9.2.4 chromium-pickle-js: 0.2.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) dmg-builder: 24.13.3(electron-builder-squirrel-windows@24.13.3) ejs: 3.1.10 electron-builder-squirrel-windows: 24.13.3(dmg-builder@24.13.3) @@ -9466,6 +9796,8 @@ snapshots: dependencies: possible-typed-array-names: 1.0.0 + aws-ssl-profiles@1.1.1: {} + axios@1.7.2: dependencies: follow-redirects: 1.15.6 @@ -9586,14 +9918,14 @@ snapshots: builder-util-runtime@9.2.3: dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) sax: 1.3.0 transitivePeerDependencies: - supports-color builder-util-runtime@9.2.4: dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) sax: 1.3.0 transitivePeerDependencies: - supports-color @@ -9607,7 +9939,7 @@ snapshots: builder-util-runtime: 9.2.4 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) fs-extra: 10.1.0 http-proxy-agent: 5.0.0 https-proxy-agent: 5.0.1 @@ -9648,6 +9980,11 @@ snapshots: transitivePeerDependencies: - bluebird + cache-content-type@1.0.1: + dependencies: + mime-types: 2.1.35 + ylru: 1.4.0 + cacheable-lookup@5.0.4: {} cacheable-request@7.0.4: @@ -9809,6 +10146,8 @@ snapshots: clsx@2.1.1: {} + co@4.6.0: {} + color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -9937,6 +10276,11 @@ snapshots: cookie@0.6.0: {} + cookies@0.9.1: + dependencies: + depd: 2.0.0 + keygrip: 1.1.0 + copy-props@4.0.0: dependencies: each-props: 3.0.0 @@ -10171,9 +10515,11 @@ snapshots: dependencies: ms: 2.0.0 - debug@4.3.4: + debug@4.3.4(supports-color@5.5.0): dependencies: ms: 2.1.2 + optionalDependencies: + supports-color: 5.5.0 decompress-response@6.0.0: dependencies: @@ -10181,6 +10527,8 @@ snapshots: dedent@0.7.0: {} + deep-equal@1.0.1: {} + deep-extend@0.6.0: {} deep-is@0.1.4: {} @@ -10224,6 +10572,10 @@ snapshots: delegates@1.0.0: {} + denque@2.1.0: {} + + depd@1.1.2: {} + depd@2.0.0: {} destroy@1.2.0: {} @@ -10559,7 +10911,7 @@ snapshots: esbuild-register@3.5.0(esbuild@0.21.4): dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) esbuild: 0.21.4 transitivePeerDependencies: - supports-color @@ -10689,7 +11041,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -10844,7 +11196,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -11093,6 +11445,10 @@ snapshots: strip-ansi: 6.0.1 wide-align: 1.1.5 + generate-function@2.3.1: + dependencies: + is-property: 1.0.2 + gensequence@7.0.0: {} get-caller-file@2.0.5: {} @@ -11372,8 +11728,21 @@ snapshots: domutils: 3.1.0 entities: 4.5.0 + http-assert@1.5.0: + dependencies: + deep-equal: 1.0.1 + http-errors: 1.8.1 + http-cache-semantics@4.1.1: {} + http-errors@1.8.1: + dependencies: + depd: 1.1.2 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 1.5.0 + toidentifier: 1.0.1 + http-errors@2.0.0: dependencies: depd: 2.0.0 @@ -11386,7 +11755,7 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -11398,7 +11767,7 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -11436,6 +11805,8 @@ snapshots: ieee754@1.2.1: {} + ignore-by-default@1.0.1: {} + ignore@5.3.1: {} immediate@3.0.6: {} @@ -11451,7 +11822,7 @@ snapshots: import-from-esm@1.3.4: dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) import-meta-resolve: 4.1.0 transitivePeerDependencies: - supports-color @@ -11617,6 +11988,8 @@ snapshots: is-plain-object@5.0.0: {} + is-property@1.0.2: {} + is-regex@1.1.4: dependencies: call-bind: 1.0.7 @@ -11777,6 +12150,10 @@ snapshots: readable-stream: 2.3.8 setimmediate: 1.0.5 + keygrip@1.1.0: + dependencies: + tsscmp: 1.0.6 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -11787,6 +12164,41 @@ snapshots: known-css-properties@0.31.0: {} + koa-compose@4.1.0: {} + + koa-convert@2.0.0: + dependencies: + co: 4.6.0 + koa-compose: 4.1.0 + + koa@2.15.3: + dependencies: + accepts: 1.3.8 + cache-content-type: 1.0.1 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookies: 0.9.1 + debug: 4.3.4(supports-color@5.5.0) + delegates: 1.0.0 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + fresh: 0.5.2 + http-assert: 1.5.0 + http-errors: 1.8.1 + is-generator-function: 1.0.10 + koa-compose: 4.1.0 + koa-convert: 2.0.0 + on-finished: 2.4.1 + only: 0.0.2 + parseurl: 1.3.3 + statuses: 1.5.0 + type-is: 1.6.18 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + last-run@2.0.0: {} lazy-val@1.0.5: {} @@ -11830,7 +12242,7 @@ snapshots: dependencies: chalk: 5.3.0 commander: 12.1.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) execa: 8.0.1 lilconfig: 3.1.1 listr2: 8.2.1 @@ -11927,6 +12339,8 @@ snapshots: strip-ansi: 7.1.0 wrap-ansi: 9.0.0 + long@5.2.3: {} + longest@2.0.1: {} loose-envify@1.4.0: @@ -11943,6 +12357,8 @@ snapshots: lru-cache@7.18.3: {} + lru-cache@8.0.5: {} + lucide-react@0.400.0(react@18.3.1): dependencies: react: 18.3.1 @@ -12129,12 +12545,28 @@ snapshots: mute-stream@0.0.8: {} + mysql2@3.11.0: + dependencies: + aws-ssl-profiles: 1.1.1 + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.6.3 + long: 5.2.3 + lru-cache: 8.0.5 + named-placeholders: 1.1.3 + seq-queue: 0.0.5 + sqlstring: 2.3.3 + mz@2.7.0: dependencies: any-promise: 1.3.0 object-assign: 4.1.1 thenify-all: 1.6.0 + named-placeholders@1.1.3: + dependencies: + lru-cache: 7.18.3 + nan@2.19.0: {} nano-memoize@3.0.16: {} @@ -12195,6 +12627,19 @@ snapshots: node-releases@2.0.14: {} + nodemon@3.1.4: + dependencies: + chokidar: 3.6.0 + debug: 4.3.4(supports-color@5.5.0) + ignore-by-default: 1.0.1 + minimatch: 3.1.2 + pstree.remy: 1.1.8 + semver: 7.6.2 + simple-update-notifier: 2.0.0 + supports-color: 5.5.0 + touch: 3.1.1 + undefsafe: 2.0.5 + nopt@6.0.0: dependencies: abbrev: 1.1.1 @@ -12295,6 +12740,8 @@ snapshots: dependencies: mimic-fn: 4.0.0 + only@0.0.2: {} + open@8.4.2: dependencies: define-lazy-prop: 2.0.0 @@ -12425,6 +12872,8 @@ snapshots: path-to-regexp@2.2.1: {} + path-to-regexp@6.2.2: {} + path-type@4.0.0: {} pend@1.2.0: {} @@ -12562,6 +13011,8 @@ snapshots: proxy-from-env@1.1.0: {} + pstree.remy@1.1.8: {} + pump@3.0.0: dependencies: end-of-stream: 1.4.4 @@ -13007,7 +13458,7 @@ snapshots: read-binary-file-arch@1.0.6: dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -13288,6 +13739,8 @@ snapshots: transitivePeerDependencies: - supports-color + seq-queue@0.0.5: {} + serialize-error@7.0.1: dependencies: type-fest: 0.13.1 @@ -13405,7 +13858,7 @@ snapshots: socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -13440,6 +13893,8 @@ snapshots: sprintf-js@1.1.3: {} + sqlstring@2.3.3: {} + ssri@9.0.1: dependencies: minipass: 3.3.6 @@ -13450,6 +13905,8 @@ snapshots: stat-mode@1.0.0: {} + statuses@1.5.0: {} + statuses@2.0.1: {} stream-composer@1.0.2: @@ -13598,7 +14055,7 @@ snapshots: cosmiconfig: 9.0.0(typescript@5.4.5) css-functions-list: 3.2.2 css-tree: 2.3.1 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) fast-glob: 3.3.2 fastest-levenshtein: 1.0.16 file-entry-cache: 8.0.0 @@ -13647,7 +14104,7 @@ snapshots: sumchecker@3.0.1: dependencies: - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -13808,6 +14265,8 @@ snapshots: toidentifier@1.0.1: {} + touch@3.1.1: {} + truncate-utf8-bytes@1.0.2: dependencies: utf8-byte-length: 1.0.5 @@ -13820,6 +14279,8 @@ snapshots: tslib@2.6.2: {} + tsscmp@1.0.6: {} + tsx@4.11.0: dependencies: esbuild: 0.20.2 @@ -13885,7 +14346,7 @@ snapshots: is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 - typeorm@0.3.20(better-sqlite3@9.6.0): + typeorm@0.3.20(better-sqlite3@9.6.0)(mysql2@3.11.0): dependencies: '@sqltools/formatter': 1.2.5 app-root-path: 3.1.0 @@ -13893,7 +14354,7 @@ snapshots: chalk: 4.1.2 cli-highlight: 2.1.11 dayjs: 1.11.11 - debug: 4.3.4 + debug: 4.3.4(supports-color@5.5.0) dotenv: 16.4.5 glob: 10.4.1 mkdirp: 2.1.6 @@ -13904,6 +14365,7 @@ snapshots: yargs: 17.7.2 optionalDependencies: better-sqlite3: 9.6.0 + mysql2: 3.11.0 transitivePeerDependencies: - supports-color @@ -13931,6 +14393,8 @@ snapshots: unc-path-regex@0.1.2: {} + undefsafe@2.0.5: {} + undertaker-registry@2.0.0: {} undertaker@2.0.0: @@ -14309,6 +14773,8 @@ snapshots: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 + ylru@1.4.0: {} + yocto-queue@0.1.0: {} yocto-queue@1.0.0: {}