mirror of
https://github.com/ragestudio/comty.js.git
synced 2025-06-09 02:24:18 +00:00
commit
da8422c8a3
15
package.json
15
package.json
@ -1,10 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "comty.js",
|
"name": "comty.js",
|
||||||
"version": "0.60.3",
|
"version": "0.62.0",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"author": "RageStudio <support@ragestudio.net>",
|
"author": "RageStudio <support@ragestudio.net>",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "ava",
|
|
||||||
"build": "hermes build"
|
"build": "hermes build"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
@ -13,15 +12,15 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@foxify/events": "^2.1.0",
|
"@foxify/events": "^2.1.0",
|
||||||
"ava": "^6.1.2",
|
"axios": "^1.8.4",
|
||||||
"axios": "^1.4.0",
|
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^4.0.0",
|
||||||
"luxon": "^3.3.0",
|
"linebridge-client": "^1.0.0",
|
||||||
"socket.io-client": "^4.6.1"
|
"luxon": "^3.6.0",
|
||||||
|
"socket.io-client": "^4.8.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ragestudio/hermes": "^0.1.0"
|
"@ragestudio/hermes": "^1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
23
src/addons.js
Normal file
23
src/addons.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export default class AddonsManager {
|
||||||
|
addons = new Map()
|
||||||
|
|
||||||
|
register(name, addon) {
|
||||||
|
this.addons.set(name, addon)
|
||||||
|
}
|
||||||
|
|
||||||
|
get(name) {
|
||||||
|
return this.addons.get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// search all addons registered, and find all addons that has a addon[operation] function
|
||||||
|
getByOperation(operation) {
|
||||||
|
return Array.from(this.addons.values())
|
||||||
|
.filter((addon) => addon[operation])
|
||||||
|
.map((addon) => {
|
||||||
|
return {
|
||||||
|
id: addon.constructor.id,
|
||||||
|
fn: addon[operation],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,33 +1,31 @@
|
|||||||
import handleRegenerationEvent from "./handleRegenerationEvent"
|
import refreshToken from "./refreshToken"
|
||||||
|
|
||||||
export default async (data, callback) => {
|
export default async (data, callback) => {
|
||||||
// handle 401, 403 responses
|
// handle 401, 403 responses
|
||||||
if (data instanceof Error) {
|
if (data instanceof Error) {
|
||||||
if (data.code && (data.code === "ECONNABORTED" || data.code === "ERR_NETWORK")) {
|
if (data.code && (data.code === "ECONNABORTED" || data.code === "ERR_NETWORK")) {
|
||||||
console.error(`Request aborted or network error, ignoring`)
|
console.error(`Request aborted or network error`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.response) {
|
||||||
if (data.response.status === 401) {
|
if (data.response.status === 401) {
|
||||||
// check if the server issue a refresh token on data
|
// check if the server issue a refresh token on data
|
||||||
if (data.response.data.refreshToken) {
|
if (data.response.data.expired) {
|
||||||
console.log(`Session expired, but the server issued a refresh token, handling regeneration event`)
|
try {
|
||||||
|
console.log(`Session expired, trying to regenerate...`)
|
||||||
|
|
||||||
// handle regeneration event
|
await refreshToken()
|
||||||
await handleRegenerationEvent(data.response.data.refreshToken)
|
} catch (error) {
|
||||||
|
__comty_shared_state.eventBus.emit("session.invalid", error.message)
|
||||||
|
|
||||||
|
console.error(`Failed to regenerate token: ${error.message}`)
|
||||||
|
|
||||||
|
throw new Error(`Invalid or Expired session`)
|
||||||
|
}
|
||||||
|
|
||||||
return await callback()
|
return await callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if route is from "session" namespace
|
|
||||||
if (data.config.url.includes("/session")) {
|
|
||||||
return __comty_shared_state.eventBus.emit("session.invalid", "Session expired, but the server did not issue a refresh token")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.response.status === 403) {
|
|
||||||
if (data.config.url.includes("/session")) {
|
|
||||||
return __comty_shared_state.eventBus.emit("session.invalid", "Session not valid or not existent")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,7 @@
|
|||||||
export default async (request) => {
|
export default async () => {
|
||||||
if (__comty_shared_state.onExpiredExceptionEvent) {
|
if (__comty_shared_state.refreshingToken === true) {
|
||||||
if (__comty_shared_state.excludedExpiredExceptionURL.includes(request.url)) return
|
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
__comty_shared_state.eventBus.once("session.regenerated", () => {
|
__comty_shared_state.eventBus.once("session:refreshed", resolve)
|
||||||
console.log(`Session has been regenerated, retrying request`)
|
|
||||||
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,43 +0,0 @@
|
|||||||
import SessionModel from "../models/session"
|
|
||||||
import request from "../request"
|
|
||||||
import { reconnectWebsockets } from "../"
|
|
||||||
|
|
||||||
export default async (refreshToken) => {
|
|
||||||
__comty_shared_state.eventBus.emit("session.expiredExceptionEvent", refreshToken)
|
|
||||||
|
|
||||||
__comty_shared_state.onExpiredExceptionEvent = true
|
|
||||||
|
|
||||||
const expiredToken = await SessionModel.token
|
|
||||||
|
|
||||||
// send request to regenerate token
|
|
||||||
const response = await request({
|
|
||||||
method: "POST",
|
|
||||||
url: "/session/regenerate",
|
|
||||||
data: {
|
|
||||||
expiredToken: expiredToken,
|
|
||||||
refreshToken,
|
|
||||||
}
|
|
||||||
}).catch((error) => {
|
|
||||||
console.error(`Failed to regenerate token: ${error.message}`)
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!response) {
|
|
||||||
return __comty_shared_state.eventBus.emit("session.invalid", "Failed to regenerate token")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.data?.token) {
|
|
||||||
return __comty_shared_state.eventBus.emit("session.invalid", "Failed to regenerate token, invalid server response.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// set new token
|
|
||||||
SessionModel.token = response.data.token
|
|
||||||
|
|
||||||
__comty_shared_state.onExpiredExceptionEvent = false
|
|
||||||
|
|
||||||
// emit event
|
|
||||||
__comty_shared_state.eventBus.emit("session.regenerated")
|
|
||||||
|
|
||||||
// reconnect websockets
|
|
||||||
reconnectWebsockets()
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
import request from "../request"
|
|
||||||
|
|
||||||
const fetchers = {
|
|
||||||
http: () => new Promise(async (resolve) => {
|
|
||||||
const start = Date.now()
|
|
||||||
|
|
||||||
const failTimeout = setTimeout(() => {
|
|
||||||
resolve("timeout")
|
|
||||||
}, 5000)
|
|
||||||
|
|
||||||
request({
|
|
||||||
method: "GET",
|
|
||||||
url: "/ping",
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
clearTimeout(failTimeout)
|
|
||||||
resolve(Date.now() - start)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log(err)
|
|
||||||
clearTimeout(failTimeout)
|
|
||||||
resolve("failed")
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
ws: () => new Promise((resolve) => {
|
|
||||||
const start = Date.now()
|
|
||||||
|
|
||||||
const failTimeout = setTimeout(() => {
|
|
||||||
resolve("failed")
|
|
||||||
}, 5000)
|
|
||||||
|
|
||||||
globalThis.__comty_shared_state.sockets["main"].on("pong", () => {
|
|
||||||
failTimeout && clearTimeout(failTimeout)
|
|
||||||
|
|
||||||
resolve(Date.now() - start)
|
|
||||||
})
|
|
||||||
|
|
||||||
globalThis.__comty_shared_state.sockets["main"].emit("ping")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async ({ select } = {}) => {
|
|
||||||
let selectedPromises = []
|
|
||||||
|
|
||||||
if (Array.isArray(select)) {
|
|
||||||
select.forEach((item) => {
|
|
||||||
if (!fetchers[item]) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
selectedPromises.push(fetchers[item]())
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
selectedPromises = [
|
|
||||||
fetchers["http"](),
|
|
||||||
fetchers["ws"](),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await Promise.all(selectedPromises)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
30
src/helpers/processWithAddons.js
Normal file
30
src/helpers/processWithAddons.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
export default async function processAddons({
|
||||||
|
operation,
|
||||||
|
initialData,
|
||||||
|
fnArguments,
|
||||||
|
normalizeAddonResult,
|
||||||
|
}) {
|
||||||
|
const addons = __comty_shared_state.addons.getByOperation(operation)
|
||||||
|
|
||||||
|
let processedData = initialData
|
||||||
|
|
||||||
|
if (typeof fnArguments === "undefined") {
|
||||||
|
fnArguments = []
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const addon of addons) {
|
||||||
|
try {
|
||||||
|
const addonResult = await addon.fn(...fnArguments)
|
||||||
|
|
||||||
|
processedData = normalizeAddonResult({
|
||||||
|
operation,
|
||||||
|
currentData: processedData,
|
||||||
|
addonResult,
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error in [${operation}] addon:`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return processedData
|
||||||
|
}
|
46
src/helpers/refreshToken.js
Executable file
46
src/helpers/refreshToken.js
Executable file
@ -0,0 +1,46 @@
|
|||||||
|
import SessionModel from "../models/session"
|
||||||
|
|
||||||
|
export default async () => {
|
||||||
|
__comty_shared_state.eventBus.emit("session:refreshing")
|
||||||
|
__comty_shared_state.refreshingToken = true
|
||||||
|
|
||||||
|
// send request to regenerate token
|
||||||
|
const response = await __comty_shared_state
|
||||||
|
.baseRequest({
|
||||||
|
method: "POST",
|
||||||
|
url: "/auth",
|
||||||
|
data: {
|
||||||
|
authToken: await SessionModel.token,
|
||||||
|
refreshToken: await SessionModel.refreshToken,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
__comty_shared_state.refreshingToken = false
|
||||||
|
|
||||||
|
throw new Error("Failed to regenerate token.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.data?.token) {
|
||||||
|
__comty_shared_state.refreshingToken = false
|
||||||
|
|
||||||
|
throw new Error("Failed to regenerate token, invalid server response.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// set new token
|
||||||
|
SessionModel.token = response.data.token
|
||||||
|
SessionModel.refreshToken = response.data.refreshToken
|
||||||
|
|
||||||
|
// emit event
|
||||||
|
__comty_shared_state.eventBus.emit("session:refreshed")
|
||||||
|
__comty_shared_state.refreshingToken = false
|
||||||
|
|
||||||
|
if (typeof __comty_shared_state.ws === "object") {
|
||||||
|
await __comty_shared_state.ws.connectAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
@ -1,14 +1,13 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
|
|
||||||
export default (method, ...args) => {
|
export default (method, ...args) => {
|
||||||
if (typeof method !== "function") {
|
|
||||||
throw new Error("useRequest: method must be a function")
|
|
||||||
}
|
|
||||||
|
|
||||||
const [loading, setLoading] = React.useState(true)
|
const [loading, setLoading] = React.useState(true)
|
||||||
const [result, setResult] = React.useState(null)
|
const [result, setResult] = React.useState(null)
|
||||||
const [error, setError] = React.useState(null)
|
const [error, setError] = React.useState(null)
|
||||||
|
|
||||||
|
if (typeof method !== "function") {
|
||||||
|
return [() => {}, null, new Error("Method is not a function"), () => {}]
|
||||||
|
}
|
||||||
const makeRequest = (...newArgs) => {
|
const makeRequest = (...newArgs) => {
|
||||||
method(...newArgs)
|
method(...newArgs)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
|
147
src/index.js
147
src/index.js
@ -1,14 +1,14 @@
|
|||||||
import pkg from "../package.json"
|
import pkg from "../package.json"
|
||||||
import EventEmitter from "@foxify/events"
|
import EventEmitter from "@foxify/events"
|
||||||
|
|
||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
import { io } from "socket.io-client"
|
|
||||||
|
|
||||||
import { createHandlers } from "./models"
|
import AddonsManager from "./addons"
|
||||||
|
import WebsocketManager from "./ws"
|
||||||
import Storage from "./helpers/withStorage"
|
import Storage from "./helpers/withStorage"
|
||||||
import remote from "./remote"
|
import Remotes from "./remotes"
|
||||||
|
|
||||||
globalThis.isServerMode = typeof window === "undefined" && typeof global !== "undefined"
|
globalThis.isServerMode =
|
||||||
|
typeof window === "undefined" && typeof global !== "undefined"
|
||||||
|
|
||||||
if (globalThis.isServerMode) {
|
if (globalThis.isServerMode) {
|
||||||
const { Buffer } = require("buffer")
|
const { Buffer } = require("buffer")
|
||||||
@ -21,125 +21,41 @@ if (globalThis.isServerMode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createWebsockets() {
|
/**
|
||||||
if (!remote.websockets) {
|
* Create a client with the specified access key, private key, and websocket enablement.
|
||||||
return false
|
*
|
||||||
}
|
* @param {Object} options - Optional parameters for accessKey, privateKey, and enableWs
|
||||||
|
* @return {Object} sharedState - Object containing eventBus, mainOrigin, baseRequest, sockets, rest, and version
|
||||||
const instances = globalThis.__comty_shared_state.sockets
|
*/
|
||||||
|
|
||||||
for (let [key, instance] of Object.entries(instances)) {
|
|
||||||
if (instance.connected) {
|
|
||||||
// disconnect first
|
|
||||||
instance.disconnect()
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove current listeners
|
|
||||||
instance.removeAllListeners()
|
|
||||||
|
|
||||||
delete globalThis.__comty_shared_state.sockets[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let ws of remote.websockets) {
|
|
||||||
let opts = {
|
|
||||||
transports: ["websocket"],
|
|
||||||
autoConnect: ws.autoConnect ?? true,
|
|
||||||
forceNew: true,
|
|
||||||
path: ws.path,
|
|
||||||
...ws.params ?? {},
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ws.noAuth !== true) {
|
|
||||||
opts.auth = {
|
|
||||||
token: Storage.engine.get("token"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
globalThis.__comty_shared_state.sockets[ws.namespace] = io(remote.origin, opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// regsister events
|
|
||||||
for (let [key, instance] of Object.entries(instances)) {
|
|
||||||
instance.on("connect", () => {
|
|
||||||
console.debug(`[WS-API][${key}] Connected`)
|
|
||||||
|
|
||||||
globalThis.__comty_shared_state.eventBus.emit(`${key}:connected`)
|
|
||||||
})
|
|
||||||
|
|
||||||
instance.on("disconnect", () => {
|
|
||||||
console.debug(`[WS-API][${key}] Disconnected`)
|
|
||||||
|
|
||||||
globalThis.__comty_shared_state.eventBus.emit(`${key}:disconnected`)
|
|
||||||
})
|
|
||||||
|
|
||||||
instance.on("error", (error) => {
|
|
||||||
console.error(`[WS-API][${key}] Error`, error)
|
|
||||||
|
|
||||||
globalThis.__comty_shared_state.eventBus.emit(`${key}:error`, error)
|
|
||||||
})
|
|
||||||
|
|
||||||
instance.onAny((event, ...args) => {
|
|
||||||
console.debug(`[WS-API][${key}] Event (${event})`, ...args)
|
|
||||||
|
|
||||||
globalThis.__comty_shared_state.eventBus.emit(`${key}:${event}`, ...args)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function disconnectWebsockets() {
|
|
||||||
const instances = globalThis.__comty_shared_state.sockets
|
|
||||||
|
|
||||||
for (let [key, instance] of Object.entries(instances)) {
|
|
||||||
if (instance.connected) {
|
|
||||||
instance.disconnect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function reconnectWebsockets() {
|
|
||||||
const instances = globalThis.__comty_shared_state.sockets
|
|
||||||
|
|
||||||
for (let [key, instance] of Object.entries(instances)) {
|
|
||||||
if (instance.connected) {
|
|
||||||
// disconnect first
|
|
||||||
instance.disconnect()
|
|
||||||
}
|
|
||||||
|
|
||||||
instance.auth = {
|
|
||||||
token: Storage.engine.get("token"),
|
|
||||||
}
|
|
||||||
|
|
||||||
instance.connect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createClient({
|
export function createClient({
|
||||||
accessKey = null,
|
accessKey = null,
|
||||||
privateKey = null,
|
privateKey = null,
|
||||||
enableWs = false,
|
ws = {
|
||||||
|
enable: false,
|
||||||
|
autoConnect: false,
|
||||||
|
},
|
||||||
|
origin = Remotes.origin,
|
||||||
|
eventBus = new EventEmitter(),
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const sharedState = globalThis.__comty_shared_state = {
|
const sharedState = (globalThis.__comty_shared_state = {
|
||||||
onExpiredExceptionEvent: false,
|
eventBus: eventBus,
|
||||||
excludedExpiredExceptionURL: ["/session/regenerate"],
|
mainOrigin: origin,
|
||||||
eventBus: new EventEmitter(),
|
|
||||||
mainOrigin: remote.origin,
|
|
||||||
baseRequest: null,
|
baseRequest: null,
|
||||||
sockets: new Map(),
|
ws: null,
|
||||||
rest: null,
|
rest: null,
|
||||||
version: pkg.version,
|
version: pkg.version,
|
||||||
}
|
addons: new AddonsManager(),
|
||||||
|
})
|
||||||
sharedState.rest = createHandlers()
|
|
||||||
|
|
||||||
if (privateKey && accessKey && globalThis.isServerMode) {
|
if (privateKey && accessKey && globalThis.isServerMode) {
|
||||||
Storage.engine.set("token", `${accessKey}:${privateKey}`)
|
Storage.engine.set("token", `${accessKey}:${privateKey}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
sharedState.baseRequest = axios.create({
|
sharedState.baseRequest = axios.create({
|
||||||
baseURL: remote.origin,
|
baseURL: origin,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// create a interceptor to attach the token every request
|
// create a interceptor to attach the token every request
|
||||||
@ -149,7 +65,8 @@ export function createClient({
|
|||||||
const sessionToken = Storage.engine.get("token")
|
const sessionToken = Storage.engine.get("token")
|
||||||
|
|
||||||
if (sessionToken) {
|
if (sessionToken) {
|
||||||
config.headers["Authorization"] = `${globalThis.isServerMode ? "Server" : "Bearer"} ${sessionToken}`
|
config.headers["Authorization"] =
|
||||||
|
`${globalThis.isServerMode ? "Server" : "Bearer"} ${sessionToken}`
|
||||||
} else {
|
} else {
|
||||||
console.warn("Making a request with no session token")
|
console.warn("Making a request with no session token")
|
||||||
}
|
}
|
||||||
@ -158,8 +75,14 @@ export function createClient({
|
|||||||
return config
|
return config
|
||||||
})
|
})
|
||||||
|
|
||||||
if (enableWs) {
|
if (typeof ws === "object") {
|
||||||
createWebsockets()
|
if (ws.enable === true) {
|
||||||
|
__comty_shared_state.ws = new WebsocketManager()
|
||||||
|
|
||||||
|
if (ws.autoConnect === true) {
|
||||||
|
sharedState.ws.connectAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sharedState
|
return sharedState
|
||||||
|
78
src/models/api/index.js
Normal file
78
src/models/api/index.js
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import request from "../../request"
|
||||||
|
|
||||||
|
export default class API {
|
||||||
|
/**
|
||||||
|
* Retrieves the server keys associated with the current user.
|
||||||
|
*
|
||||||
|
* @return {object} The server keys data
|
||||||
|
*/
|
||||||
|
static async getMyServerKeys() {
|
||||||
|
const response = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: "/server-keys/my",
|
||||||
|
})
|
||||||
|
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new server key.
|
||||||
|
*
|
||||||
|
* @param {object} options - Options for the new server key.
|
||||||
|
* @param {string} options.name - The name of the server key.
|
||||||
|
* @param {string} options.description - The description of the server key.
|
||||||
|
* @param {string} options.access - The access level of the server key.
|
||||||
|
* @return {object} The newly created server key data.
|
||||||
|
*/
|
||||||
|
static async createNewServerKey({
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
access,
|
||||||
|
} = {}) {
|
||||||
|
const response = await request({
|
||||||
|
method: "POST",
|
||||||
|
url: "/server-keys/generate",
|
||||||
|
data: {
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
access
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regenerates a secret token for a server key.
|
||||||
|
*
|
||||||
|
* @param {object} options - Options for regenerating the secret token.
|
||||||
|
* @param {string} access_id - The ID of the server key to regenerate the secret token for.
|
||||||
|
* @return {object} The regenerated secret token data.
|
||||||
|
*/
|
||||||
|
static async regenerateSecretToken(access_id) {
|
||||||
|
const response = await request({
|
||||||
|
method: "POST",
|
||||||
|
url: `/server-keys/${access_id}/regenerate`,
|
||||||
|
})
|
||||||
|
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a server key by its access ID.
|
||||||
|
*
|
||||||
|
* @param {string} access_id - The ID of the server key to delete.
|
||||||
|
* @return {Promise<object>} - A promise that resolves to the response data.
|
||||||
|
*/
|
||||||
|
static async deleteServerKey(access_id) {
|
||||||
|
const response = await request({
|
||||||
|
method: "DELETE",
|
||||||
|
url: `/server-keys/${access_id}`,
|
||||||
|
data: {
|
||||||
|
access_id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
}
|
@ -2,15 +2,18 @@ import request from "../../request"
|
|||||||
import SessionModel from "../session"
|
import SessionModel from "../session"
|
||||||
|
|
||||||
export default class AuthModel {
|
export default class AuthModel {
|
||||||
static login = async (payload, callback) => {
|
/**
|
||||||
|
* Async function to handle the login process.
|
||||||
|
*
|
||||||
|
* @param {Object} payload - The payload containing username, password, and MFA code.
|
||||||
|
* @param {Function} callback - Optional callback function to handle further actions.
|
||||||
|
* @return {Object|boolean} The response data if login successful, false if MFA is required.
|
||||||
|
*/
|
||||||
|
static async login(payload, callback) {
|
||||||
const response = await request({
|
const response = await request({
|
||||||
method: "post",
|
method: "post",
|
||||||
url: "/auth",
|
url: "/auth",
|
||||||
data: {
|
data: payload,
|
||||||
username: payload.username,
|
|
||||||
password: payload.password,
|
|
||||||
mfa_code: payload.mfa_code,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if (response.data.mfa_required) {
|
if (response.data.mfa_required) {
|
||||||
@ -29,6 +32,7 @@ export default class AuthModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SessionModel.token = response.data.token
|
SessionModel.token = response.data.token
|
||||||
|
SessionModel.refreshToken = response.data.refreshToken
|
||||||
|
|
||||||
if (typeof callback === "function") {
|
if (typeof callback === "function") {
|
||||||
await callback()
|
await callback()
|
||||||
@ -39,25 +43,42 @@ export default class AuthModel {
|
|||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
static logout = async () => {
|
/**
|
||||||
|
* Asynchronously logs out the user by destroying the current session and emitting an event for successful logout.
|
||||||
|
*
|
||||||
|
* @return {Promise<void>} A Promise that resolves after the logout process is completed.
|
||||||
|
*/
|
||||||
|
static async logout() {
|
||||||
await SessionModel.destroyCurrentSession()
|
await SessionModel.destroyCurrentSession()
|
||||||
|
|
||||||
SessionModel.removeToken()
|
await SessionModel.removeToken()
|
||||||
|
|
||||||
__comty_shared_state.eventBus.emit("auth:logout_success")
|
__comty_shared_state.eventBus.emit("auth:logout_success")
|
||||||
}
|
}
|
||||||
|
|
||||||
static register = async (payload) => {
|
/**
|
||||||
const { username, password, email } = payload
|
* Registers a new user with the provided payload.
|
||||||
|
*
|
||||||
|
* @param {Object} payload - The payload containing the user's information.
|
||||||
|
* @param {string} payload.username - The username of the user.
|
||||||
|
* @param {string} payload.password - The password of the user.
|
||||||
|
* @param {string} payload.email - The email of the user.
|
||||||
|
* @param {boolean} payload.tos - The acceptance of the terms of service.
|
||||||
|
* @return {Promise<Object>} A Promise that resolves with the response data if the registration is successful, or false if there was an error.
|
||||||
|
* @throws {Error} Throws an error if the registration fails.
|
||||||
|
*/
|
||||||
|
static async register(payload) {
|
||||||
|
const { username, password, email, tos } = payload
|
||||||
|
|
||||||
const response = await request({
|
const response = await request({
|
||||||
method: "post",
|
method: "post",
|
||||||
url: "/auth/register",
|
url: "/register",
|
||||||
data: {
|
data: {
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
email,
|
email,
|
||||||
}
|
accept_tos: tos,
|
||||||
|
},
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
|
||||||
@ -71,7 +92,37 @@ export default class AuthModel {
|
|||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
static usernameValidation = async (username) => {
|
/**
|
||||||
|
* Verifies the given token and returns the user data associated with it.
|
||||||
|
*
|
||||||
|
* @param {string} [token] - The token to verify. If not provided, the stored token is used.
|
||||||
|
* @return {Promise<Object>} A Promise that resolves with the user data if the token is valid, or false if the token is invalid.
|
||||||
|
* @throws {Error} Throws an error if there was an issue with the request.
|
||||||
|
*/
|
||||||
|
static async authToken(token) {
|
||||||
|
if (!token) {
|
||||||
|
token = await SessionModel.token
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await request({
|
||||||
|
method: "POST",
|
||||||
|
url: "/auth/token",
|
||||||
|
data: {
|
||||||
|
token: token,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the existence of a username by making a GET request to the `/auth/{username}/exists` endpoint.
|
||||||
|
*
|
||||||
|
* @param {string} username - The username to validate.
|
||||||
|
* @return {Promise<boolean|Object>} A Promise that resolves with the response data if the validation is successful,
|
||||||
|
* or false if there was an error. Throws an error if the validation fails.
|
||||||
|
*/
|
||||||
|
static async usernameValidation(username) {
|
||||||
const response = await request({
|
const response = await request({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: `/auth/${username}/exists`,
|
url: `/auth/${username}/exists`,
|
||||||
@ -88,16 +139,24 @@ export default class AuthModel {
|
|||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
static availability = async (payload) => {
|
/**
|
||||||
|
* Retrieves the availability of a username and email by making a GET request to the `/availability` endpoint.
|
||||||
|
*
|
||||||
|
* @param {Object} payload - The payload containing the username and email.
|
||||||
|
* @param {string} payload.username - The username to check availability for.
|
||||||
|
* @param {string} payload.email - The email to check availability for.
|
||||||
|
* @return {Promise<Object|boolean>} A Promise that resolves with the availability data if successful, or false if an error occurred.
|
||||||
|
*/
|
||||||
|
static async availability(payload) {
|
||||||
const { username, email } = payload
|
const { username, email } = payload
|
||||||
|
|
||||||
const response = await request({
|
const response = await request({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: `/availability`,
|
url: `/availability`,
|
||||||
data: {
|
params: {
|
||||||
username,
|
username,
|
||||||
email,
|
email,
|
||||||
}
|
},
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
|
||||||
@ -107,16 +166,98 @@ export default class AuthModel {
|
|||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
static changePassword = async (payload) => {
|
/**
|
||||||
const { currentPassword, newPassword } = payload
|
* A function to change the user's password.
|
||||||
|
*
|
||||||
|
* @param {Object} payload - An object containing the currentPassword, newPassword, and code.
|
||||||
|
* @param {string} payload.currentPassword - The current password of the user.
|
||||||
|
* @param {string} payload.newPassword - The new password to set for the user.
|
||||||
|
* @param {string} [payload.code] - The activation code sent to the user's email, optional if the old password is provided.
|
||||||
|
* @return {Promise<Object>} The data response after changing the password.
|
||||||
|
*/
|
||||||
|
static async changePassword(payload) {
|
||||||
|
const { currentPassword, newPassword, code, verificationToken } =
|
||||||
|
payload
|
||||||
|
|
||||||
const { data } = await request({
|
const { data } = await request({
|
||||||
method: "put",
|
method: "put",
|
||||||
url: "/auth/password",
|
url: "/auth/password",
|
||||||
data: {
|
data: {
|
||||||
|
code: code,
|
||||||
|
verificationToken: verificationToken,
|
||||||
old_password: currentPassword,
|
old_password: currentPassword,
|
||||||
new_password: newPassword,
|
new_password: newPassword,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activates a user account using the provided activation code.
|
||||||
|
*
|
||||||
|
* @param {string} user_id - The ID of the user to activate.
|
||||||
|
* @param {string} code - The activation code sent to the user's email.
|
||||||
|
* @return {Promise<Object>} A promise that resolves with the response data after activation.
|
||||||
|
* @throws {Error} Throws an error if the activation process fails.
|
||||||
|
*/
|
||||||
|
static async activateAccount(user_id, code) {
|
||||||
|
const { data } = await request({
|
||||||
|
method: "post",
|
||||||
|
url: "/auth/activate",
|
||||||
|
data: {
|
||||||
|
code: code,
|
||||||
|
user_id: user_id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resends the activation code to the user.
|
||||||
|
*
|
||||||
|
* @return {Promise<Object>} A promise that resolves with the response data after sending the activation code.
|
||||||
|
* @throws {Error} Throws an error if the resend activation code process fails.
|
||||||
|
* @param user_id
|
||||||
|
*/
|
||||||
|
static async resendActivationCode(user_id) {
|
||||||
|
const { data } = await request({
|
||||||
|
method: "post",
|
||||||
|
url: "/auth/resend-activation-code",
|
||||||
|
data: {
|
||||||
|
user_id: user_id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
static async disableAccount({ confirm = false } = {}) {
|
||||||
|
if (!confirm) {
|
||||||
|
console.error(
|
||||||
|
"In order to disable your account, you must confirm the action.",
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await request({
|
||||||
|
method: "post",
|
||||||
|
url: "/auth/disable-account",
|
||||||
|
})
|
||||||
|
|
||||||
|
__comty_shared_state.eventBus.emit("auth:disabled_account")
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
static async recoverPassword(usernameOrEmail) {
|
||||||
|
const { data } = await request({
|
||||||
|
method: "post",
|
||||||
|
url: "/auth/recover-password",
|
||||||
|
data: {
|
||||||
|
account: usernameOrEmail,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
37
src/models/chats/index.js
Normal file
37
src/models/chats/index.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import request from "../../request"
|
||||||
|
|
||||||
|
export default class ChatsService {
|
||||||
|
/**
|
||||||
|
* Retrieves the chat history for a given chat ID.
|
||||||
|
*
|
||||||
|
* @param {string} chat_id - The ID of the chat.
|
||||||
|
* @return {Promise<Object>} The chat history data.
|
||||||
|
* @throws {Error} If the chat_id is not provided.
|
||||||
|
*/
|
||||||
|
static async getChatHistory(chat_id) {
|
||||||
|
if (!chat_id) {
|
||||||
|
throw new Error("chat_id is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: `/chats/${chat_id}/history`,
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the recent chats for the current user.
|
||||||
|
*
|
||||||
|
* @return {Promise<Object>} The chat history data.
|
||||||
|
*/
|
||||||
|
static async getRecentChats() {
|
||||||
|
const { data } = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: "/chats/my",
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ export default class FeedModel {
|
|||||||
static async getMusicFeed({ trim, limit } = {}) {
|
static async getMusicFeed({ trim, limit } = {}) {
|
||||||
const { data } = await request({
|
const { data } = await request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `/feed/music`,
|
url: `/music/feed/my`,
|
||||||
params: {
|
params: {
|
||||||
trim: trim ?? 0,
|
trim: trim ?? 0,
|
||||||
limit: limit ?? Settings.get("feed_max_fetch"),
|
limit: limit ?? Settings.get("feed_max_fetch"),
|
||||||
@ -34,7 +34,7 @@ export default class FeedModel {
|
|||||||
static async getGlobalMusicFeed({ trim, limit } = {}) {
|
static async getGlobalMusicFeed({ trim, limit } = {}) {
|
||||||
const { data } = await request({
|
const { data } = await request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `/music/feed/global`,
|
url: `/music/feed`,
|
||||||
params: {
|
params: {
|
||||||
trim: trim ?? 0,
|
trim: trim ?? 0,
|
||||||
limit: limit ?? Settings.get("feed_max_fetch"),
|
limit: limit ?? Settings.get("feed_max_fetch"),
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
import { SessionModel } from "../../models"
|
import SessionModel from "../../models/session"
|
||||||
import request from "../../request"
|
import request from "../../request"
|
||||||
|
|
||||||
export default class FollowsModel {
|
export default class FollowsModel {
|
||||||
static imFollowing = async (user_id) => {
|
/**
|
||||||
|
* Checks if the current user is following the specified user.
|
||||||
|
*
|
||||||
|
* @param {string} user_id - The ID of the user to check if the current user is following.
|
||||||
|
* @return {Promise<Object>} A promise that resolves with the response data indicating if the current user is following the specified user.
|
||||||
|
* @throws {Error} If the user_id parameter is not provided.
|
||||||
|
*/
|
||||||
|
static async imFollowing(user_id) {
|
||||||
if (!user_id) {
|
if (!user_id) {
|
||||||
throw new Error("user_id is required")
|
throw new Error("user_id is required")
|
||||||
}
|
}
|
||||||
@ -15,9 +22,15 @@ export default class FollowsModel {
|
|||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
static getFollowers = async (user_id, fetchData) => {
|
/**
|
||||||
|
* Retrieves the list of followers for a given user.
|
||||||
|
*
|
||||||
|
* @param {string} user_id - The ID of the user. If not provided, the current user ID will be used.
|
||||||
|
* @param {boolean} fetchData - Whether to fetch additional data for each follower. Defaults to false.
|
||||||
|
* @return {Promise<Object>} A promise that resolves with the list of followers and their data.
|
||||||
|
*/
|
||||||
|
static async getFollowers(user_id, fetchData) {
|
||||||
if (!user_id) {
|
if (!user_id) {
|
||||||
// set current user_id
|
|
||||||
user_id = SessionModel.user_id
|
user_id = SessionModel.user_id
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +45,13 @@ export default class FollowsModel {
|
|||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
static toggleFollow = async ({ user_id }) => {
|
/**
|
||||||
|
* Toggles the follow status for a user.
|
||||||
|
*
|
||||||
|
* @param {Object} user_id - The ID of the user to toggle follow status.
|
||||||
|
* @return {Promise} The response data after toggling follow status.
|
||||||
|
*/
|
||||||
|
static async toggleFollow({ user_id }) {
|
||||||
if (!user_id) {
|
if (!user_id) {
|
||||||
throw new Error("user_id is required")
|
throw new Error("user_id is required")
|
||||||
}
|
}
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
import AuthModel from "./auth"
|
|
||||||
import FeedModel from "./feed"
|
|
||||||
import FollowsModel from "./follows"
|
|
||||||
import LivestreamModel from "./livestream"
|
|
||||||
import PostModel from "./post"
|
|
||||||
import SessionModel from "./session"
|
|
||||||
import SyncModel from "./sync"
|
|
||||||
import UserModel from "./user"
|
|
||||||
|
|
||||||
function getEndpointsFromModel(model) {
|
|
||||||
return Object.entries(model).reduce((acc, [key, value]) => {
|
|
||||||
acc[key] = value
|
|
||||||
|
|
||||||
return acc
|
|
||||||
}, {})
|
|
||||||
}
|
|
||||||
|
|
||||||
function createHandlers() {
|
|
||||||
return {
|
|
||||||
auth: getEndpointsFromModel(AuthModel),
|
|
||||||
feed: getEndpointsFromModel(FeedModel),
|
|
||||||
follows: getEndpointsFromModel(FollowsModel),
|
|
||||||
livestream: getEndpointsFromModel(LivestreamModel),
|
|
||||||
post: getEndpointsFromModel(PostModel),
|
|
||||||
session: getEndpointsFromModel(SessionModel),
|
|
||||||
sync: getEndpointsFromModel(SyncModel),
|
|
||||||
user: getEndpointsFromModel(UserModel),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
AuthModel,
|
|
||||||
FeedModel,
|
|
||||||
FollowsModel,
|
|
||||||
LivestreamModel,
|
|
||||||
PostModel,
|
|
||||||
SessionModel,
|
|
||||||
SyncModel,
|
|
||||||
UserModel,
|
|
||||||
createHandlers,
|
|
||||||
}
|
|
@ -1,84 +0,0 @@
|
|||||||
import request from "../../request"
|
|
||||||
|
|
||||||
export default class Livestream {
|
|
||||||
static deleteProfile = async (profile_id) => {
|
|
||||||
const response = await request({
|
|
||||||
method: "DELETE",
|
|
||||||
url: `/tv/streaming/profile`,
|
|
||||||
data: {
|
|
||||||
profile_id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
static postProfile = async (payload) => {
|
|
||||||
const response = await request({
|
|
||||||
method: "POST",
|
|
||||||
url: `/tv/streaming/profile`,
|
|
||||||
data: payload,
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
static getProfiles = async () => {
|
|
||||||
const response = await request({
|
|
||||||
method: "GET",
|
|
||||||
url: `/tv/streaming/profiles`,
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
static regenerateStreamingKey = async (profile_id) => {
|
|
||||||
const response = await request({
|
|
||||||
method: "POST",
|
|
||||||
url: `/tv/streaming/regenerate_key`,
|
|
||||||
data: {
|
|
||||||
profile_id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
static getCategories = async (key) => {
|
|
||||||
const response = await request({
|
|
||||||
method: "GET",
|
|
||||||
url: `/tv/streaming/categories`,
|
|
||||||
params: {
|
|
||||||
key,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
static getLivestream = async (payload = {}) => {
|
|
||||||
if (!payload.username) {
|
|
||||||
throw new Error("Username is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
let response = await request({
|
|
||||||
method: "GET",
|
|
||||||
url: `/tv/streams`,
|
|
||||||
params: {
|
|
||||||
username: payload.username,
|
|
||||||
profile_id: payload.profile_id,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
static getLivestreams = async () => {
|
|
||||||
const response = await request({
|
|
||||||
method: "GET",
|
|
||||||
url: `/tv/streams`,
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
}
|
|
42
src/models/music/getters/favouriteFolder.js
Normal file
42
src/models/music/getters/favouriteFolder.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
import processAddons from "../../../helpers/processWithAddons"
|
||||||
|
import standartListMerge from "../../../utils/standartListMerge"
|
||||||
|
|
||||||
|
export default async ({ limit = 100, offset = 0, order = "desc" }) => {
|
||||||
|
const addons =
|
||||||
|
__comty_shared_state.addons.getByOperation("getFavoriteFolder")
|
||||||
|
|
||||||
|
const dividedLimit = limit / (addons.length + 1)
|
||||||
|
|
||||||
|
const { data } = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: "/music/my/folder",
|
||||||
|
params: {
|
||||||
|
limit: dividedLimit,
|
||||||
|
offset: offset,
|
||||||
|
order: order,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
let results = await processAddons({
|
||||||
|
operation: "getFavoriteFolder",
|
||||||
|
initialData: data,
|
||||||
|
fnArguments: [{ limit: dividedLimit, offset: offset, order: order }],
|
||||||
|
normalizeAddonResult: ({ currentData, addonResult }) => {
|
||||||
|
return standartListMerge(currentData, addonResult)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// sort by liked_at
|
||||||
|
results.tracks.items.sort((a, b) => {
|
||||||
|
if (a.liked_at > b.liked_at) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if (a.liked_at < b.liked_at) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
11
src/models/music/getters/featuredPlaylists.ts
Normal file
11
src/models/music/getters/featuredPlaylists.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
export default async () => {
|
||||||
|
const response = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: "/music/playlists/featured",
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
33
src/models/music/getters/index.js
Normal file
33
src/models/music/getters/index.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
function exportObjs() {
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
const paths = {
|
||||||
|
...import.meta.glob("./**.ts", { eager: true, import: "default" }),
|
||||||
|
...import.meta.glob("./**.js", { eager: true, import: "default" }),
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.entries(paths).reduce((acc, [path, module]) => {
|
||||||
|
const name = path
|
||||||
|
.split("/")
|
||||||
|
.pop()
|
||||||
|
.replace(/\.(ts|js)$/, "")
|
||||||
|
acc[name] = module
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
} else {
|
||||||
|
const fs = require("fs")
|
||||||
|
const path = require("path")
|
||||||
|
|
||||||
|
return fs
|
||||||
|
.readdirSync(__dirname)
|
||||||
|
.filter((file) => file !== "index.js" && /\.js$/.test(file))
|
||||||
|
.reduce((acc, file) => {
|
||||||
|
const name = file.replace(/\.js$/, "")
|
||||||
|
acc[name] = require(path.join(__dirname, file)).default
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const exportedObjs = exportObjs()
|
||||||
|
|
||||||
|
export default exportedObjs
|
33
src/models/music/getters/isItemFavourited.js
Normal file
33
src/models/music/getters/isItemFavourited.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
const typeToNamespace = {
|
||||||
|
track: "tracks",
|
||||||
|
//playlist: "playlists",
|
||||||
|
//release: "releases",
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async (type, track_id) => {
|
||||||
|
if (!type) {
|
||||||
|
throw new Error("type is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!track_id) {
|
||||||
|
throw new Error("track_id is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
type = type.toLowerCase()
|
||||||
|
|
||||||
|
type = typeToNamespace[type]
|
||||||
|
|
||||||
|
if (!type) {
|
||||||
|
throw new Error(`Unsupported type: ${type}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: `/music/${type}/${track_id}/is_favourite`,
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
26
src/models/music/getters/myReleases.ts
Normal file
26
src/models/music/getters/myReleases.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
type Arguments = {
|
||||||
|
limit: Number
|
||||||
|
offset: Number
|
||||||
|
keywords: String
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async ({
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
keywords,
|
||||||
|
}: Arguments) => {
|
||||||
|
const response = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: "/music/releases/self",
|
||||||
|
params: {
|
||||||
|
limit: limit,
|
||||||
|
offset: offset,
|
||||||
|
keywords: keywords,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
11
src/models/music/getters/playlistData.ts
Normal file
11
src/models/music/getters/playlistData.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
export default async (id: String) => {
|
||||||
|
const response = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: `/music/playlists/${id}/data`,
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
11
src/models/music/getters/playlistItem.ts
Normal file
11
src/models/music/getters/playlistItem.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
export default async (id: String) => {
|
||||||
|
const response = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: `/music/playlists/${id}/items`,
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
29
src/models/music/getters/playlists.ts
Normal file
29
src/models/music/getters/playlists.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
type Arguments = {
|
||||||
|
keywords: String
|
||||||
|
user_id: String
|
||||||
|
limit: Number
|
||||||
|
offset: Number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async ({
|
||||||
|
keywords,
|
||||||
|
user_id,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
}: Arguments) => {
|
||||||
|
const response = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: "/music/playlists",
|
||||||
|
params: {
|
||||||
|
keywords: keywords,
|
||||||
|
user_id: user_id,
|
||||||
|
limit: limit,
|
||||||
|
offset: offset,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
11
src/models/music/getters/recentlyPlayed.js
Normal file
11
src/models/music/getters/recentlyPlayed.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
export default async (params) => {
|
||||||
|
const response = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: `/music/recently`,
|
||||||
|
params: params,
|
||||||
|
})
|
||||||
|
|
||||||
|
return response.data
|
||||||
|
}
|
11
src/models/music/getters/releaseData.ts
Normal file
11
src/models/music/getters/releaseData.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
export default async (id: String) => {
|
||||||
|
const response = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: `/music/releases/${id}/data`,
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
29
src/models/music/getters/releases.ts
Normal file
29
src/models/music/getters/releases.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
type Arguments = {
|
||||||
|
keywords: String
|
||||||
|
user_id: String
|
||||||
|
limit: Number
|
||||||
|
offset: Number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async ({
|
||||||
|
keywords,
|
||||||
|
user_id,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
}: Arguments) => {
|
||||||
|
const response = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: "/music/releases",
|
||||||
|
params: {
|
||||||
|
keywords: keywords,
|
||||||
|
user_id: user_id,
|
||||||
|
limit: limit,
|
||||||
|
offset: offset,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
26
src/models/music/getters/search.ts
Normal file
26
src/models/music/getters/search.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
type Arguments = {
|
||||||
|
keywords: String
|
||||||
|
limit: Number
|
||||||
|
offset: Number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async ({
|
||||||
|
keywords,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
}: Arguments) => {
|
||||||
|
const response = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: "/music/search",
|
||||||
|
params: {
|
||||||
|
keywords: keywords,
|
||||||
|
limit: limit,
|
||||||
|
offset: offset,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
12
src/models/music/getters/trackData.ts
Normal file
12
src/models/music/getters/trackData.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
export default async (id: String, options: Object) => {
|
||||||
|
const response = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: `/music/tracks/${id}/data`,
|
||||||
|
params: options
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
31
src/models/music/getters/trackLyrics.ts
Normal file
31
src/models/music/getters/trackLyrics.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
type RequestOptions = {
|
||||||
|
preferTranslation?: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestParams = {
|
||||||
|
translate_lang?: String
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async (
|
||||||
|
id: String,
|
||||||
|
options: RequestOptions = {
|
||||||
|
preferTranslation: false,
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
const requestParams: RequestParams = Object()
|
||||||
|
|
||||||
|
if (options.preferTranslation) {
|
||||||
|
requestParams.translate_lang = app.cores.settings.get("app:language")
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: `/music/lyrics/${id}`,
|
||||||
|
params: requestParams
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
29
src/models/music/getters/tracks.ts
Normal file
29
src/models/music/getters/tracks.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
type Arguments = {
|
||||||
|
keywords: String
|
||||||
|
user_id: String
|
||||||
|
limit: Number
|
||||||
|
offset: Number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async ({
|
||||||
|
keywords,
|
||||||
|
user_id,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
}: Arguments) => {
|
||||||
|
const response = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: "/music/tracks",
|
||||||
|
params: {
|
||||||
|
keywords: keywords,
|
||||||
|
user_id: user_id,
|
||||||
|
limit: limit,
|
||||||
|
offset: offset,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
@ -1,307 +1,9 @@
|
|||||||
import request from "../../request"
|
import Getters from "./getters"
|
||||||
import pmap from "p-map"
|
import Setters from "./setters"
|
||||||
import SyncModel from "../sync"
|
|
||||||
|
|
||||||
export default class MusicModel {
|
export default class MusicModel {
|
||||||
static get api_instance() {
|
static Getters = Getters
|
||||||
return globalThis.__comty_shared_state.instances["music"]
|
static Setters = Setters
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the official featured playlists.
|
|
||||||
*
|
|
||||||
* @return {Promise<Object>} The data containing the featured playlists.
|
|
||||||
*/
|
|
||||||
static async getFeaturedPlaylists() {
|
|
||||||
const response = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "GET",
|
|
||||||
url: "/featured/playlists",
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves track data for a given ID.
|
|
||||||
*
|
|
||||||
* @param {string} id - The ID of the track.
|
|
||||||
* @return {Promise<Object>} The track data.
|
|
||||||
*/
|
|
||||||
static async getTrackData(id) {
|
|
||||||
const response = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "GET",
|
|
||||||
url: `/tracks/${id}/data`,
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves tracks data for the given track IDs.
|
|
||||||
*
|
|
||||||
* @param {Array} ids - An array of track IDs.
|
|
||||||
* @return {Promise<Object>} A promise that resolves to the tracks data.
|
|
||||||
*/
|
|
||||||
static async getTracksData(ids) {
|
|
||||||
const response = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "GET",
|
|
||||||
url: `/tracks/many`,
|
|
||||||
params: {
|
|
||||||
ids,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves favorite tracks based on specified parameters.
|
|
||||||
*
|
|
||||||
* @param {Object} options - The options for retrieving favorite tracks.
|
|
||||||
* @param {boolean} options.useTidal - Whether to use Tidal for retrieving tracks. Defaults to false.
|
|
||||||
* @param {number} options.limit - The maximum number of tracks to retrieve.
|
|
||||||
* @param {number} options.offset - The offset from which to start retrieving tracks.
|
|
||||||
* @return {Promise<Object>} - An object containing the total length of the tracks and the retrieved tracks.
|
|
||||||
*/
|
|
||||||
static async getFavoriteTracks({ useTidal = false, limit, offset }) {
|
|
||||||
let result = []
|
|
||||||
|
|
||||||
let limitPerRequesters = limit
|
|
||||||
|
|
||||||
if (useTidal) {
|
|
||||||
limitPerRequesters = limitPerRequesters / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
const requesters = [
|
|
||||||
async () => {
|
|
||||||
let { data } = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "GET",
|
|
||||||
url: `/tracks/liked`,
|
|
||||||
params: {
|
|
||||||
limit: limitPerRequesters,
|
|
||||||
offset,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
async () => {
|
|
||||||
if (!useTidal) {
|
|
||||||
return {
|
|
||||||
total_length: 0,
|
|
||||||
tracks: [],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const tidalResult = await SyncModel.tidalCore.getMyFavoriteTracks({
|
|
||||||
limit: limitPerRequesters,
|
|
||||||
offset,
|
|
||||||
})
|
|
||||||
|
|
||||||
return tidalResult
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
result = await pmap(
|
|
||||||
requesters,
|
|
||||||
async requester => {
|
|
||||||
const data = await requester()
|
|
||||||
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
{
|
|
||||||
concurrency: 3,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
let total_length = 0
|
|
||||||
|
|
||||||
result.forEach(result => {
|
|
||||||
total_length += result.total_length
|
|
||||||
})
|
|
||||||
|
|
||||||
let tracks = result.reduce((acc, cur) => {
|
|
||||||
return [...acc, ...cur.tracks]
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
tracks = tracks.sort((a, b) => {
|
|
||||||
return b.liked_at - a.liked_at
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
total_length,
|
|
||||||
tracks,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves favorite playlists based on the specified parameters.
|
|
||||||
*
|
|
||||||
* @param {Object} options - The options for retrieving favorite playlists.
|
|
||||||
* @param {number} options.limit - The maximum number of playlists to retrieve. Default is 50.
|
|
||||||
* @param {number} options.offset - The offset of playlists to retrieve. Default is 0.
|
|
||||||
* @param {Object} options.services - The services to include for retrieving playlists. Default is an empty object.
|
|
||||||
* @param {string} options.keywords - The keywords to filter playlists by.
|
|
||||||
* @return {Promise<Object>} - An object containing the total length of the playlists and the playlist items.
|
|
||||||
*/
|
|
||||||
static async getFavoritePlaylists({ limit = 50, offset = 0, services = {}, keywords } = {}) {
|
|
||||||
let result = []
|
|
||||||
|
|
||||||
let limitPerRequesters = limit
|
|
||||||
|
|
||||||
const requesters = [
|
|
||||||
async () => {
|
|
||||||
const { data } = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "GET",
|
|
||||||
url: `/playlists/self`,
|
|
||||||
params: {
|
|
||||||
keywords,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
if (services["tidal"] === true) {
|
|
||||||
limitPerRequesters = limitPerRequesters / (requesters.length + 1)
|
|
||||||
|
|
||||||
requesters.push(async () => {
|
|
||||||
const _result = await SyncModel.tidalCore.getMyFavoritePlaylists({
|
|
||||||
limit: limitPerRequesters,
|
|
||||||
offset,
|
|
||||||
})
|
|
||||||
|
|
||||||
return _result
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
result = await pmap(
|
|
||||||
requesters,
|
|
||||||
async requester => {
|
|
||||||
const data = await requester()
|
|
||||||
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
{
|
|
||||||
concurrency: 3,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
// calculate total length
|
|
||||||
let total_length = 0
|
|
||||||
|
|
||||||
result.forEach(result => {
|
|
||||||
total_length += result.total_length
|
|
||||||
})
|
|
||||||
|
|
||||||
// reduce items
|
|
||||||
let items = result.reduce((acc, cur) => {
|
|
||||||
return [...acc, ...cur.items]
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
|
|
||||||
// sort by created_at
|
|
||||||
items = items.sort((a, b) => {
|
|
||||||
return new Date(b.created_at) - new Date(a.created_at)
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
total_length: total_length,
|
|
||||||
items,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves playlist items based on the provided parameters.
|
|
||||||
*
|
|
||||||
* @param {Object} options - The options object.
|
|
||||||
* @param {string} options.playlist_id - The ID of the playlist.
|
|
||||||
* @param {string} options.service - The service from which to retrieve the playlist items.
|
|
||||||
* @param {number} options.limit - The maximum number of items to retrieve.
|
|
||||||
* @param {number} options.offset - The number of items to skip before retrieving.
|
|
||||||
* @return {Promise<Object>} Playlist items data.
|
|
||||||
*/
|
|
||||||
static async getPlaylistItems({
|
|
||||||
playlist_id,
|
|
||||||
service,
|
|
||||||
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
}) {
|
|
||||||
if (service === "tidal") {
|
|
||||||
const result = await SyncModel.tidalCore.getPlaylistItems({
|
|
||||||
playlist_id,
|
|
||||||
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
|
|
||||||
resolve_items: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "GET",
|
|
||||||
url: `/playlists/${playlist_id}/items`,
|
|
||||||
params: {
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves playlist data based on the provided parameters.
|
|
||||||
*
|
|
||||||
* @param {Object} options - The options object.
|
|
||||||
* @param {string} options.playlist_id - The ID of the playlist.
|
|
||||||
* @param {string} options.service - The service to use.
|
|
||||||
* @param {number} options.limit - The maximum number of items to retrieve.
|
|
||||||
* @param {number} options.offset - The offset for pagination.
|
|
||||||
* @return {Promise<Object>} Playlist data.
|
|
||||||
*/
|
|
||||||
static async getPlaylistData({
|
|
||||||
playlist_id,
|
|
||||||
service,
|
|
||||||
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
}) {
|
|
||||||
if (service === "tidal") {
|
|
||||||
const result = await SyncModel.tidalCore.getPlaylistData({
|
|
||||||
playlist_id,
|
|
||||||
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
|
|
||||||
resolve_items: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "GET",
|
|
||||||
url: `/playlists/${playlist_id}/data`,
|
|
||||||
params: {
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a search based on the provided keywords, with optional parameters for limiting the number of results and pagination.
|
* Performs a search based on the provided keywords, with optional parameters for limiting the number of results and pagination.
|
||||||
@ -313,130 +15,35 @@ export default class MusicModel {
|
|||||||
* @param {boolean} options.useTidal - Whether to use Tidal for the search. Defaults to false.
|
* @param {boolean} options.useTidal - Whether to use Tidal for the search. Defaults to false.
|
||||||
* @return {Promise<Object>} The search results.
|
* @return {Promise<Object>} The search results.
|
||||||
*/
|
*/
|
||||||
static async search(keywords, { limit = 5, offset = 0, useTidal = false }) {
|
static search = Getters.search
|
||||||
const { data } = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "GET",
|
|
||||||
url: `/search`,
|
|
||||||
params: {
|
|
||||||
keywords,
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
useTidal,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new playlist.
|
* Retrieves playlist items based on the provided parameters.
|
||||||
*
|
*
|
||||||
* @param {object} payload - The payload containing the data for the new playlist.
|
* @param {Object} options - The options object.
|
||||||
* @return {Promise<Object>} The new playlist data.
|
* @param {string} options.playlist_id - The ID of the playlist.
|
||||||
|
* @param {string} options.service - The service from which to retrieve the playlist items.
|
||||||
|
* @param {number} options.limit - The maximum number of items to retrieve.
|
||||||
|
* @param {number} options.offset - The number of items to skip before retrieving.
|
||||||
|
* @return {Promise<Object>} Playlist items data.
|
||||||
*/
|
*/
|
||||||
static async newPlaylist(payload) {
|
static getPlaylistItems = Getters.PlaylistItems
|
||||||
const { data } = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "POST",
|
|
||||||
url: `/playlists/new`,
|
|
||||||
data: payload,
|
|
||||||
})
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a playlist item in the specified playlist.
|
* Retrieves playlist data based on the provided parameters.
|
||||||
*
|
*
|
||||||
* @param {string} playlist_id - The ID of the playlist to update.
|
* @param {Object} options - The options object.
|
||||||
* @param {object} item - The updated playlist item to be added.
|
* @param {string} options.playlist_id - The ID of the playlist.
|
||||||
* @return {Promise<Object>} - The updated playlist item.
|
* @param {string} options.service - The service to use.
|
||||||
|
* @param {number} options.limit - The maximum number of items to retrieve.
|
||||||
|
* @param {number} options.offset - The offset for pagination.
|
||||||
|
* @return {Promise<Object>} Playlist data.
|
||||||
*/
|
*/
|
||||||
static async putPlaylistItem(playlist_id, item) {
|
static getPlaylistData = Getters.PlaylistData
|
||||||
const response = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "PUT",
|
|
||||||
url: `/playlists/${playlist_id}/items`,
|
|
||||||
data: item,
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a playlist item.
|
|
||||||
*
|
|
||||||
* @param {string} playlist_id - The ID of the playlist.
|
|
||||||
* @param {string} item_id - The ID of the item to delete.
|
|
||||||
* @return {Promise<Object>} The data returned by the server after the item is deleted.
|
|
||||||
*/
|
|
||||||
static async deletePlaylistItem(playlist_id, item_id) {
|
|
||||||
const response = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "DELETE",
|
|
||||||
url: `/playlists/${playlist_id}/items/${item_id}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes a playlist.
|
|
||||||
*
|
|
||||||
* @param {number} playlist_id - The ID of the playlist to be deleted.
|
|
||||||
* @return {Promise<Object>} The response data from the server.
|
|
||||||
*/
|
|
||||||
static async deletePlaylist(playlist_id) {
|
|
||||||
const response = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "DELETE",
|
|
||||||
url: `/playlists/${playlist_id}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute a PUT request to update or create a release.
|
|
||||||
*
|
|
||||||
* @param {object} payload - The payload data.
|
|
||||||
* @return {Promise<Object>} The response data from the server.
|
|
||||||
*/
|
|
||||||
static async putRelease(payload) {
|
|
||||||
const response = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "PUT",
|
|
||||||
url: `/releases/release`,
|
|
||||||
data: payload
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the releases associated with the authenticated user.
|
|
||||||
*
|
|
||||||
* @param {string} keywords - The keywords to filter the releases by.
|
|
||||||
* @return {Promise<Object>} A promise that resolves to the data of the releases.
|
|
||||||
*/
|
|
||||||
static async getMyReleases(keywords) {
|
|
||||||
const response = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "GET",
|
|
||||||
url: `/releases/self`,
|
|
||||||
params: {
|
|
||||||
keywords,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves releases based on the provided parameters.
|
* Retrieves releases based on the provided parameters.
|
||||||
|
* If user_id is not provided, it will retrieve self authenticated user releases.
|
||||||
*
|
*
|
||||||
* @param {object} options - The options for retrieving releases.
|
* @param {object} options - The options for retrieving releases.
|
||||||
* @param {string} options.user_id - The ID of the user.
|
* @param {string} options.user_id - The ID of the user.
|
||||||
@ -445,25 +52,17 @@ export default class MusicModel {
|
|||||||
* @param {number} options.offset - The offset for paginated results.
|
* @param {number} options.offset - The offset for paginated results.
|
||||||
* @return {Promise<Object>} - A promise that resolves to the retrieved releases.
|
* @return {Promise<Object>} - A promise that resolves to the retrieved releases.
|
||||||
*/
|
*/
|
||||||
static async getReleases({
|
static getReleases = Getters.releases
|
||||||
user_id,
|
|
||||||
keywords,
|
|
||||||
limit = 50,
|
|
||||||
offset = 0,
|
|
||||||
}) {
|
|
||||||
const response = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "GET",
|
|
||||||
url: `/releases/user/${user_id}`,
|
|
||||||
params: {
|
|
||||||
keywords,
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
/**
|
||||||
}
|
* Retrieves self releases.
|
||||||
|
*
|
||||||
|
* @param {object} options - The options for retrieving my releases.
|
||||||
|
* @param {number} options.limit - The maximum number of releases to retrieve.
|
||||||
|
* @param {number} options.offset - The offset for paginated results.
|
||||||
|
* @return {Promise<Object>} - A promise that resolves to the retrieved releases.
|
||||||
|
*/
|
||||||
|
static getMyReleases = Getters.myReleases
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves release data by ID.
|
* Retrieves release data by ID.
|
||||||
@ -471,15 +70,49 @@ export default class MusicModel {
|
|||||||
* @param {number} id - The ID of the release.
|
* @param {number} id - The ID of the release.
|
||||||
* @return {Promise<Object>} The release data.
|
* @return {Promise<Object>} The release data.
|
||||||
*/
|
*/
|
||||||
static async getReleaseData(id) {
|
static getReleaseData = Getters.releaseData
|
||||||
const response = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "GET",
|
|
||||||
url: `/releases/${id}/data`
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
/**
|
||||||
}
|
* Retrieves track data for a given ID.
|
||||||
|
*
|
||||||
|
* @param {string} id - The ID of the track or multiple IDs separated by commas.
|
||||||
|
* @return {Promise<Object>} The track data.
|
||||||
|
*/
|
||||||
|
static getTrackData = Getters.trackData
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the official featured playlists.
|
||||||
|
*
|
||||||
|
* @return {Promise<Object>} The data containing the featured playlists.
|
||||||
|
*/
|
||||||
|
static getFeaturedPlaylists = Getters.featuredPlaylists
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves track lyrics for a given ID.
|
||||||
|
*
|
||||||
|
* @param {string} id - The ID of the track.
|
||||||
|
* @return {Promise<Object>} The track lyrics.
|
||||||
|
*/
|
||||||
|
static getTrackLyrics = Getters.trackLyrics
|
||||||
|
|
||||||
|
|
||||||
|
static putTrackLyrics = Setters.putTrackLyrics
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create or modify a track.
|
||||||
|
*
|
||||||
|
* @param {object} TrackManifest - The track manifest.
|
||||||
|
* @return {Promise<Object>} The result track data.
|
||||||
|
*/
|
||||||
|
static putTrack = Setters.putTrack
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create or modify a release.
|
||||||
|
*
|
||||||
|
* @param {object} ReleaseManifest - The release manifest.
|
||||||
|
* @return {Promise<Object>} The result release data.
|
||||||
|
*/
|
||||||
|
static putRelease = Setters.putRelease
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a release by its ID.
|
* Deletes a release by its ID.
|
||||||
@ -487,75 +120,32 @@ export default class MusicModel {
|
|||||||
* @param {string} id - The ID of the release to delete.
|
* @param {string} id - The ID of the release to delete.
|
||||||
* @return {Promise<Object>} - A Promise that resolves to the data returned by the API.
|
* @return {Promise<Object>} - A Promise that resolves to the data returned by the API.
|
||||||
*/
|
*/
|
||||||
static async deleteRelease(id) {
|
static deleteRelease = Setters.deleteRelease
|
||||||
const response = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "DELETE",
|
|
||||||
url: `/releases/${id}`
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes the track cache for a given track ID.
|
* Retrieves the favourite tracks of the current user.
|
||||||
*
|
*
|
||||||
* @param {string} track_id - The ID of the track to refresh the cache for.
|
* @return {Promise<Object>} The favorite tracks data.
|
||||||
* @throws {Error} If track_id is not provided.
|
|
||||||
* @return {Promise<Object>} The response data from the API call.
|
|
||||||
*/
|
*/
|
||||||
static async refreshTrackCache(track_id) {
|
static getFavouriteTracks = null
|
||||||
if (!track_id) {
|
|
||||||
throw new Error("Track ID is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: "POST",
|
|
||||||
url: `/tracks/${track_id}/refresh-cache`,
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles the like status of a track.
|
* Retrieves the favourite tracks/playlists/releases of the current user.
|
||||||
*
|
*
|
||||||
* @param {Object} manifest - The manifest object containing track information.
|
* @return {Promise<Object>} The favorite playlists data.
|
||||||
* @param {boolean} to - The like status to toggle (true for like, false for unlike).
|
|
||||||
* @throws {Error} Throws an error if the manifest is missing.
|
|
||||||
* @return {Object} The response data from the API.
|
|
||||||
*/
|
*/
|
||||||
static async toggleTrackLike(manifest, to) {
|
static getFavouriteFolder = Getters.favouriteFolder
|
||||||
if (!manifest) {
|
|
||||||
throw new Error("Manifest is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Toggling track ${manifest._id} like status to ${to}`)
|
/**
|
||||||
|
* Toggles the favourite status of a track, playlist or folder.
|
||||||
|
*
|
||||||
|
* @param {string} track_id - The ID of the track to toggle the favorite status.
|
||||||
|
* @throws {Error} If the track_id is not provided.
|
||||||
|
* @return {Promise<Object>} The response data after toggling the favorite status.
|
||||||
|
*/
|
||||||
|
static toggleItemFavourite = Setters.toggleItemFavourite
|
||||||
|
|
||||||
const track_id = manifest._id
|
static isItemFavourited = Getters.isItemFavourited
|
||||||
|
|
||||||
switch (manifest.service) {
|
static getRecentyPlayed = Getters.recentlyPlayed
|
||||||
case "tidal": {
|
|
||||||
const response = await SyncModel.tidalCore.toggleTrackLike({
|
|
||||||
track_id,
|
|
||||||
to,
|
|
||||||
})
|
|
||||||
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
const response = await request({
|
|
||||||
instance: MusicModel.api_instance,
|
|
||||||
method: to ? "POST" : "DELETE",
|
|
||||||
url: `/tracks/${track_id}/like`,
|
|
||||||
params: {
|
|
||||||
service: manifest.service
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
11
src/models/music/setters/deleteRelease.js
Normal file
11
src/models/music/setters/deleteRelease.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
export default async (release_id) => {
|
||||||
|
const response = await request({
|
||||||
|
method: "delete",
|
||||||
|
url: `/music/releases/${release_id}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
33
src/models/music/setters/index.js
Normal file
33
src/models/music/setters/index.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
function exportObjs() {
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
const paths = {
|
||||||
|
...import.meta.glob("./**.ts", { eager: true, import: "default" }),
|
||||||
|
...import.meta.glob("./**.js", { eager: true, import: "default" }),
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.entries(paths).reduce((acc, [path, module]) => {
|
||||||
|
const name = path
|
||||||
|
.split("/")
|
||||||
|
.pop()
|
||||||
|
.replace(/\.(ts|js)$/, "")
|
||||||
|
acc[name] = module
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
} else {
|
||||||
|
const fs = require("fs")
|
||||||
|
const path = require("path")
|
||||||
|
|
||||||
|
return fs
|
||||||
|
.readdirSync(__dirname)
|
||||||
|
.filter((file) => file !== "index.js" && /\.js$/.test(file))
|
||||||
|
.reduce((acc, file) => {
|
||||||
|
const name = file.replace(/\.js$/, "")
|
||||||
|
acc[name] = require(path.join(__dirname, file)).default
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const exportedObjs = exportObjs()
|
||||||
|
|
||||||
|
export default exportedObjs
|
12
src/models/music/setters/putRelease.js
Normal file
12
src/models/music/setters/putRelease.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
export default async (release) => {
|
||||||
|
const response = await request({
|
||||||
|
method: "PUT",
|
||||||
|
url: "/music/releases",
|
||||||
|
data: release,
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
12
src/models/music/setters/putTrack.js
Normal file
12
src/models/music/setters/putTrack.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
export default async (track) => {
|
||||||
|
const response = await request({
|
||||||
|
method: "PUT",
|
||||||
|
url: "/music/tracks",
|
||||||
|
data: track,
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
12
src/models/music/setters/putTrackLyrics.js
Normal file
12
src/models/music/setters/putTrackLyrics.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
export default async (track_id, data) => {
|
||||||
|
const response = await request({
|
||||||
|
method: "put",
|
||||||
|
url: `/music/lyrics/${track_id}`,
|
||||||
|
data: data,
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
36
src/models/music/setters/toggleItemFavourite.js
Normal file
36
src/models/music/setters/toggleItemFavourite.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
const typeToNamespace = {
|
||||||
|
track: "tracks",
|
||||||
|
//playlist: "playlists",
|
||||||
|
//release: "releases",
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async (type, track_id, to) => {
|
||||||
|
if (!type) {
|
||||||
|
throw new Error("type is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!track_id) {
|
||||||
|
throw new Error("track_id is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
type = type.toLowerCase()
|
||||||
|
|
||||||
|
type = typeToNamespace[type]
|
||||||
|
|
||||||
|
if (!type) {
|
||||||
|
throw new Error(`Unsupported type: ${type}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await request({
|
||||||
|
method: "post",
|
||||||
|
url: `/music/${type}/${track_id}/favourite`,
|
||||||
|
data: {
|
||||||
|
to: to,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
@ -1,15 +1,26 @@
|
|||||||
import request from "../../request"
|
import request from "../../request"
|
||||||
|
|
||||||
export default class NFCModel {
|
export default class NFCModel {
|
||||||
|
/**
|
||||||
|
* Retrieves the list of tags owned by the current user.
|
||||||
|
*
|
||||||
|
* @return {Promise<Object>} A promise that resolves with the data of the tags.
|
||||||
|
*/
|
||||||
static async getOwnTags() {
|
static async getOwnTags() {
|
||||||
const { data } = await request({
|
const { data } = await request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `/nfc/tags`
|
url: `/nfc/tag/my`
|
||||||
})
|
})
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a tag by its ID.
|
||||||
|
*
|
||||||
|
* @param {type} id - The ID of the tag to retrieve.
|
||||||
|
* @return {type} The data of the retrieved tag.
|
||||||
|
*/
|
||||||
static async getTagById(id) {
|
static async getTagById(id) {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
throw new Error("ID is required")
|
throw new Error("ID is required")
|
||||||
@ -17,12 +28,19 @@ export default class NFCModel {
|
|||||||
|
|
||||||
const { data } = await request({
|
const { data } = await request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `/nfc/tags/${id}`
|
url: `/nfc/tag/id/${id}`
|
||||||
})
|
})
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a tag by its serial number.
|
||||||
|
*
|
||||||
|
* @param {string} serial - The serial number of the tag to retrieve.
|
||||||
|
* @throws {Error} If the serial number is not provided.
|
||||||
|
* @return {Promise<Object>} A promise that resolves with the data of the tag.
|
||||||
|
*/
|
||||||
static async getTagBySerial(serial) {
|
static async getTagBySerial(serial) {
|
||||||
if (!serial) {
|
if (!serial) {
|
||||||
throw new Error("Serial is required")
|
throw new Error("Serial is required")
|
||||||
@ -36,6 +54,14 @@ export default class NFCModel {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a tag with the given serial number and payload.
|
||||||
|
*
|
||||||
|
* @param {string} serial - The serial number of the tag.
|
||||||
|
* @param {Object} payload - The payload data for the tag.
|
||||||
|
* @throws {Error} If the serial or payload is not provided.
|
||||||
|
* @return {Promise<Object>} The data of the registered tag.
|
||||||
|
*/
|
||||||
static async registerTag(serial, payload) {
|
static async registerTag(serial, payload) {
|
||||||
if (!serial) {
|
if (!serial) {
|
||||||
throw new Error("Serial is required")
|
throw new Error("Serial is required")
|
||||||
@ -45,10 +71,27 @@ export default class NFCModel {
|
|||||||
throw new Error("Payload is required")
|
throw new Error("Payload is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (window) {
|
||||||
|
payload.origin = window.location.host
|
||||||
|
}
|
||||||
|
|
||||||
const { data } = await request({
|
const { data } = await request({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: `/nfc/tag/${serial}`,
|
url: `/nfc/tag/register/${serial}`,
|
||||||
data: payload
|
data: payload,
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteTag(id) {
|
||||||
|
if (!id) {
|
||||||
|
throw new Error("ID is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await request({
|
||||||
|
method: "DELETE",
|
||||||
|
url: `/nfc/tag/id/${id}`
|
||||||
})
|
})
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
17
src/models/payments/index.js
Normal file
17
src/models/payments/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import request from "../../request"
|
||||||
|
|
||||||
|
export default class PaymentsModel {
|
||||||
|
/**
|
||||||
|
* Fetches the current balance from the server.
|
||||||
|
*
|
||||||
|
* @return {object} The balance data received from the server.
|
||||||
|
*/
|
||||||
|
static async fetchBalance() {
|
||||||
|
const response = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: "/payments/balance",
|
||||||
|
})
|
||||||
|
|
||||||
|
return response.data.balance
|
||||||
|
}
|
||||||
|
}
|
@ -2,15 +2,30 @@ import request from "../../request"
|
|||||||
import Settings from "../../helpers/withSettings"
|
import Settings from "../../helpers/withSettings"
|
||||||
|
|
||||||
export default class Post {
|
export default class Post {
|
||||||
|
/**
|
||||||
|
* Retrieves the maximum length allowed for the post text.
|
||||||
|
*
|
||||||
|
* @return {number} The maximum length allowed for the post text.
|
||||||
|
*/
|
||||||
static get maxPostTextLength() {
|
static get maxPostTextLength() {
|
||||||
return 3200
|
return 3200
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum length allowed for a comment.
|
||||||
|
*
|
||||||
|
* @return {number} The maximum length allowed for a comment.
|
||||||
|
*/
|
||||||
static get maxCommentLength() {
|
static get maxCommentLength() {
|
||||||
return 1200
|
return 1200
|
||||||
}
|
}
|
||||||
|
|
||||||
static getPostingPolicy = async () => {
|
/**
|
||||||
|
* Retrieves the posting policy from the server.
|
||||||
|
*
|
||||||
|
* @return {Promise<Object>} The posting policy data.
|
||||||
|
*/
|
||||||
|
static async getPostingPolicy() {
|
||||||
const { data } = await request({
|
const { data } = await request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/posting_policy",
|
url: "/posting_policy",
|
||||||
@ -19,7 +34,15 @@ export default class Post {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
static getPost = async ({ post_id }) => {
|
/**
|
||||||
|
* Retrieves the data of a post by its ID.
|
||||||
|
*
|
||||||
|
* @param {Object} options - The options for retrieving the post.
|
||||||
|
* @param {string} options.post_id - The ID of the post to retrieve.
|
||||||
|
* @throws {Error} If the post_id is not provided.
|
||||||
|
* @return {Promise<Object>} The data of the post.
|
||||||
|
*/
|
||||||
|
static async post({ post_id }) {
|
||||||
if (!post_id) {
|
if (!post_id) {
|
||||||
throw new Error("Post ID is required")
|
throw new Error("Post ID is required")
|
||||||
}
|
}
|
||||||
@ -32,6 +55,18 @@ export default class Post {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getPost = Post.post
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the replies of a post by its ID.
|
||||||
|
*
|
||||||
|
* @param {Object} options - The options for retrieving the replies.
|
||||||
|
* @param {string} options.post_id - The ID of the post to retrieve replies for.
|
||||||
|
* @param {number} [options.trim=0] - The number of characters to trim the reply content.
|
||||||
|
* @param {number} [options.limit=Settings.get("feed_max_fetch")] - The maximum number of replies to fetch.
|
||||||
|
* @throws {Error} If the post_id is not provided.
|
||||||
|
* @return {Promise<Object>} The data of the replies.
|
||||||
|
*/
|
||||||
static async replies({ post_id, trim, limit }) {
|
static async replies({ post_id, trim, limit }) {
|
||||||
if (!post_id) {
|
if (!post_id) {
|
||||||
throw new Error("Post ID is required")
|
throw new Error("Post ID is required")
|
||||||
@ -49,7 +84,15 @@ export default class Post {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
static getSavedPosts = async ({ trim, limit }) => {
|
/**
|
||||||
|
* Retrieves the saved posts with optional trimming and limiting.
|
||||||
|
*
|
||||||
|
* @param {Object} options - The options for retrieving the saved posts.
|
||||||
|
* @param {number} [options.trim=0] - The number of posts to trim from the result.
|
||||||
|
* @param {number} [options.limit=Settings.get("feed_max_fetch")] - The maximum number of posts to fetch.
|
||||||
|
* @return {Promise<Object>} The data of the saved posts.
|
||||||
|
*/
|
||||||
|
static async getSavedPosts({ trim, limit }) {
|
||||||
const { data } = await request({
|
const { data } = await request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `/posts/saved`,
|
url: `/posts/saved`,
|
||||||
@ -62,7 +105,14 @@ export default class Post {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
static getLikedPosts = async ({ trim, limit }) => {
|
/**
|
||||||
|
* Retrieves the liked posts with optional trimming and limiting.
|
||||||
|
*
|
||||||
|
* @param {number} trim - The number of characters to trim the post content.
|
||||||
|
* @param {number} limit - The maximum number of liked posts to fetch.
|
||||||
|
* @return {Promise<Object>} The data of the liked posts.
|
||||||
|
*/
|
||||||
|
static async getLikedPosts({ trim, limit }) {
|
||||||
const { data } = await request({
|
const { data } = await request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `/posts/liked`,
|
url: `/posts/liked`,
|
||||||
@ -75,7 +125,16 @@ export default class Post {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
static getUserPosts = async ({ user_id, trim, limit }) => {
|
/**
|
||||||
|
* Retrieves the posts of a user with optional trimming and limiting.
|
||||||
|
*
|
||||||
|
* @param {Object} options - The options for retrieving the user's posts.
|
||||||
|
* @param {string} options.user_id - The ID of the user whose posts to retrieve. If not provided, the current user's ID will be used.
|
||||||
|
* @param {number} [options.trim=0] - The number of characters to trim the post content.
|
||||||
|
* @param {number} [options.limit=Settings.get("feed_max_fetch")] - The maximum number of posts to fetch.
|
||||||
|
* @return {Promise<Object>} The data of the user's posts.
|
||||||
|
*/
|
||||||
|
static async getUserPosts({ user_id, trim, limit }) {
|
||||||
if (!user_id) {
|
if (!user_id) {
|
||||||
// use current user_id
|
// use current user_id
|
||||||
user_id = app.userData?._id
|
user_id = app.userData?._id
|
||||||
@ -93,7 +152,15 @@ export default class Post {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
static toggleLike = async ({ post_id }) => {
|
/**
|
||||||
|
* Toggles the like status of a post.
|
||||||
|
*
|
||||||
|
* @param {Object} options - The options for toggling the like status.
|
||||||
|
* @param {string} options.post_id - The ID of the post to toggle the like status.
|
||||||
|
* @throws {Error} If the post_id is not provided.
|
||||||
|
* @return {Promise<Object>} The response data after toggling the like status.
|
||||||
|
*/
|
||||||
|
static async toggleLike({ post_id }) {
|
||||||
if (!post_id) {
|
if (!post_id) {
|
||||||
throw new Error("Post ID is required")
|
throw new Error("Post ID is required")
|
||||||
}
|
}
|
||||||
@ -106,7 +173,13 @@ export default class Post {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
static toggleSave = async ({ post_id }) => {
|
/**
|
||||||
|
* Toggles the save status of a post.
|
||||||
|
*
|
||||||
|
* @param {string} post_id - The ID of the post to toggle the save status.
|
||||||
|
* @return {Promise<Object>} The response data after toggling the save status.
|
||||||
|
*/
|
||||||
|
static async toggleSave({ post_id }) {
|
||||||
if (!post_id) {
|
if (!post_id) {
|
||||||
throw new Error("Post ID is required")
|
throw new Error("Post ID is required")
|
||||||
}
|
}
|
||||||
@ -119,7 +192,13 @@ export default class Post {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
static create = async (payload) => {
|
/**
|
||||||
|
* Creates a new post with the given payload.
|
||||||
|
*
|
||||||
|
* @param {Object} payload - The data to create the post with.
|
||||||
|
* @return {Promise<Object>} The response data after creating the post.
|
||||||
|
*/
|
||||||
|
static async create(payload) {
|
||||||
const { data } = await request({
|
const { data } = await request({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: `/posts/new`,
|
url: `/posts/new`,
|
||||||
@ -129,7 +208,17 @@ export default class Post {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
static update = async (post_id, update) => {
|
static createPost = Post.create
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a post with the given post ID and update payload.
|
||||||
|
*
|
||||||
|
* @param {string} post_id - The ID of the post to update.
|
||||||
|
* @param {Object} update - The data to update the post with.
|
||||||
|
* @throws {Error} If the post_id is not provided.
|
||||||
|
* @return {Promise<Object>} The response data after updating the post.
|
||||||
|
*/
|
||||||
|
static async update(post_id, update) {
|
||||||
if (!post_id) {
|
if (!post_id) {
|
||||||
throw new Error("Post ID is required")
|
throw new Error("Post ID is required")
|
||||||
}
|
}
|
||||||
@ -143,7 +232,15 @@ export default class Post {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
static deletePost = async ({ post_id }) => {
|
static updatePost = Post.update
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a post with the given post ID.
|
||||||
|
*
|
||||||
|
* @param {string} post_id - The ID of the post to delete.
|
||||||
|
* @return {Object} The response data after deleting the post.
|
||||||
|
*/
|
||||||
|
static async delete({ post_id }) {
|
||||||
if (!post_id) {
|
if (!post_id) {
|
||||||
throw new Error("Post ID is required")
|
throw new Error("Post ID is required")
|
||||||
}
|
}
|
||||||
@ -155,4 +252,94 @@ export default class Post {
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static deletePost = Post.delete
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Votes for a poll with the given post ID and option ID.
|
||||||
|
*
|
||||||
|
* @param {Object} options - The options for voting.
|
||||||
|
* @param {string} options.post_id - The ID of the post to vote for.
|
||||||
|
* @param {string} options.option_id - The ID of the option to vote for.
|
||||||
|
* @throws {Error} If the post_id or option_id is not provided.
|
||||||
|
* @return {Promise<Object>} The response data after voting.
|
||||||
|
*/
|
||||||
|
static async votePoll({ post_id, option_id }) {
|
||||||
|
if (!post_id) {
|
||||||
|
throw new Error("post_id is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!option_id) {
|
||||||
|
throw new Error("option_id is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await request({
|
||||||
|
method: "POST",
|
||||||
|
url: `/posts/${post_id}/vote_poll/${option_id}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a vote for a poll with the given post ID and option ID.
|
||||||
|
*
|
||||||
|
* @param {Object} options - The options for deleting a vote.
|
||||||
|
* @param {string} options.post_id - The ID of the post to delete the vote from.
|
||||||
|
* @param {string} options.option_id - The ID of the option to delete the vote from.
|
||||||
|
* @throws {Error} If the post_id or option_id is not provided.
|
||||||
|
* @return {Promise<Object>} The response data after deleting the vote.
|
||||||
|
*/
|
||||||
|
static async deleteVotePoll({ post_id, option_id }) {
|
||||||
|
if (!post_id) {
|
||||||
|
throw new Error("post_id is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!option_id) {
|
||||||
|
throw new Error("option_id is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await request({
|
||||||
|
method: "DELETE",
|
||||||
|
url: `/posts/${post_id}/vote_poll/${option_id}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the trending hashtags and their counts.
|
||||||
|
*
|
||||||
|
* @return {Promise<Object[]>} An array of objects with two properties: "hashtag" and "count".
|
||||||
|
*/
|
||||||
|
static async getTrendings() {
|
||||||
|
const { data } = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: `/posts/trendings`,
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the trending posts for a specific hashtag with optional trimming and limiting.
|
||||||
|
*
|
||||||
|
* @param {Object} options - The options for retrieving trending posts.
|
||||||
|
* @param {string} options.trending - The hashtag to retrieve trending posts for.
|
||||||
|
* @param {number} [options.trim=0] - The number of characters to trim the post content.
|
||||||
|
* @param {number} [options.limit=Settings.get("feed_max_fetch")] - The maximum number of posts to fetch.
|
||||||
|
* @return {Promise<Object[]>} An array of posts that are trending for the given hashtag.
|
||||||
|
*/
|
||||||
|
static async getTrending({ trending, trim, limit } = {}) {
|
||||||
|
const { data } = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: `/posts/trending/${trending}`,
|
||||||
|
params: {
|
||||||
|
trim: trim ?? 0,
|
||||||
|
limit: limit ?? Settings.get("feed_max_fetch"),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
}
|
}
|
11
src/models/radio/getters/list.js
Normal file
11
src/models/radio/getters/list.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import request from "../../../request"
|
||||||
|
|
||||||
|
export default async ({ limit = 50, offset = 0 } = {}) => {
|
||||||
|
const { data } = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: "/music/radio/list",
|
||||||
|
params: { limit, offset },
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
5
src/models/radio/index.js
Normal file
5
src/models/radio/index.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import getRadioList from "./getters/list"
|
||||||
|
|
||||||
|
export default class Radio {
|
||||||
|
static getRadioList = getRadioList
|
||||||
|
}
|
@ -1,26 +1,58 @@
|
|||||||
import request from "../../request"
|
import request from "../../request"
|
||||||
|
import processAddons from "../../helpers/processWithAddons"
|
||||||
|
import standartListMerge from "../../utils/standartListMerge"
|
||||||
|
|
||||||
export default class Search {
|
export default class Search {
|
||||||
static search = async (keywords, params = {}) => {
|
/**
|
||||||
|
* Performs a search using the provided keywords and optional parameters.
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* This method is used to perform a search using the provided keywords and optional parameters.
|
||||||
|
* Additionally, supports for external addons to extend the search functionality.
|
||||||
|
*
|
||||||
|
* @param {string} keywords - The keywords to search for.
|
||||||
|
* @param {Object} [params={}] - Optional parameters for the search.
|
||||||
|
* @param {Array} [returnFields=[]] - An array of fields to return in the results. If empty, all fields will be returned.
|
||||||
|
* @return {Promise<Object>} A promise that resolves with the search results.
|
||||||
|
*/
|
||||||
|
static async search(keywords, params = {}, returnFields) {
|
||||||
|
let { limit = 50, offset = 0, sort = "desc" } = params
|
||||||
|
|
||||||
const { data } = await request({
|
const { data } = await request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `/search`,
|
url: `/search`,
|
||||||
params: {
|
params: {
|
||||||
keywords: keywords,
|
keywords: keywords,
|
||||||
params: params
|
limit: limit,
|
||||||
}
|
offset: offset,
|
||||||
|
sort: sort,
|
||||||
|
fields: params.fields,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return data
|
let results = await processAddons({
|
||||||
}
|
operation: "search",
|
||||||
|
initialData: data,
|
||||||
static async quickSearch(params) {
|
fnArguments: [
|
||||||
const response = await request({
|
keywords,
|
||||||
method: "GET",
|
{
|
||||||
url: "/search/quick",
|
limit: limit,
|
||||||
params: params
|
offset: offset,
|
||||||
|
sort: sort,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
normalizeAddonResult: ({ currentData, addonResult }) => {
|
||||||
|
return standartListMerge(currentData, addonResult)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return response.data
|
if (Array.isArray(returnFields)) {
|
||||||
|
return Array.from(new Set(returnFields)).reduce((acc, field) => {
|
||||||
|
acc[field] = results[field]
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,41 +1,102 @@
|
|||||||
import jwt_decode from "jwt-decode"
|
import { jwtDecode } from "jwt-decode"
|
||||||
import request from "../../request"
|
import request from "../../request"
|
||||||
import Storage from "../../helpers/withStorage"
|
import Storage from "../../helpers/withStorage"
|
||||||
|
|
||||||
export default class Session {
|
export default class Session {
|
||||||
static storageTokenKey = "token"
|
static storageTokenKey = "token"
|
||||||
|
static storageRefreshTokenKey = "refreshToken"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the token from the storage engine.
|
||||||
|
*
|
||||||
|
* @return {type} description of return value
|
||||||
|
*/
|
||||||
static get token() {
|
static get token() {
|
||||||
return Storage.engine.get(this.storageTokenKey)
|
return Storage.engine.get(this.storageTokenKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the token in the storage engine.
|
||||||
|
*
|
||||||
|
* @param {string} token - The token to be set.
|
||||||
|
* @return {Promise<void>} A promise that resolves when the token is successfully set.
|
||||||
|
*/
|
||||||
static set token(token) {
|
static set token(token) {
|
||||||
return Storage.engine.set(this.storageTokenKey, token)
|
return Storage.engine.set(this.storageTokenKey, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the refresh token from the storage engine.
|
||||||
|
*
|
||||||
|
* @return {string} The refresh token stored in the storage engine.
|
||||||
|
*/
|
||||||
|
static get refreshToken() {
|
||||||
|
return Storage.engine.get(this.storageRefreshTokenKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the refresh token in the storage engine.
|
||||||
|
*
|
||||||
|
* @param {string} token - The refresh token to be set.
|
||||||
|
* @return {Promise<void>} A promise that resolves when the refresh token is successfully set.
|
||||||
|
*/
|
||||||
|
static set refreshToken(token) {
|
||||||
|
return Storage.engine.set(this.storageRefreshTokenKey, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the roles from the decoded token object.
|
||||||
|
*
|
||||||
|
* @return {Array<string>|undefined} The roles if they exist, otherwise undefined.
|
||||||
|
*/
|
||||||
static get roles() {
|
static get roles() {
|
||||||
return this.getDecodedToken()?.roles
|
return this.getDecodedToken()?.roles
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the user ID from the decoded token object.
|
||||||
|
*
|
||||||
|
* @return {string|undefined} The user ID if it exists, otherwise undefined.
|
||||||
|
*/
|
||||||
static get user_id() {
|
static get user_id() {
|
||||||
return this.getDecodedToken()?.user_id
|
return this.getDecodedToken()?.user_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the session UUID from the decoded token object.
|
||||||
|
*
|
||||||
|
* @return {string} The session UUID if it exists, otherwise undefined.
|
||||||
|
*/
|
||||||
static get session_uuid() {
|
static get session_uuid() {
|
||||||
return this.getDecodedToken()?.session_uuid
|
return this.getDecodedToken()?.session_uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDecodedToken = () => {
|
/**
|
||||||
|
* Retrieves the decoded token from the session storage.
|
||||||
|
*
|
||||||
|
* @return {Object|null} The decoded token object if it exists, otherwise null.
|
||||||
|
*/
|
||||||
|
static getDecodedToken() {
|
||||||
const token = this.token
|
const token = this.token
|
||||||
|
|
||||||
return token && jwt_decode(token)
|
return token && jwtDecode(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the token from the storage engine.
|
||||||
|
*
|
||||||
|
* @return {Promise<void>} A promise that resolves when the token is successfully removed.
|
||||||
|
*/
|
||||||
static removeToken() {
|
static removeToken() {
|
||||||
return Storage.engine.remove(Session.storageTokenKey)
|
return Storage.engine.remove(Session.storageTokenKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
static getAllSessions = async () => {
|
/**
|
||||||
|
* Retrieves all sessions from the server.
|
||||||
|
*
|
||||||
|
* @return {Promise<Object>} The data of all sessions.
|
||||||
|
*/
|
||||||
|
static async getAllSessions() {
|
||||||
const response = await request({
|
const response = await request({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: "/sessions/all"
|
url: "/sessions/all"
|
||||||
@ -44,7 +105,12 @@ export default class Session {
|
|||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
static getCurrentSession = async () => {
|
/**
|
||||||
|
* Retrieves the current session from the server.
|
||||||
|
*
|
||||||
|
* @return {Promise<Object>} The data of the current session.
|
||||||
|
*/
|
||||||
|
static async getCurrentSession() {
|
||||||
const response = await request({
|
const response = await request({
|
||||||
method: "get",
|
method: "get",
|
||||||
url: "/sessions/current"
|
url: "/sessions/current"
|
||||||
@ -53,21 +119,12 @@ export default class Session {
|
|||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
static getTokenValidation = async () => {
|
/**
|
||||||
const session = await Session.token
|
* Destroys the current session by deleting it from the server.
|
||||||
|
*
|
||||||
const response = await request({
|
* @return {Promise<Object>} The response data from the server after deleting the session.
|
||||||
method: "get",
|
*/
|
||||||
url: "/sessions/validate",
|
static async destroyCurrentSession() {
|
||||||
data: {
|
|
||||||
session: session
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
static destroyCurrentSession = async () => {
|
|
||||||
const token = await Session.token
|
const token = await Session.token
|
||||||
const session = await Session.getDecodedToken()
|
const session = await Session.getDecodedToken()
|
||||||
|
|
||||||
@ -91,16 +148,16 @@ export default class Session {
|
|||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
static destroyAllSessions = async () => {
|
static async destroyAllSessions() {
|
||||||
throw new Error("Not implemented")
|
throw new Error("Not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// alias for validateToken method
|
/**
|
||||||
static validSession = async (token) => {
|
* Retrieves the validity of the current token.
|
||||||
return await Session.validateToken(token)
|
*
|
||||||
}
|
* @return {boolean} The validity status of the current token.
|
||||||
|
*/
|
||||||
static isCurrentTokenValid = async () => {
|
static async isCurrentTokenValid() {
|
||||||
const health = await Session.getTokenValidation()
|
const health = await Session.getTokenValidation()
|
||||||
|
|
||||||
return health.valid
|
return health.valid
|
||||||
|
136
src/models/spectrum/index.js
Normal file
136
src/models/spectrum/index.js
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import axios from "axios"
|
||||||
|
import SessionService from "../session"
|
||||||
|
//import User from "comty.js/models/user"
|
||||||
|
|
||||||
|
async function injectUserData(list) {
|
||||||
|
if (!Array.isArray(list)) {
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
const user_ids = list.map((item) => {
|
||||||
|
return item.user_id
|
||||||
|
})
|
||||||
|
|
||||||
|
//const users = await User.data(user_ids.join(","))
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Streaming {
|
||||||
|
static apiHostname = "https://live.ragestudio.net"
|
||||||
|
|
||||||
|
static get base() {
|
||||||
|
const baseInstance = axios.create({
|
||||||
|
baseURL: Streaming.apiHostname,
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json",
|
||||||
|
"ngrok-skip-browser-warning": "any",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (SessionService.token) {
|
||||||
|
baseInstance.defaults.headers.common["Authorization"] =
|
||||||
|
`Bearer ${SessionService.token}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
static async serverInfo() {
|
||||||
|
const { data } = await Streaming.base({
|
||||||
|
method: "get",
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
hostname: Streaming.apiHostname,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getOwnProfiles() {
|
||||||
|
const { data } = await Streaming.base({
|
||||||
|
method: "get",
|
||||||
|
url: "/streaming/profiles/self",
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getProfile({ profile_id }) {
|
||||||
|
if (!profile_id) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await Streaming.base({
|
||||||
|
method: "get",
|
||||||
|
url: `/streaming/profiles/${profile_id}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getStream({ profile_id }) {
|
||||||
|
if (!profile_id) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await Streaming.base({
|
||||||
|
method: "get",
|
||||||
|
url: `/streaming/${profile_id}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteProfile({ profile_id }) {
|
||||||
|
if (!profile_id) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await Streaming.base({
|
||||||
|
method: "delete",
|
||||||
|
url: `/streaming/profiles/${profile_id}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
static async createOrUpdateStream(update) {
|
||||||
|
const { data } = await Streaming.base({
|
||||||
|
method: "put",
|
||||||
|
url: `/streaming/profiles/self`,
|
||||||
|
data: update,
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getConnectionStatus({ profile_id }) {
|
||||||
|
console.warn("getConnectionStatus() | Not implemented")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getLivestreamsList({ limit, offset } = {}) {
|
||||||
|
let { data } = await Streaming.base({
|
||||||
|
method: "get",
|
||||||
|
url: "/streaming/list",
|
||||||
|
params: {
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
data = await injectUserData(data)
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getLivestreamData(livestream_id) {
|
||||||
|
const { data } = await Streaming.base({
|
||||||
|
method: "get",
|
||||||
|
url: `/streaming/${livestream_id}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
@ -2,11 +2,16 @@ import SessionModel from "../session"
|
|||||||
import request from "../../request"
|
import request from "../../request"
|
||||||
|
|
||||||
export default class User {
|
export default class User {
|
||||||
static data = async (payload = {}) => {
|
/**
|
||||||
let {
|
* Retrieves the data of a user.
|
||||||
username,
|
*
|
||||||
user_id,
|
* @param {Object} payload - An object containing the username and user_id.
|
||||||
} = payload
|
* @param {string} payload.username - The username of the user.
|
||||||
|
* @param {string} payload.user_id - The ID of the user.
|
||||||
|
* @return {Promise<Object>} - A promise that resolves with the data of the user.
|
||||||
|
*/
|
||||||
|
static async data(payload = {}) {
|
||||||
|
let { username, user_id, basic = false } = payload
|
||||||
|
|
||||||
if (!username && !user_id) {
|
if (!username && !user_id) {
|
||||||
user_id = SessionModel.user_id
|
user_id = SessionModel.user_id
|
||||||
@ -25,53 +30,63 @@ export default class User {
|
|||||||
const response = await request({
|
const response = await request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `/users/${user_id}/data`,
|
url: `/users/${user_id}/data`,
|
||||||
})
|
params: {
|
||||||
|
basic,
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
|
|
||||||
static updateData = async (payload) => {
|
|
||||||
const response = await request({
|
|
||||||
method: "POST",
|
|
||||||
url: "/users/self/update",
|
|
||||||
data: {
|
|
||||||
update: payload,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsetFullName = async () => {
|
/**
|
||||||
return await User.updateData({
|
* Updates the user data with the given payload.
|
||||||
full_name: null,
|
*
|
||||||
})
|
* @param {Object} payload - The data to update the user with.
|
||||||
}
|
* @return {Promise<Object>} - A promise that resolves with the updated user data.
|
||||||
|
*/
|
||||||
static selfRoles = async () => {
|
static async updateData(payload) {
|
||||||
const response = await request({
|
const response = await request({
|
||||||
method: "GET",
|
method: "POST",
|
||||||
url: "/users/self/roles",
|
url: "/users/self/update",
|
||||||
|
data: payload,
|
||||||
})
|
})
|
||||||
|
|
||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
static haveRole = async (role) => {
|
/**
|
||||||
const roles = await User.selfRoles()
|
* Update the public name to null in the user data.
|
||||||
|
*
|
||||||
if (!roles) {
|
* @return {Promise} A Promise that resolves with the response data after updating the public name
|
||||||
return false
|
*/
|
||||||
|
static async unsetPublicName() {
|
||||||
|
return await User.updateData({
|
||||||
|
public_name: null,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.isArray(roles) && roles.includes(role)
|
/**
|
||||||
|
* Retrieves the roles of a user.
|
||||||
|
*
|
||||||
|
* @param {string} user_id - The ID of the user. If not provided, the current user ID will be used.
|
||||||
|
* @return {Promise<Array>} An array of roles for the user.
|
||||||
|
*/
|
||||||
|
static async getRoles(user_id) {
|
||||||
|
const response = await request({
|
||||||
|
method: "GET",
|
||||||
|
url: `/users/${user_id ?? "self"}/roles`,
|
||||||
|
})
|
||||||
|
|
||||||
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
static haveAdmin = async () => {
|
/**
|
||||||
return User.haveRole("admin")
|
* Retrieves the badges for a given user.
|
||||||
}
|
*
|
||||||
|
* @param {string} user_id - The ID of the user. If not provided, the current session user ID will be used.
|
||||||
static getUserBadges = async (user_id) => {
|
* @return {Promise<Array>} An array of badges for the user.
|
||||||
|
*/
|
||||||
|
static async getBadges(user_id) {
|
||||||
if (!user_id) {
|
if (!user_id) {
|
||||||
user_id = SessionModel.user_id
|
user_id = SessionModel.user_id
|
||||||
}
|
}
|
||||||
@ -84,61 +99,6 @@ export default class User {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
static getUserFollowers = async ({
|
|
||||||
user_id,
|
|
||||||
limit = 20,
|
|
||||||
offset = 0,
|
|
||||||
}) => {
|
|
||||||
// if user_id or username is not provided, set with current user
|
|
||||||
if (!user_id && !username) {
|
|
||||||
user_id = SessionModel.user_id
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data } = await request({
|
|
||||||
method: "GET",
|
|
||||||
url: `/user/${user_id}/followers`,
|
|
||||||
params: {
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
static getConnectedUsersFollowing = async () => {
|
|
||||||
const { data } = await request({
|
|
||||||
method: "GET",
|
|
||||||
url: "/status/connected/following",
|
|
||||||
})
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
static checkUsernameAvailability = async (username) => {
|
|
||||||
const { data } = await request({
|
|
||||||
method: "GET",
|
|
||||||
url: `/user/username_available`,
|
|
||||||
params: {
|
|
||||||
username,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
static checkEmailAvailability = async (email) => {
|
|
||||||
const { data } = await request({
|
|
||||||
method: "GET",
|
|
||||||
url: `/user/email_available`,
|
|
||||||
params: {
|
|
||||||
email,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrive user config from server
|
* Retrive user config from server
|
||||||
*
|
*
|
||||||
@ -150,8 +110,8 @@ export default class User {
|
|||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/users/self/config",
|
url: "/users/self/config",
|
||||||
params: {
|
params: {
|
||||||
key
|
key,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return data
|
return data
|
||||||
@ -167,7 +127,7 @@ export default class User {
|
|||||||
const { data } = await request({
|
const { data } = await request({
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
url: "/users/self/config",
|
url: "/users/self/config",
|
||||||
data: update
|
data: update,
|
||||||
})
|
})
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
import request from "../../request"
|
|
||||||
|
|
||||||
export default class WidgetModel {
|
|
||||||
static browse = async ({ limit, offset, keywords } = {}) => {
|
|
||||||
const response = await request({
|
|
||||||
instance: globalThis.__comty_shared_state.instances["marketplace"],
|
|
||||||
method: "GET",
|
|
||||||
url: "/widgets",
|
|
||||||
params: {
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
keywords: JSON.stringify(keywords),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return response.data
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
function getCurrentHostname() {
|
|
||||||
if (typeof window === "undefined") {
|
|
||||||
return "localhost"
|
|
||||||
}
|
|
||||||
|
|
||||||
return window?.location?.hostname ?? "localhost"
|
|
||||||
}
|
|
||||||
|
|
||||||
const envOrigins = {
|
|
||||||
"development": `http://${getCurrentHostname()}:9000`,
|
|
||||||
"indev": "https://indev_api.comty.app",
|
|
||||||
"production": "https://api.comty.app",
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
origin: envOrigins[process.env.NODE_ENV ?? "production"],
|
|
||||||
websockets: [
|
|
||||||
{
|
|
||||||
namespace: "posts",
|
|
||||||
path: "/posts",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
namespace: "main",
|
|
||||||
path: "/main",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
namespace: "notifications",
|
|
||||||
path: "/notifications",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
36
src/remotes.js
Executable file
36
src/remotes.js
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
const envOrigins = {
|
||||||
|
development: `https://fr01.ragestudio.net:9000`, //`${location.origin}/api`,
|
||||||
|
indev: "https://indev.comty.app/api",
|
||||||
|
production: "https://api.comty.app",
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
origin: envOrigins[process.env.NODE_ENV ?? "production"],
|
||||||
|
websockets: [
|
||||||
|
{
|
||||||
|
namespace: "posts",
|
||||||
|
path: "/posts",
|
||||||
|
ng: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "main",
|
||||||
|
path: "/main",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "notifications",
|
||||||
|
path: "/notifications",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "chats",
|
||||||
|
path: "/chats",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "music",
|
||||||
|
path: "/music",
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// namespace: "payments",
|
||||||
|
// path: "/payments",
|
||||||
|
// }
|
||||||
|
],
|
||||||
|
}
|
52
src/utils/generateRequest.ts
Normal file
52
src/utils/generateRequest.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import request from "../request"
|
||||||
|
|
||||||
|
// create a regex to detect params with %% symbol, from the url
|
||||||
|
const paramMatchRegex = /(%[0-9a-f]{2}%)/g
|
||||||
|
|
||||||
|
export default (method: string = "GET", url: string = "/", params?: object, data?: object) => {
|
||||||
|
return async function generatedRequest(arg0: any, arg1: any) {
|
||||||
|
const requestObj = {
|
||||||
|
method: method,
|
||||||
|
url: url,
|
||||||
|
params: params,
|
||||||
|
data: data,
|
||||||
|
}
|
||||||
|
|
||||||
|
// search url for params
|
||||||
|
// example: /namespace/search/[0]/data => /namespace/search/${arguments[0]}/data
|
||||||
|
// if no url matches, merge params with arg0 and override data in requestObj
|
||||||
|
if (url.match(paramMatchRegex)) {
|
||||||
|
requestObj.url = url.replace(paramMatchRegex, (match) => {
|
||||||
|
console.log(match)
|
||||||
|
|
||||||
|
// replace with arguments
|
||||||
|
const fnArgumentIndex = ""
|
||||||
|
|
||||||
|
return match
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
requestObj.params = {
|
||||||
|
...requestObj.params,
|
||||||
|
...arg0
|
||||||
|
}
|
||||||
|
requestObj.data = {
|
||||||
|
...requestObj.data,
|
||||||
|
...arg1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof requestObj.params === "object" && requestObj.params) {
|
||||||
|
Object.keys(requestObj.params).forEach((key) => {
|
||||||
|
if (requestObj.params && typeof requestObj.params[key] === "string") {
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const response = await request(requestObj)
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
}
|
22
src/utils/importFrom.js
Normal file
22
src/utils/importFrom.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
async function importFilesFrom(from) {
|
||||||
|
let paths = {
|
||||||
|
// @ts-ignore
|
||||||
|
...import.meta.glob(`${from}/**.ts`),
|
||||||
|
// @ts-ignore
|
||||||
|
...import.meta.glob(`${from}/**.js`),
|
||||||
|
}
|
||||||
|
|
||||||
|
let fns = {}
|
||||||
|
|
||||||
|
for (const path in paths) {
|
||||||
|
// @ts-ignore
|
||||||
|
const name = path.split("/").pop().replace(".ts", "").replace(".js", "")
|
||||||
|
const fn = await paths[path]()
|
||||||
|
|
||||||
|
fns[name] = fn.default
|
||||||
|
}
|
||||||
|
|
||||||
|
return fns
|
||||||
|
}
|
||||||
|
|
||||||
|
export default importFilesFrom
|
63
src/utils/measurePing.js
Executable file
63
src/utils/measurePing.js
Executable file
@ -0,0 +1,63 @@
|
|||||||
|
import request from "../request"
|
||||||
|
|
||||||
|
const fetchers = {
|
||||||
|
http: () =>
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
const start = Date.now()
|
||||||
|
|
||||||
|
const failTimeout = setTimeout(() => {
|
||||||
|
resolve("timeout")
|
||||||
|
}, 5000)
|
||||||
|
|
||||||
|
request({
|
||||||
|
method: "GET",
|
||||||
|
url: "/ping",
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
clearTimeout(failTimeout)
|
||||||
|
resolve(Date.now() - start)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
clearTimeout(failTimeout)
|
||||||
|
resolve("failed")
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
ws: () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
const start = Date.now()
|
||||||
|
|
||||||
|
const failTimeout = setTimeout(() => {
|
||||||
|
resolve("failed")
|
||||||
|
}, 5000)
|
||||||
|
|
||||||
|
globalThis.__comty_shared_state.ws.sockets
|
||||||
|
.get("main")
|
||||||
|
.once("pong", () => {
|
||||||
|
failTimeout && clearTimeout(failTimeout)
|
||||||
|
|
||||||
|
resolve(Date.now() - start)
|
||||||
|
})
|
||||||
|
|
||||||
|
globalThis.__comty_shared_state.ws.sockets.get("main").emit("ping")
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async ({ select } = {}) => {
|
||||||
|
let selectedPromises = []
|
||||||
|
|
||||||
|
if (Array.isArray(select)) {
|
||||||
|
select.forEach((item) => {
|
||||||
|
if (!fetchers[item]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
selectedPromises.push(fetchers[item]())
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
selectedPromises = [fetchers["http"](), fetchers["ws"]()]
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await Promise.all(selectedPromises)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
18
src/utils/standartListMerge.js
Normal file
18
src/utils/standartListMerge.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export default (base, obj) => {
|
||||||
|
const validGroups = Object.keys(obj).filter(
|
||||||
|
(key) => Array.isArray(obj[key]?.items) && obj[key].items.length > 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
return validGroups.reduce(
|
||||||
|
(acc, group) => {
|
||||||
|
if (!acc[group]) {
|
||||||
|
acc[group] = { items: [], total_items: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[group].items = [...acc[group].items, ...obj[group].items]
|
||||||
|
acc[group].total_items += obj[group].total_items || 0
|
||||||
|
return acc
|
||||||
|
},
|
||||||
|
{ ...base },
|
||||||
|
)
|
||||||
|
}
|
145
src/ws.js
Normal file
145
src/ws.js
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import Remotes from "./remotes"
|
||||||
|
import Storage from "./helpers/withStorage"
|
||||||
|
|
||||||
|
import { io } from "socket.io-client"
|
||||||
|
import { RTEngineClient } from "linebridge-client/src"
|
||||||
|
//import { RTEngineClient } from "../../linebridge/client/src"
|
||||||
|
|
||||||
|
class WebsocketManager {
|
||||||
|
sockets = new Map()
|
||||||
|
|
||||||
|
async connect(remote) {
|
||||||
|
let opts = {
|
||||||
|
transports: ["websocket"],
|
||||||
|
autoConnect: remote.autoConnect ?? true,
|
||||||
|
forceNew: true,
|
||||||
|
path: remote.path,
|
||||||
|
...(remote.params ?? {}),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remote.noAuth !== true) {
|
||||||
|
opts.auth = {
|
||||||
|
token: Storage.engine.get("token"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const socket = io(Remotes.origin, opts)
|
||||||
|
|
||||||
|
socket.on("connect", () => {
|
||||||
|
globalThis.__comty_shared_state.eventBus.emit(
|
||||||
|
`wsmanager:${remote.namespace}:connected`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on("disconnect", () => {
|
||||||
|
globalThis.__comty_shared_state.eventBus.emit(
|
||||||
|
`wsmanager:${remote.namespace}:disconnected`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on("error", (error) => {
|
||||||
|
globalThis.__comty_shared_state.eventBus.emit(
|
||||||
|
`wsmanager:${remote.namespace}:error`,
|
||||||
|
error,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.sockets.set(remote.namespace, socket)
|
||||||
|
|
||||||
|
return socket
|
||||||
|
}
|
||||||
|
|
||||||
|
async connectNg(remote) {
|
||||||
|
console.warn(
|
||||||
|
`Creating experimental socket client, some features may not work as expected:`,
|
||||||
|
remote,
|
||||||
|
)
|
||||||
|
|
||||||
|
const client = new RTEngineClient({
|
||||||
|
refName: remote.namespace,
|
||||||
|
url: `${Remotes.origin}/${remote.namespace}`,
|
||||||
|
token: Storage.engine.get("token"),
|
||||||
|
})
|
||||||
|
|
||||||
|
client.on("connect", () => {
|
||||||
|
globalThis.__comty_shared_state.eventBus.emit(
|
||||||
|
`wsmanager:${remote.namespace}:connected`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
client.on("disconnect", () => {
|
||||||
|
globalThis.__comty_shared_state.eventBus.emit(
|
||||||
|
`wsmanager:${remote.namespace}:disconnected`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
client.on("error", (error) => {
|
||||||
|
globalThis.__comty_shared_state.eventBus.emit(
|
||||||
|
`wsmanager:${remote.namespace}:error`,
|
||||||
|
error,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await client.connect()
|
||||||
|
|
||||||
|
this.sockets.set(remote.namespace, client)
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
async disconnect(key) {
|
||||||
|
const socket = this.sockets.get(key)
|
||||||
|
|
||||||
|
if (!socket) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
socket.connected === true &&
|
||||||
|
typeof socket.disconnect === "function"
|
||||||
|
) {
|
||||||
|
await socket.disconnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof socket.removeAllListeners === "function") {
|
||||||
|
await socket.removeAllListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sockets.delete(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
async connectAll() {
|
||||||
|
if (this.sockets.size > 0) {
|
||||||
|
await this.disconnectAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
for await (const remote of Remotes.websockets) {
|
||||||
|
try {
|
||||||
|
if (remote.ng === true) {
|
||||||
|
await this.connectNg(remote)
|
||||||
|
} else {
|
||||||
|
await this.connect(remote)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`Failed to connect to [${remote.namespace}]:`,
|
||||||
|
error,
|
||||||
|
)
|
||||||
|
globalThis.__comty_shared_state.eventBus.emit(
|
||||||
|
`wsmanager:${remote.namespace}:error`,
|
||||||
|
error,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.__comty_shared_state.eventBus.emit("wsmanager:all:connected")
|
||||||
|
}
|
||||||
|
|
||||||
|
async disconnectAll() {
|
||||||
|
for (const [key, socket] of this.sockets) {
|
||||||
|
await this.disconnect(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WebsocketManager
|
Loading…
x
Reference in New Issue
Block a user