1
0
Fork 0

upload correct version

Signed-off-by: Sam Therapy <sam@samtherapy.net>
This commit is contained in:
Sam Therapy 2022-08-27 01:46:03 +02:00
parent 65fbd7ca9e
commit 018afaf0e2
Signed by: sam
GPG Key ID: 4D8B07C18F31ACBD
14 changed files with 312 additions and 51 deletions

20
.yarn/sdks/eslint/bin/eslint.js vendored Normal file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve} = require(`path`);
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require eslint/bin/eslint.js
require(absPnpApiPath).setup();
}
}
// Defer to the real eslint/bin/eslint.js your application uses
module.exports = absRequire(`eslint/bin/eslint.js`);

20
.yarn/sdks/eslint/lib/api.js vendored Normal file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve} = require(`path`);
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require eslint
require(absPnpApiPath).setup();
}
}
// Defer to the real eslint your application uses
module.exports = absRequire(`eslint`);

6
.yarn/sdks/eslint/package.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"name": "eslint",
"version": "8.22.0-sdk",
"main": "./lib/api.js",
"type": "commonjs"
}

20
.yarn/sdks/prettier/index.js vendored Normal file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve} = require(`path`);
const relPnpApiPath = "../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require prettier/index.js
require(absPnpApiPath).setup();
}
}
// Defer to the real prettier/index.js your application uses
module.exports = absRequire(`prettier/index.js`);

6
.yarn/sdks/prettier/package.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"name": "prettier",
"version": "2.7.1-sdk",
"main": "./index.js",
"type": "commonjs"
}

View File

@ -12,11 +12,11 @@ https://waifurudor.de/?tags=tohsaka_rin,-feet,-underwear,rating:safe
Clone the repo to where ever you will be hosting this and run the following command to install the dependencies.
```sh
yarn
```
npm install
```
Now that the dependencies are taken care of you can verify it runs with `yarn build && yarn start` in the root directory of the project. If it tells you it is listening on a port you're probably good to go.
Now that the dependencies are taken care of you can verify it runs with `npm start` in the root directory of the project. If it tells you it is listening on a port you're probably good to go.
## Running as a service

View File

@ -7,10 +7,10 @@
},
"name": "waifurudor.de",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"build": "tsc",
"build": "tsc --build",
"clean": "tsc --build --clean",
"start": "node ./dist/index.js"
},
"repository": {
@ -20,10 +20,10 @@
"keywords": [],
"author": "",
"license": "GPL-3.0-or-later",
"description": "",
"private": true,
"devDependencies": {
"@types/express": "4.17.13",
"@types/morgan": "1",
"@types/morgan": "1.9.3",
"typescript": "4.7.4"
}
}

49
src/app.ts Normal file
View File

@ -0,0 +1,49 @@
import express, { NextFunction, Request, Response } from "express";
import helmet from "helmet";
import morgan from "morgan";
import { ParseGet, ParsePost } from "./helpers/parse.js";
import Search from "./helpers/search.js";
const app = express();
app.use(helmet());
app.use(morgan("combined"));
// app.use(morgan(process.env.NODE_ENV === "production" ? "tiny" : "dev"));
app.use(express.json());
// Errors
app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
console.error(err.stack);
res.status(500).json({ msg: "Wife machine broke", error: err });
});
app
.route("/")
.get(ParseGet, Search)
.post(ParsePost, Search)
// Also have a fallback for anyone who tries to be silly :)
.all((_req, res) => {
res.set("ALLOW", "GET, POST");
res.status(405).json({ msg: "Method not allowed." });
});
// Politely tell all crawlers to go away since there's nothing here.
app.get("/robots.txt", (_req, res) => {
res.setHeader("content-type", "text/plain");
res.status(200).send(`User-agent: *
Disallow: /`);
});
app.get("/source", (_req, res) => {
res.redirect(301, "https://git.freecumextremist.com/grumbulon/waifurudor.de");
});
// For 404s
app.use((_req, res) => {
res.setHeader("content-type", "text/plain");
res.status(404).send("Nothing beside remains.");
});
export default app;

108
src/helpers/contentType.ts Normal file
View File

@ -0,0 +1,108 @@
// TODO: many of these are probably redundant and should be removed.
/**
* Helper function to allow adding content types because XSS bad.
* Hopefully this works as it should.
* @param extension File extension, without any additional faff
* @returns Content type that should be used
*/
export default function ContentType(extension: string): string {
switch (
extension.toLowerCase()?.replace("jpg", "jpeg").replace("svg", "svg+xml")
) {
case "aces":
case "avci":
case "avcs":
case "avif":
case "bmp":
case "cgm":
case "dpx":
case "emf":
case "fits":
case "g3fax":
case "gif":
case "heic":
case "heif":
case "hej2k":
case "hsj2":
case "ief":
case "jls":
case "jp2":
case "jpeg":
case "jph":
case "jphc":
case "jpm":
case "jpx":
case "jxr":
case "jxra":
case "jxrs":
case "jxs":
case "jxsc":
case "jxsi":
case "jxss":
case "ktx":
case "ktx2":
case "naplps":
case "png":
case "prs.btif":
case "prs.pti":
case "pwg-raster":
case "t38":
case "tiff":
case "tiff-fx":
case "wmf":
return `image/${extension}`;
case "1d-interleaved-parityfec":
case "3gpp":
case "3gpp2":
case "3gpp-tt":
case "av1":
case "bmpeg":
case "bt656":
case "celb":
case "dv":
case "encaprtp":
case "ffv1":
case "flexfec":
case "h261":
case "h263":
case "h263-1998":
case "h263-2000":
case "h264":
case "h264-rcdo":
case "h264-svc":
case "h265":
case "iso.segment":
case "jxsv":
case "mj2":
case "mp1s":
case "mp2p":
case "mp2t":
case "mp4":
case "mp4v-es":
case "mpv":
case "mpeg":
case "mpeg4-generic":
case "nv":
case "ogg":
case "parityfec":
case "pointer":
case "quicktime":
case "raptorfec":
case "raw":
case "rtp-enc-aescm128":
case "rtploopback":
case "rtx":
case "scip":
case "smpte291":
case "smpte292m":
case "ulpfec":
case "vc1":
case "vc2":
case "vp8":
case "vp9":
return `video/${extension}`;
default:
return "";
}
}

34
src/helpers/parse.ts Normal file
View File

@ -0,0 +1,34 @@
import { Request, Response, NextFunction } from "express";
/**
* Middleware for extracting the options from a GET.
*
* Why not just support JSON for both GET and POST requests?
* Don't wanna.
*/
export function ParseGet(
req: Request,
res: Response,
next: NextFunction
): void {
if (req.query.tags && typeof req.query.tags === "string") {
res.locals.tags = req.query.tags.split(",");
}
res.locals.booru = (req.query.booru as string) ?? "sb";
next();
}
/**
* Middleware for extracting the options from a POST.
*
* Accepts from the body only because.
*/
export function ParsePost(
req: Request,
res: Response,
next: NextFunction
): void {
res.locals.tags = req.body.tags;
res.locals.booru = (req.body.booru as string) ?? "sb";
next();
}

36
src/helpers/search.ts Normal file
View File

@ -0,0 +1,36 @@
import { Request, Response } from "express";
import booru from "booru";
import ContentType from "./contentType.js";
/**
* Searches the booru for an image to return.
* @param _req Express request (not used)
* @param res Node Response
*/
export default function Search(_req: Request, res: Response) {
booru
.search(res.locals.booru, res.locals.tags, { random: true, limit: 1 })
.then(async (post) => {
const imageURL = post[0]?.fileUrl as string;
const type = imageURL.split(".").pop() as string;
res.setHeader("content-type", ContentType(type));
const img = await fetch(imageURL)
// Turn the image into an ArrayBuffer (which is also a Promise)
.then(async (fetchRes) => {
return fetchRes?.arrayBuffer();
})
.catch((err: Error) => {
console.error(err);
res.status(500).json({ msg: "Wife machine broke", error: err });
});
// deepcode ignore XSS: nmp
res.status(200).end(Buffer.from(img as ArrayBuffer), "binary");
})
.catch((err: Error) => {
res.status(400).json({ msg: "WaaS Error", error: err });
console.error(err);
});
}

View File

@ -1,45 +1,7 @@
import express, { NextFunction, Request, Response } from "express";
import helmet from "helmet";
import morgan from "morgan";
import app from "./app.js";
import { ParseGet, ParsePost, Search } from "./helpers/booruSearch.js";
const app = express();
const port = process.env.PORT || 3000;
app.use(helmet());
app.use(morgan(process.env.NODE_ENV === "production" ? "tiny" : "dev"));
app.use(express.json());
app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
console.error(err.stack);
res.status(500).json({ msg: "Wife machine broke", error: err });
});
app
.route("/")
.get(ParseGet, Search)
.post(ParsePost, Search)
// Also have a fallback for anyone who tries to be silly :)
.all((_req, res) => {
res.set("ALLOW", "GET, POST");
res.status(405).json({ msg: "Method not allowed." });
});
// Politely tell all crawlers to go away since there's nothing here.
app.get("/robots.txt", (_req, res) => {
res.setHeader("content-type", "text/plain");
res.status(200).send(`User-agent: *
Disallow: /`);
});
// For 404s
app.use((_req, res) => {
res.setHeader("content-type", "text/plain");
res.status(404).send("Nothing beside remains.");
});
app.listen(port, () => {
console.log(`listening on port ${port}`);
});

View File

@ -1,14 +1,14 @@
[Unit]
Description=waifurudor.de
Description=waifurudor.de - Anime as a Service (AaaS)
[Service]
ExecStart=npm start
ExecStart=yarn start
Restart=always
User=none
Group=nogroup
Environment=PATH=/usr/bin:/usr/local/bin
Environment=NODE_ENV=production
WorkingDirectory=/whatever/user/waifurudor.de/src/
WorkingDirectory=/whatever/user/waifurudor.de/
[Install]
WantedBy=multi-user.target

View File

@ -54,7 +54,7 @@ __metadata:
languageName: node
linkType: hard
"@types/morgan@npm:1":
"@types/morgan@npm:1.9.3":
version: 1.9.3
resolution: "@types/morgan@npm:1.9.3"
dependencies:
@ -677,7 +677,7 @@ __metadata:
resolution: "waifurudor.de@workspace:."
dependencies:
"@types/express": 4.17.13
"@types/morgan": 1
"@types/morgan": 1.9.3
booru: 2.6.2
express: ">=5.0.0-beta.1"
helmet: 5.1.1