update for v70

This commit is contained in:
SrGooglo 2024-03-15 20:41:22 +00:00
parent 8acb3f0084
commit 126bad9c1e
21 changed files with 297 additions and 229 deletions

View File

@ -4,6 +4,7 @@
"main": "./dist/index.js",
"author": "RageStudio <support@ragestudio.net>",
"scripts": {
"test": "ava",
"build": "hermes build"
},
"files": [
@ -12,6 +13,7 @@
"license": "MIT",
"dependencies": {
"@foxify/events": "^2.1.0",
"ava": "^6.1.2",
"axios": "^1.4.0",
"js-cookie": "^3.0.5",
"jsonwebtoken": "^9.0.0",
@ -22,4 +24,4 @@
"devDependencies": {
"@ragestudio/hermes": "^0.1.0"
}
}
}

View File

@ -1,58 +0,0 @@
import request from "./request"
export default async () => {
const timings = {}
const promises = [
new Promise(async (resolve) => {
const start = Date.now()
const failTimeout = setTimeout(() => {
timings.http = "failed"
resolve()
}, 10000)
request({
method: "GET",
url: "/ping",
})
.then(() => {
// set http timing in ms
timings.http = Date.now() - start
failTimeout && clearTimeout(failTimeout)
resolve()
})
.catch(() => {
timings.http = "failed"
resolve()
})
}),
new Promise((resolve) => {
const start = Date.now()
const failTimeout = setTimeout(() => {
timings.ws = "failed"
resolve()
}, 10000)
__comty_shared_state.wsInstances["default"].on("pong", () => {
timings.ws = Date.now() - start
failTimeout && clearTimeout(failTimeout)
resolve()
})
__comty_shared_state.wsInstances["default"].emit("ping")
})
]
await Promise.all(promises)
return timings
}

View File

@ -1,5 +1,5 @@
import SessionModel from "../models/session"
import request from "../handlers/request"
import request from "../request"
import { reconnectWebsockets } from "../"
export default async (refreshToken) => {

62
src/helpers/measurePing.js Executable file
View File

@ -0,0 +1,62 @@
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
}

View File

