base: Fix upload support for null
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
base: Tweak serve to be slightly more extendable discord_embed: Added new site, discord embed
This commit is contained in:
parent
9b227ead62
commit
2b1e2d695a
17 changed files with 577 additions and 21 deletions
|
@ -15,20 +15,27 @@ export function uploadMedia(file) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(media)
|
||||||
|
|
||||||
|
let body = {}
|
||||||
|
|
||||||
|
if (media.preview) {
|
||||||
|
body.preview = media.preview
|
||||||
|
}
|
||||||
|
if (media.small?.avif) {
|
||||||
|
body.small = media.small.avif
|
||||||
|
}
|
||||||
|
if (media.medium?.avif) {
|
||||||
|
body.medium = media.medium.avif
|
||||||
|
}
|
||||||
|
if (media.large?.avif) {
|
||||||
|
body.large = media.large.avif
|
||||||
|
}
|
||||||
|
|
||||||
return client.upload(media.path + '?token=' + token, { file: {
|
return client.upload(media.path + '?token=' + token, { file: {
|
||||||
file: file.path,
|
file: file.path,
|
||||||
filename: file.name,
|
filename: file.name,
|
||||||
} }, 'POST', {
|
} }, 'POST', body).then(res => {
|
||||||
preview: media.preview,
|
|
||||||
small: media.small?.avif,
|
|
||||||
medium: media.medium?.avif,
|
|
||||||
large: media.large?.avif,
|
|
||||||
/*
|
|
||||||
|
|
||||||
small: media.small.jpeg,
|
|
||||||
medium: media.medium.avif,
|
|
||||||
large_jpeg: mediakl.large.jpeg,*/
|
|
||||||
}).then(res => {
|
|
||||||
out.filename = res.filename
|
out.filename = res.filename
|
||||||
out.path = res.path
|
out.path = res.path
|
||||||
out.preview = res.preview
|
out.preview = res.preview
|
||||||
|
@ -38,11 +45,18 @@ export function uploadMedia(file) {
|
||||||
out.size = file.size
|
out.size = file.size
|
||||||
out.type = file.type
|
out.type = file.type
|
||||||
|
|
||||||
return client.post(media.path + '/' + out.filename + '?token=' + token, {
|
let body = {}
|
||||||
small: media.small?.jpeg,
|
if (media.small?.jpeg) {
|
||||||
medium: media.medium?.jpeg,
|
body.small = media.small.jpeg
|
||||||
large: media.large?.jpeg,
|
}
|
||||||
})
|
if (media.medium?.jpeg) {
|
||||||
|
body.medium = media.medium.jpeg
|
||||||
|
}
|
||||||
|
if (media.large?.jpeg) {
|
||||||
|
body.large = media.large.jpeg
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.post(media.path + '/' + out.filename + '?token=' + token, body)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (out.sizes.small) { out.sizes.small.jpeg = res.small }
|
if (out.sizes.small) { out.sizes.small.jpeg = res.small }
|
||||||
if (out.sizes.medium) { out.sizes.medium.jpeg = res.medium }
|
if (out.sizes.medium) { out.sizes.medium.jpeg = res.medium }
|
||||||
|
|
|
@ -22,8 +22,11 @@ export default class ServeHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
let indexFile = fsSync.readFileSync(path.join(this.root, 'index.html'))
|
let indexFile = fsSync.readFileSync(path.join(this.root, 'index.html'))
|
||||||
|
this.loadTemplate(indexFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadTemplate(indexFile) {
|
||||||
this.template = dot.template(indexFile.toString(), { argName: ['headerDescription', 'headerImage', 'headerTitle', 'headerUrl', 'payloadData', 'payloadTree', 'version', 'nonce', 'type', 'banner', 'media', 'in_debug'] })
|
this.template = dot.template(indexFile.toString(), { argName: ['headerDescription', 'headerImage', 'headerTitle', 'headerUrl', 'payloadData', 'payloadTree', 'version', 'nonce', 'type', 'banner', 'media', 'in_debug'] })
|
||||||
// console.log(indexFile.toString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
register(server) {
|
register(server) {
|
||||||
|
|
|
@ -53,6 +53,10 @@ export default class Server {
|
||||||
|
|
||||||
this.flaska.before(function(ctx) {
|
this.flaska.before(function(ctx) {
|
||||||
ctx.state.started = new Date().getTime()
|
ctx.state.started = new Date().getTime()
|
||||||
|
ctx.req.ip = ctx.req.headers['x-forwarded-for'] || ctx.req.connection.remoteAddress
|
||||||
|
ctx.log = ctx.log.child({
|
||||||
|
id: Math.random().toString(36).substring(2, 14),
|
||||||
|
})
|
||||||
ctx.db = pool
|
ctx.db = pool
|
||||||
})
|
})
|
||||||
this.flaska.before(QueryHandler())
|
this.flaska.before(QueryHandler())
|
||||||
|
@ -92,6 +96,7 @@ export default class Server {
|
||||||
ctx.log[level]({
|
ctx.log[level]({
|
||||||
duration: requestTime,
|
duration: requestTime,
|
||||||
status: ctx.status,
|
status: ctx.status,
|
||||||
|
ip: ctx.req.ip,
|
||||||
}, (ctx.aborted ? '[ABORT]' : '<--') + ` ${status}${ctx.method} ${ctx.url}`)
|
}, (ctx.aborted ? '[ABORT]' : '<--') + ` ${status}${ctx.method} ${ctx.url}`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
85
discord_embed/api/id.mjs
Normal file
85
discord_embed/api/id.mjs
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
/**
|
||||||
|
* Javascript AlphabeticID class
|
||||||
|
* (based on a script by Kevin van Zonneveld <kevin@vanzonneveld.net>)
|
||||||
|
*
|
||||||
|
* Author: Even Simon <even.simon@gmail.com>
|
||||||
|
*
|
||||||
|
* Description: Translates a numeric identifier into a short string and backwords.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* var str = AlphabeticID.encode(9007199254740989); // str = 'fE2XnNGpF'
|
||||||
|
* var id = AlphabeticID.decode('fE2XnNGpF'); // id = 9007199254740989;
|
||||||
|
**/
|
||||||
|
|
||||||
|
const AlphabeticID = {
|
||||||
|
index:'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [@function](https://twitter.com/function) AlphabeticID.encode
|
||||||
|
* [@description](https://twitter.com/description) Encode a number into short string
|
||||||
|
* [@param](https://twitter.com/param) integer
|
||||||
|
* [@return](https://twitter.com/return) string
|
||||||
|
**/
|
||||||
|
encode:function(_number){
|
||||||
|
if('undefined' == typeof _number){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else if('number' != typeof(_number)){
|
||||||
|
throw new Error('Wrong parameter type');
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret = '';
|
||||||
|
|
||||||
|
for(var i=Math.floor(Math.log(parseInt(_number))/Math.log(AlphabeticID.index.length));i>=0;i--){
|
||||||
|
ret = ret + AlphabeticID.index.substr((Math.floor(parseInt(_number) / AlphabeticID.bcpow(AlphabeticID.index.length, i)) % AlphabeticID.index.length),1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return reverse(ret);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [@function](https://twitter.com/function) AlphabeticID.decode
|
||||||
|
* [@description](https://twitter.com/description) Decode a short string and return number
|
||||||
|
* [@param](https://twitter.com/param) string
|
||||||
|
* [@return](https://twitter.com/return) integer
|
||||||
|
**/
|
||||||
|
decode:function(_string){
|
||||||
|
if('undefined' == typeof _string){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else if('string' != typeof _string){
|
||||||
|
throw new Error('Wrong parameter type');
|
||||||
|
}
|
||||||
|
|
||||||
|
var str = reverse(_string);
|
||||||
|
var ret = 0;
|
||||||
|
|
||||||
|
for(var i=0;i<=(str.length - 1);i++){
|
||||||
|
ret = ret + AlphabeticID.index.indexOf(str.substr(i,1)) * (AlphabeticID.bcpow(AlphabeticID.index.length, (str.length - 1) - i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [@function](https://twitter.com/function) AlphabeticID.bcpow
|
||||||
|
* [@description](https://twitter.com/description) Raise _a to the power _b
|
||||||
|
* [@param](https://twitter.com/param) float _a
|
||||||
|
* [@param](https://twitter.com/param) integer _b
|
||||||
|
* [@return](https://twitter.com/return) string
|
||||||
|
**/
|
||||||
|
bcpow:function(_a, _b){
|
||||||
|
return Math.floor(Math.pow(parseFloat(_a), parseInt(_b)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [@function](https://twitter.com/function) String.reverse
|
||||||
|
* [@description](https://twitter.com/description) Reverse a string
|
||||||
|
* [@return](https://twitter.com/return) string
|
||||||
|
**/
|
||||||
|
function reverse(str){
|
||||||
|
return str.split('').reverse().join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AlphabeticID
|
96
discord_embed/api/post.mjs
Normal file
96
discord_embed/api/post.mjs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
import { uploadMedia } from '../base/media/upload.mjs'
|
||||||
|
import config from '../base/config.mjs'
|
||||||
|
|
||||||
|
export default class IndexPost {
|
||||||
|
constructor(opts = {}) {
|
||||||
|
Object.assign(this, {
|
||||||
|
frontend: opts.frontend,
|
||||||
|
uploadMedia: uploadMedia,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
register(server) {
|
||||||
|
this.serve = server.routes.serve
|
||||||
|
server.flaska.post('/', [
|
||||||
|
server.formidable({ maxFileSize: 8 * 1024 * 1024, }),
|
||||||
|
], this.createNewLink.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
hasErrors(ctx, hasMedia) {
|
||||||
|
if (!ctx.req.body.video) {
|
||||||
|
return 'Missing video link'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx.req.body.video.startsWith('http')
|
||||||
|
|| !(ctx.req.body.video.includes('mp4')
|
||||||
|
|| ctx.req.body.video.includes('webm'))) {
|
||||||
|
return 'Video link has to be a valid full url and contain mp4 or webm in it'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx.req.body.image && !hasMedia) {
|
||||||
|
return 'Missing image link or file'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.req.body.image) {
|
||||||
|
if (!ctx.req.body.image.startsWith('http')
|
||||||
|
|| !(ctx.req.body.image.includes('jpg')
|
||||||
|
|| ctx.req.body.image.includes('jpeg')
|
||||||
|
|| ctx.req.body.image.includes('webp')
|
||||||
|
|| ctx.req.body.image.includes('png')
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return 'Image link has to be a valid full url and contain jpg, jpeg, webp or png'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** PUT: /api/auth/articles/:id */
|
||||||
|
async createNewLink(ctx) {
|
||||||
|
let hasMedia = ctx.req.files.media && ctx.req.files.media.size
|
||||||
|
|
||||||
|
ctx.state.video = ctx.req.body.video
|
||||||
|
ctx.state.image = ctx.req.body.image
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
let params = [
|
||||||
|
]
|
||||||
|
let res = await ctx.db.safeCallProc('discord_embed.link_add', params)
|
||||||
|
console.log(res)
|
||||||
|
}
|
||||||
|
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
|
46
discord_embed/api/serve.mjs
Normal file
46
discord_embed/api/serve.mjs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import path from 'path'
|
||||||
|
import Parent from '../base/serve.mjs'
|
||||||
|
import fs from 'fs/promises'
|
||||||
|
import fsSync from 'fs'
|
||||||
|
import dot from 'dot'
|
||||||
|
import config from '../base/config.mjs'
|
||||||
|
|
||||||
|
export default class ServeHandler extends Parent {
|
||||||
|
loadTemplate(indexFile) {
|
||||||
|
this.template = dot.template(indexFile.toString(), { argName: [
|
||||||
|
'imageLink',
|
||||||
|
'videoLink',
|
||||||
|
'error',
|
||||||
|
'siteUrl',
|
||||||
|
'siteUrlBase',
|
||||||
|
'version',
|
||||||
|
'nonce',
|
||||||
|
'in_debug',
|
||||||
|
'inputVideo',
|
||||||
|
'inputImage'
|
||||||
|
] })
|
||||||
|
}
|
||||||
|
|
||||||
|
async serveIndex(ctx) {
|
||||||
|
if (config.get('NODE_ENV') === 'development') {
|
||||||
|
let indexFile = await fs.readFile(path.join(this.root, 'index.html'))
|
||||||
|
this.loadTemplate(indexFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload = {
|
||||||
|
imageLink: ctx.query.get('i') || '',
|
||||||
|
videoLink: ctx.query.get('v') || '',
|
||||||
|
error: ctx.state.error || '',
|
||||||
|
inputVideo: ctx.state.video || ctx.query.get('v') || '',
|
||||||
|
inputImage: ctx.state.image || ctx.query.get('i') || '',
|
||||||
|
siteUrl: this.frontend + ctx.url,
|
||||||
|
siteUrlBase: this.frontend + '/',
|
||||||
|
version: this.version,
|
||||||
|
nonce: ctx.state.nonce,
|
||||||
|
in_debug: config.get('NODE_ENV') === 'development' && false,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.body = this.template(payload)
|
||||||
|
ctx.type = 'text/html; charset=utf-8'
|
||||||
|
}
|
||||||
|
}
|
22
discord_embed/api/server.mjs
Normal file
22
discord_embed/api/server.mjs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import config from '../base/config.mjs'
|
||||||
|
import Parent from '../base/server.mjs'
|
||||||
|
import IndexPost from './post.mjs'
|
||||||
|
import ServeHandler from './serve.mjs'
|
||||||
|
|
||||||
|
export default class Server extends Parent {
|
||||||
|
init() {
|
||||||
|
super.init()
|
||||||
|
let localUtil = new this.core.sc.Util(import.meta.url)
|
||||||
|
|
||||||
|
this.routes = {
|
||||||
|
post: new IndexPost({
|
||||||
|
frontend: config.get('frontend:url'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.routes.serve = new ServeHandler({
|
||||||
|
root: localUtil.getPathFromRoot('../public'),
|
||||||
|
version: this.core.app.running,
|
||||||
|
frontend: config.get('frontend:url'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
1
discord_embed/base
Symbolic link
1
discord_embed/base
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../base
|
8
discord_embed/build-package.json
Normal file
8
discord_embed/build-package.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"build": "echo done;"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"service-core": "^3.0.0-beta.17"
|
||||||
|
}
|
||||||
|
}
|
24
discord_embed/dev.mjs
Normal file
24
discord_embed/dev.mjs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import fs from 'fs'
|
||||||
|
import { ServiceCore } from 'service-core'
|
||||||
|
import * as index from './index.mjs'
|
||||||
|
|
||||||
|
const port = 4120
|
||||||
|
|
||||||
|
var core = new ServiceCore('nfp_moe', import.meta.url, port, '')
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
frontend: {
|
||||||
|
url: 'http://localhost:' + port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
config = JSON.parse(fs.readFileSync('./config.json'))
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
config.port = port
|
||||||
|
|
||||||
|
core.setConfig(config)
|
||||||
|
core.init(index).then(function() {
|
||||||
|
return core.run()
|
||||||
|
})
|
11
discord_embed/index.mjs
Normal file
11
discord_embed/index.mjs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import config from './base/config.mjs'
|
||||||
|
|
||||||
|
export function start(http, port, ctx) {
|
||||||
|
config.sources[1].store = ctx.config
|
||||||
|
|
||||||
|
return import('./api/server.mjs')
|
||||||
|
.then(function(module) {
|
||||||
|
let server = new module.default(http, port, ctx)
|
||||||
|
return server.run()
|
||||||
|
})
|
||||||
|
}
|
47
discord_embed/package.json
Normal file
47
discord_embed/package.json
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"name": "av1_embed",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"port": 4120,
|
||||||
|
"description": "AV1 discord server embed helper",
|
||||||
|
"main": "index.js",
|
||||||
|
"directories": {
|
||||||
|
"test": "test"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "node --experimental-modules index.mjs",
|
||||||
|
"dev:server": "node dev.mjs | bunyan",
|
||||||
|
"dev": "npm-watch dev:server"
|
||||||
|
},
|
||||||
|
"watch": {
|
||||||
|
"dev:server": {
|
||||||
|
"patterns": [
|
||||||
|
"api/*",
|
||||||
|
"base/*",
|
||||||
|
"../base/*"
|
||||||
|
],
|
||||||
|
"extensions": "js,mjs",
|
||||||
|
"quiet": true,
|
||||||
|
"inherit": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.nfp.is/nfp/nfp_sites.git"
|
||||||
|
},
|
||||||
|
"author": "Jonatan Nilsson",
|
||||||
|
"license": "WTFPL",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://git.nfp.is/nfp/nfp_sites/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://git.nfp.is/nfp/nfp_sites",
|
||||||
|
"dependencies": {
|
||||||
|
"dot": "^2.0.0-beta.1",
|
||||||
|
"flaska": "^1.3.0",
|
||||||
|
"formidable": "^1.2.6",
|
||||||
|
"ioredis": "^5.2.3",
|
||||||
|
"nconf-lite": "^2.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"service-core": "^3.0.0-beta.17"
|
||||||
|
}
|
||||||
|
}
|
0
discord_embed/public/app.css
Normal file
0
discord_embed/public/app.css
Normal file
BIN
discord_embed/public/favicon.png
Normal file
BIN
discord_embed/public/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3 KiB |
BIN
discord_embed/public/heart.png
Normal file
BIN
discord_embed/public/heart.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 393 KiB |
192
discord_embed/public/index.html
Normal file
192
discord_embed/public/index.html
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Discord Embedder from AV1 server</title>
|
||||||
|
<base href="/">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
{{ if (imageLink) { }}
|
||||||
|
<meta property="og:image" content="{{=imageLink}}">
|
||||||
|
<meta property="og:type" content="video.other">
|
||||||
|
<meta property="og:video:url" content="{{=videoLink}}">
|
||||||
|
<meta property="og:video:width" content="1280">
|
||||||
|
<meta property="og:video:height" content="720">
|
||||||
|
{{ } else { }}
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:url" content="{{=siteUrl}}" />
|
||||||
|
<meta property="og:image" content="/heart.png" />
|
||||||
|
<meta property="og:description" content="Simple site to help with embedding of AV1 videos and large/external videos into Discord." />
|
||||||
|
<meta property="og:title" content="Discord Embedder Helper Website" />
|
||||||
|
{{ } }}
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.png">
|
||||||
|
<style>
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--content-max-width: 1280px;
|
||||||
|
--bg: black;
|
||||||
|
--bg-content-alt: #333;
|
||||||
|
--color: #d7dadc;
|
||||||
|
--link: #bb4d00;
|
||||||
|
--button-border: 1px solid #f57c00;
|
||||||
|
--button-bg: #ffad42;
|
||||||
|
--button-fg: #000;
|
||||||
|
--error: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Box sizing rules */
|
||||||
|
*, *::before, *::after { box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove default margin */
|
||||||
|
body, h1, h2, h3, h4, p, figure, blockquote, dl, dd {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
text-rendering: optimizeSpeed;
|
||||||
|
line-height: 1.5;
|
||||||
|
font-size: 16px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--color);
|
||||||
|
}
|
||||||
|
|
||||||
|
input, button, textarea, select {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2.488rem;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 2.074rem;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.728rem;
|
||||||
|
}
|
||||||
|
h4 {
|
||||||
|
font-size: 1.44rem;
|
||||||
|
}
|
||||||
|
h5 {
|
||||||
|
font-size: 1.0rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a, a:visited, button {
|
||||||
|
text-decoration: none;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
input[type=text] {
|
||||||
|
border: 1px solid var(--color);
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--color);
|
||||||
|
border-radius: 0;
|
||||||
|
padding: 0.25rem;
|
||||||
|
line-height: 1rem;
|
||||||
|
outline: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text]:hover,
|
||||||
|
input[type=text]:active,
|
||||||
|
input[type=text]:focus {
|
||||||
|
border-color: var(--link);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text]:focus {
|
||||||
|
outline: 1px solid var(--link);
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input[type=submit] {
|
||||||
|
border: var(--button-border);
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--button-fg);
|
||||||
|
padding: 0.25rem 1rem;
|
||||||
|
margin: 1rem 0 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background: var(--bg-content-alt);
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-item {
|
||||||
|
flex: 2 0 200px;
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-item input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-inbetween {
|
||||||
|
align-self: flex-end;
|
||||||
|
padding: 0.25rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: var(--error);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 1rem 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inside {
|
||||||
|
width: 100%;
|
||||||
|
max-width: var(--content-max-width);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<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}}">
|
||||||
|
<div class="row">
|
||||||
|
<div class="row-item">
|
||||||
|
<label>Image link (required for proper discord embed)</label>
|
||||||
|
<input type="text" name="image" value="{{=inputImage}}">
|
||||||
|
</div>
|
||||||
|
<span class="row-inbetween">or</span>
|
||||||
|
<div class="row-item">
|
||||||
|
<label>Upload image file (max 8MiB)</label>
|
||||||
|
<input type="file" name="media">
|
||||||
|
</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>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -2,7 +2,6 @@ import path from 'path'
|
||||||
import striptags from 'striptags'
|
import striptags from 'striptags'
|
||||||
import Parent from '../base/serve.mjs'
|
import Parent from '../base/serve.mjs'
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import dot from 'dot'
|
|
||||||
import config from '../base/config.mjs'
|
import config from '../base/config.mjs'
|
||||||
|
|
||||||
export default class ServeHandler extends Parent {
|
export default class ServeHandler extends Parent {
|
||||||
|
@ -49,11 +48,11 @@ export default class ServeHandler extends Parent {
|
||||||
async serveIndex(ctx) {
|
async serveIndex(ctx) {
|
||||||
if (config.get('NODE_ENV') === 'development') {
|
if (config.get('NODE_ENV') === 'development') {
|
||||||
let indexFile = await fs.readFile(path.join(this.root, 'index.html'))
|
let indexFile = await fs.readFile(path.join(this.root, 'index.html'))
|
||||||
this.template = dot.template(indexFile.toString(), { argName: ['headerDescription', 'headerImage', 'headerTitle', 'headerUrl', 'payloadData', 'payloadTree', 'version', 'nonce', 'type', 'banner', 'media', 'in_debug'] })
|
this.loadTemplate(indexFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
let payload = {
|
let payload = {
|
||||||
headerDescription: 'Small fansubbing and scanlation group translating and encoding our favourite shows from Japan.',
|
headerDescription: 'A small fansubbing and scanlation group translating and encoding our favourite shows from Japan.',
|
||||||
headerImage: this.frontend + '/assets/img/heart.png',
|
headerImage: this.frontend + '/assets/img/heart.png',
|
||||||
headerTitle: 'NFP Moe - Anime/Manga translation group',
|
headerTitle: 'NFP Moe - Anime/Manga translation group',
|
||||||
headerUrl: this.frontend + ctx.url,
|
headerUrl: this.frontend + ctx.url,
|
||||||
|
@ -87,7 +86,8 @@ export default class ServeHandler extends Parent {
|
||||||
|
|
||||||
if (data.page) {
|
if (data.page) {
|
||||||
payload.headerTitle = data.page.name + ' - NFP Moe'
|
payload.headerTitle = data.page.name + ' - NFP Moe'
|
||||||
if (data.page.content.blocks.length) {
|
payload.headerDescription = 'Page ' + data.page.name + ' over at NFP Moe. ' + payload.headerDescription
|
||||||
|
if (data.page.content.blocks?.length) {
|
||||||
payload.headerDescription = this.getDescriptionFromBlocks(data.page.content.blocks) || payload.headerDescription
|
payload.headerDescription = this.getDescriptionFromBlocks(data.page.content.blocks) || payload.headerDescription
|
||||||
}
|
}
|
||||||
if (data.page.media_alt_prefix) {
|
if (data.page.media_alt_prefix) {
|
||||||
|
@ -114,6 +114,8 @@ export default class ServeHandler extends Parent {
|
||||||
payload.media = data.article?.media_avif_preview || false
|
payload.media = data.article?.media_avif_preview || false
|
||||||
payload.payloadData = JSON.stringify(data)
|
payload.payloadData = JSON.stringify(data)
|
||||||
payload.type = 'article'
|
payload.type = 'article'
|
||||||
|
} else if (ctx.url !== '/login' && !ctx.url.startsWith('/admin/')) {
|
||||||
|
ctx.status = 404
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ctx.log.error(e)
|
ctx.log.error(e)
|
||||||
|
|
Loading…
Reference in a new issue