Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F1374612
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
6 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/back/proxy.ts b/src/back/proxy.ts
index 61e5cbe..1256978 100644
--- a/src/back/proxy.ts
+++ b/src/back/proxy.ts
@@ -1,102 +1,104 @@
import express from "express";
import type { Server } from "node:http";
import type { Request, Response, Express } from "express";
import { createProxyMiddleware, responseInterceptor } from "http-proxy-middleware";
import { HTMLRewriter } from "html-rewriter-wasm";
import type TheApp from "./app.js";
import { htmlUnescape } from "escape-goat";
import { promisify } from "util";
import { PROXY_TARGET } from "src/back/config.js";
const encoder = new TextEncoder();
const decoder = new TextDecoder();
export default class Proxy {
express_app: Express;
server: Server;
constructor(public app: TheApp) {}
async start() {
this.express_app = express();
console.log("Starting proxy...");
const { items: rules } = await this.app.collections.rules
.suList()
.format({ code: "original" })
.filter({ active: true })
.fetch();
const code = rules
.map((rule) => `{${htmlUnescape(rule.get("code") || "")}}`)
.join("\n");
console.log("The code to run is");
console.log(code);
const proxyMiddleware = createProxyMiddleware<Request, Response>({
target: PROXY_TARGET,
changeOrigin: true,
selfHandleResponse: true,
on: {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
- proxyRes: responseInterceptor(async (responseBuffer, proxyRes) => {
+ proxyRes: responseInterceptor(async (responseBuffer, proxyRes, req) => {
if (!proxyRes.headers["content-type"]?.includes("html")) {
// only rewrite html
return responseBuffer;
}
const response = responseBuffer.toString("utf8"); // convert buffer to string
let output = "";
const rewriter = new HTMLRewriter((outputChunk) => {
output += decoder.decode(outputChunk);
});
try {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const url = req.url; // to make it available in eval
eval(code);
} catch (e) {
console.error(e);
rewriter.free();
return responseBuffer;
}
try {
await rewriter.write(encoder.encode(response));
await rewriter.end();
} catch (e) {
console.error(e);
return responseBuffer;
} finally {
rewriter.free(); // Remember to free memory
}
return output;
}),
},
});
this.express_app.use("/", proxyMiddleware);
const port = 3000;
this.server = this.express_app.listen(port);
console.log(
`Proxy started. Target URL is ${PROXY_TARGET}. Listening on :${port}`
);
}
stop() {
console.log("Stopping proxy...");
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-argument, @typescript-eslint/consistent-type-assertions
return promisify(this.server.close.bind(this.server)).bind(
this.server
)() as Promise<void>;
}
}
let instance: Proxy;
export function getProxy(app: TheApp): Proxy {
if (!instance) {
instance = new Proxy(app);
}
return instance;
}
diff --git a/src/back/routes/admin/rules/index.page.ts b/src/back/routes/admin/rules/index.page.ts
index a500375..8c7bdeb 100644
--- a/src/back/routes/admin/rules/index.page.ts
+++ b/src/back/routes/admin/rules/index.page.ts
@@ -1,107 +1,107 @@
import { CRUD } from "@sealcode/crud-ui";
import type { Context } from "koa";
import type { CollectionItem } from "sealious";
import type { ListFilterRender } from "@sealcode/sealgen";
import { Controls, DefaultListFilters, Fields } from "@sealcode/sealgen";
import { Rules } from "src/back/collections/collections.js";
import html from "src/back/html.js";
import { getProxy } from "src/back/proxy.js";
export const actionName = "RulesCRUD";
const edit_fields = <const>{
name: new Fields.CollectionField(Rules.fields.name.required, Rules.fields.name),
code: new Fields.CollectionField(Rules.fields.code.required, Rules.fields.code),
active: new Fields.Boolean(Rules.fields.active.required),
};
const edit_controls = [
new Controls.SimpleInput(edit_fields.name, { label: "name" }),
new Controls.Checkbox(edit_fields.active, { label: "active" }),
new Controls.Textarea(edit_fields.code, {
label: "code",
cols: 80,
rows: 15,
autogrow: true,
placeholder: ``,
}),
new Controls.HTML(
"decoration",
/* HTML */ `<div>
Examples:
<h3>Change contents of all paragraphs to "p"</h3>
<pre><code>
rewriter.on("p", {
element(element) {
element.setInnerContent("new");
},
});
</code></pre>
<h3>Add an attribute to all links within a div with a class 'footer'</h3>
<pre><code>
-rewriter.on(".footer a", {
+rewriter.on("footer a", {
element(element) {
element.setAttribute("rel", "nofollow")
},
});
</code></pre>
</div>`
),
];
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const fields_for_display = [
{ field: "name", label: "name" },
{ field: "code", label: "code" },
{
field: "active",
label: "active",
format: (v: boolean) => (v ? "YES" : "NO"),
},
] as {
field: keyof (typeof Rules)["fields"];
label: string;
format?: (
value: unknown,
item: CollectionItem<typeof Rules>
) => string | Promise<string>;
}[];
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const fields_for_filters = [
{ field: "name", ...DefaultListFilters["text"] },
{ field: "code", ...DefaultListFilters["text"] },
{ field: "active", ...DefaultListFilters["boolean"] },
] as {
field: keyof (typeof Rules)["fields"];
render?: ListFilterRender;
prepareValue?: (filter_value: unknown) => unknown; // set this function to change what filter value is passed to Sealious
}[];
async function restart_proxy(ctx: Context) {
console.log("RESTARTING PROXY");
const proxy = getProxy(ctx.$app);
await proxy.stop();
await proxy.start();
}
export default new CRUD({
collection: Rules,
nice_collection_name: "Rules",
fields_for_display,
fields_for_filters,
html,
list_title: "Rules list",
edit_title: "Edit",
edit_button_text: "Edit",
delete_button_text: "Delete",
back_to_list_button_text: "← Back to Rules list",
edit_fields,
edit_controls,
form_value_to_sealious_value: {},
sealious_value_to_form_value: {},
post_edit: restart_proxy,
post_create: restart_proxy,
});
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Feb 25, 03:45 (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
610433
Default Alt Text
(6 KB)
Attached To
Mode
rREWRITE Configurable rewriter
Attached
Detach File
Event Timeline
Log In to Comment