@ -4,12 +4,9 @@ import EventEmitter from "@foxify/events"
import axios from "axios"
import { io } from "socket.io-client"
import remotes from "./remotes"
import Storage from "./helpers/withStorage"
import SessionModel from "./models/session"
import { createHandlers } from "./models"
import Storage from "./helpers/withStorage"
import remote from "./remote"
globalThis.isServerMode = typeof window === "undefined" && typeof global !== "undefined"
@ -25,7 +22,11 @@ if (globalThis.isServerMode) {
}
export async function createWebsockets() {
const instances = globalThis.__comty_shared_state.wsInstances
if (!remote.websockets) {
return false
}
const instances = globalThis.__comty_shared_state.sockets
for (let [key, instance] of Object.entries(instances)) {
if (instance.connected) {
@ -36,27 +37,25 @@ export async function createWebsockets() {
// remove current listeners
instance.removeAllListeners()
delete globalThis.__comty_shared_state.wsInstances[key]
delete globalThis.__comty_shared_state.sockets[key]
}
for (let [key, remote] of Object.entries(remotes)) {
if (!remote.hasWebsocket) {
continue
}
for (let ws of remote.websockets) {
let opts = {
transports: ["websocket"],
autoConnect: remote.autoConnect ?? true,
...remote.wsParams ?? {},
autoConnect: ws.autoConnect ?? true,
forceNew: true,
path: ws.path,
...ws.params ?? {},
}
if (remote.noAuth !== true) {
if (ws.noAuth !== true) {
opts.auth = {
token: SessionModel.token,
token: Storage.engine.get("token"),
}
}
globalThis.__comty_shared_state.wsInstances[key] = io(remote.wsOrigin ?? remote.origin, opts)
globalThis.__comty_shared_state.sockets[ws.namespace] = io(remote.origin, opts)
}
// regsister events
@ -64,13 +63,6 @@ export async function createWebsockets() {
instance.on("connect", () => {
console.debug(`[WS-API][${key}] Connected`)
if (remotes[key].useClassicAuth && remotes[key].noAuth !== true) {
// try to auth
instance.emit("auth", {
token: SessionModel.token,
})
}
globalThis.__comty_shared_state.eventBus.emit(`${key}:connected`)
})
@ -95,7 +87,7 @@ export async function createWebsockets() {
}
export async function disconnectWebsockets() {
const instances = globalThis.__comty_shared_state.wsInstances
const instances = globalThis.__comty_shared_state.sockets
for (let [key, instance] of Object.entries(instances)) {
if (instance.connected) {
@ -104,45 +96,24 @@ export async function disconnectWebsockets() {
}
}
export async function reconnectWebsockets({ force = false } = {}) {
const instances = globalThis.__comty_shared_state.wsInstances
export async function reconnectWebsockets() {
const instances = globalThis.__comty_shared_state.sockets
for (let [key, instance] of Object.entries(instances)) {
if (instance.connected) {
if (!instance.auth) {
instance.disconnect()
instance.auth = {
token: SessionModel.token,
}
instance.connect()
continue
}
if (!force) {
instance.emit("reauthenticate", {
token: SessionModel.token,
})
continue
}
// disconnect first
instance.disconnect()
}
if (remotes[key].noAuth !== true) {
instance.auth = {
token: SessionModel.token,
}
instance.auth = {
token: Storage.engine.get("token"),
}
instance.connect()
}
}
export default function createClient({
export function createClient({
accessKey = null,
privateKey = null,
enableWs = false,
@ -151,50 +122,47 @@ export default function createClient({
onExpiredExceptionEvent: false,
excludedExpiredExceptionURL: ["/session/regenerate"],
eventBus: new EventEmitter(),
mainOrigin: remotes.default.origin,
instances: Object(),
wsInstances: Object(),
mainOrigin: remote.origin,
baseRequest: null,
sockets: new Map(),
rest: null,
version: pkg.version,
}
sharedState.rest = createHandlers()
if (globalThis.isServerMode) {
}
if (privateKey && accessKey && globalThis.isServerMode) {
Storage.engine.set("token", `${accessKey}:${privateKey}`)
}
// create instances for every remote
for (const [key, remote] of Object.entries(remotes)) {
sharedState.instances[key] = axios.create({
baseURL: remote.origin,
headers: {
"Content-Type": "application/json",
sharedState.baseRequest = axios.create({
baseURL: remote.origin,
headers: {
"Content-Type": "application/json",
}
})
// create a interceptor to attach the token every request
sharedState.baseRequest.interceptors.request.use((config) => {
// check if current request has no Authorization header, if so, attach the token
if (!config.headers["Authorization"]) {
const sessionToken = Storage.engine.get("token")
if (sessionToken) {
config.headers["Authorization"] = `${globalThis.isServerMode ? "Server" : "Bearer"} ${sessionToken}`
} else {
console.warn("Making a request with no session token")
}
})
}
// create a interceptor to attach the token every request
sharedState.instances[key].interceptors.request.use((config) => {
// check if current request has no Authorization header, if so, attach the token
if (!config.headers["Authorization"]) {
const sessionToken = SessionModel.token
if (sessionToken) {
config.headers["Authorization"] = `${globalThis.isServerMode ? "Server" : "Bearer"} ${sessionToken}`
} else {
console.warn("Making a request with no session token")
}
}
return config
})
}
return config
})
if (enableWs) {
createWebsockets()
}
return sharedState
}
}
export default createClient

View File

@ -1,4 +1,4 @@
import request from "../../handlers/request"
import request from "../../request"
import SessionModel from "../session"
export default class AuthModel {
@ -87,4 +87,38 @@ export default class AuthModel {
return response.data
}
static availability = async (payload) => {
const { username, email } = payload
const response = await request({
method: "get",
url: `/availability`,
data: {
username,
email,
}
}).catch((error) => {
console.error(error)
return false
})
return response.data
}
static changePassword = async (payload) => {
const { currentPassword, newPassword } = payload
const { data } = await request({
method: "put",
url: "/auth/password",
data: {
old_password: currentPassword,
new_password: newPassword,
}
})
return data
}
}

View File

@ -1,8 +1,16 @@
import request from "../../handlers/request"
import request from "../../request"
import Settings from "../../helpers/withSettings"
export default class FeedModel {
static getMusicFeed = async ({ trim, limit } = {}) => {
/**
* Retrieves music feed with optional trimming and limiting.
*
* @param {Object} options - Optional parameters for trimming and limiting the feed
* @param {number} options.trim - The number of items to trim from the feed
* @param {number} options.limit - The maximum number of items to fetch from the feed
* @return {Promise<Object>} The music feed data
*/
static async getMusicFeed({ trim, limit } = {}) {
const { data } = await request({
method: "GET",
url: `/feed/music`,
@ -15,10 +23,18 @@ export default class FeedModel {
return data
}
static getGlobalMusicFeed = async ({ trim, limit } = {}) => {
/**
* Retrieves the global music feed with optional trimming and limiting.
*
* @param {Object} options - An object containing optional parameters:
* @param {number} options.trim - The number of items to trim from the feed
* @param {number} options.limit - The maximum number of items to fetch from the feed
* @return {Promise<Object>} The global music feed data
*/
static async getGlobalMusicFeed({ trim, limit } = {}) {
const { data } = await request({
method: "GET",
url: `/feed/music/global`,
url: `/music/feed/global`,
params: {
trim: trim ?? 0,
limit: limit ?? Settings.get("feed_max_fetch"),
@ -28,10 +44,18 @@ export default class FeedModel {
return data
}
static getTimelineFeed = async ({ trim, limit = 10 } = {}) => {
/**
* Retrieves the timeline feed with optional trimming and limiting.
*
* @param {object} options - Object containing trim and limit properties
* @param {number} options.trim - The number of feed items to trim
* @param {number} options.limit - The maximum number of feed items to retrieve
* @return {Promise<object>} The timeline feed data
*/
static async getTimelineFeed({ trim, limit } = {}) {
const { data } = await request({
method: "GET",
url: `/feed/timeline`,
url: `/posts/feed/timeline`,
params: {
trim: trim ?? 0,
limit: limit ?? Settings.get("feed_max_fetch"),
@ -41,10 +65,18 @@ export default class FeedModel {
return data
}
static getPostsFeed = async ({ trim, limit } = {}) => {
/**
* Retrieves the posts feed with options to trim and limit the results.
*
* @param {Object} options - An object containing optional parameters for trimming and limiting the feed.
* @param {number} options.trim - The number of characters to trim the feed content.
* @param {number} options.limit - The maximum number of posts to fetch from the feed.
* @return {Promise<Object>} The posts feed data.
*/
static async getGlobalTimelineFeed({ trim, limit } = {}) {
const { data } = await request({
method: "GET",
url: `/feed/posts`,
url: `/posts/feed/global`,
params: {
trim: trim ?? 0,
limit: limit ?? Settings.get("feed_max_fetch"),

View File

@ -1,5 +1,5 @@
import { SessionModel } from "../../models"
import request from "../../handlers/request"
import request from "../../request"
export default class FollowsModel {
static imFollowing = async (user_id) => {

View File

@ -1,4 +1,4 @@
import request from "../../handlers/request"
import request from "../../request"
export default class Livestream {
static deleteProfile = async (profile_id) => {

View File

@ -1,4 +1,4 @@
import request from "../../handlers/request"
import request from "../../request"
import pmap from "p-map"
import SyncModel from "../sync"

View File

@ -1,4 +1,4 @@
import request from "../../handlers/request"
import request from "../../request"
export default class NFCModel {
static async getOwnTags() {

View File

@ -1,4 +1,4 @@
import request from "../../handlers/request"
import request from "../../request"
import Settings from "../../helpers/withSettings"
export default class Post {
@ -26,58 +26,20 @@ export default class Post {
const { data } = await request({
method: "GET",
url: `/posts/post/${post_id}`,
url: `/posts/${post_id}/data`,
})
return data
}
static getPostComments = async ({ post_id }) => {
static async replies({ post_id, trim, limit }) {
if (!post_id) {
throw new Error("Post ID is required")
}
const { data } = await request({
method: "GET",
url: `/comments/post/${post_id}`,
})
return data
}
static sendComment = async ({ post_id, comment }) => {
if (!post_id || !comment) {
throw new Error("Post ID and/or comment are required")
}
const { data } = await request({
method: "POST",
url: `/comments/post/${post_id}`,
data: {
message: comment,
},
})
return data
}
static deleteComment = async ({ post_id, comment_id }) => {
if (!post_id || !comment_id) {
throw new Error("Post ID and/or comment ID are required")
}
const { data } = await request({
method: "DELETE",
url: `/comments/post/${post_id}/${comment_id}`,
})
return data
}
static getExplorePosts = async ({ trim, limit }) => {
const { data } = await request({
method: "GET",
url: `/posts/explore`,
url: `/posts/${post_id}/replies`,
params: {
trim: trim ?? 0,
limit: limit ?? Settings.get("feed_max_fetch"),
@ -100,6 +62,19 @@ export default class Post {
return data
}
static getLikedPosts = async ({ trim, limit }) => {
const { data } = await request({
method: "GET",
url: `/posts/liked`,
params: {
trim: trim ?? 0,
limit: limit ?? Settings.get("feed_max_fetch"),
}
})
return data
}
static getUserPosts = async ({ user_id, trim, limit }) => {
if (!user_id) {
// use current user_id
@ -154,6 +129,20 @@ export default class Post {
return data
}
static update = async (post_id, update) => {
if (!post_id) {
throw new Error("Post ID is required")
}
const { data } = await request({
method: "PUT",
url: `/posts/${post_id}/update`,
data: update,
})
return data
}
static deletePost = async ({ post_id }) => {
if (!post_id) {
throw new Error("Post ID is required")

View File

@ -1,4 +1,4 @@
import request from "../../handlers/request"
import request from "../../request"
export default class Search {
static search = async (keywords, params = {}) => {

View File

@ -1,5 +1,5 @@
import jwt_decode from "jwt-decode"
import request from "../../handlers/request"
import request from "../../request"
import Storage from "../../helpers/withStorage"
export default class Session {

View File

@ -1,7 +1,7 @@
import spotifyService from "./services/spotify"
import tidalService from "./services/tidal"
import request from "../../handlers/request"
import request from "../../request"
const namespacesServices = {
spotify: spotifyService,

View File

@ -1,4 +1,4 @@
import request from "../../../handlers/request"
import request from "../../../request"
export default class TidalService {
static get api_instance() {

View File

@ -1,5 +1,5 @@
import SessionModel from "../session"
import request from "../../handlers/request"
import request from "../../request"
export default class User {
static data = async (payload = {}) => {
@ -33,7 +33,7 @@ export default class User {
static updateData = async (payload) => {
const response = await request({
method: "POST",
url: "/users/self/update_data",
url: "/users/self/update",
data: {
update: payload,
},
@ -43,12 +43,9 @@ export default class User {
}
static unsetFullName = async () => {
const response = await request({
method: "DELETE",
url: "/users/self/public_name",
return await User.updateData({
full_name: null,
})
return response.data
}
static selfRoles = async () => {
@ -87,21 +84,6 @@ export default class User {
return data
}
static changePassword = async (payload) => {
const { currentPassword, newPassword } = payload
const { data } = await request({
method: "POST",
url: "/user/self/update_password",
data: {
currentPassword,
newPassword,
}
})
return data
}
static getUserFollowers = async ({
user_id,
limit = 20,
@ -156,4 +138,38 @@ export default class User {
return data
}
/**
* Retrive user config from server
*
* @param {type} key - A key of config
* @return {object} - Config object
*/
static async getConfig(key) {
const { data } = await request({
method: "GET",
url: "/users/self/config",
params: {
key
}
})
return data
}
/**
* Update the configuration with the given update.
*
* @param {Object} update - The object containing the updated configuration data
* @return {Promise} A Promise that resolves with the response data after the configuration is updated
*/
static async updateConfig(update) {
const { data } = await request({
method: "PUT",
url: "/users/self/config",
data: update
})
return data
}
}

View File

@ -1,4 +1,4 @@
import request from "../../handlers/request"
import request from "../../request"
export default class WidgetModel {
static browse = async ({ limit, offset, keywords } = {}) => {

View File

@ -13,8 +13,19 @@ const envOrigins = {
}
export default {
default: {
origin: envOrigins[process.env.NODE_ENV ?? "production"],
hasWebsocket: false,
}
origin: envOrigins[process.env.NODE_ENV ?? "production"],
websockets: [
{
namespace: "posts",
path: "/posts",
},
{
namespace: "main",
path: "/main",
},
{
namespace: "notifications",
path: "/notifications",
}
]
}

View File

@ -1,5 +1,5 @@
import handleBeforeRequest from "../helpers/handleBeforeRequest"
import handleAfterRequest from "../helpers/handleAfterRequest"
import handleBeforeRequest from "./helpers/handleBeforeRequest"
import handleAfterRequest from "./helpers/handleAfterRequest"
export default async (
request = {
@ -7,7 +7,7 @@ export default async (
},
...args
) => {
const instance = request.instance ?? __comty_shared_state.instances.default
const instance = request.instance ?? __comty_shared_state.baseRequest
if (!instance) {
throw new Error("No instance provided")

12
tests/main.js Normal file
View File

@ -0,0 +1,12 @@
const test = require("ava")
const lib = require("../dist/index")
test("create client", async (t) => {
console.log(lib)
const client = await lib.createClient()
console.log(client)
t.pass()
})