diff --git a/shared/classes/CacheService/index.js b/shared/classes/CacheService/index.js deleted file mode 100755 index 429b39e3..00000000 --- a/shared/classes/CacheService/index.js +++ /dev/null @@ -1,72 +0,0 @@ -import fs from "fs" -import path from "path" - -export default class CacheService { - static deletionInterval = 1000 * 60 * 5 - - static cachePath = path.join(process.cwd(), ".cache") - - constructor(params = {}) { - this.params = params - - if (!fs.existsSync(CacheService.cachePath)) { - fs.mkdirSync(CacheService.cachePath, { recursive: true }) - } - } - - intervalMaps = new Map() - - checkDeletionFilepath(filepath) { - try { - const stats = fs.statSync(filepath) - - stats.atime = new Date(stats.atime) - - if (stats.atime.getTime() + CacheService.deletionInterval < Date.now()) { - fs.promises.unlink(filepath) - } else { - return false - } - - return true - - } catch (error) { - console.error(error) - - fs.promises.unlink(filepath) - - return true - } - } - - appendToDeletion(filepath) { - // create a interval of 5 minutes to delete the file - // check the last time the file was accessed and if it was accessed in the last 5 minutes - // reset the interval until the file is not accessed for 5 minutes and then delete it - try { - const createInterval = () => { - let interval = setInterval(async () => { - try { - await this.checkDeletionFilepath(filepath) - - this.intervalMaps.delete(filepath) - - if (!results) { - this.appendToDeletion(filepath) - } - } catch (error) { - return clearInterval(interval) - } - }) - - return interval - } - - this.intervalMaps.set(filepath, createInterval()) - } catch (error) { - console.error(error) - - return fs.promises.unlink(filepath) - } - } -} \ No newline at end of file diff --git a/shared/classes/ComtyClient/index.js b/shared/classes/ComtyClient/index.js deleted file mode 100755 index 0a8fbfdf..00000000 --- a/shared/classes/ComtyClient/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import createClient from "comty.js" - -export default (params = {}) => { - return createClient({ - ...params, - accessKey: process.env.COMTY_ACCESS_KEY, - privateKey: process.env.COMTY_PRIVATE_KEY, - }) -} \ No newline at end of file diff --git a/shared/classes/DbManager/index.js b/shared/classes/DbManager/index.js deleted file mode 100755 index 8c738d13..00000000 --- a/shared/classes/DbManager/index.js +++ /dev/null @@ -1,55 +0,0 @@ -import mongoose from "mongoose" - -function getConnectionConfig(obj) { - const { DB_USER, DB_DRIVER, DB_NAME, DB_PWD, DB_HOSTNAME, DB_PORT, DB_AUTH_SOURCE } = obj - - let auth = [ - DB_DRIVER ?? "mongodb", - "://", - ] - - auth.push(DB_HOSTNAME ?? "localhost") - auth.push(`:${DB_PORT ?? "27017"}`) - - auth = auth.join("") - - const params = { - auth: {}, - dbName: DB_NAME, - user: DB_USER, - pass: DB_PWD, - useNewUrlParser: true, - useUnifiedTopology: true, - } - - if (DB_AUTH_SOURCE) { - params.auth.authSource = DB_AUTH_SOURCE - } - - return [ - auth, - params, - ] -} - -export default class DBManager { - initialize = async (config) => { - console.log(`🔌 Connecting to DB [${process.env.DB_HOSTNAME}]...`) - - const dbConfig = getConnectionConfig(config ?? process.env) - - mongoose.set("strictQuery", false) - - const connection = await mongoose.connect(...dbConfig) - .catch((err) => { - console.log(`❌ Failed to connect to DB, retrying...\n`) - console.log(err) - - return false - }) - - if (connection) { - console.log(`✅ Connected to DB.`) - } - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/NFCTags/index.js b/shared/classes/DbModels/NFCTags/index.js deleted file mode 100755 index d83c9e39..00000000 --- a/shared/classes/DbModels/NFCTags/index.js +++ /dev/null @@ -1,37 +0,0 @@ -export default { - name: "NFCTag", - collection: "nfc_tags", - schema: { - user_id: { - type: String, - required: true - }, - owner_id: { - type: String, - required: true - }, - serial: { - type: String, - required: true - }, - alias: { - type: String, - default: "My NFC Tag" - }, - active: { - type: Boolean, - default: true - }, - behavior: { - type: Object, - default: { - type: "url", - value: "https://comty.app" - } - }, - endpoint_url: { - type: String, - default: "https://comty.app/nfc/no_endpoint" - } - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/authorizedServerTokens/index.js b/shared/classes/DbModels/authorizedServerTokens/index.js deleted file mode 100755 index b569fc49..00000000 --- a/shared/classes/DbModels/authorizedServerTokens/index.js +++ /dev/null @@ -1,28 +0,0 @@ -export default { - name: "authorizedServerTokens", - collection: "authorizedServerTokens", - schema: { - client_id: { - type: String, - required: true, - }, - token: { - type: String, - required: true, - }, - access: { - type: Array, - default: [], - }, - name: { - type: String, - }, - description: { - type: String, - }, - createdAt: { - type: Date, - default: Date.now, - }, - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/badge/index.js b/shared/classes/DbModels/badge/index.js deleted file mode 100755 index ed62fb1e..00000000 --- a/shared/classes/DbModels/badge/index.js +++ /dev/null @@ -1,11 +0,0 @@ -export default { - name: "Badge", - collection: "badges", - schema: { - name: { type: String, required: true }, - label: { type: String }, - description: { type: String }, - icon: { type: String }, - color: { type: String }, - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/comment/index.js b/shared/classes/DbModels/comment/index.js deleted file mode 100755 index 953687d4..00000000 --- a/shared/classes/DbModels/comment/index.js +++ /dev/null @@ -1,10 +0,0 @@ -export default { - name: "Comment", - collection: "comments", - schema: { - user_id: { type: String, required: true }, - parent_id: { type: String, required: true }, - message: { type: String, required: true }, - created_at: { type: Date, default: Date.now }, - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/config/index.js b/shared/classes/DbModels/config/index.js deleted file mode 100755 index f17b67bd..00000000 --- a/shared/classes/DbModels/config/index.js +++ /dev/null @@ -1,15 +0,0 @@ -export default { - name: "Config", - collection: "config", - schema: { - key: { - type: String, - required: true - }, - value: { - // type can be anything - type: Object, - required: true - }, - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/featuredEvent/index.js b/shared/classes/DbModels/featuredEvent/index.js deleted file mode 100755 index af36c24f..00000000 --- a/shared/classes/DbModels/featuredEvent/index.js +++ /dev/null @@ -1,13 +0,0 @@ -export default { - name: "FeaturedEvent", - collection: "featuredEvents", - schema: { - name: { type: String, required: true }, - category: { type: String }, - description: { type: String }, - dates: { type: Object }, - location: { type: String }, - announcement: { type: Object, required: true }, - expired: { type: Boolean, default: false } - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/featuredPlaylist/index.js b/shared/classes/DbModels/featuredPlaylist/index.js deleted file mode 100755 index 48eacb7d..00000000 --- a/shared/classes/DbModels/featuredPlaylist/index.js +++ /dev/null @@ -1,12 +0,0 @@ -export default { - name: "FeaturedPlaylist", - collection: "featuredPlaylists", - schema: { - title: { type: String, required: true }, - description: { type: String }, - cover_url: { type: String }, - enabled: { type: Boolean, default: true }, - genre: { type: String }, - playlist_id: { type: String, required: true }, - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/featuredWallpaper/index.js b/shared/classes/DbModels/featuredWallpaper/index.js deleted file mode 100755 index 74fe331e..00000000 --- a/shared/classes/DbModels/featuredWallpaper/index.js +++ /dev/null @@ -1,10 +0,0 @@ -export default { - name: "FeaturedWallpaper", - collection: "featuredWallpapers", - schema: { - active: { type: Boolean, default: true }, - date: { type: Date, default: Date.now }, - url: { type: String, required: true }, - author: { type: String }, - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/index.js b/shared/classes/DbModels/index.js deleted file mode 100755 index 159b6d34..00000000 --- a/shared/classes/DbModels/index.js +++ /dev/null @@ -1,23 +0,0 @@ -import mongoose, { Schema } from "mongoose" -import fs from "fs" -import path from "path" - -function generateModels() { - let models = {} - - const dirs = fs.readdirSync(__dirname).filter(file => file !== "index.js") - - dirs.forEach((file) => { - const model = require(path.join(__dirname, file)).default - - if (mongoose.models[model.name]) { - return models[model.name] = mongoose.model(model.name) - } - - return models[model.name] = mongoose.model(model.name, new Schema(model.schema), model.collection) - }) - - return models -} - -module.exports = generateModels() \ No newline at end of file diff --git a/shared/classes/DbModels/playlist/index.js b/shared/classes/DbModels/playlist/index.js deleted file mode 100755 index 6b9a9cf9..00000000 --- a/shared/classes/DbModels/playlist/index.js +++ /dev/null @@ -1,41 +0,0 @@ -export default { - name: "Playlist", - collection: "playlists", - schema: { - user_id: { - type: String, - required: true - }, - title: { - type: String, - required: true - }, - description: { - type: String - }, - list: { - type: Object, - default: [], - required: true - }, - cover: { - type: String, - default: "https://storage.ragestudio.net/comty-static-assets/default_song.png" - }, - thumbnail: { - type: String, - default: "https://storage.ragestudio.net/comty-static-assets/default_song.png" - }, - created_at: { - type: Date, - required: true - }, - publisher: { - type: Object, - }, - public: { - type: Boolean, - default: true, - }, - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/post/index.js b/shared/classes/DbModels/post/index.js deleted file mode 100755 index 45ff832e..00000000 --- a/shared/classes/DbModels/post/index.js +++ /dev/null @@ -1,12 +0,0 @@ -export default { - name: "Post", - collection: "posts", - schema: { - user_id: { type: String, required: true }, - timestamp: { type: String, required: true }, - created_at: { type: Date, default: Date.now, required: true }, - message: { type: String }, - attachments: { type: Array, default: [] }, - flags: { type: Array, default: [] }, - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/postLike/index.js b/shared/classes/DbModels/postLike/index.js deleted file mode 100755 index 16c41044..00000000 --- a/shared/classes/DbModels/postLike/index.js +++ /dev/null @@ -1,14 +0,0 @@ -export default { - name: "PostLike", - collection: "post_likes", - schema: { - user_id: { - type: String, - required: true, - }, - post_id: { - type: String, - required: true, - } - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/regenerationToken/index.js b/shared/classes/DbModels/regenerationToken/index.js deleted file mode 100755 index 35ac7459..00000000 --- a/shared/classes/DbModels/regenerationToken/index.js +++ /dev/null @@ -1,14 +0,0 @@ -export default { - name: "RegenerationToken", - collection: "regenerationTokens", - schema: { - expiredToken: { - type: String, - required: true, - }, - refreshToken: { - type: String, - required: true, - } - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/release/index.js b/shared/classes/DbModels/release/index.js deleted file mode 100755 index 204165ff..00000000 --- a/shared/classes/DbModels/release/index.js +++ /dev/null @@ -1,38 +0,0 @@ -export default { - name: "Release", - collection: "releases", - schema: { - user_id: { - type: String, - required: true - }, - title: { - type: String, - required: true - }, - type: { - type: String, - required: true, - }, - list: { - type: Object, - default: [], - required: true - }, - cover: { - type: String, - default: "https://storage.ragestudio.net/comty-static-assets/default_song.png" - }, - created_at: { - type: Date, - required: true - }, - publisher: { - type: Object, - }, - public: { - type: Boolean, - default: true, - }, - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/role/index.js b/shared/classes/DbModels/role/index.js deleted file mode 100755 index 917c8ff9..00000000 --- a/shared/classes/DbModels/role/index.js +++ /dev/null @@ -1,15 +0,0 @@ -export default { - name: "Role", - collection: "roles", - schema: { - name: { - type: String, - }, - description: { - type: String, - }, - apply: { - type: Object, - } - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/savedPost/index.js b/shared/classes/DbModels/savedPost/index.js deleted file mode 100755 index 05634f5b..00000000 --- a/shared/classes/DbModels/savedPost/index.js +++ /dev/null @@ -1,18 +0,0 @@ -export default { - name: "SavedPost", - collection: "savedPosts", - schema: { - post_id: { - type: "string", - required: true, - }, - user_id: { - type: "string", - required: true, - }, - saved_at: { - type: "date", - default: Date.now, - } - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/serverLimit/index.js b/shared/classes/DbModels/serverLimit/index.js deleted file mode 100755 index 44c27697..00000000 --- a/shared/classes/DbModels/serverLimit/index.js +++ /dev/null @@ -1,24 +0,0 @@ -import { Schema } from "mongoose" - -export default { - name: "ServerLimit", - collection: "serverLimits", - schema: { - key: { - type: String, - required: true, - }, - value: { - type: Schema.Types.Mixed, - required: true, - }, - active: { - type: Boolean, - default: true, - }, - data: { - type: Object, - required: false, - } - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/session/index.js b/shared/classes/DbModels/session/index.js deleted file mode 100755 index 39858a03..00000000 --- a/shared/classes/DbModels/session/index.js +++ /dev/null @@ -1,14 +0,0 @@ -export default { - name: "Session", - collection: "sessions", - schema: { - session_uuid: { type: String, required: true }, - token: { type: String, required: true }, - username: { type: String, required: true }, - user_id: { type: String, required: true }, - date: { type: Number, default: 0 }, - location: { type: String, default: "Unknown" }, - ip_address: { type: String, default: "Unknown" }, - client: { type: String, default: "Unknown" }, - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/streamingCategory/index.js b/shared/classes/DbModels/streamingCategory/index.js deleted file mode 100755 index 95b90ad4..00000000 --- a/shared/classes/DbModels/streamingCategory/index.js +++ /dev/null @@ -1,14 +0,0 @@ -export default { - name: "StreamingCategory", - collection: "streamingCategories", - schema: { - key: { - type: String, - required: true, - }, - label: { - type: String, - required: true, - }, - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/streamingProfile/index.js b/shared/classes/DbModels/streamingProfile/index.js deleted file mode 100755 index 1332700c..00000000 --- a/shared/classes/DbModels/streamingProfile/index.js +++ /dev/null @@ -1,37 +0,0 @@ -export default { - name: "StreamingProfile", - collection: "streamingProfiles", - schema: { - user_id: { - type: String, - required: true, - }, - profile_name: { - type: String, - required: true, - }, - stream_key: { - type: String, - required: true, - select: false, - }, - info: { - type: Object, - default: { - title: "Untitled", - description: "No description", - category: "other", - thumbnail: null, - } - }, - options: { - type: Object, - default: { - connection_protected: true, - private: false, - chatEnabled: true, - drvEnabled: false, - } - } - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/syncEntry/index.js b/shared/classes/DbModels/syncEntry/index.js deleted file mode 100755 index c8b4684d..00000000 --- a/shared/classes/DbModels/syncEntry/index.js +++ /dev/null @@ -1,18 +0,0 @@ -export default { - name: "SyncEntry", - collection: "syncEntries", - schema: { - user_id: { - type: "string", - required: true, - }, - key: { - type: "string", - required: true, - }, - value: { - type: "string", - required: true, - } - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/track/index.js b/shared/classes/DbModels/track/index.js deleted file mode 100755 index 576ddc3c..00000000 --- a/shared/classes/DbModels/track/index.js +++ /dev/null @@ -1,53 +0,0 @@ -export default { - name: "Track", - collection: "tracks", - schema: { - title: { - type: String, - required: true, - }, - album: { - type: String, - }, - artist: { - type: String, - }, - source: { - type: String, - required: true, - }, - metadata: { - type: Object, - }, - explicit: { - type: Boolean, - default: false, - }, - public: { - type: Boolean, - default: true, - }, - cover: { - type: String, - default: "https://storage.ragestudio.net/comty-static-assets/default_song.png" - }, - thumbnail: { - type: String, - default: "https://storage.ragestudio.net/comty-static-assets/default_song.png" - }, - videoCanvas: { - type: String, - }, - spotifyId: { - type: String, - }, - lyricsEnabled: { - type: Boolean, - default: true, - }, - publisher: { - type: Object, - required: true, - }, - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/track_like/index.js b/shared/classes/DbModels/track_like/index.js deleted file mode 100755 index 9f256563..00000000 --- a/shared/classes/DbModels/track_like/index.js +++ /dev/null @@ -1,17 +0,0 @@ -export default { - name: "TrackLike", - collection: "tracks_likes", - schema: { - user_id: { - type: String, - required: true, - }, - track_id: { - type: String, - required: true, - }, - created_at: { - type: Date, - } - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/user/index.js b/shared/classes/DbModels/user/index.js deleted file mode 100755 index f9efdfba..00000000 --- a/shared/classes/DbModels/user/index.js +++ /dev/null @@ -1,21 +0,0 @@ -export default { - name: "User", - collection: "accounts", - schema: { - username: { type: String, required: true }, - password: { type: String, required: true, select: false }, - email: { type: String, required: true }, - description: { type: String, default: null }, - public_name: { type: String, default: null }, - fullName: { type: String, default: null }, - cover: { type: String, default: null }, - avatar: { type: String, default: null }, - roles: { type: Array, default: [] }, - verified: { type: Boolean, default: false }, - birthday: { type: Date, default: null }, - badges: { type: Array, default: [] }, - links: { type: Array, default: [] }, - createdAt: { type: String }, - created_at: { type: String }, - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/userFollow/index.js b/shared/classes/DbModels/userFollow/index.js deleted file mode 100755 index 7bd63e77..00000000 --- a/shared/classes/DbModels/userFollow/index.js +++ /dev/null @@ -1,8 +0,0 @@ -export default { - name: "UserFollow", - collection: "follows", - schema: { - user_id: { type: String, required: true }, - to: { type: String, required: true }, - } -} \ No newline at end of file diff --git a/shared/classes/DbModels/widget/index.js b/shared/classes/DbModels/widget/index.js deleted file mode 100755 index 283d8caf..00000000 --- a/shared/classes/DbModels/widget/index.js +++ /dev/null @@ -1,24 +0,0 @@ -export default { - name: "Widget", - collection: "widgets", - schema: { - manifest: { - type: Object, - required: true, - }, - user_id: { - type: String, - required: true, - }, - public: { - type: Boolean, - default: true, - }, - created_at: { - type: Date, - }, - updated_at: { - type: Date, - }, - } -} \ No newline at end of file diff --git a/shared/classes/Errors/index.js b/shared/classes/Errors/index.js deleted file mode 100755 index bc95a60e..00000000 --- a/shared/classes/Errors/index.js +++ /dev/null @@ -1,84 +0,0 @@ -export class AuthorizationError extends Error { - constructor(req, res, message = "This endpoint requires authorization") { - super(message) - this.name = "AuthorizationError" - - if (req && res) { - return res.status(this.constructor.statusCode).json({ - error: message, - }) - } - } - - static get statusCode() { - return 401 - } -} - -export class NotFoundError extends Error { - constructor(req, res, message = "Not found") { - super(message) - this.name = "NotFoundError" - - if (req && res) { - return res.status(this.constructor.statusCode).json({ - error: message, - }) - } - } - - static get statusCode() { - return 404 - } -} - -export class PermissionError extends Error { - constructor(req, res, message = "You don't have permission to do this") { - super(message) - this.name = "PermissionError" - - if (req && res) { - return res.status(this.constructor.statusCode).json({ - error: message, - }) - } - } - - static get statusCode() { - return 403 - } -} - -export class BadRequestError extends Error { - constructor(req, res, message = "Bad request") { - super(message) - this.name = "BadRequestError" - - if (req && res) { - return res.status(this.constructor.statusCode).json({ - error: message, - }) - } - } - - static get statusCode() { - return 400 - } -} - -export class InternalServerError extends Error { - constructor(req, res, message = "Internal server error") { - super(message) - this.name = "InternalServerError" - - if (req && res) { - return res.status(this.constructor.statusCode).json({ - error: message, - }) - } - } - - static get statusCode() { - return 500 - } -} \ No newline at end of file diff --git a/shared/classes/FileUpload/index.js b/shared/classes/FileUpload/index.js deleted file mode 100755 index 6a609c2f..00000000 --- a/shared/classes/FileUpload/index.js +++ /dev/null @@ -1,260 +0,0 @@ -// Orginal forked from: Buzut/huge-uploader-nodejs -// Copyright (c) 2018, Quentin Busuttil All rights reserved. - -import fs from "node:fs" -import path from "node:path" -import { promisify } from "node:util" -import mimetypes from "mime-types" -import crypto from "node:crypto" - -import Busboy from "busboy" - -export function getFileHash(file) { - return new Promise((resolve, reject) => { - const hash = crypto.createHash("sha256") - - file.on("data", (chunk) => hash.update(chunk)) - - file.on("end", () => resolve(hash.digest("hex"))) - - file.on("error", reject) - }) -} - -export function checkHeaders(headers) { - if ( - !headers["uploader-chunk-number"] || - !headers["uploader-chunks-total"] || - !headers["uploader-original-name"] || - !headers["uploader-file-id"] || - !headers["uploader-chunks-total"].match(/^[0-9]+$/) || - !headers["uploader-chunk-number"].match(/^[0-9]+$/) - ) { - return false - } - - return true -} - -export function checkTotalSize(maxFileSize, maxChunkSize, totalChunks) { - if (maxChunkSize * totalChunks > maxFileSize) { - return false - } - - return true -} - -export function cleanChunks(dirPath) { - fs.readdir(dirPath, (err, files) => { - let filesLength = files.length - - files.forEach((file) => { - fs.unlink(path.join(dirPath, file), () => { - if (--filesLength === 0) fs.rmdir(dirPath, () => { }) // cb does nothing but required - }) - }) - }) -} - -export function createAssembleChunksPromise({ - tmpDir, - headers, - useDate, -}) { - const asyncReadFile = promisify(fs.readFile) - const asyncAppendFile = promisify(fs.appendFile) - - const originalMimeType = mimetypes.lookup(headers["uploader-original-name"]) - const originalExtension = mimetypes.extension(originalMimeType) - - const totalChunks = +headers["uploader-chunks-total"] - - const fileId = headers["uploader-file-id"] - const workPath = path.join(tmpDir, fileId) - const chunksPath = path.resolve(workPath, "chunks") - const assembledFilepath = path.join(workPath, `assembled.${originalExtension}`) - - let chunkCount = 0 - let finalFilepath = null - - return () => { - return new Promise((resolve, reject) => { - const onEnd = async () => { - try { - const hash = await getFileHash(fs.createReadStream(assembledFilepath)) - - if (useDate) { - finalFilepath = path.resolve(workPath, `${hash}_${Date.now()}.${originalExtension}`) - } else { - finalFilepath = path.resolve(workPath, `${hash}.${originalExtension}`) - } - - fs.renameSync(assembledFilepath, finalFilepath) - - cleanChunks(chunksPath) - - return resolve({ - filename: headers["uploader-original-name"], - filepath: finalFilepath, - cachePath: workPath, - hash, - mimetype: originalMimeType, - extension: originalExtension, - }) - } catch (error) { - return reject(error) - } - } - - const pipeChunk = () => { - asyncReadFile(path.join(chunksPath, chunkCount.toString())) - .then((chunk) => asyncAppendFile(assembledFilepath, chunk)) - .then(() => { - // 0 indexed files = length - 1, so increment before comparison - if (totalChunks > ++chunkCount) { - return pipeChunk(chunkCount) - } - - return onEnd() - }) - .catch(reject) - } - - pipeChunk() - }) - } -} - -export function mkdirIfDoesntExist(dirPath, callback) { - if (!fs.existsSync(dirPath)) { - fs.mkdir(dirPath, { recursive: true }, callback) - } -} - -export function handleFile(tmpDir, headers, fileStream) { - const dirPath = path.join(tmpDir, headers["uploader-file-id"]) - const chunksPath = path.join(dirPath, "chunks") - const chunkPath = path.join(chunksPath, headers["uploader-chunk-number"]) - const useDate = headers["uploader-use-date"] === "true" - const chunkCount = +headers["uploader-chunk-number"] - const totalChunks = +headers["uploader-chunks-total"] - - let error - let assembleChunksPromise - let finished = false - let writeStream - - const writeFile = () => { - writeStream = fs.createWriteStream(chunkPath) - - writeStream.on("error", (err) => { - error = err - fileStream.resume() - }) - - writeStream.on("close", () => { - finished = true - - // if all is uploaded - if (chunkCount === totalChunks - 1) { - assembleChunksPromise = createAssembleChunksPromise({ - tmpDir, - headers, - useDate, - }) - } - }) - - fileStream.pipe(writeStream) - } - - // make sure chunk is in range - if (chunkCount < 0 || chunkCount >= totalChunks) { - error = new Error("Chunk is out of range") - fileStream.resume() - } - - else if (chunkCount === 0) { - // create file upload dir if it's first chunk - mkdirIfDoesntExist(chunksPath, (err) => { - if (err) { - error = err - fileStream.resume() - } - - else writeFile() - }) - } - - else { - // make sure dir exists if it's not first chunk - fs.stat(dirPath, (err) => { - if (err) { - error = new Error("Upload has expired") - fileStream.resume() - } - - else writeFile() - }) - } - - return (callback) => { - if (finished && !error) callback(null, assembleChunksPromise) - else if (error) callback(error) - - else { - writeStream.on("error", callback) - writeStream.on("close", () => callback(null, assembleChunksPromise)) - } - } -} - -export function uploadFile(req, tmpDir, maxFileSize, maxChunkSize) { - return new Promise((resolve, reject) => { - if (!checkHeaders(req.headers)) { - reject(new Error("Missing header(s)")) - return - } - - if (!checkTotalSize(maxFileSize, req.headers["uploader-chunks-total"])) { - reject(new Error("File is above size limit")) - return - } - - try { - let limitReached = false - let getFileStatus - - const busboy = Busboy({ headers: req.headers, limits: { files: 1, fileSize: maxChunkSize * 1000 * 1000 } }) - - busboy.on("file", (fieldname, fileStream) => { - fileStream.on("limit", () => { - limitReached = true - fileStream.resume() - }) - - getFileStatus = handleFile(tmpDir, req.headers, fileStream) - }) - - busboy.on("close", () => { - if (limitReached) { - reject(new Error("Chunk is above size limit")) - return - } - - getFileStatus((fileErr, assembleChunksF) => { - if (fileErr) reject(fileErr) - else resolve(assembleChunksF) - }) - }) - - req.pipe(busboy) - } - - catch (err) { - reject(err) - } - }) -} - -export default uploadFile \ No newline at end of file diff --git a/shared/classes/RedisClient/index.js b/shared/classes/RedisClient/index.js deleted file mode 100755 index 9f8938b7..00000000 --- a/shared/classes/RedisClient/index.js +++ /dev/null @@ -1,60 +0,0 @@ -import { createClient } from "redis" -import { createAdapter } from "@socket.io/redis-adapter" - -function composeURL() { - // support for auth - let url = "redis://" - - url += process.env.REDIS_HOST ?? "localhost" - - if (process.env.REDIS_PORT) { - url += ":" + process.env.REDIS_PORT - } - - return url -} - -export default ({ - withWsAdapter = false -} = {}) => { - let clientOpts = { - url: composeURL(), - } - - if (!process.env.REDIS_NO_AUTH) { - if (process.env.REDIS_PASSWORD) { - clientOpts.password = process.env.REDIS_PASSWORD - } - - if (process.env.REDIS_USERNAME) { - clientOpts.username = process.env.REDIS_USERNAME - } - } - - let client = createClient(clientOpts) - - if (withWsAdapter) { - client.subClient = client.duplicate() - client.ioAdapter = global.ioAdapter = createAdapter(client, client.subClient) - } - - client.initialize = async () => { - console.log("🔌 Connecting to Redis client...") - - await client.connect() - - return client - } - - // handle when client disconnects unexpectedly to avoid main crash - client.on("error", (error) => { - console.error("❌ Redis client error:", error) - }) - - // handle when client connects - client.on("connect", () => { - console.log("✅ Redis client connected.", process.env.REDIS_HOST) - }) - - return client -} \ No newline at end of file diff --git a/shared/classes/SecureSyncEntry/index.js b/shared/classes/SecureSyncEntry/index.js deleted file mode 100755 index d0ec7734..00000000 --- a/shared/classes/SecureSyncEntry/index.js +++ /dev/null @@ -1,134 +0,0 @@ -import { SyncEntry } from "@shared-classes/DbModels" - -import crypto from "crypto" - -export default class SecureSyncEntry { - static get encrytionAlgorithm() { - return "aes-256-cbc" - } - - static async set(user_id, key, value) { - if (!user_id) { - throw new Error("Missing user_id") - } - - if (!key) { - throw new Error("Missing key") - } - - if (!value) { - throw new Error("Missing value") - } - - let entry = await SyncEntry.findOne({ - user_id, - key, - }).catch(() => null) - - const encryptionKey = Buffer.from(process.env.SYNC_ENCRIPT_SECRET, "hex") - const iv = crypto.randomBytes(16) - - const cipher = crypto.createCipheriv(SecureSyncEntry.encrytionAlgorithm, encryptionKey, iv) - - let encrypted - - try { - encrypted = cipher.update(value) - } - catch (error) { - console.error(error) - } - - encrypted = Buffer.concat([encrypted, cipher.final()]) - - if (entry) { - entry.value = iv.toString("hex") + ":" + encrypted.toString("hex") - - await entry.save() - - return entry - } - - entry = new SyncEntry({ - user_id, - key, - value: iv.toString("hex") + ":" + encrypted.toString("hex"), - }) - - await entry.save() - - return entry - } - - static async get(user_id, key) { - if (!user_id) { - throw new Error("Missing user_id") - } - - if (!key) { - throw new Error("Missing key") - } - - const entry = await SyncEntry.findOne({ - user_id, - key, - }).catch(() => null) - - if (!entry) { - return null - } - - const encryptionKey = Buffer.from(process.env.SYNC_ENCRIPT_SECRET, "hex") - - const iv = Buffer.from(entry.value.split(":")[0], "hex") - const encryptedText = Buffer.from(entry.value.split(":")[1], "hex") - - const decipher = crypto.createDecipheriv(SecureSyncEntry.encrytionAlgorithm, encryptionKey, iv) - - let decrypted = decipher.update(encryptedText) - - decrypted = Buffer.concat([decrypted, decipher.final()]) - - return decrypted.toString() - } - - static async delete(user_id, key) { - if (!user_id) { - throw new Error("Missing user_id") - } - - if (!key) { - throw new Error("Missing key") - } - - const entry = await SyncEntry.findOne({ - user_id, - key, - }).catch(() => null) - - if (!entry) { - return null - } - - await entry.delete() - - return entry - } - - static async has(user_id, key) { - if (!user_id) { - throw new Error("Missing user_id") - } - - if (!key) { - throw new Error("Missing key") - } - - const entry = await SyncEntry.findOne({ - user_id, - key, - }).catch(() => null) - - return !!entry - } -} \ No newline at end of file diff --git a/shared/classes/StorageClient/index.js b/shared/classes/StorageClient/index.js deleted file mode 100755 index 886526e3..00000000 --- a/shared/classes/StorageClient/index.js +++ /dev/null @@ -1,97 +0,0 @@ -const Minio = require("minio") -import path from "path" - -export const generateDefaultBucketPolicy = (payload) => { - const { bucketName } = payload - - if (!bucketName) { - throw new Error("bucketName is required") - } - - return { - Version: "2012-10-17", - Statement: [ - { - Action: [ - "s3:GetObject" - ], - Effect: "Allow", - Principal: { - AWS: [ - "*" - ] - }, - Resource: [ - `arn:aws:s3:::${bucketName}/*` - ], - Sid: "" - } - ] - } -} - -export class StorageClient extends Minio.Client { - constructor(options) { - super(options) - - this.defaultBucket = String(options.defaultBucket) - this.defaultRegion = String(options.defaultRegion) - } - - composeRemoteURL = (key) => { - const _path = path.join(this.defaultBucket, key) - - return `${this.protocol}//${this.host}:${this.port}/${_path}` - } - - setDefaultBucketPolicy = async (bucketName) => { - const policy = generateDefaultBucketPolicy({ bucketName }) - - return this.setBucketPolicy(bucketName, JSON.stringify(policy)) - } - - initialize = async () => { - console.log("🔌 Checking if storage client have default bucket...") - - // check connection with s3 - const bucketExists = await this.bucketExists(this.defaultBucket).catch(() => { - return false - }) - - if (!bucketExists) { - console.warn("🪣 Default bucket not exists! Creating new bucket...") - - await this.makeBucket(this.defaultBucket, "s3") - - // set default bucket policy - await this.setDefaultBucketPolicy(this.defaultBucket) - } - - // check if default bucket policy exists - const bucketPolicy = await this.getBucketPolicy(this.defaultBucket).catch(() => { - return null - }) - - if (!bucketPolicy) { - // set default bucket policy - await this.setDefaultBucketPolicy(this.defaultBucket) - } - - console.log("✅ Storage client is ready.") - } -} - -export const createStorageClientInstance = (options) => { - return new StorageClient({ - endPoint: process.env.S3_ENDPOINT, - port: Number(process.env.S3_PORT), - useSSL: toBoolean(process.env.S3_USE_SSL), - accessKey: process.env.S3_ACCESS_KEY, - secretKey: process.env.S3_SECRET_KEY, - defaultBucket: process.env.S3_BUCKET, - defaultRegion: process.env.S3_REGION, - ...options, - }) -} - -export default createStorageClientInstance \ No newline at end of file diff --git a/shared/classes/TidalAPI/index.js b/shared/classes/TidalAPI/index.js deleted file mode 100755 index 53975536..00000000 --- a/shared/classes/TidalAPI/index.js +++ /dev/null @@ -1,480 +0,0 @@ -import axios from "axios" -import qs from "qs" - -const TIDAL_CLIENT_ID = process.env.TIDAL_CLIENT_ID -const TIDAL_CLIENT_SECRET = process.env.TIDAL_CLIENT_SECRET - -function tranformTrackData(data = {}) { - // TODO: Support Track remixes & versions - data._id = data.id - - const coverUID = data.album.cover.replace(/-/g, "/") - - data.cover = `https://resources.tidal.com/images/${coverUID}/1080x1080.jpg` - - data.artist = data.artists.map(artist => artist.name).join(", ") - - data.metadata = { - title: data.title, - artists: data.artists.map(artist => artist.name).join(", "), - artist: data.artists.map(artist => artist.name).join(", "), - album: data.album.title, - duration: data.duration, - } - - data.service = "tidal" - - return data -} - -export default class TidalAPI { - static API_V1 = "https://api.tidal.com/v1" - static API_V2 = "https://api.tidal.com/v2" - - static API_USERS = "https://api.tidal.com/v1/users" - - static async checkAuthStatus(device_code) { - const data = { - client_id: TIDAL_CLIENT_ID, - device_code: device_code, - grant_type: "urn:ietf:params:oauth:grant-type:device_code", - scope: "r_usr+w_usr+w_sub", - } - - const response = await axios({ - method: "POST", - url: "https://auth.tidal.com/v1/oauth2/token", - params: data, - auth: { - username: TIDAL_CLIENT_ID, - password: TIDAL_CLIENT_SECRET, - }, - }).catch(err => { - return false - }) - - if (!response) { - return false - } - - return response.data - } - - static async getAuthUrl() { - let data = { - client_id: TIDAL_CLIENT_ID, - scope: "r_usr+w_usr+w_sub", - } - - const response = await axios({ - method: "POST", - url: "https://auth.tidal.com/v1/oauth2/device_authorization", - params: data, - }) - - return { - url: "https://" + response.data.verificationUri + "/" + response.data.userCode, - device_code: response.data.deviceCode, - expires_in: response.data.expiresIn, - } - } - - static async getUserData({ access_token, user_id, country }) { - const url = `https://api.tidal.com/v1/users/${user_id}?countryCode=${country}` - - const response = await axios({ - method: "GET", - url, - headers: { - Origin: "http://listen.tidal.com", - Authorization: `Bearer ${access_token}`, - }, - }) - - return response.data - } - - static async getTrackPlaybackUrl({ track_id, quality, access_token, country }) { - let params = { - countryCode: country ?? "US", - audioquality: quality ?? "LOSSLESS", - playbackmode: "STREAM", - assetpresentation: "FULL", - } - - let response = await axios({ - method: "GET", - url: `https://api.tidal.com/v1/tracks/${track_id}/playbackinfopostpaywall`, - params: params, - headers: { - Origin: "http://listen.tidal.com", - Authorization: `Bearer ${access_token}`, - }, - }) - - let decodedManifest = JSON.parse(global.b64Decode(response.data.manifest)) - - decodedManifest.url = decodedManifest.urls[0] - - return { - metadata: { - trackId: track_id, - audioMode: response.data.audioMode, - audioQuality: response.data.audioQuality, - bitDepth: response.data.bitDepth, - bitRate: response.data.bitRate, - mimeType: response.data.manifestMimeType, - }, - ...decodedManifest, - } - } - static async getTrackMetadata({ track_id, access_token, country }) { - const response = await axios({ - method: "GET", - url: `https://api.tidal.com/v1/tracks/${track_id}`, - params: { - countryCode: country, - }, - headers: { - Origin: "http://listen.tidal.com", - Authorization: `Bearer ${access_token}`, - }, - }) - - return response.data - } - - static async getTrackManifest({ track_id, quality, access_token, country }) { - const playback = await TidalAPI.getTrackPlaybackUrl({ - track_id, - quality, - access_token, - country, - }) - - const metadata = await TidalAPI.getTrackMetadata({ - track_id, - access_token, - country, - }) - - return { - playback, - metadata, - } - } - - static async search({ query, type = "all" }) { - let url = `https://api.tidal.com/v1/search` - - switch (type) { - case "all": - url = `https://api.tidal.com/v1/search` - break - case "playlists": - url = `https://api.tidal.com/v1/search/playlists` - break - case "artists": - url = `https://api.tidal.com/v1/search/artists` - break - case "albums": - url = `https://api.tidal.com/v1/search/albums` - break - case "tracks": - url = `https://api.tidal.com/v1/search/tracks` - break - } - - const response = await axios({ - method: "GET", - url: url, - params: { - query: query, - countryCode: "AZ", - }, - headers: { - Origin: "http://listen.tidal.com", - "x-tidal-token": TIDAL_CLIENT_ID, - }, - }) - - return response.data.tracks.items.map(item => { - item = tranformTrackData(item) - - return item - }) - } - - /** - * Retrieves favorite tracks for a user. - * - * @param {Object} options - The options for retrieving favorite tracks. - * @param {number} options.user_id - The user ID. - * @param {string} options.country - The country code. - * @param {string} options.access_token - The access token. - * @param {number} [options.limit=100] - The maximum number of tracks to retrieve. - * @param {number} [options.offset=0] - The offset for pagination. - * @return {Object} The response object containing the total length and tracks. - */ - static async getFavoriteTracks({ - user_id, - access_token, - country, - limit = 100, - offset = 0, - order = "DATE", - orderDirection = "DESC", - }) { - const response = await axios({ - url: `${TidalAPI.API_USERS}/${user_id}/favorites/tracks`, - method: "GET", - headers: { - Origin: "http://listen.tidal.com", - Authorization: `Bearer ${access_token}`, - }, - params: { - countryCode: country, - order, - orderDirection, - limit, - offset, - }, - }) - - response.data.items = response.data.items.map(item => { - item.item = tranformTrackData(item.item) - - item.item.liked_at = new Date(item.created).getTime() - item.item.liked = true - item.item._computed = true - - return item.item - }) - - return { - total_length: response.data.totalNumberOfItems, - tracks: response.data.items, - } - } - - /** - * Retrieves self favorite playlists based on specified parameters. - * - * @param {Object} options - The options object. - * @param {string} options.country - The country code. - * @param {string} options.access_token - The access token for authentication. - * @param {number} [options.limit=100] - The maximum number of playlists to retrieve. - * @param {number} [options.offset=0] - The offset for pagination. - * @param {string} [options.order="DATE"] - The field to order the playlists by. - * @param {string} [options.orderDirection="DESC"] - The direction to order the playlists in. - * @return {Object} - An object containing the total length and items of the playlists. - */ - static async getFavoritePlaylists({ - country, - access_token, - limit = 100, - offset = 0, - order = "DATE", - orderDirection = "DESC", - }) { - const params = { - folderId: "root", - deviceType: "BROWSER", - countryCode: country, - offset, - limit, - order, - orderDirection, - } - - let response = await axios({ - url: `${TidalAPI.API_V2}/my-collection/playlists/folders`, - method: "GET", - headers: { - Origin: "http://listen.tidal.com", - Authorization: `Bearer ${access_token}`, - Server: "envoy", - }, - params: params, - }) - - response.data.items = response.data.items.map(item => { - item.data._id = item.data.uuid - item.data.addedAt = item.addedAt - item.data.created_at = item.addedAt - - item.data.service = "tidal" - - const coverUID = item.data.squareImage.replace(/-/g, "/") - item.data.cover = `https://resources.tidal.com/images/${coverUID}/1080x1080.jpg` - - return item.data - }) - - return { - total_length: response.data.totalNumberOfItems, - items: response.data.items, - } - } - - /** - * Retrieves playlist items based on the provided parameters. - * - * @param {Object} options - The options for retrieving playlist items. - * @param {string} options.uuid - The UUID of the playlist. - * @param {number} options.limit - The maximum number of items to retrieve. - * @param {number} options.offset - The offset of items to start retrieving from. - * @param {string} options.country - The country code for retrieving items. - * @param {string} options.access_token - The access token for authentication. - * @return {Object} An object containing the total length and items of the playlist. - */ - static async getPlaylistItems({ - uuid, - limit, - offset, - - country, - access_token, - }) { - const params = { - limit, - offset, - countryCode: country, - } - - let response = await axios({ - url: `${TidalAPI.API_V1}/playlists/${uuid}/items`, - method: "GET", - headers: { - Origin: "http://listen.tidal.com", - Authorization: `Bearer ${access_token}`, - Server: "envoy", - }, - params: params, - }) - - response.data.items = response.data.items.map((item) => { - item = tranformTrackData(item.item) - - return item - }) - - return { - total_length: response.data.totalNumberOfItems, - list: response.data.items, - } - } - - /** - * Retrieves playlist data from the Tidal API. - * - * @param {Object} options - The options for retrieving the playlist data. - * @param {string} options.uuid - The UUID of the playlist. - * @param {string} options.access_token - The access token for authentication. - * @param {string} options.country - The country code for the playlist data. - * @param {boolean} [options.resolve_items=false] - Whether to resolve the playlist items. - * @param {number} [options.limit] - The maximum number of items to retrieve. - * @param {number} [options.offset] - The offset for pagination. - * @return {Object} The playlist data retrieved from the Tidal API. - */ - static async getPlaylistData({ - uuid, - - access_token, - country, - - resolve_items = false, - limit, - offset, - }) { - const params = { - countryCode: country, - } - - let response = await axios({ - url: `${TidalAPI.API_V1}/playlists/${uuid}`, - method: "GET", - headers: { - Origin: "http://listen.tidal.com", - Authorization: `Bearer ${access_token}`, - Server: "envoy", - }, - params: params, - }) - - const coverUID = response.data.squareImage.replace(/-/g, "/") - response.data.cover = `https://resources.tidal.com/images/${coverUID}/1080x1080.jpg` - - response.data.service = "tidal" - - if (resolve_items) { - response.data.list = await TidalAPI.getPlaylistItems({ - uuid, - limit, - offset, - access_token, - country, - }) - - response.data.total_length = response.data.list.total_length - response.data.list = response.data.list.list - } - - return response.data - } - - /** - * Toggles the like status of a track. - * - * @param {Object} params - The parameters for toggling the track like. - * @param {string} params.trackId - The ID of the track to toggle the like status. - * @param {boolean} params.to - The new like status. True to like the track, false to unlike it. - * @param {string} params.user_id - The ID of the user performing the action. - * @param {string} params.access_token - The access token for authentication. - * @param {string} params.country - The country code. - * @return {Object} - The response data from the API. - */ - static async toggleTrackLike({ - trackId, - to, - user_id, - - access_token, - country, - }) { - let url = `${TidalAPI.API_V1}/users/${user_id}/favorites/tracks` - let payload = null - let headers = { - Origin: "http://listen.tidal.com", - Authorization: `Bearer ${access_token}`, - } - - if (!to) { - url = `${url}/${trackId}` - } else { - payload = qs.stringify({ - trackIds: trackId, - onArtifactNotFound: "FAIL" - }) - - headers["Content-Type"] = "application/x-www-form-urlencoded" - } - - let response = await axios({ - url: url, - method: to ? "POST" : "DELETE", - headers: headers, - params: { - countryCode: country, - deviceType: "BROWSER" - }, - data: payload - }) - - return response.data - } - - static async togglePlaylistLike(playlist_id) { - - } -} diff --git a/shared/lib/api_wrapper/index.js b/shared/lib/api_wrapper/index.js deleted file mode 100755 index 3fdfc6d8..00000000 --- a/shared/lib/api_wrapper/index.js +++ /dev/null @@ -1,120 +0,0 @@ -require("dotenv").config() - -const path = require("path") -const { registerBaseAliases } = require("linebridge/dist/server") -const { webcrypto: crypto } = require("crypto") -const infisical = require("infisical-node") - -global.isProduction = process.env.NODE_ENV === "production" - -globalThis["__root"] = path.resolve(process.cwd()) -globalThis["__src"] = path.resolve(globalThis["__root"], global.isProduction ? "dist" : "src") - -const customAliases = { - "root": globalThis["__root"], - "src": globalThis["__src"], - "@shared-classes": path.resolve(globalThis["__src"], "_shared/classes"), - "@services": path.resolve(globalThis["__src"], "services"), -} - -if (!global.isProduction) { - customAliases["comty.js"] = path.resolve(globalThis["__src"], "../../comty.js/src") - customAliases["@shared-classes"] = path.resolve(globalThis["__src"], "shared-classes") -} - -if (process.env.USE_LINKED_SHARED) { - customAliases["@shared-classes"] = path.resolve(globalThis["__src"], "shared-classes") -} - -registerBaseAliases(globalThis["__src"], customAliases) - -// patches -const { Buffer } = require("buffer") - -global.b64Decode = (data) => { - return Buffer.from(data, "base64").toString("utf-8") -} -global.b64Encode = (data) => { - return Buffer.from(data, "utf-8").toString("base64") -} - -global.nanoid = (t = 21) => crypto.getRandomValues(new Uint8Array(t)).reduce(((t, e) => t += (e &= 63) < 36 ? e.toString(36) : e < 62 ? (e - 26).toString(36).toUpperCase() : e > 62 ? "-" : "_"), ""); - -Array.prototype.updateFromObjectKeys = function (obj) { - this.forEach((value, index) => { - if (obj[value] !== undefined) { - this[index] = obj[value] - } - }) - - return this -} - -global.toBoolean = (value) => { - if (typeof value === "boolean") { - return value - } - - if (typeof value === "string") { - return value.toLowerCase() === "true" - } - - return false -} - -async function injectEnvFromInfisical() { - const envMode = global.FORCE_ENV ?? global.isProduction ? "prod" : "dev" - - console.log(`🔑 Injecting env variables from INFISICAL in [${envMode}] mode...`) - - const client = new infisical({ - token: process.env.INFISICAL_TOKEN, - }) - - const secrets = await client.getAllSecrets({ - environment: envMode, - attachToProcessEnv: false, - }) - - // inject to process.env - secrets.forEach((secret) => { - if (!(process.env[secret.secretName])) { - process.env[secret.secretName] = secret.secretValue - } - }) -} - -function handleExit(instance, code) { - if (instance.server) { - if (typeof instance.server.close === "function") { - instance.server.close() - } - } - - return process.exit(code) -} - -async function main({ - force_infisical, -} = {}) { - const API = require(path.resolve(globalThis["__src"], "api.js")).default - - if (force_infisical || process.env.INFISICAL_TOKEN) { - await injectEnvFromInfisical() - } - - const instance = new API() - - process.on("exit", () => handleExit(instance, 0)) - process.on("SIGINT", () => handleExit(instance, 0)) - process.on("uncaughtException", () => handleExit(instance, 1)) - process.on("unhandledRejection", () => handleExit(instance, 1)) - - await instance.initialize() - - return instance -} - -main().catch((error) => { - console.error(`🆘 [FATAL ERROR] >`, error) -}) \ No newline at end of file