Made Spotify API request correctly depend on the integration settings in the DB.

pull/34/head
Sander Vocke 5 years ago
parent e9d6d4055c
commit b4e4ac0162
  1. 6
      client/src/components/windows/settings/IntegrationSettingsEditor.tsx
  2. 4
      client/src/lib/integration/spotify/spotifyClientCreds.tsx
  3. 4
      server/app.ts
  4. 110
      server/integrations/integrations.ts
  5. 65
      server/integrations/spotifyClientCreds.ts

@ -13,6 +13,7 @@ import { testSpotify } from '../../../lib/integration/spotify/spotifyClientCreds
let _ = require('lodash') let _ = require('lodash')
interface EditIntegrationProps { interface EditIntegrationProps {
upstreamId: number | null,
integration: serverApi.IntegrationDetailsResponse, integration: serverApi.IntegrationDetailsResponse,
original: serverApi.IntegrationDetailsResponse, original: serverApi.IntegrationDetailsResponse,
editing: boolean, editing: boolean,
@ -112,8 +113,8 @@ function EditIntegration(props: EditIntegrationProps) {
{!props.submitting && <IconButton {!props.submitting && <IconButton
onClick={() => { props.onDelete(); }} onClick={() => { props.onDelete(); }}
><DeleteIcon /></IconButton>} ><DeleteIcon /></IconButton>}
{!props.submitting && <Button {!props.submitting && !props.editing && props.upstreamId !== null && <Button
onClick={testSpotify} onClick={() => testSpotify(props.upstreamId || 0)}
>Test</Button>} >Test</Button>}
{props.submitting && <CircularProgress />} {props.submitting && <CircularProgress />}
</Box > </Box >
@ -231,6 +232,7 @@ export default function IntegrationSettingsEditor(props: {}) {
{editors === null && <CircularProgress />} {editors === null && <CircularProgress />}
{editors && <> {editors && <>
{editors.map((state: EditorState) => <EditIntegration {editors.map((state: EditorState) => <EditIntegration
upstreamId={state.upstreamId}
integration={state.integration} integration={state.integration}
original={state.original} original={state.original}
editing={state.editing} editing={state.editing}

@ -1,10 +1,10 @@
export async function testSpotify() { export async function testSpotify(integrationId: number) {
const requestOpts = { const requestOpts = {
method: 'GET', method: 'GET',
}; };
const response = await fetch( const response = await fetch(
(process.env.REACT_APP_BACKEND || "") + '/spotifycc/v1/search?q=queens&type=artist', (process.env.REACT_APP_BACKEND || "") + `/integrations/${integrationId}/v1/search?q=queens&type=artist`,
requestOpts requestOpts
); );
if (!response.ok) { if (!response.ok) {

@ -14,7 +14,7 @@ import { RegisterUser } from './endpoints/RegisterUser';
import * as endpointTypes from './endpoints/types'; import * as endpointTypes from './endpoints/types';
import { sha512 } from 'js-sha512'; import { sha512 } from 'js-sha512';
import { useSpotifyClientCreds } from './integrations/spotifyClientCreds'; import { createIntegrations } from './integrations/integrations';
// For authentication // For authentication
var passport = require('passport'); var passport = require('passport');
@ -102,7 +102,7 @@ const SetupApp = (app: any, knex: Knex, apiBaseUrl: string) => {
} }
// Set up integration proxies // Set up integration proxies
useSpotifyClientCreds(app); app.use('/integrations', checkLogin(), createIntegrations(knex));
// Set up REST API endpoints // Set up REST API endpoints
app.post(apiBaseUrl + api.CreateSongEndpoint, checkLogin(), _invoke(PostSong)); app.post(apiBaseUrl + api.CreateSongEndpoint, checkLogin(), _invoke(PostSong));

@ -0,0 +1,110 @@
import Knex from "knex";
import { IntegrationType } from "../../client/src/api";
const { createProxyMiddleware } = require('http-proxy-middleware');
let axios = require('axios')
let qs = require('querystring')
async function getSpotifyCCAuthToken(clientId: string, clientSecret: string) {
console.log("Details: ", clientId, clientSecret);
let buf = Buffer.from(clientId + ':' + clientSecret)
let encoded = buf.toString('base64');
let response = await axios.post(
'https://accounts.spotify.com/api/token',
qs.stringify({ 'grant_type': 'client_credentials' }),
{
'headers': {
'Authorization': 'Basic ' + encoded,
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);
if (response.status != 200) {
throw new Error("Unable to get a Spotify auth token.")
}
return (await response).data.access_token;
}
export function createIntegrations(knex: Knex) {
// This will enable the app to redirect requests like:
// /integrations/5/v1/search?q=query
// To the external API represented by integration 5, e.g. for spotify:
// https://api.spotify.com/v1/search?q=query
// Requests need to already have a .user.id set.
let proxySpotifyCC = createProxyMiddleware({
target: 'https://api.spotify.com/',
changeOrigin: true,
logLevel: 'debug',
pathRewrite: (path: string, req: any) => {
// Remove e.g. "/integrations/5"
console.log("Rewrite URL:", path);
return path.replace(/^\/integrations\/[0-9]+/, '');
}
});
// In the first layer, retrieve integration details and save details
// in the request.
return async (req: any, res: any, next: any) => {
// Determine the integration to use.
req._integrationId = parseInt(req.url.match(/^\/([0-9]+)/)[1]);
console.log("URL:", req.url, 'match:', req._integrationId)
if (!req._integrationId) {
res.status(400).send({ reason: "An integration ID should be provided in the URL." });
return;
}
req._integration = (await knex.select(['id', 'name', 'type', 'details'])
.from('integrations')
.where({ 'user': req.user.id, 'id': req._integrationId }))[0];
if (!req._integration) {
res.status(404).send();
return;
}
req._integration.details = JSON.parse(req._integration.details);
switch (req._integration.type) {
case IntegrationType.SpotifyClientCredentials: {
console.log("Integration: ", req._integration)
// FIXME: persist the token
req._access_token = await getSpotifyCCAuthToken(
req._integration.details.clientId,
req._integration.details.clientSecret,
)
if (!req._access_token) {
res.status(500).send({ reason: "Unable to get Spotify auth token." })
}
req.headers["Authorization"] = "Bearer " + req._access_token;
return proxySpotifyCC(req, res, next);
}
default: {
res.status(500).send({ reason: "Unsupported integration type " + req._integration.type })
}
}
};
// // First add a layer which creates a token and saves it in the request.
// app.use((req: any, res: any, next: any) => {
// updateToken('c3e5e605e7814cdf94cd86eeba6f4c4f', '5d870c84a3c34aa3a4cf803aa95cb96a')
// .then(() => {
// req._access_token = authToken;
// next();
// })
// })
// app.use(
// '/spotifycc',
// createProxyMiddleware({
// target: 'https://api.spotify.com/',
// changeOrigin: true,
// onProxyReq: onProxyReq,
// logLevel: 'debug',
// pathRewrite: { '^/spotifycc': '' },
// })
// )
}

@ -1,65 +0,0 @@
const { createProxyMiddleware } = require('http-proxy-middleware');
let axios = require('axios')
let qs = require('querystring')
// The authorization token to use with the Spotify API.
// Will need to be refreshed once in a while.
var authToken: string | null = null;
async function updateToken(clientId: string, clientSecret: string) {
if (authToken) { return; }
let buf = Buffer.from(clientId + ':' + clientSecret)
let encoded = buf.toString('base64');
let response = await axios.post(
'https://accounts.spotify.com/api/token',
qs.stringify({ 'grant_type': 'client_credentials' }),
{
'headers': {
'Authorization': 'Basic ' + encoded,
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);
authToken = (await response).data.access_token;
}
let onProxyReq = (proxyReq: any, req: any, res: any) => {
proxyReq.setHeader("Authorization", "Bearer " + req._access_token)
console.log("Proxying request",
{
'path': req.path,
'originalUrl': req.originalUrl,
'baseUrl': req.baseUrl,
},
{
'path': proxyReq.path,
'originalUrl': proxyReq.originalUrl,
'baseUrl': req.baseUrl,
},
);
}
export function useSpotifyClientCreds(app: any) {
// First add a layer which creates a token and saves it in the request.
app.use((req: any, res: any, next: any) => {
updateToken('c3e5e605e7814cdf94cd86eeba6f4c4f', '5d870c84a3c34aa3a4cf803aa95cb96a')
.then(() => {
req._access_token = authToken;
next();
})
})
app.use(
'/spotifycc',
createProxyMiddleware({
target: 'https://api.spotify.com/',
changeOrigin: true,
onProxyReq: onProxyReq,
logLevel: 'debug',
pathRewrite: { '^/spotifycc': '' },
})
)
}
Loading…
Cancel
Save