0
Fork 0
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:
dragongoose 2023-03-07 10:29:05 -05:00
parent bc9336468b
commit 8fdf5e4a51
5 changed files with 89 additions and 25 deletions

View file

@ -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">

View file

@ -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')
}) })

View file

@ -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",

View file

@ -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
View 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)
}