added SyncController

This commit is contained in:
SrGooglo 2023-01-23 23:27:52 +00:00
parent b87f4276b1
commit ca8fcc9353
5 changed files with 268 additions and 2 deletions

View File

@ -1,5 +1,6 @@
import path from "path" import path from "path"
import { Server as LinebridgeServer } from "linebridge/dist/server" import { Server as LinebridgeServer } from "linebridge/dist/server"
import express from "express" import express from "express"
import bcrypt from "bcrypt" import bcrypt from "bcrypt"
import passport from "passport" import passport from "passport"
@ -45,11 +46,12 @@ export default class Server {
controllers.FeaturedEventsController, controllers.FeaturedEventsController,
controllers.PlaylistsController, controllers.PlaylistsController,
controllers.FeedController, controllers.FeedController,
controllers.SyncController,
] ]
middlewares = middlewares middlewares = middlewares
server = new LinebridgeServer({ server = global.server = new LinebridgeServer({
port: process.env.MAIN_LISTEN_PORT || 3000, port: process.env.MAIN_LISTEN_PORT || 3000,
headers: { headers: {
"Access-Control-Expose-Headers": "regenerated_token", "Access-Control-Expose-Headers": "regenerated_token",

View File

@ -0,0 +1,114 @@
import { SyncEntry } from "../../../models"
import crypto from "crypto"
export default class SecureSyncEntry {
static get encrytionAlgorithm() {
return "aes-256-cbc"
}
static async set(user_id, key, value) {
if (!user_id) {
throw new Error("Missing user_id")
}
if (!key) {
throw new Error("Missing key")
}
if (!value) {
throw new Error("Missing value")
}
let entry = await SyncEntry.findOne({ key }).catch(() => null)
const encryptionKey = Buffer.from(process.env.SYNC_ENCRIPT_SECRET, "hex")
const iv = crypto.randomBytes(16)
const cipher = crypto.createCipheriv(SecureSyncEntry.encrytionAlgorithm, encryptionKey, iv)
let encrypted
try {
encrypted = cipher.update(value)
}
catch (error) {
console.error(error)
}
encrypted = Buffer.concat([encrypted, cipher.final()])
if (entry) {
entry.value = iv.toString("hex") + ":" + encrypted.toString("hex")
await entry.save()
return entry
}
entry = new SyncEntry({
user_id,
key,
value: iv.toString("hex") + ":" + encrypted.toString("hex"),
})
await entry.save()
return entry
}
static async get(user_id, key) {
if (!user_id) {
throw new Error("Missing user_id")
}
if (!key) {
throw new Error("Missing key")
}
const entry = await SyncEntry.findOne({
user_id,
key,
}).catch(() => null)
if (!entry) {
return null
}
const encryptionKey = Buffer.from(process.env.SYNC_ENCRIPT_SECRET, "hex")
const iv = Buffer.from(entry.value.split(":")[0], "hex")
const encryptedText = Buffer.from(entry.value.split(":")[1], "hex")
const decipher = crypto.createDecipheriv(SecureSyncEntry.encrytionAlgorithm, encryptionKey, iv)
let decrypted = decipher.update(encryptedText)
decrypted = Buffer.concat([decrypted, decipher.final()])
return decrypted.toString()
}
static async delete(user_id, key) {
if (!user_id) {
throw new Error("Missing user_id")
}
if (!key) {
throw new Error("Missing key")
}
const entry = await SyncEntry.findOne({
user_id,
key,
}).catch(() => null)
if (!entry) {
return null
}
await entry.delete()
return entry
}
}

View File

@ -0,0 +1,149 @@
import { Controller } from "linebridge/dist/server"
import SecureSyncEntry from "./classes/secureSyncEntry"
import axios from "axios"
export default class SyncController extends Controller {
static useRoute = "/sync"
post = {
"/spotify/auth": {
middlewares: ["withAuthentication"],
fn: async (req, res) => {
const { code, redirect_uri } = req.body
if (!code) {
return res.status(400).json({
message: "Missing code",
})
}
if (!redirect_uri) {
return res.status(400).json({
message: "Missing redirect_uri",
})
}
const response = await axios({
url: "https://accounts.spotify.com/api/token",
method: "post",
params: {
grant_type: "authorization_code",
code: code,
redirect_uri: redirect_uri
},
headers: {
'Authorization': `Basic ${(Buffer.from(process.env.SPOTIFY_CLIENT_ID + ":" + process.env.SPOTIFY_CLIENT_SECRET).toString("base64"))}`,
'Content-Type': 'application/x-www-form-urlencoded'
}
})
if (!response) {
return res.status(400).json({
message: "Missing data",
})
}
await SecureSyncEntry.set(req.user._id.toString(), "spotify_access_token", response.data.access_token)
await SecureSyncEntry.set(req.user._id.toString(), "spotify_refresh_token", response.data.refresh_token)
return res.json({
message: "ok"
})
}
},
"/spotify/unlink": {
middlewares: ["withAuthentication"],
fn: async (req, res) => {
await SecureSyncEntry.delete(req.user._id.toString(), "spotify_access_token", "")
await SecureSyncEntry.delete(req.user._id.toString(), "spotify_refresh_token", "")
return res.json({
message: "ok"
})
}
}
}
get = {
"/spotify/client_id": async (req, res) => {
return res.json({
client_id: process.env.SPOTIFY_CLIENT_ID,
})
},
"/spotify/is_authorized": {
middlewares: ["withAuthentication"],
fn: async (req, res) => {
const user_id = req.user._id.toString()
const authToken = await SecureSyncEntry.get(user_id, "spotify_access_token")
if (!authToken) {
return res.json({
is_authorized: false,
})
}
return res.json({
is_authorized: true,
})
}
},
"/spotify/data": {
middlewares: ["withAuthentication"],
fn: async (req, res) => {
const user_id = req.user._id.toString()
const authToken = await SecureSyncEntry.get(user_id, "spotify_access_token")
if (!authToken) {
return res.status(400).json({
message: "Missing auth token",
})
}
const { data } = await axios.get("https://api.spotify.com/v1/me", {
headers: {
"Authorization": `Bearer ${authToken}`
},
}).catch((error) => {
console.error(error.response.data)
res.status(error.response.status).json(error.response.data)
return null
})
return res.json(data)
}
},
"/spotify/currently_playing": {
middlewares: ["withAuthentication"],
fn: async (req, res) => {
const user_id = req.user._id.toString()
const authToken = await SecureSyncEntry.get(user_id, "spotify_access_token")
if (!authToken) {
return res.status(400).json({
message: "Missing auth token",
})
}
const response = await axios.get("https://api.spotify.com/v1/me/player", {
headers: {
"Authorization": `Bearer ${authToken}`
},
}).catch((error) => {
console.error(error.response.data)
res.status(error.response.status).json(error.response.data)
return null
})
if (response) {
return res.json(response.data)
}
}
}
}
}

View File

@ -12,4 +12,5 @@ export { default as CommentsController } from "./CommentsController"
export { default as SearchController } from "./SearchController" export { default as SearchController } from "./SearchController"
export { default as FeaturedEventsController } from "./FeaturedEventsController" export { default as FeaturedEventsController } from "./FeaturedEventsController"
export { default as PlaylistsController } from "./PlaylistsController" export { default as PlaylistsController } from "./PlaylistsController"
export { default as FeedController } from "./FeedController" export { default as FeedController } from "./FeedController"
export { default as SyncController } from "./SyncController"