add backend files
parent
0767ee6530
commit
65b3db1fcc
@ -0,0 +1 @@
|
|||||||
|
./backend/node_modules
|
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "frontend"]
|
||||||
|
path = frontend
|
||||||
|
url = https://git.cybre.town/adb/spot2gether-frontend
|
@ -0,0 +1,3 @@
|
|||||||
|
APP_SPOTIFY_CLIENT_ID=
|
||||||
|
APP_SPOTIFY_CLIENT_SECRET=
|
||||||
|
APP_SPOTIFY_REDIRECT_URI="http://127.0.0.1:8083/auth/callback"
|
@ -0,0 +1,50 @@
|
|||||||
|
import { Client, Player } from "spotify-api.js";
|
||||||
|
import { store } from "../store.mjs";
|
||||||
|
import { randomString } from "../lib/randomString.js";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export const applyAuthRoutes = (router) => {
|
||||||
|
|
||||||
|
router.post('/', async (req, res) => {
|
||||||
|
if (!req.body.code || !req.body.state) {
|
||||||
|
res.status(400);
|
||||||
|
res.send({ message: 'code or state missing ' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { code, state } = req.body;
|
||||||
|
try {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append('grant_type', 'authorization_code');
|
||||||
|
params.append('code', code);
|
||||||
|
params.append('redirect_uri', store.redirectURL);
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Basic ' + (new Buffer(store.clientID + ':' + store.clientSecret).toString('base64')),
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const tokens = (await axios.post('https://accounts.spotify.com/api/token', params, config))?.data;
|
||||||
|
|
||||||
|
const client = await Client.create({
|
||||||
|
token: {
|
||||||
|
clientID: store.clientID,
|
||||||
|
clientSecret: store.clientSecret,
|
||||||
|
redirectURL: store.redirectURL,
|
||||||
|
refreshToken: tokens.refresh_token,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const player = new Player(client);
|
||||||
|
const accessToken = randomString(64);
|
||||||
|
store.users.push({ client, player, accessToken, listeners: [], role: 'none' });
|
||||||
|
res.status(200);
|
||||||
|
res.send({ message: 'authorized', accessToken });
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e.message);
|
||||||
|
res.status(500);
|
||||||
|
res.send({ message: 'unauthorized' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
@ -0,0 +1,18 @@
|
|||||||
|
import { applyUserRoutes, applyUserRoutesPublic } from "./user.mjs";
|
||||||
|
|
||||||
|
export const applyApiRoutes = (router) => {
|
||||||
|
|
||||||
|
router.get('/test', (req, res) => {
|
||||||
|
res.status(200);
|
||||||
|
res.send({ message: 'connection is working' });
|
||||||
|
});
|
||||||
|
|
||||||
|
applyUserRoutes(router);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export const applyPublicRoutes = (router) => {
|
||||||
|
|
||||||
|
applyUserRoutesPublic(router);
|
||||||
|
|
||||||
|
};
|
@ -0,0 +1,110 @@
|
|||||||
|
import { store } from "../store.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) => {
|
||||||
|
const currentlyPlaying = await res.locals.user.player?.getCurrentlyPlaying('track');
|
||||||
|
res.status(200);
|
||||||
|
res.send({ currentlyPlaying });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/user/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) => {
|
||||||
|
|
||||||
|
router.get('/users/:userId/info', async (req, res) => {
|
||||||
|
if (!req.params.userId) {
|
||||||
|
res.status(400);
|
||||||
|
res.send({ message: 'userId is missing' });
|
||||||
|
}
|
||||||
|
const { userId } = req.params;
|
||||||
|
const host = store.users.find(({ client }) => client.user.id === userId);
|
||||||
|
if (!host?.player) {
|
||||||
|
res.status(400);
|
||||||
|
res.send({ message: 'user is not registered' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const currentlyPlaying = await host?.player?.getCurrentlyPlaying('track');
|
||||||
|
res.status(200);
|
||||||
|
res.send({
|
||||||
|
currentlyPlaying,
|
||||||
|
user: {
|
||||||
|
displayName: host.client.user.displayName,
|
||||||
|
totalFollowers: host.client.user.totalFollowers,
|
||||||
|
images: host.client.user.images,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
res.status(500);
|
||||||
|
res.send({ message: 'server error' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
@ -0,0 +1,32 @@
|
|||||||
|
import express from "express";
|
||||||
|
import { auth } from "./middlewares/auth.mjs";
|
||||||
|
|
||||||
|
import { applyAuthRoutes } from "./api/auth.mjs";
|
||||||
|
import { applyApiRoutes, applyPublicRoutes } from "./api/index.mjs";
|
||||||
|
|
||||||
|
// express server
|
||||||
|
const port = 3000;
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
const authRouter = express.Router();
|
||||||
|
const publicRouter = express.Router();
|
||||||
|
|
||||||
|
router.use(express.json());
|
||||||
|
authRouter.use(express.json());
|
||||||
|
publicRouter.use(express.json());
|
||||||
|
|
||||||
|
app
|
||||||
|
.use('/api/v1', router)
|
||||||
|
.use('/api/auth', authRouter)
|
||||||
|
.use('/api/public', publicRouter);
|
||||||
|
|
||||||
|
applyAuthRoutes(authRouter);
|
||||||
|
|
||||||
|
router.use(auth);
|
||||||
|
applyApiRoutes(router);
|
||||||
|
|
||||||
|
applyPublicRoutes(publicRouter);
|
||||||
|
|
||||||
|
app.listen(port);
|
||||||
|
console.log('ready');
|
@ -0,0 +1,7 @@
|
|||||||
|
export const randomString = (length) => {
|
||||||
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
let result = '';
|
||||||
|
for ( let i = 0; i < length; i++ )
|
||||||
|
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
||||||
|
return result;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "spot2gether",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "Alban David Becker",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.27.2",
|
||||||
|
"express": "^4.18.1",
|
||||||
|
"spotify-api.js": "^9.2.3"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
export const store = {
|
||||||
|
users: [],
|
||||||
|
clientID: '02d74b98c7904d6498b0b79def49cc5c',
|
||||||
|
clientSecret: 'e43b819037eb48e6b190865fc2c47b21',
|
||||||
|
redirectURL: 'http://127.0.0.1:8083/auth/callback',
|
||||||
|
};
|
@ -1,47 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
redis:
|
|
||||||
image: redis:alpine
|
|
||||||
expose:
|
|
||||||
- 6379
|
|
||||||
restart: always
|
|
||||||
environment:
|
|
||||||
- REDIS_REPLICATION_MODE=master
|
|
||||||
|
|
||||||
backend:
|
|
||||||
image: node:alpine
|
|
||||||
restart: always
|
|
||||||
expose:
|
|
||||||
- 3000
|
|
||||||
volumes:
|
|
||||||
- ./backend/:/home/node/app/backend/
|
|
||||||
- ./helpers/:/home/node/app/helpers/
|
|
||||||
working_dir: /home/node/app/backend
|
|
||||||
environment:
|
|
||||||
- NODE_ENV=production
|
|
||||||
command: sh -c 'npm i && nodejs index.mjs'
|
|
||||||
links:
|
|
||||||
- redis
|
|
||||||
depends_on:
|
|
||||||
- redis
|
|
||||||
|
|
||||||
frontend:
|
|
||||||
image: node:alpine
|
|
||||||
volumes:
|
|
||||||
- ./frontend/:/home/node/app/frontend/
|
|
||||||
working_dir: /home/node/app/frontend
|
|
||||||
command: sh -c 'npm i --also=dev && npm run build'
|
|
||||||
|
|
||||||
nginx:
|
|
||||||
image: nginx:alpine
|
|
||||||
restart: always
|
|
||||||
volumes:
|
|
||||||
- ./nginx.conf:/etc/nginx/nginx.conf
|
|
||||||
- ./frontend/dist/:/var/www/html/
|
|
||||||
ports:
|
|
||||||
- "8083:8080"
|
|
||||||
links:
|
|
||||||
- backend
|
|
||||||
depends_on:
|
|
||||||
- backend
|
|
Loading…
Reference in New Issue