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