implement sessions; retry to create spotify client
This commit is contained in:
parent
11778e2629
commit
46dc3dc83f
@ -1,6 +1,6 @@
|
|||||||
import { store } from "../store.mjs";
|
import { store } from "../store.mjs";
|
||||||
import { randomString } from "../lib/randomString.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";
|
import { createLocalUser, findUserBySpotifyId } from "../lib/helpers.mjs";
|
||||||
|
|
||||||
export const applyAuthRoutes = (router) => {
|
export const applyAuthRoutes = (router) => {
|
||||||
@ -29,7 +29,6 @@ export const applyAuthRoutes = (router) => {
|
|||||||
{ 'spotify.userId': newUser.client.user.id },
|
{ 'spotify.userId': newUser.client.user.id },
|
||||||
{
|
{
|
||||||
accessToken,
|
accessToken,
|
||||||
role: 'none',
|
|
||||||
spotify: {
|
spotify: {
|
||||||
refreshToken: newUser.client.refreshMeta.refreshToken,
|
refreshToken: newUser.client.refreshMeta.refreshToken,
|
||||||
userId: newUser.client.user.id,
|
userId: newUser.client.user.id,
|
||||||
@ -39,6 +38,13 @@ export const applyAuthRoutes = (router) => {
|
|||||||
{ upsert: true },
|
{ upsert: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!await SessionStore.findOne().bySpotifyId(newUser.client.user.id))
|
||||||
|
await new SessionStore({
|
||||||
|
host: userStore,
|
||||||
|
clients: [],
|
||||||
|
queue: [],
|
||||||
|
}).save();
|
||||||
|
|
||||||
res.status(200);
|
res.status(200);
|
||||||
res.send({ message: 'authorized', accessToken });
|
res.send({ message: 'authorized', accessToken });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { applyUserRoutes, applyUserRoutesPublic } from "./user.mjs";
|
import { applyUserRoutes, applyUserRoutesPublic } from "./user.mjs";
|
||||||
|
import { applySessionRoutes } from "./session.mjs";
|
||||||
|
|
||||||
export const applyApiRoutes = (router) => {
|
export const applyApiRoutes = (router) => {
|
||||||
|
|
||||||
@ -8,6 +9,7 @@ export const applyApiRoutes = (router) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
applyUserRoutes(router);
|
applyUserRoutes(router);
|
||||||
|
applySessionRoutes(router);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
95
backend/api/session.mjs
Normal file
95
backend/api/session.mjs
Normal file
@ -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 { store } from "../store.mjs";
|
||||||
import { UserStore } from "../db/schemas.mjs";
|
import { SessionStore, UserStore } from "../db/schemas.mjs";
|
||||||
|
|
||||||
export const applyUserRoutes = (router) => {
|
export const applyUserRoutes = (router) => {
|
||||||
|
|
||||||
applyUserRoutesPublic(router);
|
applyUserRoutesPublic(router);
|
||||||
|
|
||||||
router.post('/user/joinSession', async (req, res) => {
|
router.get('/me/currentlyPlaying', 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) => {
|
|
||||||
const currentlyPlaying = await (await res.locals.user.spotify.local)?.player?.getCurrentlyPlaying('track');
|
const currentlyPlaying = await (await res.locals.user.spotify.local)?.player?.getCurrentlyPlaying('track');
|
||||||
res.status(200);
|
res.status(200);
|
||||||
res.send({ currentlyPlaying });
|
res.send({ currentlyPlaying });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/user/role', (req, res) => {
|
router.get('/me/role', (req, res) => {
|
||||||
res.status(200);
|
res.status(200);
|
||||||
res.send({ role: res.locals.user?.role });
|
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) => {
|
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 UserStore = model('User', userSchema);
|
||||||
|
export const SessionStore = model('Session', sessionSchema);
|
||||||
|
@ -2,7 +2,8 @@ import { Client, Player } from "spotify-api.js";
|
|||||||
import { store } from "../store.mjs";
|
import { store } from "../store.mjs";
|
||||||
import { UserStore } from "../db/schemas.mjs";
|
import { UserStore } from "../db/schemas.mjs";
|
||||||
|
|
||||||
export const createLocalUser = async ({ refreshToken = undefined, code = undefined }) => {
|
export const createLocalUser = async ({ refreshToken = undefined, code = undefined }, retry = 4) => {
|
||||||
|
try {
|
||||||
const client = await Client.create({
|
const client = await Client.create({
|
||||||
refreshToken: true,
|
refreshToken: true,
|
||||||
retryOnRateLimit: true,
|
retryOnRateLimit: true,
|
||||||
@ -22,6 +23,12 @@ export const createLocalUser = async ({ refreshToken = undefined, code = undefin
|
|||||||
});
|
});
|
||||||
const player = new Player(client);
|
const player = new Player(client);
|
||||||
return { client, player };
|
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 (
|
export const findUserBySpotifyId = async (
|
||||||
|
2
frontend
2
frontend
@ -1 +1 @@
|
|||||||
Subproject commit f3b95916688df6b4a9834bb5d4adfb9ceec6093d
|
Subproject commit cf9626402434ff2e2449457c40182bab92a8ad55
|
Loading…
Reference in New Issue
Block a user