ratelimits & some more stuff
This commit is contained in:
		
							parent
							
								
									8a4602fbfd
								
							
						
					
					
						commit
						2b4e8d25f6
					
				| 
						 | 
				
			
			@ -1,9 +1,16 @@
 | 
			
		|||
const { PrismaClient } =  require('@prisma/client')
 | 
			
		||||
const redis = require('../utils/redis.js')
 | 
			
		||||
const prisma = new PrismaClient()
 | 
			
		||||
 | 
			
		||||
exports.getLatest = async (req, res) => {
 | 
			
		||||
    res.json(await prisma.videos.findMany({
 | 
			
		||||
        take: 30,
 | 
			
		||||
    let json
 | 
			
		||||
    const cached = await redis.get('latest')
 | 
			
		||||
 | 
			
		||||
    if (cached) {
 | 
			
		||||
        json = JSON.parse(cached)
 | 
			
		||||
    } else {
 | 
			
		||||
        json = await prisma.videos.findMany({
 | 
			
		||||
            take: 90,
 | 
			
		||||
            orderBy: [
 | 
			
		||||
                {
 | 
			
		||||
                    archived: 'desc'
 | 
			
		||||
| 
						 | 
				
			
			@ -20,5 +27,9 @@ exports.getLatest = async (req, res) => {
 | 
			
		|||
                channelAvatar: true,
 | 
			
		||||
                channelVerified: true
 | 
			
		||||
            }
 | 
			
		||||
    }))
 | 
			
		||||
        })
 | 
			
		||||
        await redis.set('latest', JSON.stringify(json), 'EX', 3600)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    res.json(json)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,8 +1,22 @@
 | 
			
		|||
const crypto = require('node:crypto')
 | 
			
		||||
const validate = require('../utils/validate.js')
 | 
			
		||||
const redis = require('../utils/redis.js')
 | 
			
		||||
const { RedisRateLimiter } = require('rolling-rate-limiter')
 | 
			
		||||
const { PrismaClient } =  require('@prisma/client')
 | 
			
		||||
const prisma = new PrismaClient()
 | 
			
		||||
 | 
			
		||||
const limiter = new RedisRateLimiter({
 | 
			
		||||
    client: redis, 
 | 
			
		||||
    namespace: 'search:',
 | 
			
		||||
    interval: 5 * 60 * 1000,
 | 
			
		||||
    maxInInterval: 5
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
exports.searchVideo = async (req, res) => {
 | 
			
		||||
    const ipHash = crypto.createHash('sha256').update(req.headers['x-userip'] || '0.0.0.0').digest('hex')
 | 
			
		||||
    const isLimited = await limiter.limit(ipHash)
 | 
			
		||||
    if (isLimited) return res.status(429).send('error-You have been ratelimited.')
 | 
			
		||||
 | 
			
		||||
    const id = await validate.validateVideoInput(req.query.search)
 | 
			
		||||
    if (id.fail) {
 | 
			
		||||
        const videos = await prisma.videos.findMany({
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +38,7 @@ exports.searchPlaylist = async (req, res) => {
 | 
			
		|||
    if (id.fail) {
 | 
			
		||||
        res.status(500).send(id.message)
 | 
			
		||||
    } else {
 | 
			
		||||
        res.send(`<script>window.location.href = '${process.env.FRONTEND}/playlist?list=${id}'</script>`)
 | 
			
		||||
        res.redirect(`${process.env.FRONTEND}/playlist?list=${id}`)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -33,6 +47,6 @@ exports.searchChannel = async (req, res) => {
 | 
			
		|||
    if (id.fail) {
 | 
			
		||||
        res.status(500).send(id.message)
 | 
			
		||||
    } else {
 | 
			
		||||
        res.send(`<script>window.location.href = '${process.env.FRONTEND}/channel/${id}'</script>`)
 | 
			
		||||
        res.redirect(`${process.env.FRONTEND}/channel/${id}`)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,14 +1,25 @@
 | 
			
		|||
const { PrismaClient } =  require('@prisma/client')
 | 
			
		||||
const redis = require('../utils/redis.js')
 | 
			
		||||
const prisma = new PrismaClient()
 | 
			
		||||
 | 
			
		||||
exports.getReports = async (req, res) => {
 | 
			
		||||
    res.json((await prisma.reports.findMany()).map(r => {
 | 
			
		||||
    let json
 | 
			
		||||
    const cached = await redis.get('transparency')
 | 
			
		||||
 | 
			
		||||
    if (cached) {
 | 
			
		||||
        json = JSON.parse(cached)
 | 
			
		||||
    } else {
 | 
			
		||||
        json = (await prisma.reports.findMany()).map(r => {
 | 
			
		||||
            return {
 | 
			
		||||
                ...r,
 | 
			
		||||
                details: (r.details).split('<').join('<').split('>').join('>'),
 | 
			
		||||
                date: (r.date).toISOString().slice(0,10)
 | 
			
		||||
            }
 | 
			
		||||
    }))
 | 
			
		||||
        })
 | 
			
		||||
        await redis.set('transparency', JSON.stringify(json), 'EX', 3600)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    res.json(json)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
exports.getReport = async (req, res) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,9 +3,16 @@ const prisma = new PrismaClient()
 | 
			
		|||
 | 
			
		||||
const DOMPurify = require('isomorphic-dompurify')
 | 
			
		||||
const metadata = require('../utils/metadata.js')
 | 
			
		||||
const redis = require('../utils/redis.js')
 | 
			
		||||
 | 
			
		||||
exports.getVideo = async (req, res) => {
 | 
			
		||||
    const info = await prisma.videos.findFirst({
 | 
			
		||||
    let info
 | 
			
		||||
    const cached = await redis.get(`video:${req.params.id}`)
 | 
			
		||||
 | 
			
		||||
    if (cached) {
 | 
			
		||||
        info = JSON.parse(cached)
 | 
			
		||||
    } else {
 | 
			
		||||
        info = await prisma.videos.findFirst({
 | 
			
		||||
            where: {
 | 
			
		||||
                id: req.params.id
 | 
			
		||||
            },
 | 
			
		||||
| 
						 | 
				
			
			@ -25,6 +32,8 @@ exports.getVideo = async (req, res) => {
 | 
			
		|||
        })
 | 
			
		||||
        
 | 
			
		||||
        if (!info) return res.json({ error: '404' })
 | 
			
		||||
        await redis.set(`video:${req.params.id}`, JSON.stringify(info), 'EX', 3600)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    res.json({
 | 
			
		||||
        ...info,
 | 
			
		||||
| 
						 | 
				
			
			@ -33,11 +42,17 @@ exports.getVideo = async (req, res) => {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
exports.getChannel = async (req, res) => {
 | 
			
		||||
    const videos = await metadata.getChannelVideos(req.params.id)
 | 
			
		||||
    const channel = await metadata.getChannel(req.params.id)
 | 
			
		||||
    const cached = await redis.get(`channel:${req.params.id}`)
 | 
			
		||||
    if (cached) return res.json(JSON.parse(cached))
 | 
			
		||||
 | 
			
		||||
    if (!videos || !channel) return res.json({ error: '500' })
 | 
			
		||||
    if (videos.error) return res.json({ error: '404' })
 | 
			
		||||
    const [videos, channel] = await Promise.all([
 | 
			
		||||
        metadata.getChannelVideos(req.params.id),
 | 
			
		||||
        metadata.getChannel(req.params.id)
 | 
			
		||||
    ])
 | 
			
		||||
 | 
			
		||||
    if (!videos || !channel || videos.error) {
 | 
			
		||||
        return res.json({ error: '404' });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const archived = await prisma.videos.findMany({
 | 
			
		||||
        where: {
 | 
			
		||||
| 
						 | 
				
			
			@ -52,43 +67,66 @@ exports.getChannel = async (req, res) => {
 | 
			
		|||
        }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    var allVideos = []
 | 
			
		||||
    allVideos = allVideos.concat((videos).map(video => {
 | 
			
		||||
        return { 
 | 
			
		||||
    const processedVideos = videos.map(video => ({
 | 
			
		||||
        id: video.url.replace('/watch?v=', ''),
 | 
			
		||||
            published: (new Date(video.uploaded)).toISOString().slice(0,10),
 | 
			
		||||
        published: new Date(video.uploaded).toISOString().slice(0, 10),
 | 
			
		||||
        ...video
 | 
			
		||||
        }
 | 
			
		||||
    }))
 | 
			
		||||
    }));
 | 
			
		||||
    
 | 
			
		||||
    await Promise.all(archived.map(async (v) => {
 | 
			
		||||
        const allVideo = allVideos.find(o => o.id == v.id)
 | 
			
		||||
        if (allVideo) {
 | 
			
		||||
            const index = allVideos.findIndex(o => o.id == v.id)
 | 
			
		||||
            allVideos[index] = v
 | 
			
		||||
    archived.forEach(v => {
 | 
			
		||||
        const existingVideoIndex = processedVideos.findIndex(video => video.id === v.id);
 | 
			
		||||
        if (existingVideoIndex !== -1) {
 | 
			
		||||
            processedVideos[existingVideoIndex] = v;
 | 
			
		||||
        } else {
 | 
			
		||||
            allVideos.push({
 | 
			
		||||
                ...v, 
 | 
			
		||||
                deleted: undefined
 | 
			
		||||
            })
 | 
			
		||||
            processedVideos.push({ ...v, deleted: undefined });
 | 
			
		||||
        }
 | 
			
		||||
    }))
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    allVideos.sort((a, b) => new Date(b.published) - new Date(a.published))
 | 
			
		||||
    processedVideos.sort((a, b) => new Date(b.published) - new Date(a.published));
 | 
			
		||||
    
 | 
			
		||||
    res.json({
 | 
			
		||||
    const json = {
 | 
			
		||||
        name: channel.author,
 | 
			
		||||
        avatar: channel.authorThumbnails[1].url,
 | 
			
		||||
        verified: channel.authorVerified,
 | 
			
		||||
        videos: allVideos 
 | 
			
		||||
        videos: processedVideos
 | 
			
		||||
    }
 | 
			
		||||
    await redis.set(`channel:${req.params.id}`, JSON.stringify(json), 'EX', 3600)
 | 
			
		||||
    res.json(json)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
exports.getOnlyChannelVideos = async (req, res) => {
 | 
			
		||||
    const cached = await redis.get(`channelVideos:${req.params.id}`)
 | 
			
		||||
    if (cached) return res.json(JSON.parse(cached))
 | 
			
		||||
 | 
			
		||||
    const archived = await prisma.videos.findMany({
 | 
			
		||||
        where: {
 | 
			
		||||
            channelId: req.params.id
 | 
			
		||||
        }, 
 | 
			
		||||
        select: {
 | 
			
		||||
            id: true,
 | 
			
		||||
            title: true,
 | 
			
		||||
            thumbnail: true,
 | 
			
		||||
            published: true,
 | 
			
		||||
            archived: true
 | 
			
		||||
        },
 | 
			
		||||
        orderBy: {
 | 
			
		||||
            published: 'desc'
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const json = {
 | 
			
		||||
        videos: archived 
 | 
			
		||||
    }
 | 
			
		||||
    await redis.set(`channelVideos:${req.params.id}`, JSON.stringify(json), 'EX', 3600)
 | 
			
		||||
    res.json(json)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
exports.getPlaylist = async (req, res) => {
 | 
			
		||||
    const playlist = await metadata.getPlaylistVideos(req.params.id)
 | 
			
		||||
    const cached = await redis.get(`playlist:${req.params.id}`)
 | 
			
		||||
    if (cached) return res.json(JSON.parse(cached))
 | 
			
		||||
 | 
			
		||||
    if (!playlist) return res.json({ error: '500' })
 | 
			
		||||
    if (playlist.error) return res.json({ error: '404' })
 | 
			
		||||
    const playlist = await metadata.getPlaylistVideos(req.params.id)
 | 
			
		||||
    if (!playlist || playlist.error) return res.json({ error: '404' })
 | 
			
		||||
 | 
			
		||||
    const playlistArchived = await prisma.videos.findMany({
 | 
			
		||||
        where: {
 | 
			
		||||
| 
						 | 
				
			
			@ -103,32 +141,27 @@ exports.getPlaylist = async (req, res) => {
 | 
			
		|||
        }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    var allVideos = []
 | 
			
		||||
    allVideos = allVideos.concat((playlist.relatedStreams).map(video => {
 | 
			
		||||
        return { 
 | 
			
		||||
    const allVideos = playlist.relatedStreams.map(video => ({
 | 
			
		||||
        id: video.url.replace('/watch?v=', ''),
 | 
			
		||||
            published: (new Date(video.uploaded)).toISOString().slice(0,10),
 | 
			
		||||
        published: new Date(video.uploaded).toISOString().slice(0, 10),
 | 
			
		||||
        ...video
 | 
			
		||||
        }
 | 
			
		||||
    }))
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
    await Promise.all(playlistArchived.map(async (v) => {
 | 
			
		||||
        const allVideo = allVideos.find(o => o.id == v.id)
 | 
			
		||||
        const allVideo = allVideos.find(o => o.id == v.id);
 | 
			
		||||
        if (allVideo) {
 | 
			
		||||
            const index = allVideos.findIndex(o => o.id == v.id)
 | 
			
		||||
            allVideos[index] = v
 | 
			
		||||
            const index = allVideos.findIndex(o => o.id == v.id);
 | 
			
		||||
            allVideos[index] = v;
 | 
			
		||||
        } else {
 | 
			
		||||
            const live = await metadata.getVideoMetadata(v.id)
 | 
			
		||||
 | 
			
		||||
            const live = await metadata.getVideoMetadata(v.id);
 | 
			
		||||
            allVideos.push({
 | 
			
		||||
                ...v,
 | 
			
		||||
                deleted: live.error ? true : false
 | 
			
		||||
            })
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }))
 | 
			
		||||
    }));
 | 
			
		||||
    
 | 
			
		||||
    await Promise.all(allVideos.map(async (v) => {
 | 
			
		||||
        if (!v.archived) {
 | 
			
		||||
    await Promise.all(allVideos.filter(v => !v.archived).map(async (v) => {
 | 
			
		||||
        const video = await prisma.videos.findFirst({
 | 
			
		||||
            where: {
 | 
			
		||||
                id: v.id
 | 
			
		||||
| 
						 | 
				
			
			@ -140,22 +173,22 @@ exports.getPlaylist = async (req, res) => {
 | 
			
		|||
                published: true,
 | 
			
		||||
                archived: true
 | 
			
		||||
            }
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
        if (video) {
 | 
			
		||||
                const index = allVideos.findIndex(o => o.id == v.id)
 | 
			
		||||
                allVideos[index] = video
 | 
			
		||||
            const index = allVideos.findIndex(o => o.id == v.id);
 | 
			
		||||
            allVideos[index] = video;
 | 
			
		||||
        }
 | 
			
		||||
        }
 | 
			
		||||
    }))
 | 
			
		||||
    }));
 | 
			
		||||
    
 | 
			
		||||
    allVideos.sort((a, b) => new Date(a.published) - new Date(b.published))
 | 
			
		||||
    allVideos.sort((a, b) => new Date(b.published) - new Date(a.published));
 | 
			
		||||
    
 | 
			
		||||
    res.json({
 | 
			
		||||
    const json = {
 | 
			
		||||
        name: playlist.name,
 | 
			
		||||
        channel: playlist.uploader,
 | 
			
		||||
        url: playlist.uploaderUrl,
 | 
			
		||||
        avatar: playlist.uploaderAvatar,
 | 
			
		||||
        videos: allVideos
 | 
			
		||||
    })
 | 
			
		||||
    }
 | 
			
		||||
    await redis.set(`playlist:${req.params.id}`, JSON.stringify(json), 'EX', 3600)
 | 
			
		||||
    res.json(json)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								index.js
								
								
								
								
							
							
						
						
									
										1
									
								
								index.js
								
								
								
								
							| 
						 | 
				
			
			@ -19,6 +19,7 @@ app.use(cors())
 | 
			
		|||
app.get('/latest', latestController.getLatest)
 | 
			
		||||
app.get('/video/:id', videoController.getVideo)
 | 
			
		||||
app.get('/channel/:id', videoController.getChannel)
 | 
			
		||||
app.get('/channel/:id/videos', videoController.getOnlyChannelVideos)
 | 
			
		||||
app.get('/playlist/:id', videoController.getPlaylist)
 | 
			
		||||
 | 
			
		||||
app.get('/search/video', searchController.searchVideo)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,13 +21,13 @@ async function downloadVideo(url, ws) {
 | 
			
		|||
        })
 | 
			
		||||
 | 
			
		||||
        child.on("close", async (code, signal) => {
 | 
			
		||||
            if (code == 2) {
 | 
			
		||||
            if (code == 0) { // https://itsfoss.com/linux-exit-codes/
 | 
			
		||||
                reject({
 | 
			
		||||
                    fail: true
 | 
			
		||||
                    fail: false
 | 
			
		||||
                })
 | 
			
		||||
            } else {
 | 
			
		||||
                resolve({
 | 
			
		||||
                    fail: false
 | 
			
		||||
                    fail: true
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue