Move plugins to express-twtkpr-core-plugins package
Add plugin structure Fix stale cache after posting Update to v0.9.0
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.13.0.cjs
|
||||
npmMinimalAgeGate: 7d
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.14.1.cjs
|
||||
|
||||
49
README.md
49
README.md
@@ -5,7 +5,8 @@ An [ExpressJS](https://expressjs.com/) router for serving, sharing, and updating
|
||||
|
||||
> [!WARNING]
|
||||
> **STILL IN ALPHA**: Although this is a fully-functional plugin which is actively deployed to at least one site (my
|
||||
> own), it currently lacks documentation, examples, tests, installation flexibility, or polish.
|
||||
> own), it currently lacks documentation, examples, tests, installation flexibility, or polish (and still has a couple
|
||||
> of bugs that need to be fixed).
|
||||
> _USE AT YOUR OWN RISK_
|
||||
|
||||
### Features
|
||||
@@ -17,10 +18,56 @@ An [ExpressJS](https://expressjs.com/) router for serving, sharing, and updating
|
||||
|
||||
## Installing
|
||||
|
||||
Add package:
|
||||
|
||||
```sh
|
||||
yarn add express-twtkpr
|
||||
```
|
||||
|
||||
Generate hash:
|
||||
|
||||
```sh
|
||||
yarn run get:hash
|
||||
```
|
||||
|
||||
Create username and password:
|
||||
|
||||
```sh
|
||||
yarn run set:user
|
||||
```
|
||||
|
||||
## Using
|
||||
|
||||
Use it in an express application:
|
||||
|
||||
```
|
||||
import express, { Request, Response } from "express";
|
||||
|
||||
import twtkpr from "express-twtkpr";
|
||||
|
||||
// import other middleware
|
||||
|
||||
const app = express();
|
||||
|
||||
// add other middleware
|
||||
|
||||
app.use(
|
||||
"/",
|
||||
twtkpr(
|
||||
{
|
||||
// config options
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// add error handler
|
||||
|
||||
export default app;
|
||||
|
||||
```
|
||||
|
||||
Starting with v0.9.0, extra features (like files uploading) were moved to the [express-twtkpr-core-plugins](https://www.npmjs.com/package/express-twtkpr-core-plugins) package.
|
||||
|
||||
More to come!
|
||||
|
||||
---
|
||||
|
||||
33
dist/package.json
vendored
33
dist/package.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "express-twtkpr",
|
||||
"version": "0.8.2",
|
||||
"version": "0.9.0",
|
||||
"description": "An express library for hosting and maintaining a twtxt.txt file.",
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
@@ -26,18 +26,16 @@
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "node --env-file=.env dist/index-app.js",
|
||||
"build": "tsc && cp -r src/client dist/src",
|
||||
"dev": "DEBUG='twtkpr:*' tsx watch --env-file=.env src/index-app.ts",
|
||||
"build": "tsc && cp -r src/client dist/src && cp -r src/plugins dist/src",
|
||||
"get:hash": "tsx --env-file=.env src/cli.ts get-hash",
|
||||
"lint": "eslint --fix src test",
|
||||
"lint": "eslint --fix src",
|
||||
"prepublishOnly": "yarn build",
|
||||
"set:user": "tsx --env-file=.env src/cli.ts set-user",
|
||||
"test": "vitest",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cacheable/node-cache": "^2.0.2",
|
||||
"@cacheable/node-cache": "^3.0.0",
|
||||
"@exodus/blakejs": "^1.1.1-exodus.0",
|
||||
"base32.js": "^0.1.0",
|
||||
"bcryptjs": "^3.0.3",
|
||||
@@ -45,37 +43,36 @@
|
||||
"dayjs": "^1.11.20",
|
||||
"debug": "^4.4.3",
|
||||
"express": "^5.2.1",
|
||||
"express-rate-limit": "^8.3.1",
|
||||
"express-rate-limit": "^8.5.0",
|
||||
"express-session": "^1.19.0",
|
||||
"express-slow-down": "^3.1.0",
|
||||
"formidable": "^3.5.4",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"link": "^2.1.2",
|
||||
"session-file-store": "^1.5.0",
|
||||
"twtxt-lib": "^0.9.4",
|
||||
"uuid": "^13.0.0",
|
||||
"zod": "^4.3.6"
|
||||
"twtxt-lib": "^0.10.1",
|
||||
"uuid": "^14.0.0",
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cookie-parser": "^1.4.10",
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/debug": "^4.1.13",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/express-session": "^1.18.2",
|
||||
"@types/formidable": "^3.5.0",
|
||||
"@types/express-session": "^1.19.0",
|
||||
"@types/formidable": "^3.5.1",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/morgan": "^1.9.10",
|
||||
"@types/node": "^25.5.0",
|
||||
"@types/node": "^25.6.0",
|
||||
"@types/supertest": "^7.2.0",
|
||||
"eslint": "^10.0.3",
|
||||
"eslint": "^10.3.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-prettier": "^5.5.5",
|
||||
"eslint-plugin-security": "^4.0.0",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier": "^3.8.3",
|
||||
"supertest": "^7.2.2",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "^4.1.0"
|
||||
"vitest": "^4.1.5"
|
||||
},
|
||||
"packageManager": "yarn@4.13.0"
|
||||
"packageManager": "yarn@4.14.1"
|
||||
}
|
||||
|
||||
261
dist/src/client/script.js
vendored
261
dist/src/client/script.js
vendored
@@ -1,3 +1,4 @@
|
||||
// @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT
|
||||
const DEBUG_ON = true;
|
||||
|
||||
// served from same path as TWTXT file
|
||||
@@ -9,13 +10,67 @@ const debug = (...vals) => {
|
||||
if (DEBUG_ON) console.log(...vals);
|
||||
};
|
||||
|
||||
window.token = undefined;
|
||||
|
||||
window.setCookie = (name, value, expireDays) => {
|
||||
const isSecure = window.location.protocol === 'https';
|
||||
const expireDate = new Date(); // current date
|
||||
expireDate.setTime(
|
||||
expireDate.getTime() + (expireDays ?? 0) * 24 * 60 * 60 * 1000
|
||||
);
|
||||
let expires =
|
||||
expireDays !== undefined ? `expires=${expireDate.toUTCString()}; ` : '';
|
||||
document.cookie = `${name}=${encodeURIComponent(value)}; ${expires}${isSecure ? 'Secure; ' : ''}SameSite=Strict; Path=/`;
|
||||
};
|
||||
|
||||
window.showToast = (message, type = 'success') => {
|
||||
const toast = document.createElement('div');
|
||||
toast.classList.add('toast');
|
||||
if (type === 'error') toast.classList.add('error');
|
||||
|
||||
toast.textContent = message;
|
||||
document.getElementById('toast-container').appendChild(toast);
|
||||
|
||||
setTimeout(() => {
|
||||
toast.style.animation = 'fadeOut 0.5s forwards';
|
||||
setTimeout(() => toast.remove(), 500);
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
window.refreshToken = async (hideToast = false) => {
|
||||
const rememberToggleVal = !!localStorage.getItem(REMEMBER_LOGIN_STORAGE_KEY);
|
||||
|
||||
const res = await fetch(`${TWTXT_FILE_URL}`, {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams({
|
||||
rememberToggle: rememberToggleVal,
|
||||
type: 'refresh',
|
||||
}),
|
||||
credentials: 'include', // Include cookies
|
||||
});
|
||||
|
||||
if (res.ok && res?.body) {
|
||||
token = await new Response(res.body).text();
|
||||
if (rememberToggleVal) {
|
||||
setCookie(ACCESS_TOKEN_COOKIE_KEY, token);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle refresh failure
|
||||
if (!hideToast)
|
||||
showToast('Unable to refresh token, please try again later.', 'error');
|
||||
token = undefined;
|
||||
document.body.classList.remove('js-authorized');
|
||||
throw new Error('Failed to refresh token');
|
||||
};
|
||||
|
||||
export default (async () => {
|
||||
/* DOM Elements */
|
||||
const twtForm = document.getElementById('twtForm'),
|
||||
loginForm = document.getElementById('loginControls-form'),
|
||||
fileBox = document.getElementById('fileBox'),
|
||||
fileContentsSection = document.getElementById('fileContentsSection'),
|
||||
toastContainer = document.getElementById('toast-container'),
|
||||
twtControlsContentInput = document.getElementById(
|
||||
'twtControlsContentInput'
|
||||
),
|
||||
@@ -24,27 +79,12 @@ export default (async () => {
|
||||
twtFileEditButton = document.getElementById('twtControlsEditButton'),
|
||||
menuCheckbox = document.getElementById('hamburgerToggleCheckbox'),
|
||||
twtxtEditFormText = document.getElementById('twtxtEditFormText'),
|
||||
uploadInputs = document.querySelectorAll('.twtControls-uploadInput'),
|
||||
rememberToggle = document.getElementById('loginControls-rememberToggle');
|
||||
|
||||
const lastModifiedDates = {};
|
||||
let isEditing = false,
|
||||
cookie,
|
||||
fileText,
|
||||
token;
|
||||
|
||||
const showToast = (message, type = 'success') => {
|
||||
const toast = document.createElement('div');
|
||||
toast.classList.add('toast');
|
||||
if (type === 'error') toast.classList.add('error');
|
||||
toast.textContent = message;
|
||||
|
||||
toastContainer.appendChild(toast);
|
||||
setTimeout(() => {
|
||||
toast.style.animation = 'fadeOut 0.5s forwards';
|
||||
setTimeout(() => toast.remove(), 500);
|
||||
}, 3000);
|
||||
};
|
||||
fileText;
|
||||
|
||||
const beginEditMode = () => {
|
||||
isEditing = true;
|
||||
@@ -70,17 +110,6 @@ export default (async () => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const setCookie = (name, value, expireDays) => {
|
||||
const isSecure = window.location.protocol === 'https';
|
||||
const expireDate = new Date(); // current date
|
||||
expireDate.setTime(
|
||||
expireDate.getTime() + (expireDays ?? 0) * 24 * 60 * 60 * 1000
|
||||
);
|
||||
let expires =
|
||||
expireDays !== undefined ? `expires=${expireDate.toUTCString()}; ` : '';
|
||||
document.cookie = `${name}=${encodeURIComponent(value)}; ${expires}${isSecure ? 'Secure; ' : ''}SameSite=Strict; Path=/`;
|
||||
};
|
||||
|
||||
const loadTwtxtFile = async (filePath = TWTXT_FILE_URL) => {
|
||||
debug('loadTwtxtFile start');
|
||||
let response;
|
||||
@@ -119,142 +148,8 @@ export default (async () => {
|
||||
debug('loadTwtxtFile end');
|
||||
};
|
||||
|
||||
const refreshToken = async (hideToast = false) => {
|
||||
debug('refreshToken start', hideToast);
|
||||
const rememberToggleVal =
|
||||
localStorage.getItem(REMEMBER_LOGIN_STORAGE_KEY) === 'true';
|
||||
|
||||
const res = await fetch(`${TWTXT_FILE_URL}`, {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams({
|
||||
rememberToggle: rememberToggleVal,
|
||||
type: 'refresh',
|
||||
}),
|
||||
credentials: 'include', // Include cookies
|
||||
});
|
||||
|
||||
if (res.ok && res?.body) {
|
||||
token = await new Response(res.body).text();
|
||||
if (rememberToggleVal) {
|
||||
debug('refreshToken set new accessToken cookie');
|
||||
setCookie(ACCESS_TOKEN_COOKIE_KEY, token);
|
||||
}
|
||||
debug('refreshToken end OK');
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle refresh failure
|
||||
if (!hideToast)
|
||||
showToast('Unable to refresh token, please try again later.', 'error');
|
||||
token = undefined;
|
||||
document.body.classList.remove('js-authorized');
|
||||
debug('refreshToken end error');
|
||||
throw new Error('Failed to refresh token');
|
||||
};
|
||||
|
||||
const uploadFiles = async (files, uploadRoute, secondAttempt = false) => {
|
||||
if (!uploadRoute) return;
|
||||
|
||||
debug('uploadFiles', token, files, uploadRoute, secondAttempt);
|
||||
|
||||
const formData = new FormData();
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
formData.append('files', files[i]);
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(uploadRoute, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
showToast(`File${files.length !== 1 ? 's' : ''} uploaded`);
|
||||
|
||||
const filePath = await res.text();
|
||||
twtControlsContentInput.value += filePath
|
||||
.split('\n')
|
||||
.map((currFilePath) =>
|
||||
[
|
||||
' ',
|
||||
location.protocol,
|
||||
'//',
|
||||
location.hostname,
|
||||
location.protocol !== 'https' && location.port !== 80
|
||||
? ':' + location.port
|
||||
: '',
|
||||
currFilePath,
|
||||
].join('')
|
||||
)
|
||||
.join('');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!secondAttempt) {
|
||||
await refreshToken();
|
||||
return uploadFiles(files, uploadRoute, true);
|
||||
}
|
||||
|
||||
showToast(
|
||||
`Unable to upload image${files.length !== 1 ? 's' : ''} refresh token, please try again later.`,
|
||||
'error'
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
/* Handlers */
|
||||
|
||||
const dragOverHandler = (ev) => {
|
||||
const files = [...ev.dataTransfer.items].filter(
|
||||
(item) => item.kind === 'file'
|
||||
);
|
||||
|
||||
if (files.length > 0) {
|
||||
ev.preventDefault();
|
||||
ev.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
};
|
||||
|
||||
const dragOverWindowHandler = (ev) => {
|
||||
const files = [...ev.dataTransfer.items].filter(
|
||||
(item) => item.kind === 'file'
|
||||
);
|
||||
|
||||
if (files.length > 0) {
|
||||
ev.preventDefault();
|
||||
|
||||
if (!twtControlsContentInput.contains(ev.target)) {
|
||||
ev.dataTransfer.dropEffect = 'none';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const dropHandler = (ev) => {
|
||||
ev.preventDefault();
|
||||
|
||||
if (!uploadInputs.length) return;
|
||||
|
||||
const files = [...ev.dataTransfer.items]
|
||||
.map((item) => item.getAsFile())
|
||||
.filter((file) => file);
|
||||
|
||||
debug('dropHandler', files);
|
||||
uploadFiles(files, uploadInputs[0].getAttribute('data-route'));
|
||||
};
|
||||
|
||||
const dropWindowHandler = (ev) => {
|
||||
if ([...ev.dataTransfer.items].some((item) => item.kind === 'file')) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
const editClickHandler = () => {
|
||||
if (isEditing) return;
|
||||
|
||||
@@ -384,7 +279,8 @@ export default (async () => {
|
||||
};
|
||||
|
||||
const rememberToggleHandler = (ev) => {
|
||||
if (ev.target.checked) {
|
||||
debug('toggle!', ev?.target?.checked);
|
||||
if (ev?.target?.checked) {
|
||||
localStorage.setItem(REMEMBER_LOGIN_STORAGE_KEY, 'true');
|
||||
return;
|
||||
}
|
||||
@@ -392,6 +288,14 @@ export default (async () => {
|
||||
localStorage.removeItem(REMEMBER_LOGIN_STORAGE_KEY);
|
||||
};
|
||||
|
||||
const twtContentInputHandler = () => {
|
||||
console.log('input!');
|
||||
const hasContent =
|
||||
(twtControlsContentInput.value?.trim() ?? '').length !== 0;
|
||||
if (twtSubmitButton && hasContent && !isEditing)
|
||||
twtSubmitButton.removeAttribute('disabled');
|
||||
};
|
||||
|
||||
const twtContentKeyupHandler = (ev) => {
|
||||
const hasContent =
|
||||
(twtControlsContentInput.value?.trim() ?? '').length !== 0;
|
||||
@@ -422,7 +326,7 @@ export default (async () => {
|
||||
debug('twtForm submit data', { twtData });
|
||||
if (!twtContent) return;
|
||||
|
||||
twtData.set('content', twtContent.replaceAll('\n', '\u2028'));
|
||||
twtData.set('content', twtContent.replace(/\n/g, ' \u2028'));
|
||||
const twtBody = new URLSearchParams(twtData);
|
||||
debug('twtForm submit body', { twtBody });
|
||||
|
||||
@@ -461,25 +365,15 @@ export default (async () => {
|
||||
return false;
|
||||
};
|
||||
|
||||
const uploadChangeHandler = (ev) => {
|
||||
uploadFiles(ev.target.files, ev.target.getAttribute('data-route'));
|
||||
};
|
||||
|
||||
/* Attach Handlers to Listeners */
|
||||
|
||||
Array.from(uploadInputs).forEach((uploadInput) => {
|
||||
uploadInput.addEventListener('change', uploadChangeHandler);
|
||||
});
|
||||
|
||||
loginForm.addEventListener('submit', loginFormSubmitHandler);
|
||||
|
||||
twtForm.addEventListener('submit', twtFormSubmitHandler);
|
||||
|
||||
twtForm.addEventListener('keyup', twtContentKeyupHandler);
|
||||
|
||||
twtControlsContentInput.addEventListener('drop', dropHandler);
|
||||
|
||||
twtControlsContentInput.addEventListener('dragover', dragOverHandler);
|
||||
twtForm.addEventListener('input', twtContentInputHandler);
|
||||
|
||||
twtLogoutButton.addEventListener('click', logoutHandler);
|
||||
|
||||
@@ -489,11 +383,19 @@ export default (async () => {
|
||||
|
||||
twtxtEditForm.addEventListener('submit', editSubmitHandler);
|
||||
|
||||
window.addEventListener('dragover', dragOverWindowHandler);
|
||||
|
||||
window.addEventListener('drop', dropWindowHandler);
|
||||
|
||||
rememberToggle.addEventListener('change', rememberToggleHandler);
|
||||
rememberToggle.setAttribute(
|
||||
'checked',
|
||||
!!localStorage.getItem(REMEMBER_LOGIN_STORAGE_KEY)
|
||||
);
|
||||
|
||||
menuCheckbox.addEventListener('click', (evt) => {
|
||||
evt.stopPropagation();
|
||||
});
|
||||
|
||||
window.addEventListener('click', () => {
|
||||
menuCheckbox.checked = false;
|
||||
});
|
||||
|
||||
/* Start App*/
|
||||
|
||||
@@ -504,3 +406,4 @@ export default (async () => {
|
||||
|
||||
debug('client loaded');
|
||||
})();
|
||||
// @license-end
|
||||
|
||||
31
dist/src/client/styles.css
vendored
31
dist/src/client/styles.css
vendored
@@ -77,7 +77,7 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Local styles
|
||||
* Begin TwtKpr Client CSS
|
||||
*/
|
||||
:root {
|
||||
/* #0a0a14 / Vulcan*/
|
||||
@@ -483,33 +483,6 @@ input:disabled {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.twtControls-uploadInputLabel {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
max-width: 100%;
|
||||
text-align: center;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.twtControls-uploadInputLabel-normal {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.twtControls-uploadInputLabel-small {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.twtControls-uploadInputLabel:hover {
|
||||
background-color: var(--bg-hl);
|
||||
color: var(--fg-hl);
|
||||
}
|
||||
|
||||
.twtControls-uploadInput {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.twtControls-submitButton {
|
||||
background-color: var(--bg-hl);
|
||||
border: 1px solid var(--link);
|
||||
@@ -715,3 +688,5 @@ input:disabled {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** End TwtKpr Client CSS */
|
||||
|
||||
3
dist/src/expressPlugin.d.ts
vendored
Normal file
3
dist/src/expressPlugin.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Router } from 'express';
|
||||
import { TwtKprPluginConfigurator, TwtKprConfiguration } from './types.js';
|
||||
export default function expressPlugin(initialConfig?: Partial<TwtKprConfiguration>, initialPlugins?: TwtKprPluginConfigurator[]): Router;
|
||||
58
dist/src/expressPlugin.js
vendored
Normal file
58
dist/src/expressPlugin.js
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
import cookieParser from 'cookie-parser';
|
||||
import Debug from 'debug';
|
||||
import express from 'express';
|
||||
import authCheck from './middlewares/authCheckJWT.js';
|
||||
import twtxtCache from './lib/twtxtCache.js';
|
||||
import getConfiguration from './lib/getConfiguration.js';
|
||||
import queryHandler from './middlewares/queryHandler/index.js';
|
||||
import postHandler, { pluginPostHandler, } from './middlewares/postHandler/index.js';
|
||||
import putHandler, { pluginPutHandler, } from './middlewares/putHandler/index.js';
|
||||
// import emojiPlugin from './plugins/emojiButton/index.js';
|
||||
// import uploadPlugin from './plugins/uploadButton/index.js';
|
||||
// import postToMastodon from './plugins/postToMastodon/index.js';
|
||||
export default function expressPlugin(initialConfig, initialPlugins = []) {
|
||||
const debug = Debug('twtkpr:expressPlugin');
|
||||
const router = express.Router();
|
||||
const config = getConfiguration(initialConfig ?? {});
|
||||
const { mainRoute, publicDirectory, twtxtFilename } = config;
|
||||
const verifyAuthRequest = (req) => authCheck(req, config);
|
||||
const preparedPlugins = initialPlugins
|
||||
// .concat([emojiPlugin, uploadPlugin, postToMastodon])
|
||||
.map((plugin) => plugin(config));
|
||||
debug('initializing cache');
|
||||
const { getFromCache, reloadCache } = twtxtCache({
|
||||
publicDirectory,
|
||||
twtxtFilename,
|
||||
});
|
||||
preparedPlugins.forEach((plugin) => {
|
||||
if (plugin.useFirst)
|
||||
router.use(plugin.useFirst);
|
||||
});
|
||||
debug('adding URL encoder');
|
||||
router.use(express.urlencoded({ extended: true }));
|
||||
debug('adding cookieParser');
|
||||
router.use(cookieParser());
|
||||
debug('adding queryRouter');
|
||||
router.use(mainRoute, queryHandler(config, getFromCache, verifyAuthRequest, preparedPlugins));
|
||||
debug('adding postHandler and putHandler');
|
||||
router.use(mainRoute, postHandler(config, preparedPlugins, reloadCache), putHandler(config, preparedPlugins, reloadCache));
|
||||
debug('adding postHandlers and putHandlers for plugins');
|
||||
router.use(config.pluginRoute, pluginPostHandler(preparedPlugins, verifyAuthRequest, reloadCache), pluginPutHandler(preparedPlugins, verifyAuthRequest, reloadCache));
|
||||
debug('adding use handlers for plugins');
|
||||
preparedPlugins.forEach((plugin) => {
|
||||
if (plugin.use)
|
||||
router.use(plugin.use);
|
||||
});
|
||||
debug('adding static');
|
||||
router.use(express.static(config.publicDirectory));
|
||||
debug('adding default redirect');
|
||||
router.get('/', (_, res) => {
|
||||
res.redirect(mainRoute);
|
||||
});
|
||||
preparedPlugins.forEach((plugin) => {
|
||||
if (plugin.useLast)
|
||||
router.use(plugin.useLast);
|
||||
});
|
||||
return router;
|
||||
}
|
||||
//# sourceMappingURL=expressPlugin.js.map
|
||||
1
dist/src/expressPlugin.js.map
vendored
Normal file
1
dist/src/expressPlugin.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"expressPlugin.js","sourceRoot":"","sources":["../../src/expressPlugin.ts"],"names":[],"mappings":"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"}
|
||||
4
dist/src/index.d.ts
vendored
4
dist/src/index.d.ts
vendored
@@ -1 +1,3 @@
|
||||
export { default } from "./plugin.js";
|
||||
export { default } from './expressPlugin.js';
|
||||
export { combineStreams, getReadStream } from './lib/utils.js';
|
||||
export type { MimeOptions, TwtKprConfiguration, TwtKprPluginConfiguration, } from './types.js';
|
||||
|
||||
3
dist/src/index.js
vendored
3
dist/src/index.js
vendored
@@ -1,2 +1,3 @@
|
||||
export { default } from "./plugin.js";
|
||||
export { default } from './expressPlugin.js';
|
||||
export { combineStreams, getReadStream } from './lib/utils.js';
|
||||
//# sourceMappingURL=index.js.map
|
||||
2
dist/src/index.js.map
vendored
2
dist/src/index.js.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC"}
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
||||
5
dist/src/lib/constants.d.ts
vendored
5
dist/src/lib/constants.d.ts
vendored
@@ -1,7 +1,8 @@
|
||||
export declare const DEFAULT_PRIVATE_DIRECTORY = ".data";
|
||||
export declare const DEFAULT_PUBLIC_DIRECTORY = "public";
|
||||
export declare const DEFAULT_TWTXT_FILENAME = "twtxt.txt";
|
||||
export declare const DEFAULT_ROUTE = "/twtxt.txt";
|
||||
export declare const DEFAULT_PRIVATE_DIRECTORY = ".data";
|
||||
export declare const DEFAULT_PUBLIC_DIRECTORY = "public";
|
||||
export declare const DEFAULT_PLUGIN_ROUTE = "/";
|
||||
export declare const DEFAULT_POST_LIMITER_ACTIVE = true;
|
||||
export declare const DEFAULT_QUERY_PARAMETER_APP = "app";
|
||||
export declare const DEFAULT_QUERY_PARAMETER_CSS = "css";
|
||||
|
||||
5
dist/src/lib/constants.js
vendored
5
dist/src/lib/constants.js
vendored
@@ -1,9 +1,10 @@
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
export const DEFAULT_PRIVATE_DIRECTORY = '.data';
|
||||
export const DEFAULT_PUBLIC_DIRECTORY = 'public';
|
||||
export const DEFAULT_TWTXT_FILENAME = 'twtxt.txt';
|
||||
export const DEFAULT_ROUTE = `/${DEFAULT_TWTXT_FILENAME}`;
|
||||
export const DEFAULT_PRIVATE_DIRECTORY = '.data';
|
||||
export const DEFAULT_PUBLIC_DIRECTORY = 'public';
|
||||
export const DEFAULT_PLUGIN_ROUTE = '/';
|
||||
export const DEFAULT_POST_LIMITER_ACTIVE = true;
|
||||
export const DEFAULT_QUERY_PARAMETER_APP = 'app';
|
||||
export const DEFAULT_QUERY_PARAMETER_CSS = 'css';
|
||||
|
||||
2
dist/src/lib/constants.js.map
vendored
2
dist/src/lib/constants.js.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/lib/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,CAAC,MAAM,yBAAyB,GAAG,OAAO,CAAC;AACjD,MAAM,CAAC,MAAM,wBAAwB,GAAG,QAAQ,CAAC;AACjD,MAAM,CAAC,MAAM,sBAAsB,GAAG,WAAW,CAAC;AAClD,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,sBAAsB,EAAE,CAAC;AAE1D,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC;AAEhD,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,CAAC;AACjD,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,CAAC;AACjD,MAAM,CAAC,MAAM,iCAAiC,GAAG,WAAW,CAAC;AAC7D,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;AAC/C,MAAM,CAAC,MAAM,8BAA8B,GAAG,QAAQ,CAAC;AACvD,MAAM,CAAC,MAAM,gCAAgC,GAAG,UAAU,CAAC;AAC3D,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,CAAC;AACjD,MAAM,CAAC,MAAM,4BAA4B,GAAG,MAAM,CAAC;AAEnD,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAC1C,MAAM,CAAC,MAAM,iCAAiC,GAAG,EAAE,CAAC;AACpD,MAAM,CAAC,MAAM,oBAAoB,GAAG,OAAO,CAAC;AAC5C,MAAM,CAAC,MAAM,uBAAuB,GAAG,OAAO,CAAC;AAE/C,kBAAkB;AAClB,MAAM,CAAC,MAAM,6BAA6B,GAAG,QAAQ,CAAC;AACtD,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,CAAC;AAEnD,MAAM,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC"}
|
||||
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/lib/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,CAAC,MAAM,sBAAsB,GAAG,WAAW,CAAC;AAClD,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,sBAAsB,EAAE,CAAC;AAC1D,MAAM,CAAC,MAAM,yBAAyB,GAAG,OAAO,CAAC;AACjD,MAAM,CAAC,MAAM,wBAAwB,GAAG,QAAQ,CAAC;AACjD,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAExC,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC;AAEhD,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,CAAC;AACjD,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,CAAC;AACjD,MAAM,CAAC,MAAM,iCAAiC,GAAG,WAAW,CAAC;AAC7D,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;AAC/C,MAAM,CAAC,MAAM,8BAA8B,GAAG,QAAQ,CAAC;AACvD,MAAM,CAAC,MAAM,gCAAgC,GAAG,UAAU,CAAC;AAC3D,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,CAAC;AACjD,MAAM,CAAC,MAAM,4BAA4B,GAAG,MAAM,CAAC;AAEnD,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAC1C,MAAM,CAAC,MAAM,iCAAiC,GAAG,EAAE,CAAC;AACpD,MAAM,CAAC,MAAM,oBAAoB,GAAG,OAAO,CAAC;AAC5C,MAAM,CAAC,MAAM,uBAAuB,GAAG,OAAO,CAAC;AAE/C,kBAAkB;AAClB,MAAM,CAAC,MAAM,6BAA6B,GAAG,QAAQ,CAAC;AACtD,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,CAAC;AAEnD,MAAM,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC"}
|
||||
1
dist/src/lib/env.d.ts
vendored
1
dist/src/lib/env.d.ts
vendored
@@ -4,6 +4,7 @@ export declare const env: {
|
||||
TWTKPR_REFRESH_SECRET: string;
|
||||
TWTKPR_ACCESS_SECRET: string;
|
||||
TWTKPR_DEFAULT_ROUTE: string;
|
||||
TWTKPR_PLUGIN_ROUTE: string;
|
||||
TWTKPR_PRIVATE_DIRECTORY: string;
|
||||
TWTKPR_PUBLIC_DIRECTORY: string;
|
||||
TWTKPR_QUERY_PARAMETER_APP: string;
|
||||
|
||||
4
dist/src/lib/env.js
vendored
4
dist/src/lib/env.js
vendored
@@ -1,7 +1,7 @@
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { z } from 'zod/v4';
|
||||
import { DEFAULT_POST_LIMITER_ACTIVE, DEFAULT_PRIVATE_DIRECTORY, DEFAULT_PUBLIC_DIRECTORY, DEFAULT_QUERY_PARAMETER_APP, DEFAULT_QUERY_PARAMETER_CSS, DEFAULT_QUERY_PARAMETER_FOLLOWING, DEFAULT_QUERY_PARAMETER_JS, DEFAULT_QUERY_PARAMETER_LOGOUT, DEFAULT_QUERY_PARAMETER_METADATA, DEFAULT_QUERY_PARAMETER_TWT, DEFAULT_QUERY_PARAMETER_TWTS, DEFAULT_ROUTE, DEFAULT_TWTXT_FILENAME, DEFAULT_UPLOAD_ACTIVE, DEFAULT_UPLOAD_ALLOWED_MIME_TYPES, DEFAULT_UPLOAD_ENCODING, DEFAULT_UPLOAD_HASH_ALGORITHM, DEFAULT_UPLOAD_KEEP_EXTENSIONS, DEFAULT_UPLOAD_ROUTE, } from './constants.js';
|
||||
import { DEFAULT_PLUGIN_ROUTE, DEFAULT_POST_LIMITER_ACTIVE, DEFAULT_PRIVATE_DIRECTORY, DEFAULT_PUBLIC_DIRECTORY, DEFAULT_QUERY_PARAMETER_APP, DEFAULT_QUERY_PARAMETER_CSS, DEFAULT_QUERY_PARAMETER_FOLLOWING, DEFAULT_QUERY_PARAMETER_JS, DEFAULT_QUERY_PARAMETER_LOGOUT, DEFAULT_QUERY_PARAMETER_METADATA, DEFAULT_QUERY_PARAMETER_TWT, DEFAULT_QUERY_PARAMETER_TWTS, DEFAULT_ROUTE, DEFAULT_TWTXT_FILENAME, DEFAULT_UPLOAD_ACTIVE, DEFAULT_UPLOAD_ALLOWED_MIME_TYPES, DEFAULT_UPLOAD_ENCODING, DEFAULT_UPLOAD_HASH_ALGORITHM, DEFAULT_UPLOAD_KEEP_EXTENSIONS, DEFAULT_UPLOAD_ROUTE, } from './constants.js';
|
||||
/*
|
||||
The following keys are expected to exist in `process.env`, either as listed, or without the
|
||||
`TWTKPR_` prefix
|
||||
@@ -18,6 +18,7 @@ const envSchema = z.object({
|
||||
TWTKPR_ACCESS_SECRET: z.string().default(''),
|
||||
// vars with default values
|
||||
TWTKPR_DEFAULT_ROUTE: z.string().default(DEFAULT_ROUTE),
|
||||
TWTKPR_PLUGIN_ROUTE: z.string().default(DEFAULT_PLUGIN_ROUTE),
|
||||
TWTKPR_PRIVATE_DIRECTORY: z.string().default(DEFAULT_PRIVATE_DIRECTORY),
|
||||
TWTKPR_PUBLIC_DIRECTORY: z.string().default(DEFAULT_PUBLIC_DIRECTORY),
|
||||
TWTKPR_QUERY_PARAMETER_APP: z.string().default(DEFAULT_QUERY_PARAMETER_APP),
|
||||
@@ -100,6 +101,7 @@ const parseEnv = () => {
|
||||
NODE_ENV: process.env.TWTKPR_NODE_ENV || process.env.NODE_ENV,
|
||||
TWTKPR_ACCESS_SECRET: process.env.TWTKPR_ACCESS_SECRET || process.env.ACCESS_SECRET,
|
||||
TWTKPR_DEFAULT_ROUTE: process.env.TWTKPR_DEFAULT_ROUTE || process.env.DEFAULT_ROUTE,
|
||||
TWTKPR_PLUGIN_ROUTE: process.env.TWTKPR_PLUGIN_ROUTE || process.env.TWTKPR_PLUGIN_ROUTE,
|
||||
TWTKPR_PRIVATE_DIRECTORY: process.env.TWTKPR_PRIVATE_DIRECTORY || process.env.PRIVATE_DIRECTORY,
|
||||
TWTKPR_PUBLIC_DIRECTORY: process.env.TWTKPR_PUBLIC_DIRECTORY || process.env.PUBLIC_DIRECTORY,
|
||||
TWTKPR_REFRESH_SECRET: process.env.TWTKPR_REFRESH_SECRET || process.env.REFRESH_SECRET,
|
||||
|
||||
2
dist/src/lib/env.js.map
vendored
2
dist/src/lib/env.js.map
vendored
File diff suppressed because one or more lines are too long
28
dist/src/lib/getConfiguration.js
vendored
28
dist/src/lib/getConfiguration.js
vendored
@@ -52,18 +52,23 @@ const getDestinationByMimeTypeConfiguration = (allowedMimeTypes) => {
|
||||
* @returns
|
||||
*/
|
||||
export default function getConfiguration(initialConfiguration) {
|
||||
const { mainRoute = env.TWTKPR_DEFAULT_ROUTE, privateDirectory = env.TWTKPR_PRIVATE_DIRECTORY, publicDirectory = env.TWTKPR_PUBLIC_DIRECTORY, twtxtFilename = env.TWTKPR_TWTXT_FILENAME, postLimiterConfiguration, queryParameters, uploadConfiguration, } = initialConfiguration ?? {};
|
||||
const { mainRoute = env.TWTKPR_DEFAULT_ROUTE, pluginRoute = env.TWTKPR_PLUGIN_ROUTE, privateDirectory = env.TWTKPR_PRIVATE_DIRECTORY, publicDirectory = env.TWTKPR_PUBLIC_DIRECTORY, twtxtFilename = env.TWTKPR_TWTXT_FILENAME, postLimiterConfiguration, queryParameters,
|
||||
// uploadConfiguration,
|
||||
} = initialConfiguration ?? {};
|
||||
const { active: postLimiterActive = env.TWTKPR_POST_LIMITER_ACTIVE, ...otherPostLimiterProps } = postLimiterConfiguration ?? {};
|
||||
const { app = env.TWTKPR_QUERY_PARAMETER_APP, css = env.TWTKPR_QUERY_PARAMETER_CSS, following = env.TWTKPR_QUERY_PARAMETER_FOLLOWING, js = env.TWTKPR_QUERY_PARAMETER_JS, logout = env.TWTKPR_QUERY_PARAMETER_LOGOUT, metadata = env.TWTKPR_QUERY_PARAMETER_METADATA, twt = env.TWTKPR_QUERY_PARAMETER_TWT, twts = env.TWTKPR_QUERY_PARAMETER_TWTS, } = queryParameters ?? {};
|
||||
const { active: uploadActive = env.TWTKPR_UPLOAD_ACTIVE, allowEmptyFiles = env.TWTKPR_UPLOAD_ALLOW_EMPTY_FILES, allowedMimeTypes = env.TWTKPR_UPLOAD_ALLOWED_MIME_TYPES, createDirsFromUploads = env.TWTKPR_UPLOAD_CREATE_DIRS_FROM_UPLOADS, directory = env.TWTKPR_UPLOAD_DIRECTORY, encoding = env.TWTKPR_UPLOAD_ENCODING, fileWriteStreamHandler, filter = () => true, hashAlgorithm = env.TWTKPR_UPLOAD_HASH_ALGORITHM, keepExtensions = env.TWTKPR_UPLOAD_KEEP_EXTENSIONS, maxFields = env.TWTKPR_UPLOAD_MAX_FIELDS, maxFileSize = env.TWTKPR_UPLOAD_MAX_FIELDS_SIZE, maxFiles = env.TWTKPR_UPLOAD_MAX_FILES, maxTotalFileSize = env.TWTKPR_UPLOAD_MAX_TOTAL_FILE_SIZE, minFileSize = env.TWTKPR_UPLOAD_MIN_FILE_SIZE, route = env.TWTKPR_UPLOAD_ROUTE, } = uploadConfiguration ?? {};
|
||||
return {
|
||||
// secrets cannot be provided through configuration file, must use ENV / .env
|
||||
accessSecret: env.TWTKPR_ACCESS_SECRET,
|
||||
refreshSecret: env.TWTKPR_REFRESH_SECRET,
|
||||
mainRoute,
|
||||
pluginRoute,
|
||||
privateDirectory,
|
||||
publicDirectory,
|
||||
twtxtFilename,
|
||||
plugins: {
|
||||
...(initialConfiguration?.plugins ?? {}),
|
||||
},
|
||||
postLimiterConfiguration: {
|
||||
active: postLimiterActive,
|
||||
...(otherPostLimiterProps ?? {}),
|
||||
@@ -79,25 +84,6 @@ export default function getConfiguration(initialConfiguration) {
|
||||
twt,
|
||||
twts,
|
||||
},
|
||||
uploadConfiguration: {
|
||||
...uploadConfiguration,
|
||||
active: uploadActive,
|
||||
allowEmptyFiles,
|
||||
allowedMimeTypes: getDestinationByMimeTypeConfiguration(allowedMimeTypes),
|
||||
createDirsFromUploads,
|
||||
directory,
|
||||
encoding,
|
||||
fileWriteStreamHandler,
|
||||
filter,
|
||||
hashAlgorithm: hashAlgorithm,
|
||||
keepExtensions,
|
||||
maxFields,
|
||||
maxFileSize,
|
||||
maxFiles,
|
||||
maxTotalFileSize,
|
||||
minFileSize,
|
||||
route,
|
||||
},
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=getConfiguration.js.map
|
||||
2
dist/src/lib/getConfiguration.js.map
vendored
2
dist/src/lib/getConfiguration.js.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"getConfiguration.js","sourceRoot":"","sources":["../../../src/lib/getConfiguration.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"getConfiguration.js","sourceRoot":"","sources":["../../../src/lib/getConfiguration.ts"],"names":[],"mappings":"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"}
|
||||
3
dist/src/lib/twtxtCache.d.ts
vendored
3
dist/src/lib/twtxtCache.d.ts
vendored
@@ -1,4 +1,3 @@
|
||||
import { NodeCache } from '@cacheable/node-cache';
|
||||
import { TwtKprConfiguration } from '../types.js';
|
||||
/**
|
||||
*
|
||||
@@ -6,6 +5,6 @@ import { TwtKprConfiguration } from '../types.js';
|
||||
* @returns
|
||||
*/
|
||||
export default function twtxtCache({ publicDirectory, twtxtFilename, }: Pick<TwtKprConfiguration, 'publicDirectory' | 'twtxtFilename'>): {
|
||||
cache: NodeCache<unknown>;
|
||||
getFromCache: (key?: string) => Promise<unknown>;
|
||||
reloadCache: () => Promise<void>;
|
||||
};
|
||||
|
||||
23
dist/src/lib/twtxtCache.js
vendored
23
dist/src/lib/twtxtCache.js
vendored
@@ -11,8 +11,20 @@ import { parseTwtxt } from 'twtxt-lib';
|
||||
export default function twtxtCache({ publicDirectory, twtxtFilename, }) {
|
||||
let isLoaded = false;
|
||||
const debug = Debug('twtkpr:twtxtCache');
|
||||
const cache = new NodeCache();
|
||||
const reloadCache = async () => {
|
||||
const defaultCacheOptions = {
|
||||
stdTTL: 299,
|
||||
checkperiod: 300,
|
||||
};
|
||||
const cache = new NodeCache(defaultCacheOptions);
|
||||
const getFromCache = async (key = '') => {
|
||||
debug(`checking cache for key: ${key}`);
|
||||
if (!key)
|
||||
return undefined;
|
||||
const value = cache.get(key);
|
||||
if (value)
|
||||
return value;
|
||||
debug('Not found, reloading keys');
|
||||
cache.flushAll();
|
||||
const fileText = await fsp.readFile(path.join(publicDirectory, twtxtFilename), 'utf8');
|
||||
const parsedFile = parseTwtxt(fileText);
|
||||
Object.keys(parsedFile).forEach((key) => {
|
||||
@@ -21,10 +33,13 @@ export default function twtxtCache({ publicDirectory, twtxtFilename, }) {
|
||||
cache.set('source', fileText);
|
||||
debug(`cache ${isLoaded ? 're' : ''}loaded`);
|
||||
isLoaded = true;
|
||||
return cache.get(key);
|
||||
};
|
||||
const reloadCache = async () => {
|
||||
cache.flushAll();
|
||||
};
|
||||
reloadCache();
|
||||
return {
|
||||
cache,
|
||||
getFromCache,
|
||||
reloadCache,
|
||||
};
|
||||
}
|
||||
|
||||
2
dist/src/lib/twtxtCache.js.map
vendored
2
dist/src/lib/twtxtCache.js.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"twtxtCache.js","sourceRoot":"","sources":["../../../src/lib/twtxtCache.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,kBAAkB,CAAC;AACnC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAGvC;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,EAClC,eAAe,EACf,aAAa,GACmD;IAChE,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,MAAM,KAAK,GAAG,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAEzC,MAAM,KAAK,GAAG,IAAI,SAAS,EAAE,CAAC;IAE9B,MAAM,WAAW,GAAG,KAAK,IAAI,EAAE;QAC9B,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,QAAQ,CAClC,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,EACzC,MAAM,CACN,CAAC;QAEF,MAAM,UAAU,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;YACvC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,GAA8B,CAAC,CAAC,CAAC,CAAC,aAAa;QAC1E,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC9B,KAAK,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;QAE7C,QAAQ,GAAG,IAAI,CAAC;IACjB,CAAC,CAAC;IAEF,WAAW,EAAE,CAAC;IAEd,OAAO;QACN,KAAK;QACL,WAAW;KACX,CAAC;AACH,CAAC"}
|
||||
{"version":3,"file":"twtxtCache.js","sourceRoot":"","sources":["../../../src/lib/twtxtCache.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,kBAAkB,CAAC;AACnC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,SAAS,EAAoB,MAAM,uBAAuB,CAAC;AACpE,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAGvC;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,EAClC,eAAe,EACf,aAAa,GACmD;IAChE,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,MAAM,KAAK,GAAG,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAEzC,MAAM,mBAAmB,GAAqB;QAC7C,MAAM,EAAE,GAAG;QACX,WAAW,EAAE,GAAG;KAChB,CAAC;IAEF,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAEjD,MAAM,YAAY,GAAG,KAAK,EAAE,GAAG,GAAG,EAAE,EAAE,EAAE;QACvC,KAAK,CAAC,2BAA2B,GAAG,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,GAAG;YAAE,OAAO,SAAS,CAAC;QAE3B,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;QAExB,KAAK,CAAC,2BAA2B,CAAC,CAAC;QACnC,KAAK,CAAC,QAAQ,EAAE,CAAC;QAEjB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,QAAQ,CAClC,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,EACzC,MAAM,CACN,CAAC;QAEF,MAAM,UAAU,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;YACvC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,GAA8B,CAAC,CAAC,CAAC,CAAC,aAAa;QAC1E,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC9B,KAAK,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;QAE7C,QAAQ,GAAG,IAAI,CAAC;QAEhB,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,KAAK,IAAI,EAAE;QAC9B,KAAK,CAAC,QAAQ,EAAE,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO;QACN,YAAY;QACZ,WAAW;KACX,CAAC;AACH,CAAC"}
|
||||
8
dist/src/lib/utils.d.ts
vendored
8
dist/src/lib/utils.d.ts
vendored
@@ -1,3 +1,5 @@
|
||||
import { PassThrough } from 'node:stream';
|
||||
export declare function combineStreams(streams: any[]): PassThrough;
|
||||
/**
|
||||
*
|
||||
* @param userId
|
||||
@@ -25,6 +27,12 @@ export declare const generateRefreshToken: (userId: string, secret?: string, ext
|
||||
* @returns
|
||||
*/
|
||||
export declare const getQueryParameterArray: (value?: unknown | unknown[]) => string[];
|
||||
/**
|
||||
*
|
||||
* @param pathToFile
|
||||
* @returns
|
||||
*/
|
||||
export declare const getReadStream: (pathToFile: string) => import("node:fs").ReadStream;
|
||||
/**
|
||||
*
|
||||
* @param value
|
||||
|
||||
41
dist/src/lib/utils.js
vendored
41
dist/src/lib/utils.js
vendored
@@ -1,7 +1,34 @@
|
||||
import crypto from 'node:crypto';
|
||||
import { createReadStream } from 'node:fs';
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
import { PassThrough } from 'node:stream';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
async function combineWithPassthrough(sources, destination) {
|
||||
for (const stream of sources) {
|
||||
await new Promise((resolve, reject) => {
|
||||
let s = stream;
|
||||
if (typeof stream === 'function') {
|
||||
s = stream();
|
||||
}
|
||||
if (typeof s === 'string') {
|
||||
destination.push(s);
|
||||
destination.push(null);
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
s.pipe(destination, { end: false });
|
||||
s.on('end', resolve);
|
||||
s.on('error', reject);
|
||||
});
|
||||
}
|
||||
destination.emit('end');
|
||||
}
|
||||
export function combineStreams(streams) {
|
||||
const stream = new PassThrough();
|
||||
combineWithPassthrough(streams, stream).catch((err) => stream.destroy(err));
|
||||
return stream;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param userId
|
||||
@@ -37,6 +64,20 @@ export const generateRefreshToken = (userId, secret = '', extendRefresh = false)
|
||||
export const getQueryParameterArray = (value = []) => Array.isArray(value)
|
||||
? value.map((val) => `${val}`.trim())
|
||||
: [`${value}`.trim()];
|
||||
/**
|
||||
*
|
||||
* @param pathToFile
|
||||
* @returns
|
||||
*/
|
||||
export const getReadStream = (pathToFile) => {
|
||||
const theStream = createReadStream(pathToFile);
|
||||
theStream.on('error', (err) => {
|
||||
console.error(err);
|
||||
theStream.close();
|
||||
theStream.push(null);
|
||||
});
|
||||
return theStream;
|
||||
};
|
||||
/**
|
||||
*
|
||||
* @param value
|
||||
|
||||
2
dist/src/lib/utils.js.map
vendored
2
dist/src/lib/utils.js.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,GAAG,MAAM,cAAc,CAAC;AAC/B,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC;AAEpC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAc,EAAE,MAAM,GAAG,EAAE,EAAE,EAAE,CAClE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;AAExD;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAW,EAAE,EAAE,CAC3C,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAEvD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CACnC,MAAc,EACd,MAAM,GAAG,EAAE,EACX,aAAa,GAAG,KAAK,EACpB,EAAE;IACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,CAAC,kCAAkC;IAE5D,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE;QACvD,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;KACtC,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC;AACd,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,QAA6B,EAAE,EAAE,EAAE,CACzE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;IACnB,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAExB;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,KAAwB,EAAE,EAAE,CAChE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAEzD;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EAAE,QAAgB,EAAE,EAAE;IAC5D,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IAChE,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AAC7B,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAC9B,QAAyB,EACzB,QAAgB,EACf,EAAE;IACH,MAAM,cAAc,GACnB,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAE7E,MAAM,SAAS,CAAC,QAAQ,EAAE,cAAc,EAAE;QACzC,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,GAAG;KACT,CAAC,CAAC;AACJ,CAAC,CAAC"}
|
||||
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/lib/utils.ts"],"names":[],"mappings":"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"}
|
||||
3
dist/src/middlewares/postHandler/index.d.ts
vendored
3
dist/src/middlewares/postHandler/index.d.ts
vendored
@@ -1 +1,2 @@
|
||||
export { default } from "./postHandler.js";
|
||||
export { default } from './postHandler.js';
|
||||
export { default as pluginPostHandler } from './pluginPostHandler.js';
|
||||
|
||||
3
dist/src/middlewares/postHandler/index.js
vendored
3
dist/src/middlewares/postHandler/index.js
vendored
@@ -1,2 +1,3 @@
|
||||
export { default } from "./postHandler.js";
|
||||
export { default } from './postHandler.js';
|
||||
export { default as pluginPostHandler } from './pluginPostHandler.js';
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/middlewares/postHandler/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC"}
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/middlewares/postHandler/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,wBAAwB,CAAC"}
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { NextFunction, Request, Response } from 'express';
|
||||
import NodeCache from '@cacheable/node-cache';
|
||||
/**
|
||||
*
|
||||
* @param req
|
||||
@@ -9,4 +8,4 @@ import NodeCache from '@cacheable/node-cache';
|
||||
* @param reloadCache
|
||||
* @returns
|
||||
*/
|
||||
export default function memoryCache(req: Request, res: Response, next: NextFunction, cache: NodeCache<unknown>, reloadCache: () => Promise<void>): Promise<void>;
|
||||
export default function memoryCache(req: Request, res: Response, next: NextFunction, reloadCache: () => Promise<void>): Promise<void>;
|
||||
|
||||
@@ -9,13 +9,15 @@ const debug = Debug('twtkpr:memoryCache');
|
||||
* @param reloadCache
|
||||
* @returns
|
||||
*/
|
||||
export default async function memoryCache(req, res, next, cache, reloadCache) {
|
||||
if (cache.keys().length && !['DELETE', 'POST', 'PUT'].includes(req.method)) {
|
||||
export default async function memoryCache(req, res, next, reloadCache) {
|
||||
debug(req.method);
|
||||
if (!['DELETE', 'POST', 'PUT'].includes(req.method)) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
reloadCache()
|
||||
.then(() => {
|
||||
debug('Cache reloaded');
|
||||
next();
|
||||
})
|
||||
.catch((err) => {
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"memoryCache.js","sourceRoot":"","sources":["../../../../src/middlewares/postHandler/memoryCache.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,KAAK,GAAG,KAAK,CAAC,oBAAoB,CAAC,CAAC;AAE1C;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,WAAW,CACxC,GAAY,EACZ,GAAa,EACb,IAAkB,EAClB,KAAyB,EACzB,WAAgC;IAEhC,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5E,IAAI,EAAE,CAAC;QACP,OAAO;IACR,CAAC;IAED,WAAW,EAAE;SACX,IAAI,CAAC,GAAG,EAAE;QACV,IAAI,EAAE,CAAC;IACR,CAAC,CAAC;SACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACd,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
||||
{"version":3,"file":"memoryCache.js","sourceRoot":"","sources":["../../../../src/middlewares/postHandler/memoryCache.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,KAAK,GAAG,KAAK,CAAC,oBAAoB,CAAC,CAAC;AAE1C;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,WAAW,CACxC,GAAY,EACZ,GAAa,EACb,IAAkB,EAClB,WAAgC;IAEhC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClB,IAAI,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACrD,IAAI,EAAE,CAAC;QACP,OAAO;IACR,CAAC;IAED,WAAW,EAAE;SACX,IAAI,CAAC,GAAG,EAAE;QACV,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACxB,IAAI,EAAE,CAAC;IACR,CAAC,CAAC;SACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACd,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
||||
7
dist/src/middlewares/postHandler/pluginPostHandler.d.ts
vendored
Normal file
7
dist/src/middlewares/postHandler/pluginPostHandler.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Request } from 'express';
|
||||
import { TwtKprPluginConfiguration } from '../../types.js';
|
||||
/**
|
||||
*
|
||||
* @param config * @returns
|
||||
*/
|
||||
export default function pluginPostHandler(plugins: TwtKprPluginConfiguration[] | undefined, verifyAuthRequest: (r: Request) => Promise<boolean>, reloadCache?: () => Promise<void>): import("express-serve-static-core").Router;
|
||||
30
dist/src/middlewares/postHandler/pluginPostHandler.js
vendored
Normal file
30
dist/src/middlewares/postHandler/pluginPostHandler.js
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import Debug from 'debug';
|
||||
import express from 'express';
|
||||
const debug = Debug('twtkpr:postHandler');
|
||||
/**
|
||||
*
|
||||
* @param config * @returns
|
||||
*/
|
||||
export default function pluginPostHandler(plugins = [], verifyAuthRequest, reloadCache) {
|
||||
const router = express.Router();
|
||||
const pluginRoutes = [].concat(...Object.keys(plugins)
|
||||
.filter((key) => plugins[key]
|
||||
?.postRoutes?.length)
|
||||
.map((key) => plugins[key]
|
||||
.postRoutes));
|
||||
pluginRoutes.forEach(({ handler, path, requiresAuth }) => {
|
||||
debug(`adding POST plugin router for ${path}`);
|
||||
router.post(path, async (req, res, next) => {
|
||||
debug(`handling POST plugin route to ${path}`);
|
||||
if (requiresAuth && !(await verifyAuthRequest(req))) {
|
||||
debug('auth check failed');
|
||||
next();
|
||||
return;
|
||||
}
|
||||
handler(req, res, next);
|
||||
reloadCache?.();
|
||||
});
|
||||
});
|
||||
return router;
|
||||
}
|
||||
//# sourceMappingURL=pluginPostHandler.js.map
|
||||
1
dist/src/middlewares/postHandler/pluginPostHandler.js.map
vendored
Normal file
1
dist/src/middlewares/postHandler/pluginPostHandler.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"pluginPostHandler.js","sourceRoot":"","sources":["../../../../src/middlewares/postHandler/pluginPostHandler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,OAA4C,MAAM,SAAS,CAAC;AAGnE,MAAM,KAAK,GAAG,KAAK,CAAC,oBAAoB,CAAC,CAAC;AAE1C;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,iBAAiB,CACxC,UAAuC,EAAE,EACzC,iBAAmD,EACnD,WAAiC;IAEjC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAEhC,MAAM,YAAY,GAAI,EAA0B,CAAC,MAAM,CACtD,GAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;SACtB,MAAM,CACN,CAAC,GAAG,EAAE,EAAE,CACN,OAAO,CAAC,GAA2B,CAA+B;QAClE,EAAE,UAAU,EAAE,MAAM,CACtB;SACA,GAAG,CACH,CAAC,GAAG,EAAE,EAAE,CACN,OAAO,CAAC,GAA2B,CAA+B;SACjE,UAAU,CACa,CAC5B,CAAC;IAEF,YAAY,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE;QACxD,KAAK,CAAC,iCAAiC,IAAI,EAAE,CAAC,CAAC;QAE/C,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YAC1C,KAAK,CAAC,iCAAiC,IAAI,EAAE,CAAC,CAAC;YAE/C,IAAI,YAAY,IAAI,CAAC,CAAC,MAAM,iBAAiB,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBACrD,KAAK,CAAC,mBAAmB,CAAC,CAAC;gBAC3B,IAAI,EAAE,CAAC;gBACP,OAAO;YACR,CAAC;YAED,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YACxB,WAAW,EAAE,EAAE,CAAC;QACjB,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AACf,CAAC"}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { TwtKprConfiguration } from '../../types.js';
|
||||
import { TwtKprConfiguration, TwtKprPluginConfiguration } from '../../types.js';
|
||||
/**
|
||||
*
|
||||
* @param config
|
||||
* @returns
|
||||
*/
|
||||
export default function postHandler(config: TwtKprConfiguration): import("express-serve-static-core").Router;
|
||||
export default function postHandler(config: TwtKprConfiguration, plugins?: TwtKprPluginConfiguration[], reloadCache?: () => Promise<void>): import("express-serve-static-core").Router;
|
||||
|
||||
@@ -13,7 +13,7 @@ const debug = Debug('twtkpr:postHandler');
|
||||
* @param config
|
||||
* @returns
|
||||
*/
|
||||
export default function postHandler(config) {
|
||||
export default function postHandler(config, plugins = [], reloadCache) {
|
||||
const { postLimiterConfiguration } = config;
|
||||
const { active: isLimiterActive, ...otherLimiterProps } = postLimiterConfiguration ?? {};
|
||||
const postLimiter = isLimiterActive
|
||||
@@ -49,8 +49,11 @@ export default function postHandler(config) {
|
||||
return;
|
||||
}
|
||||
debug('auth check succeeded');
|
||||
if (type === 'twt' || content)
|
||||
return twt(req, res, config);
|
||||
if (type === 'twt' || content) {
|
||||
twt(req, res, config, plugins);
|
||||
reloadCache?.();
|
||||
return;
|
||||
}
|
||||
if (type === 'editFile')
|
||||
return editFile(req, res, config);
|
||||
next();
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"postHandler.js","sourceRoot":"","sources":["../../../../src/middlewares/postHandler/postHandler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,OAA4C,MAAM,SAAS,CAAC;AACnE,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAE3C,OAAO,SAAS,MAAM,mCAAmC,CAAC;AAE1D,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,QAAQ,MAAM,2BAA2B,CAAC;AAEjD,MAAM,KAAK,GAAG,KAAK,CAAC,oBAAoB,CAAC,CAAC;AAE1C;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,MAA2B;IAC9D,MAAM,EAAE,wBAAwB,EAAE,GAAG,MAAM,CAAC;IAC5C,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,iBAAiB,EAAE,GACtD,wBAAwB,IAAI,EAAE,CAAC;IAEhC,MAAM,WAAW,GAAG,eAAe;QAClC,CAAC,CAAC,SAAS,CAAC;YACV,GAAG,iBAAiB;SACpB,CAAC;QACH,CAAC,CAAC,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;YACpD,IAAI,EAAE,CAAC;QACR,CAAC,CAAC;IAEJ,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAE7B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAEhC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACtD,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;QACzC,KAAK,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAExC,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvB,KAAK,CAAC,aAAa,CAAC,CAAC;YACrB,GAAG,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;YAChC,GAAG,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;YAC/B,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;YACxB,OAAO;QACR,CAAC;QAED,IAAI,IAAI,KAAK,OAAO;YAAE,OAAO,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QACrD,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QACvD,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QAEzD,KAAK,CAAC,eAAe,CAAC,CAAC;QACvB,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAChD,IAAI,CAAC,UAAU,EAAE,CAAC;YACjB,KAAK,CAAC,mBAAmB,CAAC,CAAC;YAC3B,IAAI,EAAE,CAAC;YACP,OAAO;QACR,CAAC;QACD,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAE9B,IAAI,IAAI,KAAK,KAAK,IAAI,OAAO;YAAE,OAAO,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QAC5D,IAAI,IAAI,KAAK,UAAU;YAAE,OAAO,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QAE3D,IAAI,EAAE,CAAC;IACR,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AACf,CAAC"}
|
||||
{"version":3,"file":"postHandler.js","sourceRoot":"","sources":["../../../../src/middlewares/postHandler/postHandler.ts"],"names":[],"mappings":"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"}
|
||||
19
dist/src/middlewares/postHandler/refresh.js
vendored
19
dist/src/middlewares/postHandler/refresh.js
vendored
@@ -11,32 +11,33 @@ const debug = Debug('twtkpr:refresh');
|
||||
* @param res
|
||||
*/
|
||||
export default async function refresh(req, res, config) {
|
||||
const send401 = (message) => {
|
||||
const sendError = (message, code = 401) => {
|
||||
debug(message);
|
||||
res
|
||||
.clearCookie('accessToken')
|
||||
.clearCookie('refreshToken')
|
||||
.status(401)
|
||||
.status(code)
|
||||
.send(message ?? 'Unauthorized');
|
||||
return;
|
||||
};
|
||||
try {
|
||||
const tokens = await refreshTokensDB(config.privateDirectory);
|
||||
const oldToken = req.cookies.refreshToken;
|
||||
debug(oldToken);
|
||||
debug(`Using old token: ${oldToken}`);
|
||||
if (!oldToken)
|
||||
return send401('Unauthorized');
|
||||
return sendError('Unauthorized');
|
||||
let decoded = { id: '' };
|
||||
try {
|
||||
decoded = jwt.verify(oldToken, config.refreshSecret);
|
||||
debug({ decoded });
|
||||
debug('Decoded token: ', { decoded });
|
||||
}
|
||||
catch (err) {
|
||||
return send401('Refresh token invalid');
|
||||
debug('Error decoding refresh token:', err);
|
||||
return sendError('Refresh token invalid', 403);
|
||||
}
|
||||
const username = req.username ?? decoded.id;
|
||||
if (!username)
|
||||
return send401('Missing username');
|
||||
return sendError('Missing username');
|
||||
const currentTime = Math.floor(Date.now() / 1000);
|
||||
// cleanup tokens on load
|
||||
const validTokens = (tokens.get(decoded.id) ?? []).filter((token) => {
|
||||
@@ -46,11 +47,11 @@ export default async function refresh(req, res, config) {
|
||||
// If token is invalid or not the latest one
|
||||
if (!validTokens.includes(oldToken)) {
|
||||
debug('token missing from list');
|
||||
return send401('Invalid refresh token');
|
||||
return sendError('Invalid refresh token');
|
||||
}
|
||||
debug('generating new tokens');
|
||||
const newAccessToken = generateAccessToken(req.username || decoded.id, config.accessSecret);
|
||||
const newRefreshToken = generateRefreshToken(req.username || decoded.id, config.refreshSecret);
|
||||
const newRefreshToken = generateRefreshToken(req.username || decoded.id, config.refreshSecret, !!req.query.rememberToggle);
|
||||
debug('updating token list');
|
||||
tokens.set(req.username || decoded.id, validTokens
|
||||
.filter((token) => token !== oldToken)
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../../../src/middlewares/postHandler/refresh.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../../../src/middlewares/postHandler/refresh.ts"],"names":[],"mappings":"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"}
|
||||
4
dist/src/middlewares/postHandler/twt.d.ts
vendored
4
dist/src/middlewares/postHandler/twt.d.ts
vendored
@@ -1,9 +1,9 @@
|
||||
import type { Request, Response } from 'express';
|
||||
import { TwtKprConfiguration } from '../../types.js';
|
||||
import { TwtKprConfiguration, TwtKprPluginConfiguration } from '../../types.js';
|
||||
/**
|
||||
* Creates a new twt, appending it to the bottom of the TWTXT file
|
||||
*
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export default function twt(req: Request, res: Response, config: TwtKprConfiguration): void;
|
||||
export default function twt(req: Request, res: Response, config: TwtKprConfiguration, plugins?: TwtKprPluginConfiguration[]): void;
|
||||
|
||||
16
dist/src/middlewares/postHandler/twt.js
vendored
16
dist/src/middlewares/postHandler/twt.js
vendored
@@ -1,21 +1,33 @@
|
||||
import dayjs from 'dayjs';
|
||||
import fs from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import dayjs from 'dayjs';
|
||||
import Debug from 'debug';
|
||||
const debug = Debug('twtkpr:twt');
|
||||
/**
|
||||
* Creates a new twt, appending it to the bottom of the TWTXT file
|
||||
*
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export default function twt(req, res, config) {
|
||||
export default function twt(req, res, config, plugins = []) {
|
||||
debug('Beginning twt add');
|
||||
const { content } = req.body ?? {};
|
||||
const date = dayjs().format();
|
||||
const twt = `${date}\t${content.trim()}\n`;
|
||||
debug(`Formatted twt: ${twt}`);
|
||||
debug('Beginning stream...');
|
||||
const stream = fs.createWriteStream(join(config.publicDirectory, config.twtxtFilename), {
|
||||
flags: 'a',
|
||||
});
|
||||
stream.write(twt);
|
||||
stream.end();
|
||||
debug('Streaming complete');
|
||||
plugins.forEach(async (plugin) => {
|
||||
if (!plugin.onAfterTwt)
|
||||
return;
|
||||
debug(`Handling plugin onAfterTwt function${plugin.name ? ' from ' + plugin.name : ''}`);
|
||||
plugin.onAfterTwt(twt);
|
||||
});
|
||||
res.status(200).send(twt);
|
||||
}
|
||||
//# sourceMappingURL=twt.js.map
|
||||
2
dist/src/middlewares/postHandler/twt.js.map
vendored
2
dist/src/middlewares/postHandler/twt.js.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"twt.js","sourceRoot":"","sources":["../../../../src/middlewares/postHandler/twt.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,GAAG,CAC1B,GAAY,EACZ,GAAa,EACb,MAA2B;IAE3B,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;IAEnC,MAAM,IAAI,GAAG,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC;IAC9B,MAAM,GAAG,GAAG,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC;IAE3C,MAAM,MAAM,GAAG,EAAE,CAAC,iBAAiB,CAClC,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,aAAa,CAAC,EAClD;QACC,KAAK,EAAE,GAAG;KACV,CACD,CAAC;IAEF,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,MAAM,CAAC,GAAG,EAAE,CAAC;IAEb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC"}
|
||||
{"version":3,"file":"twt.js","sourceRoot":"","sources":["../../../../src/middlewares/postHandler/twt.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC;AAElC;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,GAAG,CAC1B,GAAY,EACZ,GAAa,EACb,MAA2B,EAC3B,UAAuC,EAAE;IAEzC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAE3B,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;IAEnC,MAAM,IAAI,GAAG,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC;IAC9B,MAAM,GAAG,GAAG,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC;IAE3C,KAAK,CAAC,kBAAkB,GAAG,EAAE,CAAC,CAAC;IAE/B,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC7B,MAAM,MAAM,GAAG,EAAE,CAAC,iBAAiB,CAClC,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,aAAa,CAAC,EAClD;QACC,KAAK,EAAE,GAAG;KACV,CACD,CAAC;IAEF,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,MAAM,CAAC,GAAG,EAAE,CAAC;IAEb,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAE5B,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;QAChC,IAAI,CAAC,MAAM,CAAC,UAAU;YAAE,OAAO;QAE/B,KAAK,CACJ,sCACC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EACxC,EAAE,CACF,CAAC;QAEF,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC"}
|
||||
3
dist/src/middlewares/putHandler/index.d.ts
vendored
3
dist/src/middlewares/putHandler/index.d.ts
vendored
@@ -1 +1,2 @@
|
||||
export { default } from "./putHandler.js";
|
||||
export { default } from './putHandler.js';
|
||||
export { default as pluginPutHandler } from './pluginPutHandler.js';
|
||||
|
||||
3
dist/src/middlewares/putHandler/index.js
vendored
3
dist/src/middlewares/putHandler/index.js
vendored
@@ -1,2 +1,3 @@
|
||||
export { default } from "./putHandler.js";
|
||||
export { default } from './putHandler.js';
|
||||
export { default as pluginPutHandler } from './pluginPutHandler.js';
|
||||
//# sourceMappingURL=index.js.map
|
||||
2
dist/src/middlewares/putHandler/index.js.map
vendored
2
dist/src/middlewares/putHandler/index.js.map
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/middlewares/putHandler/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC"}
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/middlewares/putHandler/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,uBAAuB,CAAC"}
|
||||
8
dist/src/middlewares/putHandler/pluginPutHandler.d.ts
vendored
Normal file
8
dist/src/middlewares/putHandler/pluginPutHandler.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Request } from 'express';
|
||||
import { TwtKprPluginConfiguration } from '../../types.js';
|
||||
/**
|
||||
*
|
||||
* @param plugins
|
||||
* @returns
|
||||
*/
|
||||
export default function pluginPutHandler(plugins: TwtKprPluginConfiguration[] | undefined, verifyAuthRequest: (r: Request) => Promise<boolean>, reloadCache?: () => Promise<void>): import("express-serve-static-core").Router;
|
||||
31
dist/src/middlewares/putHandler/pluginPutHandler.js
vendored
Normal file
31
dist/src/middlewares/putHandler/pluginPutHandler.js
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import Debug from 'debug';
|
||||
import express from 'express';
|
||||
const debug = Debug('twtkpr:postHandler');
|
||||
/**
|
||||
*
|
||||
* @param plugins
|
||||
* @returns
|
||||
*/
|
||||
export default function pluginPutHandler(plugins = [], verifyAuthRequest, reloadCache) {
|
||||
const router = express.Router();
|
||||
const pluginRoutes = [].concat(...Object.keys(plugins)
|
||||
.filter((key) => plugins[key]
|
||||
?.putRoutes?.length)
|
||||
.map((key) => plugins[key]
|
||||
.putRoutes));
|
||||
pluginRoutes.forEach(({ handler, path, requiresAuth }) => {
|
||||
debug(`adding PUT plugin router for ${path}`);
|
||||
router.put(path, async (req, res, next) => {
|
||||
debug(`handling PUT plugin route to ${path}`);
|
||||
if (requiresAuth && !(await verifyAuthRequest(req))) {
|
||||
debug('auth check failed');
|
||||
next();
|
||||
return;
|
||||
}
|
||||
handler(req, res, next);
|
||||
reloadCache?.();
|
||||
});
|
||||
});
|
||||
return router;
|
||||
}
|
||||
//# sourceMappingURL=pluginPutHandler.js.map
|
||||
1
dist/src/middlewares/putHandler/pluginPutHandler.js.map
vendored
Normal file
1
dist/src/middlewares/putHandler/pluginPutHandler.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"pluginPutHandler.js","sourceRoot":"","sources":["../../../../src/middlewares/putHandler/pluginPutHandler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,OAA4C,MAAM,SAAS,CAAC;AAGnE,MAAM,KAAK,GAAG,KAAK,CAAC,oBAAoB,CAAC,CAAC;AAE1C;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CACvC,UAAuC,EAAE,EACzC,iBAAmD,EACnD,WAAiC;IAEjC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAEhC,MAAM,YAAY,GAAI,EAA0B,CAAC,MAAM,CACtD,GAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;SACtB,MAAM,CACN,CAAC,GAAG,EAAE,EAAE,CACN,OAAO,CAAC,GAA2B,CAA+B;QAClE,EAAE,SAAS,EAAE,MAAM,CACrB;SACA,GAAG,CACH,CAAC,GAAG,EAAE,EAAE,CACN,OAAO,CAAC,GAA2B,CAA+B;SACjE,SAAS,CACc,CAC5B,CAAC;IAEF,YAAY,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE;QACxD,KAAK,CAAC,gCAAgC,IAAI,EAAE,CAAC,CAAC;QAE9C,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YACzC,KAAK,CAAC,gCAAgC,IAAI,EAAE,CAAC,CAAC;YAE9C,IAAI,YAAY,IAAI,CAAC,CAAC,MAAM,iBAAiB,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBACrD,KAAK,CAAC,mBAAmB,CAAC,CAAC;gBAC3B,IAAI,EAAE,CAAC;gBACP,OAAO;YACR,CAAC;YAED,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAExB,WAAW,EAAE,EAAE,CAAC;QACjB,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AACf,CAAC"}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { TwtKprConfiguration } from '../../types.js';
|
||||
import { TwtKprConfiguration, TwtKprPluginConfiguration } from '../../types.js';
|
||||
/**
|
||||
*
|
||||
* @param config
|
||||
* @returns
|
||||
*/
|
||||
export default function putHandler(config: TwtKprConfiguration): import("express-serve-static-core").Router;
|
||||
export default function putHandler(config: TwtKprConfiguration, plugins?: TwtKprPluginConfiguration[], reloadCache?: () => Promise<void>): import("express-serve-static-core").Router;
|
||||
|
||||
@@ -8,7 +8,7 @@ const debug = Debug('twtkpr:putHandler');
|
||||
* @param config
|
||||
* @returns
|
||||
*/
|
||||
export default function putHandler(config) {
|
||||
export default function putHandler(config, plugins = [], reloadCache) {
|
||||
const router = express.Router();
|
||||
router.put('/', (req, res, next) => {
|
||||
debug('put', { path: req.path });
|
||||
@@ -19,7 +19,8 @@ export default function putHandler(config) {
|
||||
return;
|
||||
}
|
||||
debug('auth check succeeded');
|
||||
return editFile(req, res, config);
|
||||
editFile(req, res, config);
|
||||
reloadCache?.();
|
||||
});
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"putHandler.js","sourceRoot":"","sources":["../../../../src/middlewares/putHandler/putHandler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,SAAS,MAAM,mCAAmC,CAAC;AAE1D,OAAO,QAAQ,MAAM,eAAe,CAAC;AAErC,MAAM,KAAK,GAAG,KAAK,CAAC,mBAAmB,CAAC,CAAC;AAEzC;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,MAA2B;IAC7D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAEhC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAClC,KAAK,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAEjC,KAAK,CAAC,eAAe,CAAC,CAAC;QAEvB,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC;YAC7B,KAAK,CAAC,mBAAmB,CAAC,CAAC;YAC3B,IAAI,EAAE,CAAC;YACP,OAAO;QACR,CAAC;QAED,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAE9B,OAAO,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AACf,CAAC"}
|
||||
{"version":3,"file":"putHandler.js","sourceRoot":"","sources":["../../../../src/middlewares/putHandler/putHandler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,SAAS,MAAM,mCAAmC,CAAC;AAE1D,OAAO,QAAQ,MAAM,eAAe,CAAC;AAErC,MAAM,KAAK,GAAG,KAAK,CAAC,mBAAmB,CAAC,CAAC;AAEzC;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,UAAU,CACjC,MAA2B,EAC3B,UAAuC,EAAE,EACzC,WAAiC;IAEjC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAEhC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAClC,KAAK,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAEjC,KAAK,CAAC,eAAe,CAAC,CAAC;QAEvB,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC;YAC7B,KAAK,CAAC,mBAAmB,CAAC,CAAC;YAC3B,IAAI,EAAE,CAAC;YACP,OAAO;QACR,CAAC;QAED,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAE9B,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QAE3B,WAAW,EAAE,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AACf,CAAC"}
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Request, Response } from 'express';
|
||||
import type { Twttr } from 'twtxt-lib';
|
||||
import { QueryParameters } from '../../types.js';
|
||||
import NodeCache from '@cacheable/node-cache';
|
||||
/**
|
||||
*
|
||||
* @param req
|
||||
@@ -8,4 +8,4 @@ import NodeCache from '@cacheable/node-cache';
|
||||
* @param cache
|
||||
* @param followingParameter
|
||||
*/
|
||||
export default function followingHandler(req: Request, res: Response, cache: NodeCache<unknown>, followingParameter: QueryParameters['following']): void;
|
||||
export default function followingHandler(req: Request, res: Response, following: Twttr[], followingParameter: QueryParameters['following']): void;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { generateEtag, getQueryParameterArray, getValueOrFirstEntry, } from '../
|
||||
* @param cache
|
||||
* @param followingParameter
|
||||
*/
|
||||
export default function followingHandler(req, res, cache, followingParameter) {
|
||||
export default function followingHandler(req, res, following, followingParameter) {
|
||||
const followingsToMatch = getQueryParameterArray(req.query[followingParameter]);
|
||||
const nicksToMatch = getQueryParameterArray(req.query.nick);
|
||||
const urlsToMatch = getQueryParameterArray(req.query.url);
|
||||
@@ -20,7 +20,7 @@ export default function followingHandler(req, res, cache, followingParameter) {
|
||||
res.set('content-type', 'application/json');
|
||||
else
|
||||
res.set('content-type', 'text/plain');
|
||||
const matchedFollowing = cache.get('following').filter(({ nick, url }) => (!followingsToMatch.length ||
|
||||
const matchedFollowing = following.filter(({ nick, url }) => (!followingsToMatch.length ||
|
||||
(followingsToMatch.length === 1 && followingsToMatch[0] === '') ||
|
||||
followingsToMatch.includes(nick) ||
|
||||
followingsToMatch.includes(`@${nick}`) ||
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"followingHandler.js","sourceRoot":"","sources":["../../../../src/middlewares/queryHandler/followingHandler.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,YAAY,EACZ,sBAAsB,EACtB,oBAAoB,GACpB,MAAM,oBAAoB,CAAC;AAI5B;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CACvC,GAAY,EACZ,GAAa,EACb,KAAyB,EACzB,kBAAgD;IAEhD,MAAM,iBAAiB,GAAG,sBAAsB,CAC/C,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAC7B,CAAC;IAEF,MAAM,YAAY,GAAG,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5D,MAAM,WAAW,GAAG,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE1D,MAAM,kBAAkB,GAAG;QAC1B,GAAG,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;QAC3C,GAAG,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;KACtC,CAAC;IAEF,MAAM,SAAS,GACd,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC;QACd,oBAAoB,CAAC,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC;IAC3E,IAAI,SAAS;QAAE,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;;QACtD,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;IAE3C,MAAM,gBAAgB,GAAI,KAAK,CAAC,GAAG,CAAC,WAAW,CAAa,CAAC,MAAM,CAClE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,CACjB,CAAC,CAAC,iBAAiB,CAAC,MAAM;QACzB,CAAC,iBAAiB,CAAC,MAAM,KAAK,CAAC,IAAI,iBAAiB,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;QAC/D,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC;QAChC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC;QACtC,iBAAiB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACjC,CAAC,CAAC,YAAY,CAAC,MAAM;YACpB,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC;YAC3B,YAAY,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QACnC,CAAC,CAAC,WAAW,CAAC,MAAM,IAAI,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAClD,CAAC,CAAC,kBAAkB,CAAC,MAAM;YAC1B,kBAAkB,CAAC,IAAI,CACtB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CACnD,CAAC,CACJ,CAAC;IAEF,MAAM,MAAM,GAAG,SAAS;QACvB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC;QAClC,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,IAAI,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEzE,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACpD,CAAC"}
|
||||
{"version":3,"file":"followingHandler.js","sourceRoot":"","sources":["../../../../src/middlewares/queryHandler/followingHandler.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,YAAY,EACZ,sBAAsB,EACtB,oBAAoB,GACpB,MAAM,oBAAoB,CAAC;AAI5B;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CACvC,GAAY,EACZ,GAAa,EACb,SAAkB,EAClB,kBAAgD;IAEhD,MAAM,iBAAiB,GAAG,sBAAsB,CAC/C,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAC7B,CAAC;IAEF,MAAM,YAAY,GAAG,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5D,MAAM,WAAW,GAAG,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE1D,MAAM,kBAAkB,GAAG;QAC1B,GAAG,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;QAC3C,GAAG,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;KACtC,CAAC;IAEF,MAAM,SAAS,GACd,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC;QACd,oBAAoB,CAAC,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC;IAC3E,IAAI,SAAS;QAAE,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;;QACtD,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;IAE3C,MAAM,gBAAgB,GAAG,SAAS,CAAC,MAAM,CACxC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,CACjB,CAAC,CAAC,iBAAiB,CAAC,MAAM;QACzB,CAAC,iBAAiB,CAAC,MAAM,KAAK,CAAC,IAAI,iBAAiB,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;QAC/D,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC;QAChC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC;QACtC,iBAAiB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACjC,CAAC,CAAC,YAAY,CAAC,MAAM;YACpB,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC;YAC3B,YAAY,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QACnC,CAAC,CAAC,WAAW,CAAC,MAAM,IAAI,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAClD,CAAC,CAAC,kBAAkB,CAAC,MAAM;YAC1B,kBAAkB,CAAC,IAAI,CACtB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CACnD,CAAC,CACJ,CAAC;IAEF,MAAM,MAAM,GAAG,SAAS;QACvB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC;QAClC,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,IAAI,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEzE,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACpD,CAAC"}
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Request, Response } from 'express';
|
||||
import NodeCache from '@cacheable/node-cache';
|
||||
import type { Metadata } from 'twtxt-lib';
|
||||
import { NodeCache } from '@cacheable/node-cache';
|
||||
import { QueryParameters } from '../../types.js';
|
||||
export interface MetadataHandler {
|
||||
cache: NodeCache<unknown>;
|
||||
@@ -14,4 +15,4 @@ export interface MetadataHandler {
|
||||
* @param cache
|
||||
* @param metadataParameter
|
||||
*/
|
||||
export default function metadataHandler(req: Request, res: Response, cache: NodeCache<unknown>, metadataParameter: QueryParameters['metadata']): void;
|
||||
export default function metadataHandler(req: Request, res: Response, metadata: Metadata, metadataParameter: QueryParameters['metadata']): void;
|
||||
|
||||
@@ -6,13 +6,12 @@ import { generateEtag, getQueryParameterArray, getValueOrFirstEntry, } from '../
|
||||
* @param cache
|
||||
* @param metadataParameter
|
||||
*/
|
||||
export default function metadataHandler(req, res, cache, metadataParameter) {
|
||||
export default function metadataHandler(req, res, metadata, metadataParameter) {
|
||||
const metadataToMatch = getQueryParameterArray(req.query[metadataParameter]);
|
||||
const searchTermsToMatch = [
|
||||
...getQueryParameterArray(req.query.search),
|
||||
...getQueryParameterArray(req.query.s),
|
||||
];
|
||||
const metadata = cache.get('metadata') ?? {};
|
||||
const wantsJson = req.is('json') ||
|
||||
getValueOrFirstEntry(getQueryParameterArray(req.query.format)) === 'json';
|
||||
if (wantsJson)
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"metadataHandler.js","sourceRoot":"","sources":["../../../../src/middlewares/queryHandler/metadataHandler.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"metadataHandler.js","sourceRoot":"","sources":["../../../../src/middlewares/queryHandler/metadataHandler.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,6 +1,5 @@
|
||||
import NodeCache from '@cacheable/node-cache';
|
||||
import type { NextFunction, Request, Response } from 'express';
|
||||
import { TwtKprConfiguration } from '../../types.js';
|
||||
import { TwtKprConfiguration, TwtKprPluginConfiguration } from '../../types.js';
|
||||
/**
|
||||
*
|
||||
* @param config
|
||||
@@ -8,4 +7,4 @@ import { TwtKprConfiguration } from '../../types.js';
|
||||
* @param verifyAuthRequest
|
||||
* @returns
|
||||
*/
|
||||
export default function queryHandler(config: TwtKprConfiguration, cache: NodeCache<unknown>, verifyAuthRequest: (r: Request) => Promise<boolean>): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
||||
export default function queryHandler(config: TwtKprConfiguration, getFromCache: (key?: string) => Promise<unknown>, verifyAuthRequest: (r: Request) => Promise<boolean>, plugins?: TwtKprPluginConfiguration[]): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import Debug from 'debug';
|
||||
import { __dirname } from '../../lib/constants.js';
|
||||
import { generateEtag } from '../../lib/utils.js';
|
||||
import { combineStreams, generateEtag } from '../../lib/utils.js';
|
||||
import renderApp from '../renderApp/index.js';
|
||||
import followingHandler from './followingHandler.js';
|
||||
import metadataHandler from './metadataHandler.js';
|
||||
@@ -14,44 +15,89 @@ const debug = Debug('twtkpr:queryHandler');
|
||||
* @param verifyAuthRequest
|
||||
* @returns
|
||||
*/
|
||||
export default function queryHandler(config, cache, verifyAuthRequest) {
|
||||
const { mainRoute, queryParameters, uploadConfiguration } = config;
|
||||
export default function queryHandler(config, getFromCache, verifyAuthRequest, plugins = []) {
|
||||
const { mainRoute, queryParameters } = config;
|
||||
const getPluginStreams = (type) => plugins.filter((plugin) => !!plugin[type]).map((plugin) => plugin[type]);
|
||||
return async (req, res, next) => {
|
||||
debug({ query: JSON.stringify(req.query) });
|
||||
debug(`handling query`, JSON.stringify(req.query));
|
||||
if (!Object.keys(req.query).length) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
if (req.query[queryParameters.app] !== undefined) {
|
||||
const appContent = renderApp({ mainRoute, uploadConfiguration });
|
||||
debug('rendering app');
|
||||
const appContent = renderApp({ mainRoute });
|
||||
res.set('etag', generateEtag(appContent)).send(appContent);
|
||||
return;
|
||||
}
|
||||
if (req.query[queryParameters.css] !== undefined) {
|
||||
res.sendFile('styles.css', {
|
||||
root: path.resolve(__dirname, 'client'),
|
||||
debug('rendering css');
|
||||
const mainStream = fs.createReadStream(path.join(__dirname, 'client', 'styles.css'));
|
||||
const pluginStreams = getPluginStreams('clientCSS');
|
||||
const streams = [mainStream, ...pluginStreams];
|
||||
const combined = combineStreams(streams);
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/css',
|
||||
});
|
||||
combined.pipe(res);
|
||||
combined.on('error', (error) => {
|
||||
console.error('Error streaming file:', error);
|
||||
res.end();
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (req.query[queryParameters.js] !== undefined) {
|
||||
res.sendFile('script.js', {
|
||||
root: path.resolve(__dirname, 'client'),
|
||||
debug('rendering js');
|
||||
const mainStream = fs.createReadStream(path.join(__dirname, 'client', 'script.js'));
|
||||
const pluginStreams = getPluginStreams('clientJS');
|
||||
const streams = [mainStream, ...pluginStreams];
|
||||
const combined = combineStreams(streams);
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/javascript',
|
||||
});
|
||||
combined.pipe(res);
|
||||
combined.on('error', (error) => {
|
||||
console.error('Error streaming file:', error);
|
||||
res.end();
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (req.query[queryParameters.following] !== undefined &&
|
||||
cache.get('following')) {
|
||||
return followingHandler(req, res, cache, queryParameters.following);
|
||||
(await getFromCache('following'))) {
|
||||
debug('rendering following');
|
||||
const following = await getFromCache('following');
|
||||
return followingHandler(req, res, following, queryParameters.following);
|
||||
}
|
||||
if (req.query[queryParameters.metadata] !== undefined &&
|
||||
cache.get('metadata')) {
|
||||
return metadataHandler(req, res, cache, queryParameters.metadata);
|
||||
(await getFromCache('metadata'))) {
|
||||
debug('rendering metadata');
|
||||
const metadata = (await getFromCache('metadata'));
|
||||
return metadataHandler(req, res, metadata, queryParameters.metadata);
|
||||
}
|
||||
if ((req.query[queryParameters.twt] !== undefined ||
|
||||
req.query[queryParameters.twts] !== undefined) &&
|
||||
cache.get('twts')) {
|
||||
return twtHandler(req, res, cache.get('twts'), queryParameters.twt, queryParameters.twts);
|
||||
(await getFromCache('twts'))) {
|
||||
debug('rendering twts');
|
||||
const twts = await getFromCache('twts');
|
||||
return twtHandler(req, res, twts, queryParameters.twt, queryParameters.twts);
|
||||
}
|
||||
plugins.forEach((plugin) => {
|
||||
if (!plugin?.queryRoutes?.length)
|
||||
return;
|
||||
plugin.queryRoutes.forEach(async (route) => {
|
||||
// default to no auth
|
||||
const { handler, queryParameter, requiresAuth = false } = route ?? {};
|
||||
if (queryParameter && req.query[queryParameter] !== undefined) {
|
||||
debug(`rendering plugin queryParameter ${queryParameter}`);
|
||||
if (requiresAuth && !(await verifyAuthRequest(req))) {
|
||||
debug('auth check failed');
|
||||
next();
|
||||
return;
|
||||
}
|
||||
return handler(req, res, next);
|
||||
}
|
||||
});
|
||||
});
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"queryHandler.js","sourceRoot":"","sources":["../../../../src/middlewares/queryHandler/queryHandler.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"queryHandler.js","sourceRoot":"","sources":["../../../../src/middlewares/queryHandler/queryHandler.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"twtHandler.js","sourceRoot":"","sources":["../../../../src/middlewares/queryHandler/twtHandler.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"twtHandler.js","sourceRoot":"","sources":["../../../../src/middlewares/queryHandler/twtHandler.ts"],"names":[],"mappings":"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"}
|
||||
@@ -4,4 +4,4 @@ import { TwtKprConfiguration } from '../../types.js';
|
||||
* @param param0
|
||||
* @returns
|
||||
*/
|
||||
export default function renderApp({ mainRoute, uploadConfiguration, }: Pick<TwtKprConfiguration, 'mainRoute' | 'uploadConfiguration'>): string;
|
||||
export default function renderApp({ mainRoute, }: Pick<TwtKprConfiguration, 'mainRoute'>): string;
|
||||
|
||||
7
dist/src/middlewares/renderApp/renderApp.js
vendored
7
dist/src/middlewares/renderApp/renderApp.js
vendored
@@ -1,11 +1,10 @@
|
||||
import { version } from '../../packageInfo.js';
|
||||
import renderUploadButton from './renderUploadButton.js';
|
||||
/**
|
||||
*
|
||||
* @param param0
|
||||
* @returns
|
||||
*/
|
||||
export default function renderApp({ mainRoute, uploadConfiguration, }) {
|
||||
export default function renderApp({ mainRoute, }) {
|
||||
return `<!doctype html>
|
||||
<html class="no-js" lang="en" xmlns:fb="http://ogp.me/ns/fb#">
|
||||
|
||||
@@ -85,9 +84,8 @@ export default function renderApp({ mainRoute, uploadConfiguration, }) {
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
${renderUploadButton(uploadConfiguration)}
|
||||
<label class="twtControls-contentLabel" for="twtControlsContentInput">
|
||||
<textarea class="twtControls-contentInput"
|
||||
<textarea class="twtControls-contentInput emoji_target"
|
||||
id="twtControlsContentInput" name="content"
|
||||
placeholder="What do you want to say?"></textarea>
|
||||
</label>
|
||||
@@ -101,7 +99,6 @@ export default function renderApp({ mainRoute, uploadConfiguration, }) {
|
||||
<div class="popupMenu-appInfo appInfo">
|
||||
TwtKpr v${version ?? 'Unknown'}
|
||||
</div>
|
||||
${renderUploadButton(uploadConfiguration, 'small')}
|
||||
<button class="twtControls-editButton button" id="twtControlsEditButton">
|
||||
Edit File
|
||||
</button>
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"renderApp.js","sourceRoot":"","sources":["../../../../src/middlewares/renderApp/renderApp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAG/C,OAAO,kBAAkB,MAAM,yBAAyB,CAAC;AAEzD;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EACjC,SAAS,EACT,mBAAmB,GAC6C;IAChE,OAAO;;;;;;;;;;;;;;mCAc2B,SAAS;;;;;;;;8CAQE,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uEAyCgB,SAAS;;;;sCAI1C,OAAO,IAAI,SAAS;;;;;;;;;;;;0BAYhC,kBAAkB,CAAC,mBAAmB,CAAC;;;;;;;;;;;;;;8CAcnB,OAAO,IAAI,SAAS;;kCAEhC,kBAAkB,CAAC,mBAAmB,EAAE,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iCAiCjD,SAAS;;;;CAIzC,CAAC;AACF,CAAC"}
|
||||
{"version":3,"file":"renderApp.js","sourceRoot":"","sources":["../../../../src/middlewares/renderApp/renderApp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAG/C;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EACjC,SAAS,GAC+B;IACxC,OAAO;;;;;;;;;;;;;;mCAc2B,SAAS;;;;;;;;8CAQE,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uEAyCgB,SAAS;;;;sCAI1C,OAAO,IAAI,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;8CAyBZ,OAAO,IAAI,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iCAkCjC,SAAS;;;;CAIzC,CAAC;AACF,CAAC"}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { TwtKprConfiguration } from '../../types.js';
|
||||
/**
|
||||
*
|
||||
* @param uploadConfiguration
|
||||
* @param variant
|
||||
* @returns
|
||||
*/
|
||||
export default function renderUploadButton(uploadConfiguration: TwtKprConfiguration['uploadConfiguration'], variant?: 'normal' | 'small'): string;
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @param uploadConfiguration
|
||||
* @param variant
|
||||
* @returns
|
||||
*/
|
||||
export default function renderUploadButton(uploadConfiguration, variant = 'normal') {
|
||||
const { active, allowedMimeTypes, route } = uploadConfiguration ?? {};
|
||||
if (!active)
|
||||
return '';
|
||||
// determine accept from allowed mime types - may need to rebuild value based on fallback n getConfiguration, rather than at the end.
|
||||
return `
|
||||
<label class="button twtControls-uploadInputLabel twtControls-uploadInputLabel-${variant}"
|
||||
for="twtControlsUploadInput-${variant}"
|
||||
>
|
||||
Upload${variant === 'normal' ? '<br />' : ' '}Files
|
||||
<input accept="*" class="twtControls-uploadInput" data-route="${route}"
|
||||
id="twtControlsUploadInput-${variant}"
|
||||
multiple type="file" />
|
||||
</label>
|
||||
`;
|
||||
}
|
||||
//# sourceMappingURL=renderUploadButton.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"renderUploadButton.js","sourceRoot":"","sources":["../../../../src/middlewares/renderApp/renderUploadButton.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,kBAAkB,CACzC,mBAA+D,EAC/D,UAA8B,QAAQ;IAEtC,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,GAAG,mBAAmB,IAAI,EAAE,CAAC;IAEtE,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAEvB,qIAAqI;IAErI,OAAO;yFACiF,OAAO;0CACtD,OAAO;;oBAE7B,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG;4EACmB,KAAK;6CACpC,OAAO;;;CAGnD,CAAC;AACF,CAAC"}
|
||||
9
dist/src/middlewares/uploadHandler.d.ts
vendored
9
dist/src/middlewares/uploadHandler.d.ts
vendored
@@ -1,9 +0,0 @@
|
||||
import type { NextFunction, Request, Response } from 'express';
|
||||
import { TwtKprConfiguration } from '../types.js';
|
||||
/**
|
||||
*
|
||||
* @param config
|
||||
* @param verifyAuthRequest
|
||||
* @returns
|
||||
*/
|
||||
export default function uploadHandler(config: TwtKprConfiguration, verifyAuthRequest: (r: Request) => Promise<boolean>): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
||||
129
dist/src/middlewares/uploadHandler.js
vendored
129
dist/src/middlewares/uploadHandler.js
vendored
@@ -1,129 +0,0 @@
|
||||
import fsp from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import formidable from 'formidable';
|
||||
import Debug from 'debug';
|
||||
const debug = Debug('twtkpr:uploadHandler');
|
||||
/**
|
||||
*
|
||||
* @param allowedMimeTypes
|
||||
* @returns
|
||||
*/
|
||||
const getDestinationByMimeTypeConfiguration = (allowedMimeTypes) => {
|
||||
const fallback = {
|
||||
audio: 'audio',
|
||||
image: 'images',
|
||||
text: 'texts',
|
||||
video: 'videos',
|
||||
'*': 'files',
|
||||
};
|
||||
const mimeTypeArrayReducer = (acc, curr) => {
|
||||
if (fallback[curr])
|
||||
acc[curr] = fallback[curr];
|
||||
else
|
||||
acc[curr] = `${curr}s`;
|
||||
return acc;
|
||||
};
|
||||
if (!allowedMimeTypes)
|
||||
return fallback;
|
||||
if (typeof allowedMimeTypes === 'string')
|
||||
return allowedMimeTypes
|
||||
.split(',')
|
||||
.map((val) => val.trim())
|
||||
.reduce(mimeTypeArrayReducer, {});
|
||||
if (Array.isArray(allowedMimeTypes))
|
||||
return allowedMimeTypes.reduce(mimeTypeArrayReducer, {});
|
||||
if (typeof allowedMimeTypes === 'object')
|
||||
return allowedMimeTypes;
|
||||
return fallback;
|
||||
};
|
||||
/**
|
||||
*
|
||||
* @param config
|
||||
* @param verifyAuthRequest
|
||||
* @returns
|
||||
*/
|
||||
export default function uploadHandler(config, verifyAuthRequest) {
|
||||
return async (req, res, next) => {
|
||||
debug('checking auth');
|
||||
if (!(await verifyAuthRequest(req))) {
|
||||
debug('auth check failed');
|
||||
res.status(401).send('Unauthorized');
|
||||
return;
|
||||
}
|
||||
debug('auth check succeeded');
|
||||
const { active, allowedMimeTypes, directory, route, ...otherProps } = config.uploadConfiguration;
|
||||
if (!active ||
|
||||
(Array.isArray(allowedMimeTypes) && !allowedMimeTypes.length)) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
debug('using configuration: ', {
|
||||
uploadConfiguration: config.uploadConfiguration,
|
||||
});
|
||||
const form = formidable({
|
||||
uploadDir: directory,
|
||||
...otherProps,
|
||||
});
|
||||
form.parse(req, async (err, fields, files) => {
|
||||
if (err) {
|
||||
next(err);
|
||||
return;
|
||||
}
|
||||
const uploadsDir = (route ?? '').replaceAll('/', '');
|
||||
let hadFileError = false;
|
||||
const processedFiles = [];
|
||||
const destinationByMimeType = allowedMimeTypes;
|
||||
debug(`processing ${(files?.files ?? []).length} files`);
|
||||
for (const file of files?.files ?? []) {
|
||||
const { filepath, hash, mimetype, newFilename, originalFilename } = file ?? {};
|
||||
if (!(filepath && newFilename && originalFilename))
|
||||
return;
|
||||
console.log({ file });
|
||||
let ext = path.extname(originalFilename).toLocaleLowerCase();
|
||||
if (ext === '.jpeg')
|
||||
ext = '.jpg';
|
||||
const finalFilename = (hash && (mimetype?.includes('image') || mimetype?.includes('video'))
|
||||
? `${hash}${ext}`
|
||||
: originalFilename)
|
||||
.replace(/\s+/g, '-')
|
||||
.toLocaleLowerCase();
|
||||
let destinationDir = '';
|
||||
Object.keys(destinationByMimeType).forEach((mimeType) => {
|
||||
if (file.mimetype?.split('/')?.[0] === mimeType.toLocaleLowerCase())
|
||||
destinationDir =
|
||||
destinationByMimeType[mimeType].directory ?? '';
|
||||
});
|
||||
if (destinationDir === '')
|
||||
destinationDir =
|
||||
destinationByMimeType['*'].directory ?? uploadsDir;
|
||||
const finalPath = path.join(process.cwd(), 'public', destinationDir);
|
||||
debug(`creating '${finalPath}'`);
|
||||
fsp.mkdir(finalPath, { recursive: true });
|
||||
debug(`copying '${filepath}' to '/${destinationDir}/${finalFilename}'`);
|
||||
try {
|
||||
await fsp.copyFile(filepath, path.join(finalPath, finalFilename));
|
||||
debug(`cleaning up '${filepath}'`);
|
||||
await fsp.rm(filepath);
|
||||
debug(`processed successfully`);
|
||||
processedFiles.push(`/${destinationDir}/${finalFilename}`);
|
||||
}
|
||||
catch (err) {
|
||||
debug(`error!`);
|
||||
hadFileError = true;
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
debug('generating reply...');
|
||||
if (hadFileError && processedFiles.length) {
|
||||
res.type('text/plain').status(206).send(processedFiles.join('\n'));
|
||||
return;
|
||||
}
|
||||
if (!processedFiles.length) {
|
||||
res.type('text/plain').status(500).send('No files processed');
|
||||
return;
|
||||
}
|
||||
res.type('text/plain').status(201).send(processedFiles.join('\n'));
|
||||
});
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=uploadHandler.js.map
|
||||
1
dist/src/middlewares/uploadHandler.js.map
vendored
1
dist/src/middlewares/uploadHandler.js.map
vendored
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"uploadHandler.js","sourceRoot":"","sources":["../../../src/middlewares/uploadHandler.ts"],"names":[],"mappings":"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"}
|
||||
3
dist/src/plugin.d.ts
vendored
3
dist/src/plugin.d.ts
vendored
@@ -1,3 +0,0 @@
|
||||
import { Router } from 'express';
|
||||
import { TwtKprPluginConfiguration } from './types.js';
|
||||
export default function plugin(initialConfig?: TwtKprPluginConfiguration): Router;
|
||||
40
dist/src/plugin.js
vendored
40
dist/src/plugin.js
vendored
@@ -1,40 +0,0 @@
|
||||
import cookieParser from 'cookie-parser';
|
||||
import Debug from 'debug';
|
||||
import express from 'express';
|
||||
import authCheck from './middlewares/authCheckJWT.js';
|
||||
import twtxtCache from './lib/twtxtCache.js';
|
||||
import getConfiguration from './lib/getConfiguration.js';
|
||||
import queryHandler from './middlewares/queryHandler/index.js';
|
||||
import uploadHandler from './middlewares/uploadHandler.js';
|
||||
import postHandler from './middlewares/postHandler/index.js';
|
||||
import memoryCache from './middlewares/postHandler/memoryCache.js';
|
||||
import putHandler from './middlewares/putHandler/index.js';
|
||||
export default function plugin(initialConfig) {
|
||||
const debug = Debug('twtkpr:plugin');
|
||||
const router = express.Router();
|
||||
const config = getConfiguration(initialConfig ?? {});
|
||||
const { publicDirectory, twtxtFilename } = config;
|
||||
const verifyAuthRequest = (req) => authCheck(req, config);
|
||||
debug('initializing cache');
|
||||
const { cache, reloadCache } = twtxtCache({ publicDirectory, twtxtFilename });
|
||||
debug('adding URL encoder');
|
||||
router.use(express.urlencoded({ extended: true }));
|
||||
debug('adding cookieParser');
|
||||
router.use(cookieParser());
|
||||
debug('adding queryRouter');
|
||||
router.use(config.mainRoute, queryHandler(config, cache, verifyAuthRequest));
|
||||
debug(`adding uploadHandler at /${config.uploadConfiguration.route}`);
|
||||
router.post(`/${config.uploadConfiguration.route}`, uploadHandler(config, verifyAuthRequest));
|
||||
debug('adding postHandler and putHandler');
|
||||
router.use(config.mainRoute, postHandler(config), putHandler(config));
|
||||
debug('adding static');
|
||||
router.use(express.static(config.publicDirectory));
|
||||
debug('adding default redirect');
|
||||
router.get('/', (_, res) => {
|
||||
res.redirect(config.mainRoute);
|
||||
});
|
||||
debug('adding memoryCache');
|
||||
router.use((req, res, next) => memoryCache(req, res, next, cache, reloadCache));
|
||||
return router;
|
||||
}
|
||||
//# sourceMappingURL=plugin.js.map
|
||||
1
dist/src/plugin.js.map
vendored
1
dist/src/plugin.js.map
vendored
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,OAA4B,MAAM,SAAS,CAAC;AAGnD,OAAO,SAAS,MAAM,+BAA+B,CAAC;AACtD,OAAO,UAAU,MAAM,qBAAqB,CAAC;AAC7C,OAAO,gBAAgB,MAAM,2BAA2B,CAAC;AACzD,OAAO,YAAY,MAAM,qCAAqC,CAAC;AAC/D,OAAO,aAAa,MAAM,gCAAgC,CAAC;AAC3D,OAAO,WAAW,MAAM,oCAAoC,CAAC;AAC7D,OAAO,WAAW,MAAM,0CAA0C,CAAC;AACnE,OAAO,UAAU,MAAM,mCAAmC,CAAC;AAE3D,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,aAAyC;IACvE,MAAM,KAAK,GAAG,KAAK,CAAC,eAAe,CAAC,CAAC;IAErC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAEhC,MAAM,MAAM,GAAG,gBAAgB,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC;IACrD,MAAM,EAAE,eAAe,EAAE,aAAa,EAAE,GAAG,MAAM,CAAC;IAElD,MAAM,iBAAiB,GAAG,CAAC,GAAY,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAEnE,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAC5B,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,UAAU,CAAC,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC,CAAC;IAE9E,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAC5B,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAEnD,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC7B,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;IAE3B,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAC5B,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAE7E,KAAK,CAAC,4BAA4B,MAAM,CAAC,mBAAmB,CAAC,KAAK,EAAE,CAAC,CAAC;IACtE,MAAM,CAAC,IAAI,CACV,IAAI,MAAM,CAAC,mBAAmB,CAAC,KAAK,EAAE,EACtC,aAAa,CAAC,MAAM,EAAE,iBAAiB,CAAC,CACxC,CAAC;IAEF,KAAK,CAAC,mCAAmC,CAAC,CAAC;IAC3C,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAEtE,KAAK,CAAC,eAAe,CAAC,CAAC;IACvB,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC;IAEnD,KAAK,CAAC,yBAAyB,CAAC,CAAC;IACjC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE;QAC1B,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAC5B,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,CAC7B,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,CAAC,CAC/C,CAAC;IAEF,OAAO,MAAgB,CAAC;AACzB,CAAC"}
|
||||
42
dist/src/types.d.ts
vendored
42
dist/src/types.d.ts
vendored
@@ -1,20 +1,9 @@
|
||||
import { RequestHandler } from 'express';
|
||||
import { Options } from 'express-rate-limit';
|
||||
import formidable from 'formidable';
|
||||
export interface MimeOptions {
|
||||
directory?: string;
|
||||
rename?: boolean;
|
||||
}
|
||||
export interface MimeImageOptions extends MimeOptions {
|
||||
compression?: string;
|
||||
maxHeight?: string;
|
||||
maxWidth?: string;
|
||||
}
|
||||
export interface UploadConfiguration extends Partial<Omit<formidable.Options, 'uploadDir'>> {
|
||||
active: boolean;
|
||||
directory: string;
|
||||
allowedMimeTypes: string | string[] | Record<string, MimeOptions>;
|
||||
route: string;
|
||||
}
|
||||
export interface QueryParameters {
|
||||
app: string;
|
||||
css: string;
|
||||
@@ -31,16 +20,37 @@ export interface PostLimiterConfiguration extends Partial<Options> {
|
||||
export interface TwtKprConfiguration {
|
||||
accessSecret: string;
|
||||
mainRoute: string;
|
||||
pluginRoute: string;
|
||||
plugins?: Record<string, Record<string, any>>;
|
||||
postLimiterConfiguration?: PostLimiterConfiguration;
|
||||
privateDirectory: string;
|
||||
publicDirectory: string;
|
||||
queryParameters: QueryParameters;
|
||||
refreshSecret: string;
|
||||
twtxtFilename: string;
|
||||
postLimiterConfiguration?: PostLimiterConfiguration;
|
||||
queryParameters: QueryParameters;
|
||||
uploadConfiguration: UploadConfiguration;
|
||||
}
|
||||
export interface TwtKprPluginQueryRoute extends Omit<TwtKprPluginRoute, 'path'> {
|
||||
queryParameter: string;
|
||||
}
|
||||
export interface TwtKprPluginRoute {
|
||||
path: string;
|
||||
handler: RequestHandler;
|
||||
requiresAuth?: boolean;
|
||||
}
|
||||
export type TwtKprPluginConfigurator = (config: TwtKprConfiguration) => TwtKprPluginConfiguration;
|
||||
export interface TwtKprPluginConfiguration {
|
||||
clientCSS?: string | NodeJS.ReadableStream | (() => string | NodeJS.ReadableStream);
|
||||
clientJS?: string | NodeJS.ReadableStream | (() => string | NodeJS.ReadableStream);
|
||||
name?: string;
|
||||
onAfterTwt?: (twt: string) => void;
|
||||
postRoutes?: TwtKprPluginRoute[];
|
||||
putRoutes?: TwtKprPluginRoute[];
|
||||
queryRoutes?: TwtKprPluginQueryRoute[];
|
||||
use?: RequestHandler;
|
||||
useFirst?: RequestHandler;
|
||||
useLast?: RequestHandler;
|
||||
}
|
||||
export interface TwtKprPluginConfiguration extends Omit<Partial<TwtKprConfiguration>, 'postLimiterConfiguration' | 'queryParameters' | 'uploadConfiguration'> {
|
||||
postLimiterConfiguration?: Partial<PostLimiterConfiguration>;
|
||||
queryParameters?: Partial<QueryParameters>;
|
||||
uploadConfiguration?: Partial<UploadConfiguration>;
|
||||
}
|
||||
|
||||
33
package.json
33
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "express-twtkpr",
|
||||
"version": "0.8.2",
|
||||
"version": "0.9.0",
|
||||
"description": "An express library for hosting and maintaining a twtxt.txt file.",
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
@@ -26,18 +26,16 @@
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "node --env-file=.env dist/index-app.js",
|
||||
"build": "tsc && cp -r src/client dist/src",
|
||||
"dev": "DEBUG='twtkpr:*' tsx watch --env-file=.env src/index-app.ts",
|
||||
"build": "tsc && cp -r src/client dist/src && cp -r src/plugins dist/src",
|
||||
"get:hash": "tsx --env-file=.env src/cli.ts get-hash",
|
||||
"lint": "eslint --fix src test",
|
||||
"lint": "eslint --fix src",
|
||||
"prepublishOnly": "yarn build",
|
||||
"set:user": "tsx --env-file=.env src/cli.ts set-user",
|
||||
"test": "vitest",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cacheable/node-cache": "^2.0.2",
|
||||
"@cacheable/node-cache": "^3.0.0",
|
||||
"@exodus/blakejs": "^1.1.1-exodus.0",
|
||||
"base32.js": "^0.1.0",
|
||||
"bcryptjs": "^3.0.3",
|
||||
@@ -45,37 +43,36 @@
|
||||
"dayjs": "^1.11.20",
|
||||
"debug": "^4.4.3",
|
||||
"express": "^5.2.1",
|
||||
"express-rate-limit": "^8.3.1",
|
||||
"express-rate-limit": "^8.5.0",
|
||||
"express-session": "^1.19.0",
|
||||
"express-slow-down": "^3.1.0",
|
||||
"formidable": "^3.5.4",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"link": "^2.1.2",
|
||||
"session-file-store": "^1.5.0",
|
||||
"twtxt-lib": "^0.9.4",
|
||||
"uuid": "^13.0.0",
|
||||
"zod": "^4.3.6"
|
||||
"twtxt-lib": "^0.10.1",
|
||||
"uuid": "^14.0.0",
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cookie-parser": "^1.4.10",
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/debug": "^4.1.13",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/express-session": "^1.18.2",
|
||||
"@types/formidable": "^3.5.0",
|
||||
"@types/express-session": "^1.19.0",
|
||||
"@types/formidable": "^3.5.1",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/morgan": "^1.9.10",
|
||||
"@types/node": "^25.5.0",
|
||||
"@types/node": "^25.6.0",
|
||||
"@types/supertest": "^7.2.0",
|
||||
"eslint": "^10.0.3",
|
||||
"eslint": "^10.3.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-prettier": "^5.5.5",
|
||||
"eslint-plugin-security": "^4.0.0",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier": "^3.8.3",
|
||||
"supertest": "^7.2.2",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "^4.1.0"
|
||||
"vitest": "^4.1.5"
|
||||
},
|
||||
"packageManager": "yarn@4.13.0"
|
||||
"packageManager": "yarn@4.14.1"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT
|
||||
const DEBUG_ON = true;
|
||||
|
||||
// served from same path as TWTXT file
|
||||
@@ -9,13 +10,67 @@ const debug = (...vals) => {
|
||||
if (DEBUG_ON) console.log(...vals);
|
||||
};
|
||||
|
||||
window.token = undefined;
|
||||
|
||||
window.setCookie = (name, value, expireDays) => {
|
||||
const isSecure = window.location.protocol === 'https';
|
||||
const expireDate = new Date(); // current date
|
||||
expireDate.setTime(
|
||||
expireDate.getTime() + (expireDays ?? 0) * 24 * 60 * 60 * 1000
|
||||
);
|
||||
let expires =
|
||||
expireDays !== undefined ? `expires=${expireDate.toUTCString()}; ` : '';
|
||||
document.cookie = `${name}=${encodeURIComponent(value)}; ${expires}${isSecure ? 'Secure; ' : ''}SameSite=Strict; Path=/`;
|
||||
};
|
||||
|
||||
window.showToast = (message, type = 'success') => {
|
||||
const toast = document.createElement('div');
|
||||
toast.classList.add('toast');
|
||||
if (type === 'error') toast.classList.add('error');
|
||||
|
||||
toast.textContent = message;
|
||||
document.getElementById('toast-container').appendChild(toast);
|
||||
|
||||
setTimeout(() => {
|
||||
toast.style.animation = 'fadeOut 0.5s forwards';
|
||||
setTimeout(() => toast.remove(), 500);
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
window.refreshToken = async (hideToast = false) => {
|
||||
const rememberToggleVal = !!localStorage.getItem(REMEMBER_LOGIN_STORAGE_KEY);
|
||||
|
||||
const res = await fetch(`${TWTXT_FILE_URL}`, {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams({
|
||||
rememberToggle: rememberToggleVal,
|
||||
type: 'refresh',
|
||||
}),
|
||||
credentials: 'include', // Include cookies
|
||||
});
|
||||
|
||||
if (res.ok && res?.body) {
|
||||
token = await new Response(res.body).text();
|
||||
if (rememberToggleVal) {
|
||||
setCookie(ACCESS_TOKEN_COOKIE_KEY, token);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle refresh failure
|
||||
if (!hideToast)
|
||||
showToast('Unable to refresh token, please try again later.', 'error');
|
||||
token = undefined;
|
||||
document.body.classList.remove('js-authorized');
|
||||
throw new Error('Failed to refresh token');
|
||||
};
|
||||
|
||||
export default (async () => {
|
||||
/* DOM Elements */
|
||||
const twtForm = document.getElementById('twtForm'),
|
||||
loginForm = document.getElementById('loginControls-form'),
|
||||
fileBox = document.getElementById('fileBox'),
|
||||
fileContentsSection = document.getElementById('fileContentsSection'),
|
||||
toastContainer = document.getElementById('toast-container'),
|
||||
twtControlsContentInput = document.getElementById(
|
||||
'twtControlsContentInput'
|
||||
),
|
||||
@@ -24,27 +79,12 @@ export default (async () => {
|
||||
twtFileEditButton = document.getElementById('twtControlsEditButton'),
|
||||
menuCheckbox = document.getElementById('hamburgerToggleCheckbox'),
|
||||
twtxtEditFormText = document.getElementById('twtxtEditFormText'),
|
||||
uploadInputs = document.querySelectorAll('.twtControls-uploadInput'),
|
||||
rememberToggle = document.getElementById('loginControls-rememberToggle');
|
||||
|
||||
const lastModifiedDates = {};
|
||||
let isEditing = false,
|
||||
cookie,
|
||||
fileText,
|
||||
token;
|
||||
|
||||
const showToast = (message, type = 'success') => {
|
||||
const toast = document.createElement('div');
|
||||
toast.classList.add('toast');
|
||||
if (type === 'error') toast.classList.add('error');
|
||||
toast.textContent = message;
|
||||
|
||||
toastContainer.appendChild(toast);
|
||||
setTimeout(() => {
|
||||
toast.style.animation = 'fadeOut 0.5s forwards';
|
||||
setTimeout(() => toast.remove(), 500);
|
||||
}, 3000);
|
||||
};
|
||||
fileText;
|
||||
|
||||
const beginEditMode = () => {
|
||||
isEditing = true;
|
||||
@@ -70,17 +110,6 @@ export default (async () => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const setCookie = (name, value, expireDays) => {
|
||||
const isSecure = window.location.protocol === 'https';
|
||||
const expireDate = new Date(); // current date
|
||||
expireDate.setTime(
|
||||
expireDate.getTime() + (expireDays ?? 0) * 24 * 60 * 60 * 1000
|
||||
);
|
||||
let expires =
|
||||
expireDays !== undefined ? `expires=${expireDate.toUTCString()}; ` : '';
|
||||
document.cookie = `${name}=${encodeURIComponent(value)}; ${expires}${isSecure ? 'Secure; ' : ''}SameSite=Strict; Path=/`;
|
||||
};
|
||||
|
||||
const loadTwtxtFile = async (filePath = TWTXT_FILE_URL) => {
|
||||
debug('loadTwtxtFile start');
|
||||
let response;
|
||||
@@ -119,142 +148,8 @@ export default (async () => {
|
||||
debug('loadTwtxtFile end');
|
||||
};
|
||||
|
||||
const refreshToken = async (hideToast = false) => {
|
||||
debug('refreshToken start', hideToast);
|
||||
const rememberToggleVal =
|
||||
localStorage.getItem(REMEMBER_LOGIN_STORAGE_KEY) === 'true';
|
||||
|
||||
const res = await fetch(`${TWTXT_FILE_URL}`, {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams({
|
||||
rememberToggle: rememberToggleVal,
|
||||
type: 'refresh',
|
||||
}),
|
||||
credentials: 'include', // Include cookies
|
||||
});
|
||||
|
||||
if (res.ok && res?.body) {
|
||||
token = await new Response(res.body).text();
|
||||
if (rememberToggleVal) {
|
||||
debug('refreshToken set new accessToken cookie');
|
||||
setCookie(ACCESS_TOKEN_COOKIE_KEY, token);
|
||||
}
|
||||
debug('refreshToken end OK');
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle refresh failure
|
||||
if (!hideToast)
|
||||
showToast('Unable to refresh token, please try again later.', 'error');
|
||||
token = undefined;
|
||||
document.body.classList.remove('js-authorized');
|
||||
debug('refreshToken end error');
|
||||
throw new Error('Failed to refresh token');
|
||||
};
|
||||
|
||||
const uploadFiles = async (files, uploadRoute, secondAttempt = false) => {
|
||||
if (!uploadRoute) return;
|
||||
|
||||
debug('uploadFiles', token, files, uploadRoute, secondAttempt);
|
||||
|
||||
const formData = new FormData();
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
formData.append('files', files[i]);
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(uploadRoute, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
showToast(`File${files.length !== 1 ? 's' : ''} uploaded`);
|
||||
|
||||
const filePath = await res.text();
|
||||
twtControlsContentInput.value += filePath
|
||||
.split('\n')
|
||||
.map((currFilePath) =>
|
||||
[
|
||||
' ',
|
||||
location.protocol,
|
||||
'//',
|
||||
location.hostname,
|
||||
location.protocol !== 'https' && location.port !== 80
|
||||
? ':' + location.port
|
||||
: '',
|
||||
currFilePath,
|
||||
].join('')
|
||||
)
|
||||
.join('');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!secondAttempt) {
|
||||
await refreshToken();
|
||||
return uploadFiles(files, uploadRoute, true);
|
||||
}
|
||||
|
||||
showToast(
|
||||
`Unable to upload image${files.length !== 1 ? 's' : ''} refresh token, please try again later.`,
|
||||
'error'
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
/* Handlers */
|
||||
|
||||
const dragOverHandler = (ev) => {
|
||||
const files = [...ev.dataTransfer.items].filter(
|
||||
(item) => item.kind === 'file'
|
||||
);
|
||||
|
||||
if (files.length > 0) {
|
||||
ev.preventDefault();
|
||||
ev.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
};
|
||||
|
||||
const dragOverWindowHandler = (ev) => {
|
||||
const files = [...ev.dataTransfer.items].filter(
|
||||
(item) => item.kind === 'file'
|
||||
);
|
||||
|
||||
if (files.length > 0) {
|
||||
ev.preventDefault();
|
||||
|
||||
if (!twtControlsContentInput.contains(ev.target)) {
|
||||
ev.dataTransfer.dropEffect = 'none';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const dropHandler = (ev) => {
|
||||
ev.preventDefault();
|
||||
|
||||
if (!uploadInputs.length) return;
|
||||
|
||||
const files = [...ev.dataTransfer.items]
|
||||
.map((item) => item.getAsFile())
|
||||
.filter((file) => file);
|
||||
|
||||
debug('dropHandler', files);
|
||||
uploadFiles(files, uploadInputs[0].getAttribute('data-route'));
|
||||
};
|
||||
|
||||
const dropWindowHandler = (ev) => {
|
||||
if ([...ev.dataTransfer.items].some((item) => item.kind === 'file')) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
const editClickHandler = () => {
|
||||
if (isEditing) return;
|
||||
|
||||
@@ -384,7 +279,8 @@ export default (async () => {
|
||||
};
|
||||
|
||||
const rememberToggleHandler = (ev) => {
|
||||
if (ev.target.checked) {
|
||||
debug('toggle!', ev?.target?.checked);
|
||||
if (ev?.target?.checked) {
|
||||
localStorage.setItem(REMEMBER_LOGIN_STORAGE_KEY, 'true');
|
||||
return;
|
||||
}
|
||||
@@ -392,6 +288,14 @@ export default (async () => {
|
||||
localStorage.removeItem(REMEMBER_LOGIN_STORAGE_KEY);
|
||||
};
|
||||
|
||||
const twtContentInputHandler = () => {
|
||||
console.log('input!');
|
||||
const hasContent =
|
||||
(twtControlsContentInput.value?.trim() ?? '').length !== 0;
|
||||
if (twtSubmitButton && hasContent && !isEditing)
|
||||
twtSubmitButton.removeAttribute('disabled');
|
||||
};
|
||||
|
||||
const twtContentKeyupHandler = (ev) => {
|
||||
const hasContent =
|
||||
(twtControlsContentInput.value?.trim() ?? '').length !== 0;
|
||||
@@ -422,7 +326,7 @@ export default (async () => {
|
||||
debug('twtForm submit data', { twtData });
|
||||
if (!twtContent) return;
|
||||
|
||||
twtData.set('content', twtContent.replaceAll('\n', '\u2028'));
|
||||
twtData.set('content', twtContent.replace(/\n/g, ' \u2028'));
|
||||
const twtBody = new URLSearchParams(twtData);
|
||||
debug('twtForm submit body', { twtBody });
|
||||
|
||||
@@ -461,25 +365,15 @@ export default (async () => {
|
||||
return false;
|
||||
};
|
||||
|
||||
const uploadChangeHandler = (ev) => {
|
||||
uploadFiles(ev.target.files, ev.target.getAttribute('data-route'));
|
||||
};
|
||||
|
||||
/* Attach Handlers to Listeners */
|
||||
|
||||
Array.from(uploadInputs).forEach((uploadInput) => {
|
||||
uploadInput.addEventListener('change', uploadChangeHandler);
|
||||
});
|
||||
|
||||
loginForm.addEventListener('submit', loginFormSubmitHandler);
|
||||
|
||||
twtForm.addEventListener('submit', twtFormSubmitHandler);
|
||||
|
||||
twtForm.addEventListener('keyup', twtContentKeyupHandler);
|
||||
|
||||
twtControlsContentInput.addEventListener('drop', dropHandler);
|
||||
|
||||
twtControlsContentInput.addEventListener('dragover', dragOverHandler);
|
||||
twtForm.addEventListener('input', twtContentInputHandler);
|
||||
|
||||
twtLogoutButton.addEventListener('click', logoutHandler);
|
||||
|
||||
@@ -489,11 +383,19 @@ export default (async () => {
|
||||
|
||||
twtxtEditForm.addEventListener('submit', editSubmitHandler);
|
||||
|
||||
window.addEventListener('dragover', dragOverWindowHandler);
|
||||
|
||||
window.addEventListener('drop', dropWindowHandler);
|
||||
|
||||
rememberToggle.addEventListener('change', rememberToggleHandler);
|
||||
rememberToggle.setAttribute(
|
||||
'checked',
|
||||
!!localStorage.getItem(REMEMBER_LOGIN_STORAGE_KEY)
|
||||
);
|
||||
|
||||
menuCheckbox.addEventListener('click', (evt) => {
|
||||
evt.stopPropagation();
|
||||
});
|
||||
|
||||
window.addEventListener('click', () => {
|
||||
menuCheckbox.checked = false;
|
||||
});
|
||||
|
||||
/* Start App*/
|
||||
|
||||
@@ -504,3 +406,4 @@ export default (async () => {
|
||||
|
||||
debug('client loaded');
|
||||
})();
|
||||
// @license-end
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Local styles
|
||||
* Begin TwtKpr Client CSS
|
||||
*/
|
||||
:root {
|
||||
/* #0a0a14 / Vulcan*/
|
||||
@@ -483,33 +483,6 @@ input:disabled {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.twtControls-uploadInputLabel {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
max-width: 100%;
|
||||
text-align: center;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.twtControls-uploadInputLabel-normal {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.twtControls-uploadInputLabel-small {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.twtControls-uploadInputLabel:hover {
|
||||
background-color: var(--bg-hl);
|
||||
color: var(--fg-hl);
|
||||
}
|
||||
|
||||
.twtControls-uploadInput {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.twtControls-submitButton {
|
||||
background-color: var(--bg-hl);
|
||||
border: 1px solid var(--link);
|
||||
@@ -715,3 +688,5 @@ input:disabled {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** End TwtKpr Client CSS */
|
||||
|
||||
92
src/expressPlugin.ts
Normal file
92
src/expressPlugin.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import cookieParser from 'cookie-parser';
|
||||
import Debug from 'debug';
|
||||
import express, { Request, Router } from 'express';
|
||||
import { TwtKprPluginConfigurator, TwtKprConfiguration } from './types.js';
|
||||
|
||||
import authCheck from './middlewares/authCheckJWT.js';
|
||||
import twtxtCache from './lib/twtxtCache.js';
|
||||
import getConfiguration from './lib/getConfiguration.js';
|
||||
import queryHandler from './middlewares/queryHandler/index.js';
|
||||
import postHandler, {
|
||||
pluginPostHandler,
|
||||
} from './middlewares/postHandler/index.js';
|
||||
import putHandler, {
|
||||
pluginPutHandler,
|
||||
} from './middlewares/putHandler/index.js';
|
||||
|
||||
// import emojiPlugin from './plugins/emojiButton/index.js';
|
||||
// import uploadPlugin from './plugins/uploadButton/index.js';
|
||||
// import postToMastodon from './plugins/postToMastodon/index.js';
|
||||
|
||||
export default function expressPlugin(
|
||||
initialConfig?: Partial<TwtKprConfiguration>,
|
||||
initialPlugins: TwtKprPluginConfigurator[] = []
|
||||
) {
|
||||
const debug = Debug('twtkpr:expressPlugin');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const config = getConfiguration(initialConfig ?? {});
|
||||
const { mainRoute, publicDirectory, twtxtFilename } = config;
|
||||
|
||||
const verifyAuthRequest = (req: Request) => authCheck(req, config);
|
||||
|
||||
const preparedPlugins = initialPlugins
|
||||
// .concat([emojiPlugin, uploadPlugin, postToMastodon])
|
||||
.map((plugin) => plugin(config));
|
||||
|
||||
debug('initializing cache');
|
||||
const { getFromCache, reloadCache } = twtxtCache({
|
||||
publicDirectory,
|
||||
twtxtFilename,
|
||||
});
|
||||
|
||||
preparedPlugins.forEach((plugin) => {
|
||||
if (plugin.useFirst) router.use(plugin.useFirst);
|
||||
});
|
||||
|
||||
debug('adding URL encoder');
|
||||
router.use(express.urlencoded({ extended: true }));
|
||||
|
||||
debug('adding cookieParser');
|
||||
router.use(cookieParser());
|
||||
|
||||
debug('adding queryRouter');
|
||||
router.use(
|
||||
mainRoute,
|
||||
queryHandler(config, getFromCache, verifyAuthRequest, preparedPlugins)
|
||||
);
|
||||
|
||||
debug('adding postHandler and putHandler');
|
||||
router.use(
|
||||
mainRoute,
|
||||
postHandler(config, preparedPlugins, reloadCache),
|
||||
putHandler(config, preparedPlugins, reloadCache)
|
||||
);
|
||||
|
||||
debug('adding postHandlers and putHandlers for plugins');
|
||||
router.use(
|
||||
config.pluginRoute,
|
||||
pluginPostHandler(preparedPlugins, verifyAuthRequest, reloadCache),
|
||||
pluginPutHandler(preparedPlugins, verifyAuthRequest, reloadCache)
|
||||
);
|
||||
|
||||
debug('adding use handlers for plugins');
|
||||
preparedPlugins.forEach((plugin) => {
|
||||
if (plugin.use) router.use(plugin.use);
|
||||
});
|
||||
|
||||
debug('adding static');
|
||||
router.use(express.static(config.publicDirectory));
|
||||
|
||||
debug('adding default redirect');
|
||||
router.get('/', (_, res) => {
|
||||
res.redirect(mainRoute);
|
||||
});
|
||||
|
||||
preparedPlugins.forEach((plugin) => {
|
||||
if (plugin.useLast) router.use(plugin.useLast);
|
||||
});
|
||||
|
||||
return router as Router;
|
||||
}
|
||||
@@ -1 +1,8 @@
|
||||
export { default } from "./plugin.js";
|
||||
export { default } from './expressPlugin.js';
|
||||
export { combineStreams, getReadStream } from './lib/utils.js';
|
||||
|
||||
export type {
|
||||
MimeOptions,
|
||||
TwtKprConfiguration,
|
||||
TwtKprPluginConfiguration,
|
||||
} from './types.js';
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
export const DEFAULT_PRIVATE_DIRECTORY = '.data';
|
||||
export const DEFAULT_PUBLIC_DIRECTORY = 'public';
|
||||
export const DEFAULT_TWTXT_FILENAME = 'twtxt.txt';
|
||||
export const DEFAULT_ROUTE = `/${DEFAULT_TWTXT_FILENAME}`;
|
||||
export const DEFAULT_PRIVATE_DIRECTORY = '.data';
|
||||
export const DEFAULT_PUBLIC_DIRECTORY = 'public';
|
||||
export const DEFAULT_PLUGIN_ROUTE = '/';
|
||||
|
||||
export const DEFAULT_POST_LIMITER_ACTIVE = true;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { fileURLToPath } from 'node:url';
|
||||
import { z } from 'zod/v4';
|
||||
|
||||
import {
|
||||
DEFAULT_PLUGIN_ROUTE,
|
||||
DEFAULT_POST_LIMITER_ACTIVE,
|
||||
DEFAULT_PRIVATE_DIRECTORY,
|
||||
DEFAULT_PUBLIC_DIRECTORY,
|
||||
@@ -42,6 +43,7 @@ const envSchema = z.object({
|
||||
|
||||
// vars with default values
|
||||
TWTKPR_DEFAULT_ROUTE: z.string().default(DEFAULT_ROUTE),
|
||||
TWTKPR_PLUGIN_ROUTE: z.string().default(DEFAULT_PLUGIN_ROUTE),
|
||||
TWTKPR_PRIVATE_DIRECTORY: z.string().default(DEFAULT_PRIVATE_DIRECTORY),
|
||||
TWTKPR_PUBLIC_DIRECTORY: z.string().default(DEFAULT_PUBLIC_DIRECTORY),
|
||||
TWTKPR_QUERY_PARAMETER_APP: z.string().default(DEFAULT_QUERY_PARAMETER_APP),
|
||||
@@ -139,6 +141,8 @@ const parseEnv = () => {
|
||||
process.env.TWTKPR_ACCESS_SECRET || process.env.ACCESS_SECRET,
|
||||
TWTKPR_DEFAULT_ROUTE:
|
||||
process.env.TWTKPR_DEFAULT_ROUTE || process.env.DEFAULT_ROUTE,
|
||||
TWTKPR_PLUGIN_ROUTE:
|
||||
process.env.TWTKPR_PLUGIN_ROUTE || process.env.TWTKPR_PLUGIN_ROUTE,
|
||||
TWTKPR_PRIVATE_DIRECTORY:
|
||||
process.env.TWTKPR_PRIVATE_DIRECTORY || process.env.PRIVATE_DIRECTORY,
|
||||
TWTKPR_PUBLIC_DIRECTORY:
|
||||
|
||||
@@ -72,12 +72,13 @@ export default function getConfiguration(
|
||||
) {
|
||||
const {
|
||||
mainRoute = env.TWTKPR_DEFAULT_ROUTE,
|
||||
pluginRoute = env.TWTKPR_PLUGIN_ROUTE,
|
||||
privateDirectory = env.TWTKPR_PRIVATE_DIRECTORY,
|
||||
publicDirectory = env.TWTKPR_PUBLIC_DIRECTORY,
|
||||
twtxtFilename = env.TWTKPR_TWTXT_FILENAME,
|
||||
postLimiterConfiguration,
|
||||
queryParameters,
|
||||
uploadConfiguration,
|
||||
// uploadConfiguration,
|
||||
} = initialConfiguration ?? {};
|
||||
|
||||
const {
|
||||
@@ -96,33 +97,18 @@ export default function getConfiguration(
|
||||
twts = env.TWTKPR_QUERY_PARAMETER_TWTS,
|
||||
} = queryParameters ?? {};
|
||||
|
||||
const {
|
||||
active: uploadActive = env.TWTKPR_UPLOAD_ACTIVE,
|
||||
allowEmptyFiles = env.TWTKPR_UPLOAD_ALLOW_EMPTY_FILES,
|
||||
allowedMimeTypes = env.TWTKPR_UPLOAD_ALLOWED_MIME_TYPES,
|
||||
createDirsFromUploads = env.TWTKPR_UPLOAD_CREATE_DIRS_FROM_UPLOADS,
|
||||
directory = env.TWTKPR_UPLOAD_DIRECTORY,
|
||||
encoding = env.TWTKPR_UPLOAD_ENCODING,
|
||||
fileWriteStreamHandler,
|
||||
filter = () => true,
|
||||
hashAlgorithm = env.TWTKPR_UPLOAD_HASH_ALGORITHM,
|
||||
keepExtensions = env.TWTKPR_UPLOAD_KEEP_EXTENSIONS,
|
||||
maxFields = env.TWTKPR_UPLOAD_MAX_FIELDS,
|
||||
maxFileSize = env.TWTKPR_UPLOAD_MAX_FIELDS_SIZE,
|
||||
maxFiles = env.TWTKPR_UPLOAD_MAX_FILES,
|
||||
maxTotalFileSize = env.TWTKPR_UPLOAD_MAX_TOTAL_FILE_SIZE,
|
||||
minFileSize = env.TWTKPR_UPLOAD_MIN_FILE_SIZE,
|
||||
route = env.TWTKPR_UPLOAD_ROUTE,
|
||||
} = uploadConfiguration ?? {};
|
||||
|
||||
return {
|
||||
// secrets cannot be provided through configuration file, must use ENV / .env
|
||||
accessSecret: env.TWTKPR_ACCESS_SECRET,
|
||||
refreshSecret: env.TWTKPR_REFRESH_SECRET,
|
||||
mainRoute,
|
||||
pluginRoute,
|
||||
privateDirectory,
|
||||
publicDirectory,
|
||||
twtxtFilename,
|
||||
plugins: {
|
||||
...(initialConfiguration?.plugins ?? {}),
|
||||
},
|
||||
postLimiterConfiguration: {
|
||||
active: postLimiterActive,
|
||||
...(otherPostLimiterProps ?? {}),
|
||||
@@ -138,24 +124,5 @@ export default function getConfiguration(
|
||||
twt,
|
||||
twts,
|
||||
},
|
||||
uploadConfiguration: {
|
||||
...uploadConfiguration,
|
||||
active: uploadActive,
|
||||
allowEmptyFiles,
|
||||
allowedMimeTypes: getDestinationByMimeTypeConfiguration(allowedMimeTypes),
|
||||
createDirsFromUploads,
|
||||
directory,
|
||||
encoding,
|
||||
fileWriteStreamHandler,
|
||||
filter,
|
||||
hashAlgorithm: hashAlgorithm as string | false | undefined,
|
||||
keepExtensions,
|
||||
maxFields,
|
||||
maxFileSize,
|
||||
maxFiles,
|
||||
maxTotalFileSize,
|
||||
minFileSize,
|
||||
route,
|
||||
},
|
||||
} as TwtKprConfiguration;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import fsp from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
import { NodeCache } from '@cacheable/node-cache';
|
||||
import { NodeCache, NodeCacheOptions } from '@cacheable/node-cache';
|
||||
import Debug from 'debug';
|
||||
|
||||
import { parseTwtxt } from 'twtxt-lib';
|
||||
@@ -20,9 +20,23 @@ export default function twtxtCache({
|
||||
|
||||
const debug = Debug('twtkpr:twtxtCache');
|
||||
|
||||
const cache = new NodeCache();
|
||||
const defaultCacheOptions: NodeCacheOptions = {
|
||||
stdTTL: 299,
|
||||
checkperiod: 300,
|
||||
};
|
||||
|
||||
const cache = new NodeCache(defaultCacheOptions);
|
||||
|
||||
const getFromCache = async (key = '') => {
|
||||
debug(`checking cache for key: ${key}`);
|
||||
if (!key) return undefined;
|
||||
|
||||
const value = cache.get(key);
|
||||
if (value) return value;
|
||||
|
||||
debug('Not found, reloading keys');
|
||||
cache.flushAll();
|
||||
|
||||
const reloadCache = async () => {
|
||||
const fileText = await fsp.readFile(
|
||||
path.join(publicDirectory, twtxtFilename),
|
||||
'utf8'
|
||||
@@ -37,12 +51,16 @@ export default function twtxtCache({
|
||||
debug(`cache ${isLoaded ? 're' : ''}loaded`);
|
||||
|
||||
isLoaded = true;
|
||||
|
||||
return cache.get(key);
|
||||
};
|
||||
|
||||
reloadCache();
|
||||
const reloadCache = async () => {
|
||||
cache.flushAll();
|
||||
};
|
||||
|
||||
return {
|
||||
cache,
|
||||
getFromCache,
|
||||
reloadCache,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,42 @@
|
||||
import crypto from 'node:crypto';
|
||||
import { createReadStream } from 'node:fs';
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
import { PassThrough } from 'node:stream';
|
||||
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { __dirname } from './constants.js';
|
||||
|
||||
async function combineWithPassthrough(sources: any[], destination: any) {
|
||||
for (const stream of sources) {
|
||||
await new Promise((resolve, reject) => {
|
||||
let s = stream;
|
||||
if (typeof stream === 'function') {
|
||||
s = stream();
|
||||
}
|
||||
|
||||
if (typeof s === 'string') {
|
||||
destination.push(s);
|
||||
destination.push(null);
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
|
||||
s.pipe(destination, { end: false });
|
||||
s.on('end', resolve);
|
||||
s.on('error', reject);
|
||||
});
|
||||
}
|
||||
destination.emit('end');
|
||||
}
|
||||
|
||||
export function combineStreams(streams: any[]) {
|
||||
const stream = new PassThrough();
|
||||
combineWithPassthrough(streams, stream).catch((err) => stream.destroy(err));
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param userId
|
||||
@@ -51,6 +85,23 @@ export const getQueryParameterArray = (value: unknown | unknown[] = []) =>
|
||||
? value.map((val) => `${val}`.trim())
|
||||
: [`${value}`.trim()];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param pathToFile
|
||||
* @returns
|
||||
*/
|
||||
export const getReadStream = (pathToFile: string) => {
|
||||
const theStream = createReadStream(pathToFile);
|
||||
|
||||
theStream.on('error', (err) => {
|
||||
console.error(err);
|
||||
theStream.close();
|
||||
theStream.push(null);
|
||||
});
|
||||
|
||||
return theStream;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export { default } from "./postHandler.js";
|
||||
export { default } from './postHandler.js';
|
||||
export { default as pluginPostHandler } from './pluginPostHandler.js';
|
||||
|
||||
@@ -18,16 +18,17 @@ export default async function memoryCache(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction,
|
||||
cache: NodeCache<unknown>,
|
||||
reloadCache: () => Promise<void>
|
||||
) {
|
||||
if (cache.keys().length && !['DELETE', 'POST', 'PUT'].includes(req.method)) {
|
||||
debug(req.method);
|
||||
if (!['DELETE', 'POST', 'PUT'].includes(req.method)) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
reloadCache()
|
||||
.then(() => {
|
||||
debug('Cache reloaded');
|
||||
next();
|
||||
})
|
||||
.catch((err) => {
|
||||
|
||||
50
src/middlewares/postHandler/pluginPostHandler.ts
Normal file
50
src/middlewares/postHandler/pluginPostHandler.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import Debug from 'debug';
|
||||
import express, { NextFunction, Request, Response } from 'express';
|
||||
import { TwtKprPluginConfiguration, TwtKprPluginRoute } from '../../types.js';
|
||||
|
||||
const debug = Debug('twtkpr:postHandler');
|
||||
|
||||
/**
|
||||
*
|
||||
* @param config * @returns
|
||||
*/
|
||||
export default function pluginPostHandler(
|
||||
plugins: TwtKprPluginConfiguration[] = [],
|
||||
verifyAuthRequest: (r: Request) => Promise<boolean>,
|
||||
reloadCache?: () => Promise<void>
|
||||
) {
|
||||
const router = express.Router();
|
||||
|
||||
const pluginRoutes = ([] as TwtKprPluginRoute[]).concat(
|
||||
...(Object.keys(plugins)
|
||||
.filter(
|
||||
(key) =>
|
||||
(plugins[key as keyof typeof plugins] as TwtKprPluginConfiguration)
|
||||
?.postRoutes?.length
|
||||
)
|
||||
.map(
|
||||
(key) =>
|
||||
(plugins[key as keyof typeof plugins] as TwtKprPluginConfiguration)
|
||||
.postRoutes
|
||||
) as TwtKprPluginRoute[][])
|
||||
);
|
||||
|
||||
pluginRoutes.forEach(({ handler, path, requiresAuth }) => {
|
||||
debug(`adding POST plugin router for ${path}`);
|
||||
|
||||
router.post(path, async (req, res, next) => {
|
||||
debug(`handling POST plugin route to ${path}`);
|
||||
|
||||
if (requiresAuth && !(await verifyAuthRequest(req))) {
|
||||
debug('auth check failed');
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
handler(req, res, next);
|
||||
reloadCache?.();
|
||||
});
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import express, { NextFunction, Request, Response } from 'express';
|
||||
import rateLimit from 'express-rate-limit';
|
||||
|
||||
import authCheck from '../../middlewares/authCheckJWT.js';
|
||||
import { TwtKprConfiguration } from '../../types.js';
|
||||
import { TwtKprConfiguration, TwtKprPluginConfiguration } from '../../types.js';
|
||||
import login from './login.js';
|
||||
import logout from './logout.js';
|
||||
import refresh from './refresh.js';
|
||||
@@ -17,7 +17,11 @@ const debug = Debug('twtkpr:postHandler');
|
||||
* @param config
|
||||
* @returns
|
||||
*/
|
||||
export default function postHandler(config: TwtKprConfiguration) {
|
||||
export default function postHandler(
|
||||
config: TwtKprConfiguration,
|
||||
plugins: TwtKprPluginConfiguration[] = [],
|
||||
reloadCache?: () => Promise<void>
|
||||
) {
|
||||
const { postLimiterConfiguration } = config;
|
||||
const { active: isLimiterActive, ...otherLimiterProps } =
|
||||
postLimiterConfiguration ?? {};
|
||||
@@ -59,7 +63,11 @@ export default function postHandler(config: TwtKprConfiguration) {
|
||||
}
|
||||
debug('auth check succeeded');
|
||||
|
||||
if (type === 'twt' || content) return twt(req, res, config);
|
||||
if (type === 'twt' || content) {
|
||||
twt(req, res, config, plugins);
|
||||
reloadCache?.();
|
||||
return;
|
||||
}
|
||||
if (type === 'editFile') return editFile(req, res, config);
|
||||
|
||||
next();
|
||||
|
||||
@@ -25,13 +25,13 @@ export default async function refresh(
|
||||
res: Response,
|
||||
config: TwtKprConfiguration
|
||||
) {
|
||||
const send401 = (message: string) => {
|
||||
const sendError = (message: string, code = 401) => {
|
||||
debug(message);
|
||||
|
||||
res
|
||||
.clearCookie('accessToken')
|
||||
.clearCookie('refreshToken')
|
||||
.status(401)
|
||||
.status(code)
|
||||
.send(message ?? 'Unauthorized');
|
||||
|
||||
return;
|
||||
@@ -41,9 +41,9 @@ export default async function refresh(
|
||||
const tokens = await refreshTokensDB(config.privateDirectory);
|
||||
const oldToken = req.cookies.refreshToken;
|
||||
|
||||
debug(oldToken);
|
||||
debug(`Using old token: ${oldToken}`);
|
||||
|
||||
if (!oldToken) return send401('Unauthorized');
|
||||
if (!oldToken) return sendError('Unauthorized');
|
||||
|
||||
let decoded = { id: '' };
|
||||
|
||||
@@ -52,14 +52,15 @@ export default async function refresh(
|
||||
id: string;
|
||||
};
|
||||
|
||||
debug({ decoded });
|
||||
debug('Decoded token: ', { decoded });
|
||||
} catch (err) {
|
||||
return send401('Refresh token invalid');
|
||||
debug('Error decoding refresh token:', err);
|
||||
return sendError('Refresh token invalid', 403);
|
||||
}
|
||||
|
||||
const username = req.username ?? decoded.id;
|
||||
|
||||
if (!username) return send401('Missing username');
|
||||
if (!username) return sendError('Missing username');
|
||||
|
||||
const currentTime = Math.floor(Date.now() / 1000);
|
||||
|
||||
@@ -72,7 +73,7 @@ export default async function refresh(
|
||||
// If token is invalid or not the latest one
|
||||
if (!validTokens.includes(oldToken)) {
|
||||
debug('token missing from list');
|
||||
return send401('Invalid refresh token');
|
||||
return sendError('Invalid refresh token');
|
||||
}
|
||||
|
||||
debug('generating new tokens');
|
||||
@@ -84,7 +85,8 @@ export default async function refresh(
|
||||
|
||||
const newRefreshToken = generateRefreshToken(
|
||||
req.username || decoded.id,
|
||||
config.refreshSecret
|
||||
config.refreshSecret,
|
||||
!!req.query.rememberToggle
|
||||
);
|
||||
|
||||
debug('updating token list');
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import type { Request, Response } from 'express';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import fs from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import dayjs from 'dayjs';
|
||||
import Debug from 'debug';
|
||||
|
||||
import { TwtKprConfiguration } from '../../types.js';
|
||||
import { TwtKprConfiguration, TwtKprPluginConfiguration } from '../../types.js';
|
||||
|
||||
const debug = Debug('twtkpr:twt');
|
||||
|
||||
/**
|
||||
* Creates a new twt, appending it to the bottom of the TWTXT file
|
||||
@@ -15,13 +18,19 @@ import { TwtKprConfiguration } from '../../types.js';
|
||||
export default function twt(
|
||||
req: Request,
|
||||
res: Response,
|
||||
config: TwtKprConfiguration
|
||||
config: TwtKprConfiguration,
|
||||
plugins: TwtKprPluginConfiguration[] = []
|
||||
) {
|
||||
debug('Beginning twt add');
|
||||
|
||||
const { content } = req.body ?? {};
|
||||
|
||||
const date = dayjs().format();
|
||||
const twt = `${date}\t${content.trim()}\n`;
|
||||
|
||||
debug(`Formatted twt: ${twt}`);
|
||||
|
||||
debug('Beginning stream...');
|
||||
const stream = fs.createWriteStream(
|
||||
join(config.publicDirectory, config.twtxtFilename),
|
||||
{
|
||||
@@ -32,5 +41,19 @@ export default function twt(
|
||||
stream.write(twt);
|
||||
stream.end();
|
||||
|
||||
debug('Streaming complete');
|
||||
|
||||
plugins.forEach(async (plugin) => {
|
||||
if (!plugin.onAfterTwt) return;
|
||||
|
||||
debug(
|
||||
`Handling plugin onAfterTwt function${
|
||||
plugin.name ? ' from ' + plugin.name : ''
|
||||
}`
|
||||
);
|
||||
|
||||
plugin.onAfterTwt(twt);
|
||||
});
|
||||
|
||||
res.status(200).send(twt);
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export { default } from "./putHandler.js";
|
||||
export { default } from './putHandler.js';
|
||||
export { default as pluginPutHandler } from './pluginPutHandler.js';
|
||||
|
||||
52
src/middlewares/putHandler/pluginPutHandler.ts
Normal file
52
src/middlewares/putHandler/pluginPutHandler.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import Debug from 'debug';
|
||||
import express, { NextFunction, Request, Response } from 'express';
|
||||
import { TwtKprPluginConfiguration, TwtKprPluginRoute } from '../../types.js';
|
||||
|
||||
const debug = Debug('twtkpr:postHandler');
|
||||
|
||||
/**
|
||||
*
|
||||
* @param plugins
|
||||
* @returns
|
||||
*/
|
||||
export default function pluginPutHandler(
|
||||
plugins: TwtKprPluginConfiguration[] = [],
|
||||
verifyAuthRequest: (r: Request) => Promise<boolean>,
|
||||
reloadCache?: () => Promise<void>
|
||||
) {
|
||||
const router = express.Router();
|
||||
|
||||
const pluginRoutes = ([] as TwtKprPluginRoute[]).concat(
|
||||
...(Object.keys(plugins)
|
||||
.filter(
|
||||
(key) =>
|
||||
(plugins[key as keyof typeof plugins] as TwtKprPluginConfiguration)
|
||||
?.putRoutes?.length
|
||||
)
|
||||
.map(
|
||||
(key) =>
|
||||
(plugins[key as keyof typeof plugins] as TwtKprPluginConfiguration)
|
||||
.putRoutes
|
||||
) as TwtKprPluginRoute[][])
|
||||
);
|
||||
|
||||
pluginRoutes.forEach(({ handler, path, requiresAuth }) => {
|
||||
debug(`adding PUT plugin router for ${path}`);
|
||||
|
||||
router.put(path, async (req, res, next) => {
|
||||
debug(`handling PUT plugin route to ${path}`);
|
||||
|
||||
if (requiresAuth && !(await verifyAuthRequest(req))) {
|
||||
debug('auth check failed');
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
handler(req, res, next);
|
||||
|
||||
reloadCache?.();
|
||||
});
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import Debug from 'debug';
|
||||
import express from 'express';
|
||||
|
||||
import authCheck from '../../middlewares/authCheckJWT.js';
|
||||
import { TwtKprConfiguration } from '../../types.js';
|
||||
import { TwtKprConfiguration, TwtKprPluginConfiguration } from '../../types.js';
|
||||
import editFile from './editFile.js';
|
||||
|
||||
const debug = Debug('twtkpr:putHandler');
|
||||
@@ -12,7 +12,11 @@ const debug = Debug('twtkpr:putHandler');
|
||||
* @param config
|
||||
* @returns
|
||||
*/
|
||||
export default function putHandler(config: TwtKprConfiguration) {
|
||||
export default function putHandler(
|
||||
config: TwtKprConfiguration,
|
||||
plugins: TwtKprPluginConfiguration[] = [],
|
||||
reloadCache?: () => Promise<void>
|
||||
) {
|
||||
const router = express.Router();
|
||||
|
||||
router.put('/', (req, res, next) => {
|
||||
@@ -28,7 +32,10 @@ export default function putHandler(config: TwtKprConfiguration) {
|
||||
|
||||
debug('auth check succeeded');
|
||||
|
||||
return editFile(req, res, config);
|
||||
editFile(req, res, config);
|
||||
|
||||
reloadCache?.();
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import NodeCache from '@cacheable/node-cache';
|
||||
export default function followingHandler(
|
||||
req: Request,
|
||||
res: Response,
|
||||
cache: NodeCache<unknown>,
|
||||
following: Twttr[],
|
||||
followingParameter: QueryParameters['following']
|
||||
) {
|
||||
const followingsToMatch = getQueryParameterArray(
|
||||
@@ -40,7 +40,7 @@ export default function followingHandler(
|
||||
if (wantsJson) res.set('content-type', 'application/json');
|
||||
else res.set('content-type', 'text/plain');
|
||||
|
||||
const matchedFollowing = (cache.get('following') as Twttr[]).filter(
|
||||
const matchedFollowing = following.filter(
|
||||
({ nick, url }) =>
|
||||
(!followingsToMatch.length ||
|
||||
(followingsToMatch.length === 1 && followingsToMatch[0] === '') ||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import type { Request, Response } from 'express';
|
||||
import type { Metadata } from 'twtxt-lib';
|
||||
|
||||
import { env } from '../../lib/env.js';
|
||||
import twtxtCache from '../../lib/twtxtCache.js';
|
||||
import {
|
||||
generateEtag,
|
||||
getQueryParameterArray,
|
||||
getValueOrFirstEntry,
|
||||
} from '../../lib/utils.js';
|
||||
import NodeCache from '@cacheable/node-cache';
|
||||
import { NodeCache } from '@cacheable/node-cache';
|
||||
import { QueryParameters } from '../../types.js';
|
||||
|
||||
export interface MetadataHandler {
|
||||
@@ -28,7 +26,7 @@ export interface MetadataHandler {
|
||||
export default function metadataHandler(
|
||||
req: Request,
|
||||
res: Response,
|
||||
cache: NodeCache<unknown>,
|
||||
metadata: Metadata,
|
||||
metadataParameter: QueryParameters['metadata']
|
||||
) {
|
||||
const metadataToMatch = getQueryParameterArray(req.query[metadataParameter]);
|
||||
@@ -38,8 +36,6 @@ export default function metadataHandler(
|
||||
...getQueryParameterArray(req.query.s),
|
||||
];
|
||||
|
||||
const metadata = (cache.get('metadata') as Metadata) ?? {};
|
||||
|
||||
const wantsJson =
|
||||
req.is('json') ||
|
||||
getValueOrFirstEntry(getQueryParameterArray(req.query.format)) === 'json';
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import NodeCache from '@cacheable/node-cache';
|
||||
import Debug from 'debug';
|
||||
import type { NextFunction, Request, Response } from 'express';
|
||||
import { __dirname } from '../../lib/constants.js';
|
||||
import { generateEtag } from '../../lib/utils.js';
|
||||
import { TwtKprConfiguration } from '../../types.js';
|
||||
import { combineStreams, generateEtag } from '../../lib/utils.js';
|
||||
import { TwtKprConfiguration, TwtKprPluginConfiguration } from '../../types.js';
|
||||
import renderApp from '../renderApp/index.js';
|
||||
import followingHandler from './followingHandler.js';
|
||||
import metadataHandler from './metadataHandler.js';
|
||||
import twtHandler from './twtHandler.js';
|
||||
import { Twt } from 'twtxt-lib';
|
||||
import { Metadata, Twt, Twttr } from 'twtxt-lib';
|
||||
|
||||
const debug = Debug('twtkpr:queryHandler');
|
||||
|
||||
@@ -22,13 +22,17 @@ const debug = Debug('twtkpr:queryHandler');
|
||||
*/
|
||||
export default function queryHandler(
|
||||
config: TwtKprConfiguration,
|
||||
cache: NodeCache<unknown>,
|
||||
verifyAuthRequest: (r: Request) => Promise<boolean>
|
||||
getFromCache: (key?: string) => Promise<unknown>,
|
||||
verifyAuthRequest: (r: Request) => Promise<boolean>,
|
||||
plugins: TwtKprPluginConfiguration[] = []
|
||||
) {
|
||||
const { mainRoute, queryParameters, uploadConfiguration } = config;
|
||||
const { mainRoute, queryParameters } = config;
|
||||
|
||||
const getPluginStreams = (type: keyof TwtKprPluginConfiguration) =>
|
||||
plugins.filter((plugin) => !!plugin[type]).map((plugin) => plugin[type]);
|
||||
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
debug({ query: JSON.stringify(req.query) });
|
||||
debug(`handling query`, JSON.stringify(req.query));
|
||||
|
||||
if (!Object.keys(req.query).length) {
|
||||
next();
|
||||
@@ -36,53 +40,119 @@ export default function queryHandler(
|
||||
}
|
||||
|
||||
if (req.query[queryParameters.app] !== undefined) {
|
||||
const appContent = renderApp({ mainRoute, uploadConfiguration });
|
||||
debug('rendering app');
|
||||
const appContent = renderApp({ mainRoute });
|
||||
res.set('etag', generateEtag(appContent)).send(appContent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.query[queryParameters.css] !== undefined) {
|
||||
res.sendFile('styles.css', {
|
||||
root: path.resolve(__dirname, 'client'),
|
||||
debug('rendering css');
|
||||
const mainStream = fs.createReadStream(
|
||||
path.join(__dirname, 'client', 'styles.css')
|
||||
);
|
||||
|
||||
const pluginStreams = getPluginStreams('clientCSS');
|
||||
|
||||
const streams = [mainStream, ...pluginStreams];
|
||||
|
||||
const combined = combineStreams(streams);
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/css',
|
||||
});
|
||||
combined.pipe(res);
|
||||
|
||||
combined.on('error', (error) => {
|
||||
console.error('Error streaming file:', error);
|
||||
res.end();
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.query[queryParameters.js] !== undefined) {
|
||||
res.sendFile('script.js', {
|
||||
root: path.resolve(__dirname, 'client'),
|
||||
debug('rendering js');
|
||||
const mainStream = fs.createReadStream(
|
||||
path.join(__dirname, 'client', 'script.js')
|
||||
);
|
||||
|
||||
const pluginStreams = getPluginStreams('clientJS');
|
||||
const streams = [mainStream, ...pluginStreams];
|
||||
|
||||
const combined = combineStreams(streams);
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/javascript',
|
||||
});
|
||||
combined.pipe(res);
|
||||
|
||||
combined.on('error', (error) => {
|
||||
console.error('Error streaming file:', error);
|
||||
res.end();
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
req.query[queryParameters.following] !== undefined &&
|
||||
cache.get('following')
|
||||
(await getFromCache('following'))
|
||||
) {
|
||||
return followingHandler(req, res, cache, queryParameters.following);
|
||||
debug('rendering following');
|
||||
const following = await getFromCache('following');
|
||||
return followingHandler(
|
||||
req,
|
||||
res,
|
||||
following as Twttr[],
|
||||
queryParameters.following
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
req.query[queryParameters.metadata] !== undefined &&
|
||||
cache.get('metadata')
|
||||
(await getFromCache('metadata'))
|
||||
) {
|
||||
return metadataHandler(req, res, cache, queryParameters.metadata);
|
||||
debug('rendering metadata');
|
||||
const metadata = (await getFromCache('metadata')) as Metadata;
|
||||
return metadataHandler(req, res, metadata, queryParameters.metadata);
|
||||
}
|
||||
|
||||
if (
|
||||
(req.query[queryParameters.twt] !== undefined ||
|
||||
req.query[queryParameters.twts] !== undefined) &&
|
||||
cache.get('twts')
|
||||
(await getFromCache('twts'))
|
||||
) {
|
||||
debug('rendering twts');
|
||||
const twts = await getFromCache('twts');
|
||||
return twtHandler(
|
||||
req,
|
||||
res,
|
||||
cache.get('twts') as Twt[],
|
||||
twts as Twt[],
|
||||
queryParameters.twt,
|
||||
queryParameters.twts
|
||||
);
|
||||
}
|
||||
|
||||
(plugins as TwtKprPluginConfiguration[]).forEach((plugin) => {
|
||||
if (!plugin?.queryRoutes?.length) return;
|
||||
|
||||
plugin.queryRoutes.forEach(async (route) => {
|
||||
// default to no auth
|
||||
const { handler, queryParameter, requiresAuth = false } = route ?? {};
|
||||
|
||||
if (queryParameter && req.query[queryParameter] !== undefined) {
|
||||
debug(`rendering plugin queryParameter ${queryParameter}`);
|
||||
|
||||
if (requiresAuth && !(await verifyAuthRequest(req))) {
|
||||
debug('auth check failed');
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
return handler(req, res, next);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Request, Response } from 'express';
|
||||
import type { Metadata, Twt } from 'twtxt-lib';
|
||||
import type { Twt } from 'twtxt-lib';
|
||||
|
||||
import {
|
||||
generateEtag,
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
} from '../../lib/utils.js';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc.js';
|
||||
import NodeCache from '@cacheable/node-cache';
|
||||
import { QueryParameters } from '../../types.js';
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { version } from '../../packageInfo.js';
|
||||
import { TwtKprConfiguration } from '../../types.js';
|
||||
|
||||
import renderUploadButton from './renderUploadButton.js';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param param0
|
||||
@@ -10,8 +8,7 @@ import renderUploadButton from './renderUploadButton.js';
|
||||
*/
|
||||
export default function renderApp({
|
||||
mainRoute,
|
||||
uploadConfiguration,
|
||||
}: Pick<TwtKprConfiguration, 'mainRoute' | 'uploadConfiguration'>) {
|
||||
}: Pick<TwtKprConfiguration, 'mainRoute'>) {
|
||||
return `<!doctype html>
|
||||
<html class="no-js" lang="en" xmlns:fb="http://ogp.me/ns/fb#">
|
||||
|
||||
@@ -91,9 +88,8 @@ export default function renderApp({
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
${renderUploadButton(uploadConfiguration)}
|
||||
<label class="twtControls-contentLabel" for="twtControlsContentInput">
|
||||
<textarea class="twtControls-contentInput"
|
||||
<textarea class="twtControls-contentInput emoji_target"
|
||||
id="twtControlsContentInput" name="content"
|
||||
placeholder="What do you want to say?"></textarea>
|
||||
</label>
|
||||
@@ -107,7 +103,6 @@ export default function renderApp({
|
||||
<div class="popupMenu-appInfo appInfo">
|
||||
TwtKpr v${version ?? 'Unknown'}
|
||||
</div>
|
||||
${renderUploadButton(uploadConfiguration, 'small')}
|
||||
<button class="twtControls-editButton button" id="twtControlsEditButton">
|
||||
Edit File
|
||||
</button>
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import { TwtKprConfiguration } from '../../types.js';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param uploadConfiguration
|
||||
* @param variant
|
||||
* @returns
|
||||
*/
|
||||
export default function renderUploadButton(
|
||||
uploadConfiguration: TwtKprConfiguration['uploadConfiguration'],
|
||||
variant: 'normal' | 'small' = 'normal'
|
||||
) {
|
||||
const { active, allowedMimeTypes, route } = uploadConfiguration ?? {};
|
||||
|
||||
if (!active) return '';
|
||||
|
||||
// determine accept from allowed mime types - may need to rebuild value based on fallback n getConfiguration, rather than at the end.
|
||||
|
||||
return `
|
||||
<label class="button twtControls-uploadInputLabel twtControls-uploadInputLabel-${variant}"
|
||||
for="twtControlsUploadInput-${variant}"
|
||||
>
|
||||
Upload${variant === 'normal' ? '<br />' : ' '}Files
|
||||
<input accept="*" class="twtControls-uploadInput" data-route="${route}"
|
||||
id="twtControlsUploadInput-${variant}"
|
||||
multiple type="file" />
|
||||
</label>
|
||||
`;
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
import fsp from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import formidable from 'formidable';
|
||||
import type { NextFunction, Request, Response } from 'express';
|
||||
import Debug from 'debug';
|
||||
|
||||
import { __dirname } from '../lib/env.js';
|
||||
import { MimeOptions, TwtKprConfiguration } from '../types.js';
|
||||
|
||||
const debug = Debug('twtkpr:uploadHandler');
|
||||
|
||||
/**
|
||||
*
|
||||
* @param allowedMimeTypes
|
||||
* @returns
|
||||
*/
|
||||
const getDestinationByMimeTypeConfiguration = (
|
||||
allowedMimeTypes?: string | string[] | Record<string, string>
|
||||
) => {
|
||||
const fallback: Record<string, string> = {
|
||||
audio: 'audio',
|
||||
image: 'images',
|
||||
text: 'texts',
|
||||
video: 'videos',
|
||||
'*': 'files',
|
||||
};
|
||||
|
||||
const mimeTypeArrayReducer = (acc: Record<string, string>, curr: string) => {
|
||||
if (fallback[curr]) acc[curr] = fallback[curr];
|
||||
else acc[curr] = `${curr}s`;
|
||||
return acc;
|
||||
};
|
||||
|
||||
if (!allowedMimeTypes) return fallback;
|
||||
|
||||
if (typeof allowedMimeTypes === 'string')
|
||||
return allowedMimeTypes
|
||||
.split(',')
|
||||
.map((val) => val.trim())
|
||||
.reduce(mimeTypeArrayReducer, {});
|
||||
|
||||
if (Array.isArray(allowedMimeTypes))
|
||||
return (allowedMimeTypes as string[]).reduce(mimeTypeArrayReducer, {});
|
||||
|
||||
if (typeof allowedMimeTypes === 'object') return allowedMimeTypes;
|
||||
|
||||
return fallback;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param config
|
||||
* @param verifyAuthRequest
|
||||
* @returns
|
||||
*/
|
||||
export default function uploadHandler(
|
||||
config: TwtKprConfiguration,
|
||||
verifyAuthRequest: (r: Request) => Promise<boolean>
|
||||
) {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
debug('checking auth');
|
||||
if (!(await verifyAuthRequest(req))) {
|
||||
debug('auth check failed');
|
||||
res.status(401).send('Unauthorized');
|
||||
return;
|
||||
}
|
||||
debug('auth check succeeded');
|
||||
|
||||
const { active, allowedMimeTypes, directory, route, ...otherProps } =
|
||||
config.uploadConfiguration;
|
||||
|
||||
if (
|
||||
!active ||
|
||||
(Array.isArray(allowedMimeTypes) && !allowedMimeTypes.length)
|
||||
) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
debug('using configuration: ', {
|
||||
uploadConfiguration: config.uploadConfiguration,
|
||||
});
|
||||
|
||||
const form = formidable({
|
||||
uploadDir: directory,
|
||||
...otherProps,
|
||||
});
|
||||
|
||||
form.parse(req, async (err, fields, files) => {
|
||||
if (err) {
|
||||
next(err);
|
||||
return;
|
||||
}
|
||||
const uploadsDir = (route ?? '').replaceAll('/', '');
|
||||
|
||||
let hadFileError = false;
|
||||
const processedFiles: string[] = [];
|
||||
const destinationByMimeType = allowedMimeTypes;
|
||||
|
||||
debug(`processing ${(files?.files ?? []).length} files`);
|
||||
|
||||
for (const file of files?.files ?? []) {
|
||||
const { filepath, hash, mimetype, newFilename, originalFilename } =
|
||||
file ?? {};
|
||||
if (!(filepath && newFilename && originalFilename)) return;
|
||||
|
||||
console.log({ file });
|
||||
|
||||
let ext = path.extname(originalFilename).toLocaleLowerCase();
|
||||
if (ext === '.jpeg') ext = '.jpg';
|
||||
|
||||
const finalFilename = (
|
||||
hash && (mimetype?.includes('image') || mimetype?.includes('video'))
|
||||
? `${hash}${ext}`
|
||||
: originalFilename
|
||||
)
|
||||
.replace(/\s+/g, '-')
|
||||
.toLocaleLowerCase();
|
||||
|
||||
let destinationDir = '';
|
||||
Object.keys(destinationByMimeType).forEach((mimeType) => {
|
||||
if (file.mimetype?.split('/')?.[0] === mimeType.toLocaleLowerCase())
|
||||
destinationDir =
|
||||
(
|
||||
destinationByMimeType[
|
||||
mimeType as keyof typeof destinationByMimeType
|
||||
] as MimeOptions
|
||||
).directory ?? '';
|
||||
});
|
||||
if (destinationDir === '')
|
||||
destinationDir =
|
||||
(
|
||||
destinationByMimeType[
|
||||
'*' as keyof typeof destinationByMimeType
|
||||
] as MimeOptions
|
||||
).directory ?? uploadsDir;
|
||||
|
||||
const finalPath = path.join(process.cwd(), 'public', destinationDir);
|
||||
|
||||
debug(`creating '${finalPath}'`);
|
||||
fsp.mkdir(finalPath, { recursive: true });
|
||||
|
||||
debug(`copying '${filepath}' to '/${destinationDir}/${finalFilename}'`);
|
||||
|
||||
try {
|
||||
await fsp.copyFile(filepath, path.join(finalPath, finalFilename));
|
||||
|
||||
debug(`cleaning up '${filepath}'`);
|
||||
await fsp.rm(filepath);
|
||||
|
||||
debug(`processed successfully`);
|
||||
processedFiles.push(`/${destinationDir}/${finalFilename}`);
|
||||
} catch (err) {
|
||||
debug(`error!`);
|
||||
hadFileError = true;
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
debug('generating reply...');
|
||||
if (hadFileError && processedFiles.length) {
|
||||
res.type('text/plain').status(206).send(processedFiles.join('\n'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!processedFiles.length) {
|
||||
res.type('text/plain').status(500).send('No files processed');
|
||||
return;
|
||||
}
|
||||
|
||||
res.type('text/plain').status(201).send(processedFiles.join('\n'));
|
||||
});
|
||||
};
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user