2022-10-17 14:13:20 +00:00
|
|
|
import { uploadMedia, deleteFile } from '../base/media/upload.mjs'
|
2022-10-17 07:52:55 +00:00
|
|
|
import config from '../base/config.mjs'
|
2022-10-17 14:13:20 +00:00
|
|
|
import AlphabeticID from './id.mjs'
|
2022-10-17 07:52:55 +00:00
|
|
|
|
|
|
|
export default class IndexPost {
|
|
|
|
constructor(opts = {}) {
|
|
|
|
Object.assign(this, {
|
|
|
|
frontend: opts.frontend,
|
|
|
|
uploadMedia: uploadMedia,
|
2022-10-17 14:13:20 +00:00
|
|
|
deleteFile: deleteFile,
|
2022-10-17 07:52:55 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
register(server) {
|
|
|
|
this.serve = server.routes.serve
|
|
|
|
server.flaska.post('/', [
|
|
|
|
server.formidable({ maxFileSize: 8 * 1024 * 1024, }),
|
|
|
|
], this.createNewLink.bind(this))
|
2022-10-29 12:30:46 +00:00
|
|
|
server.flaska.get('/video/:id', this.videoRedirect.bind(this))
|
2022-10-29 12:03:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async videoRedirect(ctx) {
|
|
|
|
try {
|
2022-10-29 12:30:46 +00:00
|
|
|
let id = AlphabeticID.decode(ctx.params.id.slice(0,-5))
|
2022-10-29 12:03:30 +00:00
|
|
|
let videoLink = null
|
|
|
|
if (id) {
|
|
|
|
let res = await ctx.db.safeCallProc('discord_embed.link_get', [id - 3843])
|
|
|
|
if (res.first.length) {
|
|
|
|
videoLink = res.first[0].video_link
|
|
|
|
} else {
|
|
|
|
ctx.status = 404
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (videoLink) {
|
|
|
|
ctx.status = 302
|
|
|
|
ctx.headers['Location'] = videoLink
|
2022-10-29 12:30:46 +00:00
|
|
|
ctx.type = 'text/html; charset=utf-8'
|
2022-10-29 12:03:30 +00:00
|
|
|
ctx.body = `
|
|
|
|
Redirecting
|
|
|
|
<a href="${videoLink}">Click here if it doesn't redirect</a>
|
|
|
|
`
|
|
|
|
return
|
|
|
|
} else {
|
|
|
|
ctx.status = 404
|
|
|
|
ctx.state.error = 'Video not found.'
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
ctx.log.error(err, 'Unable to fetch resource ' + ctx.url.slice(1))
|
|
|
|
ctx.state.error = 'Unknown error while fetching link.'
|
|
|
|
}
|
|
|
|
return this.serve.serveIndex(ctx)
|
2022-10-17 07:52:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
hasErrors(ctx, hasMedia) {
|
|
|
|
if (!ctx.req.body.video) {
|
|
|
|
return 'Missing video link'
|
|
|
|
}
|
|
|
|
|
2022-10-29 02:08:21 +00:00
|
|
|
if (!ctx.req.body.video.startsWith('http')) {
|
|
|
|
return 'Video link has to be a valid full url'
|
2022-10-17 07:52:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!ctx.req.body.image && !hasMedia) {
|
|
|
|
return 'Missing image link or file'
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.req.body.image) {
|
2022-10-29 02:08:21 +00:00
|
|
|
if (!ctx.req.body.image.startsWith('http')) {
|
|
|
|
return 'Image link has to be a valid full url'
|
2022-10-17 07:52:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-17 14:13:20 +00:00
|
|
|
async getLink(ctx) {
|
2022-10-17 07:52:55 +00:00
|
|
|
|
2022-10-17 14:13:20 +00:00
|
|
|
return this.serve.serveIndex(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
/** POST: / */
|
|
|
|
async createNewLink(ctx) {
|
2022-10-17 07:52:55 +00:00
|
|
|
ctx.state.video = ctx.req.body.video
|
2023-04-14 09:31:18 +00:00
|
|
|
ctx.state.image = ctx.req.body.image || 'https://cdn.nfp.is/av1/empty.png'
|
2022-10-17 14:13:20 +00:00
|
|
|
|
|
|
|
let rateLimited = false
|
|
|
|
let redisKey = 'ratelimit_' + ctx.req.ip.replace(/:/g, '-')
|
|
|
|
|
|
|
|
try {
|
|
|
|
let val = (await ctx.redis.get(redisKey))
|
|
|
|
val = val && Number(val) || 0
|
|
|
|
if (val > 3) {
|
|
|
|
rateLimited = true
|
|
|
|
} else if (val > 2) {
|
|
|
|
await ctx.redis.setex(redisKey, 60 * 15, val + 1)
|
|
|
|
rateLimited = true
|
|
|
|
} else {
|
|
|
|
await ctx.redis.setex(redisKey, 15, val + 1)
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
ctx.log.error(err, 'Error checking rate limit for ' + redisKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rateLimited) {
|
|
|
|
ctx.state.error = 'You have reached rate limit. Please wait at least 15 minutes.'
|
|
|
|
return this.serve.serveIndex(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
let hasMedia = ctx.req.files.media && ctx.req.files.media.size
|
2022-10-17 07:52:55 +00:00
|
|
|
let redirect = ''
|
|
|
|
let error = this.hasErrors(ctx, hasMedia)
|
|
|
|
|
|
|
|
if (!error && hasMedia) {
|
|
|
|
try {
|
|
|
|
let temp = await this.uploadMedia(ctx.req.files.media)
|
|
|
|
ctx.state.image = ctx.req.body.image = 'https://cdn.nfp.is' + temp.sizes.small.jpeg.path
|
2022-10-17 14:13:20 +00:00
|
|
|
|
|
|
|
await this.deleteFile(temp.filename).catch(err => {
|
|
|
|
ctx.log.error(err, 'Error removing ' + temp.filename)
|
|
|
|
})
|
2022-10-17 07:52:55 +00:00
|
|
|
}
|
|
|
|
catch (err) {
|
|
|
|
ctx.log.error(err)
|
|
|
|
error = 'Unable to upload file: ' + err.message
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!error) {
|
|
|
|
redirect = `${this.frontend}/?v=${ctx.state.video}&i=${ctx.state.image}`
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!error) {
|
|
|
|
try {
|
2022-10-29 12:03:30 +00:00
|
|
|
let params = [
|
|
|
|
ctx.state.video,
|
|
|
|
ctx.state.image,
|
|
|
|
ctx.req.ip,
|
|
|
|
]
|
|
|
|
let res = await ctx.db.safeCallProc('discord_embed.link_add', params)
|
2022-10-29 12:16:00 +00:00
|
|
|
let id = AlphabeticID.encode(res.first[0].id + 3843)
|
2022-10-17 14:13:20 +00:00
|
|
|
redirect = `${this.frontend}/${id}`
|
2022-10-17 07:52:55 +00:00
|
|
|
}
|
|
|
|
catch (err) {
|
|
|
|
ctx.log.error(err)
|
|
|
|
error = 'Error while generating shortened link.'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (redirect && !error) {
|
|
|
|
ctx.status = 302
|
|
|
|
ctx.headers['Location'] = redirect
|
|
|
|
ctx.type = 'text/html; charset=utf-8'
|
|
|
|
ctx.body = `
|
|
|
|
Redirecting
|
|
|
|
<a href="${redirect}">Click here if it doesn't redirect</a>
|
|
|
|
`
|
|
|
|
}
|
|
|
|
ctx.state.error = error
|
|
|
|
return this.serve.serveIndex(ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// https://litter.catbox.moe/cnl6hy.mp4
|