Update index.js
Browse files
index.js
CHANGED
|
@@ -9,6 +9,9 @@ const express = require('express')
|
|
| 9 |
const cp = require('child_process')
|
| 10 |
const PDFDocument = require('pdfkit')
|
| 11 |
const playwright = require('playwright-extra')
|
|
|
|
|
|
|
|
|
|
| 12 |
// const stealth = require('puppeteer-extra-plugin-stealth')
|
| 13 |
// playwright.chromium.use(stealth())
|
| 14 |
const { NinexbuddySource, NinexbuddyScraper } = require("./lib/buddy.js")
|
|
@@ -18,6 +21,10 @@ const kyou = new NinexbuddyScraper()
|
|
| 18 |
const app = express()
|
| 19 |
app.set('json spaces', 4)
|
| 20 |
app.use(morgan('dev'))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
const limitSize = '500mb'
|
| 23 |
app.use(express.json({ limit: limitSize }))
|
|
@@ -37,6 +44,11 @@ app.use((req, res, next) => {
|
|
| 37 |
next()
|
| 38 |
})
|
| 39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
app.use('/file', express.static(tmpFolder))
|
| 41 |
|
| 42 |
app.all('/', (req, res) => {
|
|
@@ -85,6 +97,130 @@ app.get('/proxy', async (req, res) => {
|
|
| 85 |
}
|
| 86 |
});
|
| 87 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
app.get('/pages', async (req, res) => {
|
| 89 |
const targetUrl = req.query.url;
|
| 90 |
|
|
|
|
| 9 |
const cp = require('child_process')
|
| 10 |
const PDFDocument = require('pdfkit')
|
| 11 |
const playwright = require('playwright-extra')
|
| 12 |
+
const cookieParser = require('cookie-parser');
|
| 13 |
+
const { Buffer } = require('buffer');
|
| 14 |
+
const querystring = require('querystring');
|
| 15 |
// const stealth = require('puppeteer-extra-plugin-stealth')
|
| 16 |
// playwright.chromium.use(stealth())
|
| 17 |
const { NinexbuddySource, NinexbuddyScraper } = require("./lib/buddy.js")
|
|
|
|
| 21 |
const app = express()
|
| 22 |
app.set('json spaces', 4)
|
| 23 |
app.use(morgan('dev'))
|
| 24 |
+
app.use(cookieParser());
|
| 25 |
+
|
| 26 |
+
const CLIENT_ID = process.env.CLIENT_ID || 'your_client_id';
|
| 27 |
+
const CLIENT_SECRET = process.env.CLIENT_SECRET || 'your_client_secret';
|
| 28 |
|
| 29 |
const limitSize = '500mb'
|
| 30 |
app.use(express.json({ limit: limitSize }))
|
|
|
|
| 44 |
next()
|
| 45 |
})
|
| 46 |
|
| 47 |
+
|
| 48 |
+
function getFrontendUri(req) {
|
| 49 |
+
return `${req.protocol}://${req.get('host')}`;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
app.use('/file', express.static(tmpFolder))
|
| 53 |
|
| 54 |
app.all('/', (req, res) => {
|
|
|
|
| 97 |
}
|
| 98 |
});
|
| 99 |
|
| 100 |
+
// 1. Root route - redirect ke Spotify login
|
| 101 |
+
app.get('/spotify', (req, res) => {
|
| 102 |
+
const FRONTEND_URI = getFrontendUri(req);
|
| 103 |
+
const REDIRECT_URI = `${FRONTEND_URI}/callback`;
|
| 104 |
+
|
| 105 |
+
const scopes = ['user-read-recently-played'];
|
| 106 |
+
const queryParams = querystring.stringify({
|
| 107 |
+
response_type: 'code',
|
| 108 |
+
client_id: CLIENT_ID,
|
| 109 |
+
scope: scopes.join(' '),
|
| 110 |
+
redirect_uri: REDIRECT_URI,
|
| 111 |
+
show_dialog: false,
|
| 112 |
+
});
|
| 113 |
+
|
| 114 |
+
res.redirect(`https://accounts.spotify.com/authorize?${queryParams}`);
|
| 115 |
+
});
|
| 116 |
+
|
| 117 |
+
// 2. Callback route
|
| 118 |
+
app.get('/callback', async (req, res) => {
|
| 119 |
+
const FRONTEND_URI = getFrontendUri(req);
|
| 120 |
+
const REDIRECT_URI = `${FRONTEND_URI}/callback`;
|
| 121 |
+
|
| 122 |
+
const { code, error } = req.query;
|
| 123 |
+
|
| 124 |
+
if (error) {
|
| 125 |
+
console.error('Spotify Auth Error:', error);
|
| 126 |
+
return res.redirect(`${FRONTEND_URI}/error?message=${encodeURIComponent(error)}`);
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
try {
|
| 130 |
+
// Dapatkan tokens
|
| 131 |
+
const authResponse = await axios.post('https://accounts.spotify.com/api/token',
|
| 132 |
+
querystring.stringify({
|
| 133 |
+
grant_type: 'authorization_code',
|
| 134 |
+
code,
|
| 135 |
+
redirect_uri: REDIRECT_URI,
|
| 136 |
+
}), {
|
| 137 |
+
headers: {
|
| 138 |
+
'Content-Type': 'application/x-www-form-urlencoded',
|
| 139 |
+
'Authorization': 'Basic ' + Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64'),
|
| 140 |
+
},
|
| 141 |
+
});
|
| 142 |
+
|
| 143 |
+
const { access_token, refresh_token, expires_in } = authResponse.data;
|
| 144 |
+
|
| 145 |
+
// Simpan tokens di cookie
|
| 146 |
+
res.cookie('spotify_access', access_token, {
|
| 147 |
+
httpOnly: true,
|
| 148 |
+
secure: process.env.NODE_ENV === 'production',
|
| 149 |
+
maxAge: expires_in * 1000
|
| 150 |
+
});
|
| 151 |
+
|
| 152 |
+
res.cookie('spotify_refresh', refresh_token, {
|
| 153 |
+
httpOnly: true,
|
| 154 |
+
secure: process.env.NODE_ENV === 'production'
|
| 155 |
+
});
|
| 156 |
+
|
| 157 |
+
// Redirect ke endpoint JSON
|
| 158 |
+
res.redirect(`${FRONTEND_URI}/api/recently-played/json`);
|
| 159 |
+
} catch (err) {
|
| 160 |
+
console.error('Callback Error:', err);
|
| 161 |
+
res.redirect(`${FRONTEND_URI}/error?message=${encodeURIComponent('Authentication failed')}`);
|
| 162 |
+
}
|
| 163 |
+
});
|
| 164 |
+
|
| 165 |
+
// 3. Recently Played JSON endpoint
|
| 166 |
+
app.get('/api/recently-played/json', async (req, res) => {
|
| 167 |
+
const FRONTEND_URI = getFrontendUri(req);
|
| 168 |
+
const accessToken = req.cookies.spotify_access;
|
| 169 |
+
|
| 170 |
+
if (!accessToken) {
|
| 171 |
+
return res.redirect('/');
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
try {
|
| 175 |
+
// Get recently played tracks
|
| 176 |
+
const limit = parseInt(req.query.limit) || 20;
|
| 177 |
+
const response = await axios.get('https://api.spotify.com/v1/me/player/recently-played', {
|
| 178 |
+
params: { limit },
|
| 179 |
+
headers: { 'Authorization': `Bearer ${accessToken}` },
|
| 180 |
+
});
|
| 181 |
+
|
| 182 |
+
const tracks = response.data.items.map(item => ({
|
| 183 |
+
played_at: item.played_at,
|
| 184 |
+
track: {
|
| 185 |
+
id: item.track.id,
|
| 186 |
+
name: item.track.name,
|
| 187 |
+
artists: item.track.artists.map(a => ({ id: a.id, name: a.name })),
|
| 188 |
+
album: {
|
| 189 |
+
id: item.track.album.id,
|
| 190 |
+
name: item.track.album.name,
|
| 191 |
+
images: item.track.album.images,
|
| 192 |
+
release_date: item.track.album.release_date
|
| 193 |
+
},
|
| 194 |
+
duration_ms: item.track.duration_ms,
|
| 195 |
+
preview_url: item.track.preview_url
|
| 196 |
+
}
|
| 197 |
+
}));
|
| 198 |
+
|
| 199 |
+
res.json({
|
| 200 |
+
status: 'success',
|
| 201 |
+
data: tracks,
|
| 202 |
+
count: tracks.length
|
| 203 |
+
});
|
| 204 |
+
} catch (error) {
|
| 205 |
+
if (error.response?.status === 401) {
|
| 206 |
+
// Token expired, redirect ke login
|
| 207 |
+
return res.redirect('/');
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
console.error('Recently Played Error:', error);
|
| 211 |
+
res.status(500).json({
|
| 212 |
+
error: 'Failed to fetch recently played tracks',
|
| 213 |
+
details: error.response?.data || error.message
|
| 214 |
+
});
|
| 215 |
+
}
|
| 216 |
+
});
|
| 217 |
+
|
| 218 |
+
// Error handler
|
| 219 |
+
app.use((err, req, res, next) => {
|
| 220 |
+
console.error('Server Error:', err);
|
| 221 |
+
res.status(500).json({ error: 'Internal server error' });
|
| 222 |
+
});
|
| 223 |
+
|
| 224 |
app.get('/pages', async (req, res) => {
|
| 225 |
const targetUrl = req.query.url;
|
| 226 |
|