comty.js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +21 -0
- package/src/handlers/measurePing.js +53 -0
- package/src/handlers/request.js +60 -0
- package/src/helpers/handleAfterRequest.js +34 -0
- package/src/helpers/handleBeforeRequest.js +13 -0
- package/src/helpers/handleRegenerationEvent.js +39 -0
- package/src/helpers/withSettings.js +25 -0
- package/src/helpers/withStorage.js +31 -0
- package/src/hooks/useRequest/index.js +32 -0
- package/src/index.js +103 -0
- package/src/models/auth/index.js +53 -0
- package/src/models/feed/index.js +82 -0
- package/src/models/follows/index.js +48 -0
- package/src/models/index.js +44 -0
- package/src/models/livestream/index.js +84 -0
- package/src/models/playlists/index.js +48 -0
- package/src/models/post/index.js +169 -0
- package/src/models/session/index.js +126 -0
- package/src/models/sync/cores/spotifyCore.js +87 -0
- package/src/models/sync/index.js +11 -0
- package/src/models/user/index.js +159 -0
- package/src/models/widget/index.js +18 -0
- package/src/remotes.js +45 -0
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "comty.js",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "./dist/index.js",
|
|
5
|
+
"author": "RageStudio <support@ragestudio.net>",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "corenode-cli build"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@foxify/events": "^2.1.0",
|
|
12
|
+
"axios": "^1.4.0",
|
|
13
|
+
"js-cookie": "^3.0.5",
|
|
14
|
+
"jsonwebtoken": "^9.0.0",
|
|
15
|
+
"jwt-decode": "^3.1.2",
|
|
16
|
+
"linebridge": "^0.15.12"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"corenode": "^0.28.26"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import request from "./request"
|
|
2
|
+
|
|
3
|
+
export default async () => {
|
|
4
|
+
const timings = {}
|
|
5
|
+
|
|
6
|
+
const promises = [
|
|
7
|
+
new Promise(async (resolve) => {
|
|
8
|
+
const start = Date.now()
|
|
9
|
+
|
|
10
|
+
request({
|
|
11
|
+
method: "GET",
|
|
12
|
+
url: "/ping",
|
|
13
|
+
})
|
|
14
|
+
.then(() => {
|
|
15
|
+
// set http timing in ms
|
|
16
|
+
timings.http = Date.now() - start
|
|
17
|
+
|
|
18
|
+
resolve()
|
|
19
|
+
})
|
|
20
|
+
.catch(() => {
|
|
21
|
+
timings.http = "failed"
|
|
22
|
+
resolve()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
setTimeout(() => {
|
|
26
|
+
timings.http = "failed"
|
|
27
|
+
|
|
28
|
+
resolve()
|
|
29
|
+
}, 10000)
|
|
30
|
+
}),
|
|
31
|
+
new Promise((resolve) => {
|
|
32
|
+
const start = Date.now()
|
|
33
|
+
|
|
34
|
+
__comty_shared_state.wsInstances["default"].on("pong", () => {
|
|
35
|
+
timings.ws = Date.now() - start
|
|
36
|
+
|
|
37
|
+
resolve()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
__comty_shared_state.wsInstances["default"].emit("ping")
|
|
41
|
+
|
|
42
|
+
setTimeout(() => {
|
|
43
|
+
timings.ws = "failed"
|
|
44
|
+
|
|
45
|
+
resolve()
|
|
46
|
+
}, 10000)
|
|
47
|
+
})
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
await Promise.all(promises)
|
|
51
|
+
|
|
52
|
+
return timings
|
|
53
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import handleBeforeRequest from "../helpers/handleBeforeRequest"
|
|
2
|
+
import handleAfterRequest from "../helpers/handleAfterRequest"
|
|
3
|
+
import SessionModel from "../models/session"
|
|
4
|
+
|
|
5
|
+
export default async (
|
|
6
|
+
request = {
|
|
7
|
+
method: "GET",
|
|
8
|
+
},
|
|
9
|
+
...args
|
|
10
|
+
) => {
|
|
11
|
+
const instance = request.instance ?? __comty_shared_state.instances.default
|
|
12
|
+
|
|
13
|
+
if (!instance) {
|
|
14
|
+
throw new Error("No instance provided")
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// handle before request
|
|
18
|
+
await handleBeforeRequest(request)
|
|
19
|
+
|
|
20
|
+
if (typeof request === "string") {
|
|
21
|
+
request = {
|
|
22
|
+
url: request,
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (typeof request.headers !== "object") {
|
|
27
|
+
request.headers = {}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let result = null
|
|
31
|
+
|
|
32
|
+
const makeRequest = async () => {
|
|
33
|
+
const sessionToken = await SessionModel.token
|
|
34
|
+
|
|
35
|
+
if (sessionToken) {
|
|
36
|
+
request.headers["Authorization"] = `${globalThis.isServerMode ? "Server" : "Bearer"} ${sessionToken}`
|
|
37
|
+
} else {
|
|
38
|
+
console.warn("Making a request with no session token")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const _result = await instance(request, ...args)
|
|
42
|
+
.catch((error) => {
|
|
43
|
+
return error
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
result = _result
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
await makeRequest()
|
|
50
|
+
|
|
51
|
+
// handle after request
|
|
52
|
+
await handleAfterRequest(result, makeRequest)
|
|
53
|
+
|
|
54
|
+
// if error, throw it
|
|
55
|
+
if (result instanceof Error) {
|
|
56
|
+
throw result
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return result
|
|
60
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import handleRegenerationEvent from "./handleRegenerationEvent"
|
|
2
|
+
|
|
3
|
+
export default async (data, callback) => {
|
|
4
|
+
// handle 401, 403 responses
|
|
5
|
+
if (data instanceof Error) {
|
|
6
|
+
if (data.code && (data.code === "ECONNABORTED" || data.code === "ERR_NETWORK")) {
|
|
7
|
+
console.error(`Request aborted or network error, ignoring`)
|
|
8
|
+
return false
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (data.response.status === 401) {
|
|
12
|
+
// check if the server issue a refresh token on data
|
|
13
|
+
if (data.response.data.refreshToken) {
|
|
14
|
+
console.log(`Session expired, but the server issued a refresh token, handling regeneration event`)
|
|
15
|
+
|
|
16
|
+
// handle regeneration event
|
|
17
|
+
await handleRegenerationEvent(data.response.data.refreshToken)
|
|
18
|
+
|
|
19
|
+
return await callback()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// check if route is from "session" namespace
|
|
23
|
+
if (data.config.url.includes("/session")) {
|
|
24
|
+
return __comty_shared_state.eventBus.emit("session.invalid", "Session expired, but the server did not issue a refresh token")
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (data.response.status === 403) {
|
|
29
|
+
if (data.config.url.includes("/session")) {
|
|
30
|
+
return __comty_shared_state.eventBus.emit("session.invalid", "Session not valid or not existent")
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default async (request) => {
|
|
2
|
+
if (__comty_shared_state.onExpiredExceptionEvent) {
|
|
3
|
+
if (__comty_shared_state.excludedExpiredExceptionURL.includes(request.url)) return
|
|
4
|
+
|
|
5
|
+
await new Promise((resolve) => {
|
|
6
|
+
__comty_shared_state.eventBus.once("session.regenerated", () => {
|
|
7
|
+
console.log(`Session has been regenerated, retrying request`)
|
|
8
|
+
|
|
9
|
+
resolve()
|
|
10
|
+
})
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import SessionModel from "../models/session"
|
|
2
|
+
import request from "../handlers/request"
|
|
3
|
+
|
|
4
|
+
export default async (refreshToken) =>{
|
|
5
|
+
__comty_shared_state.eventBus.emit("session.expiredExceptionEvent", refreshToken)
|
|
6
|
+
|
|
7
|
+
__comty_shared_state.onExpiredExceptionEvent = true
|
|
8
|
+
|
|
9
|
+
const expiredToken = await SessionModel.token
|
|
10
|
+
|
|
11
|
+
// send request to regenerate token
|
|
12
|
+
const response = await request({
|
|
13
|
+
method: "POST",
|
|
14
|
+
url: "/session/regenerate",
|
|
15
|
+
data: {
|
|
16
|
+
expiredToken: expiredToken,
|
|
17
|
+
refreshToken,
|
|
18
|
+
}
|
|
19
|
+
}).catch((error) => {
|
|
20
|
+
console.error(`Failed to regenerate token: ${error.message}`)
|
|
21
|
+
return false
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
if (!response) {
|
|
25
|
+
return __comty_shared_state.eventBus.emit("session.invalid", "Failed to regenerate token")
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!response.data?.token) {
|
|
29
|
+
return __comty_shared_state.eventBus.emit("session.invalid", "Failed to regenerate token, invalid server response.")
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// set new token
|
|
33
|
+
SessionModel.token = response.data.token
|
|
34
|
+
|
|
35
|
+
__comty_shared_state.onExpiredExceptionEvent = false
|
|
36
|
+
|
|
37
|
+
// emit event
|
|
38
|
+
__comty_shared_state.eventBus.emit("session.regenerated")
|
|
39
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export default class Settings {
|
|
2
|
+
static get = (key) => {
|
|
3
|
+
if (typeof window === "undefined") {
|
|
4
|
+
return null
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
return window?.app?.cores?.settings.get(key)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static set = (key, value) => {
|
|
11
|
+
if (typeof window === "undefined") {
|
|
12
|
+
return null
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return window?.app?.cores?.settings.set(key, value)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static is = (key) => {
|
|
19
|
+
if (typeof window === "undefined") {
|
|
20
|
+
return null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return window?.app?.cores?.settings.is(key)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import jscookies from "js-cookie"
|
|
2
|
+
|
|
3
|
+
class InternalStorage {
|
|
4
|
+
#storage = {}
|
|
5
|
+
|
|
6
|
+
get(key) {
|
|
7
|
+
// get value from storage
|
|
8
|
+
return this.#storage[key]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
set(key, value) {
|
|
12
|
+
// storage securely in memory
|
|
13
|
+
return this.#storage[key] = value
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default class Storage {
|
|
18
|
+
static get engine() {
|
|
19
|
+
// check if is running in browser, if is import js-cookie
|
|
20
|
+
// else use in-memory safe storage
|
|
21
|
+
if (typeof window !== "undefined") {
|
|
22
|
+
return jscookies
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!globalThis.__comty_shared_state["_internal_storage"]) {
|
|
26
|
+
globalThis.__comty_shared_state["_internal_storage"] = new InternalStorage()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return globalThis.__comty_shared_state["_internal_storage"]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
|
|
3
|
+
export default (method, ...args) => {
|
|
4
|
+
if (typeof method !== "function") {
|
|
5
|
+
throw new Error("useRequest: method must be a function")
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const [loading, setLoading] = React.useState(true)
|
|
9
|
+
const [result, setResult] = React.useState(null)
|
|
10
|
+
const [error, setError] = React.useState(null)
|
|
11
|
+
|
|
12
|
+
const makeRequest = (...newArgs) => {
|
|
13
|
+
method(...newArgs)
|
|
14
|
+
.then((data) => {
|
|
15
|
+
setResult(data)
|
|
16
|
+
setLoading(false)
|
|
17
|
+
})
|
|
18
|
+
.catch((err) => {
|
|
19
|
+
setError(err)
|
|
20
|
+
setLoading(false)
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
React.useEffect(() => {
|
|
25
|
+
makeRequest(...args)
|
|
26
|
+
}, [])
|
|
27
|
+
|
|
28
|
+
return [loading, result, error, (...newArgs) => {
|
|
29
|
+
setLoading(true)
|
|
30
|
+
makeRequest(...newArgs)
|
|
31
|
+
}]
|
|
32
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import EventEmitter from "@foxify/events"
|
|
2
|
+
|
|
3
|
+
import axios from "axios"
|
|
4
|
+
import { io } from "socket.io-client"
|
|
5
|
+
|
|
6
|
+
import remotes from "./remotes"
|
|
7
|
+
|
|
8
|
+
import request from "./handlers/request"
|
|
9
|
+
import Storage from "./helpers/withStorage"
|
|
10
|
+
|
|
11
|
+
import SessionModel from "./models/session"
|
|
12
|
+
import { createHandlers } from "./models"
|
|
13
|
+
|
|
14
|
+
globalThis.isServerMode = typeof window === "undefined" && typeof global !== "undefined"
|
|
15
|
+
|
|
16
|
+
if (globalThis.isServerMode) {
|
|
17
|
+
const { Buffer } = require("buffer")
|
|
18
|
+
|
|
19
|
+
globalThis.b64Decode = (data) => {
|
|
20
|
+
return Buffer.from(data, "base64").toString("utf-8")
|
|
21
|
+
}
|
|
22
|
+
globalThis.b64Encode = (data) => {
|
|
23
|
+
return Buffer.from(data, "utf-8").toString("base64")
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default function createClient({
|
|
28
|
+
wsEvents = Object(),
|
|
29
|
+
useWs = false,
|
|
30
|
+
accessKey = null,
|
|
31
|
+
privateKey = null,
|
|
32
|
+
} = {}) {
|
|
33
|
+
const sharedState = globalThis.__comty_shared_state = {
|
|
34
|
+
onExpiredExceptionEvent: false,
|
|
35
|
+
excludedExpiredExceptionURL: ["/session/regenerate"],
|
|
36
|
+
eventBus: new EventEmitter(),
|
|
37
|
+
mainOrigin: remotes.default.origin,
|
|
38
|
+
instances: Object(),
|
|
39
|
+
wsInstances: Object(),
|
|
40
|
+
rest: null,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (globalThis.isServerMode) {
|
|
44
|
+
sharedState.rest = createHandlers()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (privateKey && accessKey && globalThis.isServerMode) {
|
|
48
|
+
Storage.engine.set("token", `${accessKey}:${privateKey}`)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// create instances for every remote
|
|
52
|
+
for (const [key, remote] of Object.entries(remotes)) {
|
|
53
|
+
sharedState.instances[key] = axios.create({
|
|
54
|
+
baseURL: remote.origin,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
if (useWs && remote.hasWebsocket) {
|
|
58
|
+
sharedState.wsInstances[key] = io(remote.wsOrigin ?? remote.origin, {
|
|
59
|
+
transports: ["websocket"],
|
|
60
|
+
autoConnect: true,
|
|
61
|
+
...remote.wsParams ?? {},
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// register ws events
|
|
67
|
+
Object.keys(sharedState.wsInstances).forEach((key) => {
|
|
68
|
+
const ws = sharedState.wsInstances[key]
|
|
69
|
+
|
|
70
|
+
ws.on("connect", () => {
|
|
71
|
+
console.log(`[WS-API][${key}] Connected`)
|
|
72
|
+
|
|
73
|
+
if (remotes[key].needsAuth) {
|
|
74
|
+
// try to auth
|
|
75
|
+
ws.emit("authenticate", {
|
|
76
|
+
token: SessionModel.token,
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
ws.on("disconnect", () => {
|
|
82
|
+
console.log(`[WS-API][${key}] Disconnected`)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
ws.on("error", (error) => {
|
|
86
|
+
console.error(`[WS-API][${key}] Error`, error)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
ws.onAny((event, ...args) => {
|
|
90
|
+
console.log(`[WS-API][${key}] Event recived`, event, ...args)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
const customEvents = wsEvents[key]
|
|
94
|
+
|
|
95
|
+
if (customEvents) {
|
|
96
|
+
for (const [eventName, eventHandler] of Object.entries(customEvents)) {
|
|
97
|
+
ws.on(eventName, eventHandler)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
return sharedState
|
|
103
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import request from "../../handlers/request"
|
|
2
|
+
import SessionModel from "../session"
|
|
3
|
+
|
|
4
|
+
export default class AuthModel {
|
|
5
|
+
static login = async (payload) => {
|
|
6
|
+
const response = await request({
|
|
7
|
+
method: "post",
|
|
8
|
+
url: "/auth/login",
|
|
9
|
+
data: {
|
|
10
|
+
username: payload.username, //window.btoa(payload.username),
|
|
11
|
+
password: payload.password, //window.btoa(payload.password),
|
|
12
|
+
},
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
SessionModel.token = response.data.token
|
|
16
|
+
|
|
17
|
+
__comty_shared_state.eventBus.emit("auth:login_success")
|
|
18
|
+
|
|
19
|
+
return response.data
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static logout = async () => {
|
|
23
|
+
await SessionModel.destroyCurrentSession()
|
|
24
|
+
|
|
25
|
+
SessionModel.removeToken()
|
|
26
|
+
|
|
27
|
+
__comty_shared_state.eventBus.emit("auth:logout_success")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static register = async (payload) => {
|
|
31
|
+
const { username, password, email } = payload
|
|
32
|
+
|
|
33
|
+
const response = await request({
|
|
34
|
+
method: "post",
|
|
35
|
+
url: "/auth/register",
|
|
36
|
+
data: {
|
|
37
|
+
username,
|
|
38
|
+
password,
|
|
39
|
+
email,
|
|
40
|
+
}
|
|
41
|
+
}).catch((error) => {
|
|
42
|
+
console.error(error)
|
|
43
|
+
|
|
44
|
+
return false
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
if (!response) {
|
|
48
|
+
throw new Error("Unable to register user")
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return response
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import request from "../../handlers/request"
|
|
2
|
+
import Settings from "../../helpers/withSettings"
|
|
3
|
+
|
|
4
|
+
export default class FeedModel {
|
|
5
|
+
static getMusicFeed = async ({ trim, limit } = {}) => {
|
|
6
|
+
const { data } = await request({
|
|
7
|
+
method: "GET",
|
|
8
|
+
url: `/feed/music`,
|
|
9
|
+
params: {
|
|
10
|
+
trim: trim ?? 0,
|
|
11
|
+
limit: limit ?? Settings.get("feed_max_fetch"),
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
return data
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static getGlobalMusicFeed = async ({ trim, limit } = {}) => {
|
|
19
|
+
const { data } = await request({
|
|
20
|
+
method: "GET",
|
|
21
|
+
url: `/feed/music/global`,
|
|
22
|
+
params: {
|
|
23
|
+
trim: trim ?? 0,
|
|
24
|
+
limit: limit ?? Settings.get("feed_max_fetch"),
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
return data
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static getTimelineFeed = async ({ trim, limit } = {}) => {
|
|
32
|
+
const { data } = await request({
|
|
33
|
+
method: "GET",
|
|
34
|
+
url: `/feed/timeline`,
|
|
35
|
+
params: {
|
|
36
|
+
trim: trim ?? 0,
|
|
37
|
+
limit: limit ?? Settings.get("feed_max_fetch"),
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
return data
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static getPostsFeed = async ({ trim, limit } = {}) => {
|
|
45
|
+
const { data } = await request({
|
|
46
|
+
method: "GET",
|
|
47
|
+
url: `/feed/posts`,
|
|
48
|
+
params: {
|
|
49
|
+
trim: trim ?? 0,
|
|
50
|
+
limit: limit ?? Settings.get("feed_max_fetch"),
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
return data
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static getPlaylistsFeed = async ({ trim, limit } = {}) => {
|
|
58
|
+
const { data } = await request({
|
|
59
|
+
method: "GET",
|
|
60
|
+
url: `/feed/playlists`,
|
|
61
|
+
params: {
|
|
62
|
+
trim: trim ?? 0,
|
|
63
|
+
limit: limit ?? Settings.get("feed_max_fetch"),
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return data
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static search = async (keywords, params = {}) => {
|
|
71
|
+
const { data } = await request({
|
|
72
|
+
method: "GET",
|
|
73
|
+
url: `/search`,
|
|
74
|
+
params: {
|
|
75
|
+
keywords: keywords,
|
|
76
|
+
params: params
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
return data
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { SessionModel } from "../../models"
|
|
2
|
+
import request from "../../handlers/request"
|
|
3
|
+
|
|
4
|
+
export default class FollowsModel {
|
|
5
|
+
static imFollowing = async (user_id) => {
|
|
6
|
+
if (!user_id) {
|
|
7
|
+
throw new Error("user_id is required")
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const response = await request({
|
|
11
|
+
method: "GET",
|
|
12
|
+
url: `/follow/user/${user_id}`,
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
return response.data
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static getFollowers = async (user_id) => {
|
|
19
|
+
if (!user_id) {
|
|
20
|
+
// set current user_id
|
|
21
|
+
user_id = SessionModel.user_id
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const response = await request({
|
|
25
|
+
method: "GET",
|
|
26
|
+
url: `/follow/user/${user_id}/followers`,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
return response.data
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static toogleFollow = async ({ user_id, username }) => {
|
|
33
|
+
if (!user_id && !username) {
|
|
34
|
+
throw new Error("user_id or username is required")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const response = await request({
|
|
38
|
+
method: "POST",
|
|
39
|
+
url: "/follow/user/toogle",
|
|
40
|
+
data: {
|
|
41
|
+
user_id: user_id,
|
|
42
|
+
username: username
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
return response.data
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import AuthModel from "./auth"
|
|
2
|
+
import FeedModel from "./feed"
|
|
3
|
+
import FollowsModel from "./follows"
|
|
4
|
+
import LivestreamModel from "./livestream"
|
|
5
|
+
import PlaylistsModel from "./playlists"
|
|
6
|
+
import PostModel from "./post"
|
|
7
|
+
import SessionModel from "./session"
|
|
8
|
+
import SyncModel from "./sync"
|
|
9
|
+
import UserModel from "./user"
|
|
10
|
+
|
|
11
|
+
function getEndpointsFromModel(model) {
|
|
12
|
+
return Object.entries(model).reduce((acc, [key, value]) => {
|
|
13
|
+
acc[key] = value
|
|
14
|
+
|
|
15
|
+
return acc
|
|
16
|
+
}, {})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function createHandlers() {
|
|
20
|
+
return {
|
|
21
|
+
auth: getEndpointsFromModel(AuthModel),
|
|
22
|
+
feed: getEndpointsFromModel(FeedModel),
|
|
23
|
+
follows: getEndpointsFromModel(FollowsModel),
|
|
24
|
+
livestream: getEndpointsFromModel(LivestreamModel),
|
|
25
|
+
playlists: getEndpointsFromModel(PlaylistsModel),
|
|
26
|
+
post: getEndpointsFromModel(PostModel),
|
|
27
|
+
session: getEndpointsFromModel(SessionModel),
|
|
28
|
+
sync: getEndpointsFromModel(SyncModel),
|
|
29
|
+
user: getEndpointsFromModel(UserModel),
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
AuthModel,
|
|
35
|
+
FeedModel,
|
|
36
|
+
FollowsModel,
|
|
37
|
+
LivestreamModel,
|
|
38
|
+
PlaylistsModel,
|
|
39
|
+
PostModel,
|
|
40
|
+
SessionModel,
|
|
41
|
+
SyncModel,
|
|
42
|
+
UserModel,
|
|
43
|
+
createHandlers,
|
|
44
|
+
}
|