discord_embed: Finished initial version of discord_embed
This commit is contained in:
parent
bcc4dfe745
commit
e737909b45
7 changed files with 149 additions and 24 deletions
1
discord_embed/.npmrc
Normal file
1
discord_embed/.npmrc
Normal file
|
@ -0,0 +1 @@
|
|||
package-lock=false
|
|
@ -12,7 +12,7 @@
|
|||
**/
|
||||
|
||||
const AlphabeticID = {
|
||||
index:'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
index:'WzPmtXQhwGnOiB8Vvu9fC1SL2l4F7MrUNc0RbD6dAayI3YTosejpZxJH5KqgEk', // abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
|
||||
/**
|
||||
* [@function](https://twitter.com/function) AlphabeticID.encode
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 {
|
|||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{{ if (videoLink && imageLink) { }}
|
||||
<p>Your link is:</p>
|
||||
<pre>{{=siteUrl}}</pre>
|
||||
<div class="center">
|
||||
<video controls poster="{{=imageLink}}">
|
||||
<source src="{{=videoLink}}">
|
||||
</video>
|
||||
</div>
|
||||
{{ } }}
|
||||
<form action="/" method="post" enctype="multipart/form-data" class="inside">
|
||||
<h1>Create/generate embed url</h1>
|
||||
{{ if (error) { }}<p class="error">{{=error}}</p>{{ } }}
|
||||
<label>Video link</label>
|
||||
<input type="text" name="video" value="{{=inputVideo}}">
|
||||
<input id="inputVideo" type="text" name="video" value="{{=inputVideo}}">
|
||||
<div class="row">
|
||||
<div class="row-item">
|
||||
<label>Image link (required for proper discord embed)</label>
|
||||
<input type="text" name="image" value="{{=inputImage}}">
|
||||
<input id="inputImage" type="text" name="image" value="{{=inputImage}}">
|
||||
</div>
|
||||
<span class="row-inbetween">or</span>
|
||||
<div class="row-item">
|
||||
|
@ -179,14 +200,41 @@ pre {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<input type="submit" value="Generate embed url">
|
||||
|
||||
<p>
|
||||
Alternatively, you can generate a link yourself using the following syntax:
|
||||
</p>
|
||||
<pre>
|
||||
{{=siteUrlBase}}?v=<video link>&i=<image link>
|
||||
</pre>
|
||||
<input type="submit" value="Generate short url">
|
||||
</form>
|
||||
|
||||
<p>
|
||||
Alternatively, copy paste the following full url:
|
||||
</p>
|
||||
<pre id="generateurl">
|
||||
{{=siteUrlBase}}?v=<video link>&i=<image link>
|
||||
</pre>
|
||||
<script type="text/javascript" nonce="{{=nonce}}">
|
||||
var baseSite = '{{=siteUrlBase}}';
|
||||
var generateurl = document.getElementById('generateurl');
|
||||
var inputVideo = document.getElementById('inputVideo');
|
||||
var inputImage = document.getElementById('inputImage');
|
||||
var isExample = true;
|
||||
|
||||
function checkChange() {
|
||||
var currentIsExample = true;
|
||||
if (inputVideo.value && inputImage.value) {
|
||||
currentIsExample = false;
|
||||
}
|
||||
if (isExample && currentIsExample) { return; }
|
||||
isExample = currentIsExample;
|
||||
if (isExample) {
|
||||
generateurl.innerText = baseSite + '?v=<video link>&i=<image link>';
|
||||
} else {
|
||||
generateurl.innerText = baseSite + '?v=' + encodeURI(inputVideo.value) + '&i=' + encodeURI(inputImage.value);
|
||||
}
|
||||
};
|
||||
|
||||
inputVideo.addEventListener('change', checkChange);
|
||||
inputImage.addEventListener('change', checkChange);
|
||||
inputVideo.addEventListener('keyup', checkChange);
|
||||
inputImage.addEventListener('keyup', checkChange);
|
||||
checkChange()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in a new issue