From e737909b45bdcb8db7d2287f389c386df4374739 Mon Sep 17 00:00:00 2001 From: Jonatan Nilsson Date: Mon, 17 Oct 2022 14:13:20 +0000 Subject: [PATCH] discord_embed: Finished initial version of discord_embed --- discord_embed/.npmrc | 1 + discord_embed/api/id.mjs | 2 +- discord_embed/api/post.mjs | 49 ++++++++++++++++++--- discord_embed/api/serve.mjs | 28 ++++++++++-- discord_embed/api/server.mjs | 16 +++++++ discord_embed/package.json | 1 + discord_embed/public/index.html | 76 +++++++++++++++++++++++++++------ 7 files changed, 149 insertions(+), 24 deletions(-) create mode 100644 discord_embed/.npmrc diff --git a/discord_embed/.npmrc b/discord_embed/.npmrc new file mode 100644 index 0000000..9cf9495 --- /dev/null +++ b/discord_embed/.npmrc @@ -0,0 +1 @@ +package-lock=false \ No newline at end of file diff --git a/discord_embed/api/id.mjs b/discord_embed/api/id.mjs index 1496857..dcaea2d 100644 --- a/discord_embed/api/id.mjs +++ b/discord_embed/api/id.mjs @@ -12,7 +12,7 @@ **/ const AlphabeticID = { - index:'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ', + index:'WzPmtXQhwGnOiB8Vvu9fC1SL2l4F7MrUNc0RbD6dAayI3YTosejpZxJH5KqgEk', // abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ /** * [@function](https://twitter.com/function) AlphabeticID.encode diff --git a/discord_embed/api/post.mjs b/discord_embed/api/post.mjs index 40aa7a1..b85e745 100644 --- a/discord_embed/api/post.mjs +++ b/discord_embed/api/post.mjs @@ -1,11 +1,13 @@ -import { uploadMedia } from '../base/media/upload.mjs' +import { uploadMedia, deleteFile } from '../base/media/upload.mjs' import config from '../base/config.mjs' +import AlphabeticID from './id.mjs' export default class IndexPost { constructor(opts = {}) { Object.assign(this, { frontend: opts.frontend, uploadMedia: uploadMedia, + deleteFile: deleteFile, }) } @@ -44,12 +46,41 @@ export default class IndexPost { } } - /** PUT: /api/auth/articles/:id */ - async createNewLink(ctx) { - let hasMedia = ctx.req.files.media && ctx.req.files.media.size + async getLink(ctx) { + return this.serve.serveIndex(ctx) + } + + /** POST: / */ + async createNewLink(ctx) { ctx.state.video = ctx.req.body.video ctx.state.image = ctx.req.body.image + + let rateLimited = false + let redisKey = 'ratelimit_' + ctx.req.ip.replace(/:/g, '-') + + try { + let val = (await ctx.redis.get(redisKey)) + console.log(redisKey, val) + 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 let redirect = '' let error = this.hasErrors(ctx, hasMedia) @@ -57,6 +88,10 @@ export default class IndexPost { 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 + + await this.deleteFile(temp.filename).catch(err => { + ctx.log.error(err, 'Error removing ' + temp.filename) + }) } catch (err) { ctx.log.error(err) @@ -70,9 +105,13 @@ export default class IndexPost { if (!error) { try { let params = [ + ctx.state.video, + ctx.state.image, + ctx.req.ip, ] let res = await ctx.db.safeCallProc('discord_embed.link_add', params) - console.log(res) + let id = AlphabeticID.encode(res.first[0].id + 3843) + redirect = `${this.frontend}/${id}` } catch (err) { ctx.log.error(err) diff --git a/discord_embed/api/serve.mjs b/discord_embed/api/serve.mjs index 993ddf7..79066dc 100644 --- a/discord_embed/api/serve.mjs +++ b/discord_embed/api/serve.mjs @@ -4,6 +4,7 @@ import fs from 'fs/promises' import fsSync from 'fs' import dot from 'dot' import config from '../base/config.mjs' +import AlphabeticID from './id.mjs' export default class ServeHandler extends Parent { loadTemplate(indexFile) { @@ -27,12 +28,31 @@ export default class ServeHandler extends Parent { this.loadTemplate(indexFile) } + let videoLink = ctx.query.get('v') || '' + let imageLink = ctx.query.get('i') || '' + + if (ctx.url.match(/^\/[a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9]+$/)) { + try { + let id = AlphabeticID.decode(ctx.url.slice(1)) + if (id) { + let res = await ctx.db.safeCallProc('discord_embed.link_get', [id - 3843]) + if (res.first.length) { + videoLink = res.first[0].video_link + imageLink = res.first[0].image_link + } + } + } catch (err) { + ctx.log.error(err) + ctx.state.error = 'Unknown error while fetching link.' + } + } + let payload = { - imageLink: ctx.query.get('i') || '', - videoLink: ctx.query.get('v') || '', + videoLink: videoLink, + imageLink: imageLink, error: ctx.state.error || '', - inputVideo: ctx.state.video || ctx.query.get('v') || '', - inputImage: ctx.state.image || ctx.query.get('i') || '', + inputVideo: ctx.state.video || videoLink || '', + inputImage: ctx.state.image || imageLink || '', siteUrl: this.frontend + ctx.url, siteUrlBase: this.frontend + '/', version: this.version, diff --git a/discord_embed/api/server.mjs b/discord_embed/api/server.mjs index 8aeaaf8..9b26f02 100644 --- a/discord_embed/api/server.mjs +++ b/discord_embed/api/server.mjs @@ -1,3 +1,4 @@ +import Redis from 'ioredis' import config from '../base/config.mjs' import Parent from '../base/server.mjs' import IndexPost from './post.mjs' @@ -8,6 +9,12 @@ export default class Server extends Parent { super.init() let localUtil = new this.core.sc.Util(import.meta.url) + this.flaskaOptions.appendHeaders['Content-Security-Policy'] = `default-src 'self'; script-src 'self' 'nonce-0d1valZOnjp8ZpR6vBd4dg=='; style-src 'self' 'unsafe-inline'; img-src * data: blob:; media-src *; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'` + this.flaskaOptions.appendHeaders['Cross-Origin-Embedder-Policy'] = 'unsafe-none' + this.redis = new Redis(config.get('redis')) + this.redis.on('error', (err) => { + this.core.log.error(err) + }) this.routes = { post: new IndexPost({ frontend: config.get('frontend:url'), @@ -19,4 +26,13 @@ export default class Server extends Parent { frontend: config.get('frontend:url'), }) } + + runCreateServer() { + super.runCreateServer() + + + this.flaska.before((ctx) => { + ctx.redis = this.redis + }) + } } diff --git a/discord_embed/package.json b/discord_embed/package.json index 1c3870a..ca40405 100644 --- a/discord_embed/package.json +++ b/discord_embed/package.json @@ -35,6 +35,7 @@ }, "homepage": "https://git.nfp.is/nfp/nfp_sites", "dependencies": { + "bunyan-lite": "^1.2.1", "dot": "^2.0.0-beta.1", "flaska": "^1.3.0", "formidable": "^1.2.6", diff --git a/discord_embed/public/index.html b/discord_embed/public/index.html index e8c9d93..483690b 100644 --- a/discord_embed/public/index.html +++ b/discord_embed/public/index.html @@ -47,6 +47,7 @@ body { text-rendering: optimizeSpeed; line-height: 1.5; font-size: 16px; + padding: 1rem 0.5rem; font-family: sans-serif; background: var(--bg); color: var(--color); @@ -57,16 +58,16 @@ input, button, textarea, select { } h1 { - font-size: 2.488rem; + font-size: 1.88rem; } h2 { - font-size: 2.074rem; + font-size: 1.66rem; } h3 { - font-size: 1.728rem; + font-size: 1.44rem; } h4 { - font-size: 1.44rem; + font-size: 1.22rem; } h5 { font-size: 1.0rem; @@ -123,6 +124,8 @@ input[type=submit] { pre { background: var(--bg-content-alt); padding: 0.5rem; + white-space: break-spaces; + word-break: break-all; } .row { @@ -150,6 +153,15 @@ pre { padding: 1rem 1rem 0; } +.center { + text-align: center; +} + +video { + max-width: calc(100vw - 2rem); + max-height: calc(100vh - 140px); +} + .inside { width: 100%; max-width: var(--content-max-width); @@ -162,15 +174,24 @@ pre { + {{ if (videoLink && imageLink) { }} +

Your link is:

+
{{=siteUrl}}
+
+ +
+ {{ } }}

Create/generate embed url

{{ if (error) { }}

{{=error}}

{{ } }} - +
- +
or
@@ -179,14 +200,41 @@ pre {
- - -

- Alternatively, you can generate a link yourself using the following syntax: -

-
-      {{=siteUrlBase}}?v=<video link>&i=<image link>
-    
+
+ +

+ Alternatively, copy paste the following full url: +

+
+    {{=siteUrlBase}}?v=<video link>&i=<image link>
+  
+