implement sessions; retry to create spotify client

master
adb-sh 2 years ago
parent 11778e2629
commit 46dc3dc83f

@ -1,6 +1,6 @@
import { store } from "../store.mjs";
import { randomString } from "../lib/randomString.mjs";
import { UserStore } from "../db/schemas.mjs";
import { SessionStore, UserStore } from "../db/schemas.mjs";
import { createLocalUser, findUserBySpotifyId } from "../lib/helpers.mjs";
export const applyAuthRoutes = (router) => {
@ -29,7 +29,6 @@ export const applyAuthRoutes = (router) => {
{ 'spotify.userId': newUser.client.user.id },
{
accessToken,
role: 'none',
spotify: {
refreshToken: newUser.client.refreshMeta.refreshToken,
userId: newUser.client.user.id,
@ -39,6 +38,13 @@ export const applyAuthRoutes = (router) => {
{ upsert: true },
);
if (!await SessionStore.findOne().bySpotifyId(newUser.client.user.id))
await new SessionStore({
host: userStore,
clients: [],
queue: [],
}).save();
res.status(200);
res.send({ message: 'authorized', accessToken });
} catch (e) {

@ -1,4 +1,5 @@
import { applyUserRoutes, applyUserRoutesPublic } from "./user.mjs";
import { applySessionRoutes } from "./session.mjs";
export const applyApiRoutes = (router) => {
@ -8,6 +9,7 @@ export const applyApiRoutes = (router) => {
});
applyUserRoutes(router);
applySessionRoutes(router);
};

@ -0,0 +1,95 @@
import { SessionStore } from "../db/schemas.mjs";
export const applySessionRoutes = (router) => {
router.post('/session', async (req, res) => {
const user = res.locals.user;
if (await SessionStore.findOne().bySpotifyId(user.id)) {
res.status(400);
res.send({ message: 'you are already in a session' });
return;
}
const sessionStore = new SessionStore({
host: user,
clients: [],
});
await sessionStore.save();
res.status(201);
res.send({ message: 'created' });
});
router.get('/session', async (req, res) => {
const user = res.locals.user;
const sessionStore = await SessionStore.findOne().byHostSpotifyId(user.id);
if (!sessionStore) {
res.status(404);
res.send('you are not hosting a session');
return;
}
res.status(200);
res.send({ session: sessionStore });
});
router.delete('/session', async (req, res) => {
const user = res.locals.user;
const sessionStore = await SessionStore.findOne().byHostSpotifyId(user.id);
if (!sessionStore) {
res.status(404);
res.send('you are not hosting a session');
return;
}
await sessionStore.delete();
res.status(204);
res.send({ message: 'session deleted' });
});
router.post('/session/join', async (req, res) => {
if (!req.body?.hostId) {
res.status(400);
res.send({ message: 'hostId is undefined' });
return;
}
const { hostId } = req.body.hostId;
const user = await res.locals.user;
if (await SessionStore.findOne().bySpotifyId(user.id)) {
res.status(400);
res.send({ message: 'you are already in a session' });
return;
}
const sessionStore = SessionStore.findOne().byHostSpotifyId(hostId);
if (!sessionStore) {
res.status(400);
res.send({ message: 'session does not exist' });
return;
}
sessionStore.clients.push(user);
await sessionStore.save();
res.status(200);
res.send({ message: 'you joined' });
});
router.post('/session/leave', async (req, res) => {
const user = await res.locals.user;
const sessionStore = SessionStore.findOne().byClientSpotifyId(user.id);
if (!sessionStore) {
res.status(400);
res.send({ message: 'you are not a client of any session' });
return;
}
sessionStore.clients = sessionStore.clients.filter(client => client.id !== user.id);
await sessionStore.save();
res.status(200);
res.send({ message: 'you left' });
});
};

@ -1,79 +1,21 @@
import { store } from "../store.mjs";
import { UserStore } from "../db/schemas.mjs";
import { SessionStore, UserStore } from "../db/schemas.mjs";
export const applyUserRoutes = (router) => {
applyUserRoutesPublic(router);
router.post('/user/joinSession', async (req, res) => {
if (!req.body?.userId) {
res.status(400);
res.send({ message: 'userId is undefined' });
return;
}
const { userId } = req.body.userId;
if (res.locals.user.role === 'host') {
res.status(400);
res.send({ message: 'user is host' });
return;
}
const host = store.users.find(({ client }) => client.user.id === userId)
host.listeners.push(res.locals.user);
res.locals.user.role = 'listener';
res.locals.user.listeningTo = host;
res.status(200);
res.send({ message: 'joined' });
});
router.delete('/user/leaveSession', async (req, res) => {
if (res.locals.user.role === 'host') {
res.status(400);
res.send({ message: 'user is host' });
return;
}
const host = store.users.find(({ client }) => client.user.id === userId)
host.listeners.push(res.locals.user);
res.locals.user.role = 'none';
res.locals.user.listeningTo.listeners = res.locals.user.listeningTo.listeners.filter(
({ client }) => client.user.id !== res.locals.user.client.user.id
);
res.locals.user.listeningTo = null;
res.status(200);
res.send({ message: 'left' });
});
router.get('/user/currentlyPlaying', async (req, res) => {
router.get('/me/currentlyPlaying', async (req, res) => {
const currentlyPlaying = await (await res.locals.user.spotify.local)?.player?.getCurrentlyPlaying('track');
res.status(200);
res.send({ currentlyPlaying });
});
router.get('/user/role', (req, res) => {
router.get('/me/role', (req, res) => {
res.status(200);
res.send({ role: res.locals.user?.role });
});
/*router.post('/user/role', async (req, res) => {
if (
req.body.role !== 'host' ||
req.body.role !== 'listener' ||
req.body.role !== 'none'
) {
res.status(400);
res.send({ message: 'role value is invalid' });
}
const { role } = req.body;
try {
res.locals.user.listeners = [];
res.locals.user.role = role;
res.status(200);
res.send({ role });
} catch (e) {
res.status(500);
res.send({ message: 'server error' });
}
});*/
};
export const applyUserRoutesPublic = (router) => {

@ -30,4 +30,26 @@ const userSchema = new Schema({
},
});
const sessionSchema = new Schema({
host: userSchema,
clients: Array,
queue: Array,
}, {
query: {
byHostSpotifyId(id) {
return this.where({ 'host.spotify.userId': id });
},
byClientSpotifyId(id) {
return this.where({ 'clients.userId': id });
},
bySpotifyId(id) {
return this.where({ '$or': [
{ 'host.spotify.userId': id },
{ 'clients.spotify.userId': id },
]});
},
},
});
export const UserStore = model('User', userSchema);
export const SessionStore = model('Session', sessionSchema);

@ -2,26 +2,33 @@ import { Client, Player } from "spotify-api.js";
import { store } from "../store.mjs";
import { UserStore } from "../db/schemas.mjs";
export const createLocalUser = async ({ refreshToken = undefined, code = undefined }) => {
const client = await Client.create({
refreshToken: true,
retryOnRateLimit: true,
token: {
clientID: store.clientID,
clientSecret: store.clientSecret,
redirectURL: store.redirectURL,
refreshToken,
code,
},
async onRefresh() {
await UserStore.findOneAndUpdate(
{ 'spotify.userId': client.user.id },
{ 'spotify.refreshToken': client.refreshMeta.refreshToken },
);
},
});
const player = new Player(client);
return { client, player };
export const createLocalUser = async ({ refreshToken = undefined, code = undefined }, retry = 4) => {
try {
const client = await Client.create({
refreshToken: true,
retryOnRateLimit: true,
token: {
clientID: store.clientID,
clientSecret: store.clientSecret,
redirectURL: store.redirectURL,
refreshToken,
code,
},
async onRefresh() {
await UserStore.findOneAndUpdate(
{ 'spotify.userId': client.user.id },
{ 'spotify.refreshToken': client.refreshMeta.refreshToken },
);
},
});
const player = new Player(client);
return { client, player };
} catch (e) {
if (retry-- < 1) throw e;
if (e.response.data.status === 503) await new Promise(_ => setTimeout(_, 500));
else throw e;
return await createLocalUser({ refreshToken, code }, retry);
}
};
export const findUserBySpotifyId = async (

@ -1 +1 @@
Subproject commit f3b95916688df6b4a9834bb5d4adfb9ceec6093d
Subproject commit cf9626402434ff2e2449457c40182bab92a8ad55
Loading…
Cancel
Save