mirror of
https://codeberg.org/SafeTwitch/safetwitch.git
synced 2024-12-22 13:22:58 -05:00
Implement error tracking and logging
This commit is contained in:
parent
bc9336468b
commit
8fdf5e4a51
5 changed files with 89 additions and 25 deletions
|
@ -15,7 +15,19 @@ export default {
|
||||||
const res = await fetch(`http://localhost:7000/api/users/${username}`)
|
const res = await fetch(`http://localhost:7000/api/users/${username}`)
|
||||||
|
|
||||||
if (res.status !== 200) {
|
if (res.status !== 200) {
|
||||||
return;
|
const data = await res.json()
|
||||||
|
|
||||||
|
if(!data.code) {
|
||||||
|
return {
|
||||||
|
status: "error",
|
||||||
|
code: "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...data
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const data: StreamerData = await res.json()
|
const data: StreamerData = await res.json()
|
||||||
|
@ -57,6 +69,21 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="data.status === 'error'" class="flex flex-col max-w-prose justify-center text-center mx-auto p-6 bg-ctp-crust rounded-lg text-white">
|
||||||
|
<div class="mb-6">
|
||||||
|
<h1 class="font-bold text-5xl">oops...</h1>
|
||||||
|
<p class="font-bold text-3xl">this wasn't supposed to happen</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<p class="text-xl">the server was encountered an error while retriving the data, and now we're here :3</p>
|
||||||
|
|
||||||
|
<div class="mt-5">
|
||||||
|
<p class="text-xl">please contact the administrator with this code</p>
|
||||||
|
<p class="text-xl">error identifier: {{ data.code }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-else class="flex flex-wrap mx-auto bg-ctp-crust p-6 rounded-lg max-w-prose text-white">
|
<div v-else class="flex flex-wrap mx-auto bg-ctp-crust p-6 rounded-lg max-w-prose text-white">
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,7 +98,7 @@ export default {
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="container w-28 h-28 relative">
|
<div class="container w-28 h-28 relative">
|
||||||
<img :src="data.pfp" class="rounded-full border-4 border-ctp-teal p-0.5 w-auto h-28">
|
<img :src="data.pfp" class="rounded-full border-4 border-ctp-teal p-0.5 w-auto h-28">
|
||||||
<span class="absolute bottom-0 right-[1.8rem] bg-ctp-red font-bold p-2.5 py-0.5 rounded-md">LIVE</span>
|
<span v-if="data.isLive" class="absolute bottom-0 right-[1.8rem] bg-ctp-red font-bold p-2.5 py-0.5 rounded-md">LIVE</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ml-3">
|
<div class="ml-3">
|
||||||
|
@ -87,7 +114,7 @@ export default {
|
||||||
|
|
||||||
<div class="pt-5 pr-5 flex rounded-lg">
|
<div class="pt-5 pr-5 flex rounded-lg">
|
||||||
<span v-if="!data.isLive"
|
<span v-if="!data.isLive"
|
||||||
class=" font-bold text-sm bg-ctp-mantle border border-ctp-red p-3.5 rounded-lg">OFFLINE</span>
|
class="font-bold text-sm bg-ctp-mantle border border-ctp-red p-3.5 rounded-lg">OFFLINE</span>
|
||||||
<div v-else class="justify-end">
|
<div v-else class="justify-end">
|
||||||
<ul class="flex font-bold flex-wrap text-sm justify-end float-right max-h-24 overflow-y-auto">
|
<ul class="flex font-bold flex-wrap text-sm justify-end float-right max-h-24 overflow-y-auto">
|
||||||
<li v-for="tag in data.stream.tags" class="p-2.5 py-1 m-0.5 bg-ctp-mantle rounded-md inline-flex">
|
<li v-for="tag in data.stream.tags" class="p-2.5 py-1 m-0.5 bg-ctp-mantle rounded-md inline-flex">
|
||||||
|
|
|
@ -2,12 +2,15 @@ import express, { Express, NextFunction, Request, Response } from 'express';
|
||||||
import dotenv from 'dotenv'
|
import dotenv from 'dotenv'
|
||||||
import history from 'connect-history-api-fallback'
|
import history from 'connect-history-api-fallback'
|
||||||
import routes from './routes'
|
import routes from './routes'
|
||||||
|
import { logger, errorHandler, uuid } from './util/logger'
|
||||||
|
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
const app: Express = express();
|
const app: Express = express();
|
||||||
const port = process.env.PORT
|
const port = process.env.PORT
|
||||||
|
|
||||||
|
|
||||||
|
app.use(uuid)
|
||||||
app.use(routes)
|
app.use(routes)
|
||||||
app.use(history())
|
app.use(history())
|
||||||
app.use(express.static('../frontend/dist'))
|
app.use(express.static('../frontend/dist'))
|
||||||
|
@ -22,15 +25,7 @@ app.use((req, res) => {
|
||||||
// handle errors
|
// handle errors
|
||||||
app.use(errorHandler)
|
app.use(errorHandler)
|
||||||
|
|
||||||
function errorHandler(err: Error, req: Request, res: Response, next: NextFunction) {
|
|
||||||
if (res.headersSent) {
|
|
||||||
return next(err)
|
|
||||||
}
|
|
||||||
res.status(500)
|
|
||||||
res.send('error')
|
|
||||||
console.log(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.log('Server up')
|
console.log('Server up')
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dragongoose/streamlink": "^1.0.3",
|
"@dragongoose/streamlink": "^1.1.1",
|
||||||
"connect-history-api-fallback": "^2.0.0",
|
"connect-history-api-fallback": "^2.0.0",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"puppeteer": "^19.7.2"
|
"puppeteer": "^19.7.2",
|
||||||
|
"winston": "^3.8.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/connect-history-api-fallback": "^1.3.5",
|
"@types/connect-history-api-fallback": "^1.3.5",
|
||||||
|
|
|
@ -53,7 +53,7 @@ const abbreviatedNumberToNumber = (num: string) => {
|
||||||
// https:// advancedweb.hu/how-to-speed-up-puppeteer-scraping-with-parallelization/
|
// https:// advancedweb.hu/how-to-speed-up-puppeteer-scraping-with-parallelization/
|
||||||
const withBrowser = async (fn: Function) => {
|
const withBrowser = async (fn: Function) => {
|
||||||
const browser = await puppeteer.launch({
|
const browser = await puppeteer.launch({
|
||||||
headless: false,
|
headless: true,
|
||||||
args: ['--no-sandbox']
|
args: ['--no-sandbox']
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
|
@ -180,16 +180,15 @@ const getAboutData = async (page: Page) => {
|
||||||
return aboutData as StreamerData
|
return aboutData as StreamerData
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStreamerData = async (username: string) => {
|
const getStreamerData = async (username: string): Promise<StreamerData> => {
|
||||||
let recoveredData: LooseObject = {}
|
let recoveredData: LooseObject = {}
|
||||||
|
|
||||||
await withBrowser(async (browser: Browser) => {
|
await withBrowser(async (browser: Browser) => {
|
||||||
const result = await withPage(browser)(async (page: Page) => {
|
const result = await withPage(browser)(async (page: Page) => {
|
||||||
await page.goto(`https://twitch.tv/${username}`)
|
await page.goto(`https://twitch.tv/${username}`)
|
||||||
|
|
||||||
return Promise.all([getStreamData(page), getAboutData(page)])
|
return Promise.all([getStreamData(page), getAboutData(page)])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
recoveredData = result[1]
|
recoveredData = result[1]
|
||||||
recoveredData.stream = result[0]
|
recoveredData.stream = result[0]
|
||||||
|
@ -198,23 +197,25 @@ const getStreamerData = async (username: string) => {
|
||||||
await browser.close()
|
await browser.close()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
recoveredData.username = username
|
recoveredData.username = username
|
||||||
return recoveredData as StreamerData
|
return recoveredData as StreamerData
|
||||||
}
|
}
|
||||||
|
|
||||||
profileRouter.get('/users/:username', async (req, res) => {
|
profileRouter.get('/users/:username', async (req, res, next) => {
|
||||||
const username = req.params.username
|
const username = req.params.username
|
||||||
const streamlink = new Streamlink(`https://twitch.tv/${username}`, {})
|
const streamlink = new Streamlink(`https://twitch.tv/${username}`, {})
|
||||||
isLive = await streamlink.isLive()
|
isLive = await streamlink.isLive()
|
||||||
const qualities = await streamlink.getQualities()
|
|
||||||
|
|
||||||
|
|
||||||
let streamerData = await getStreamerData(username)
|
let streamerData = await getStreamerData(username)
|
||||||
if(streamerData.stream)
|
.catch(next)
|
||||||
streamerData.stream.qualities = qualities
|
|
||||||
|
|
||||||
res.send(streamerData)
|
if(streamerData && streamerData.stream && isLive)
|
||||||
|
streamerData.stream.qualities = await streamlink.getQualities()
|
||||||
|
|
||||||
|
if(streamerData) {
|
||||||
|
streamerData.isLive = isLive
|
||||||
|
res.send(streamerData)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export default profileRouter
|
export default profileRouter
|
40
server/util/logger.ts
Normal file
40
server/util/logger.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { randomUUID } from 'crypto'
|
||||||
|
import { createLogger, format, transports } from 'winston'
|
||||||
|
import { NextFunction, Request, Response } from 'express'
|
||||||
|
import util from 'util'
|
||||||
|
|
||||||
|
const logLevels = {
|
||||||
|
fatal: 0,
|
||||||
|
error: 1,
|
||||||
|
warn: 2,
|
||||||
|
info: 3,
|
||||||
|
debug: 4,
|
||||||
|
trace: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
let currentUUID: string;
|
||||||
|
const addReqId = format((info) => {
|
||||||
|
info.reqId = currentUUID
|
||||||
|
return info
|
||||||
|
})
|
||||||
|
|
||||||
|
export const logger = createLogger({
|
||||||
|
format: format.combine(addReqId(), format.timestamp(), format.json()),
|
||||||
|
transports: [new transports.Console({}), new transports.File({ filename: './serverLog.log' })],
|
||||||
|
levels: logLevels
|
||||||
|
});
|
||||||
|
|
||||||
|
export const errorHandler = (err: Error, req: Request, res: Response, next: NextFunction) => {
|
||||||
|
if (res.headersSent) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentUUID = res.locals.uuid
|
||||||
|
res.status(500).send({ status: 'error', message: err.message, code: res.locals.uuid })
|
||||||
|
logger.warn(err.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const uuid = (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
res.locals.uuid = randomUUID()
|
||||||
|
next(res)
|
||||||
|
}
|
Loading…
Reference in a new issue