full first commit
This commit is contained in:
parent
80f6b69d04
commit
3849dfa92c
6 changed files with 141 additions and 148 deletions
35
package-lock.json
generated
35
package-lock.json
generated
|
@ -7,6 +7,11 @@
|
|||
"": {
|
||||
"name": "status",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"yaml": "^2.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"wrangler": "^3.0.0"
|
||||
}
|
||||
|
@ -923,6 +928,25 @@
|
|||
"node": ">=16.13"
|
||||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.30.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/moment-timezone": {
|
||||
"version": "0.5.45",
|
||||
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz",
|
||||
"integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==",
|
||||
"dependencies": {
|
||||
"moment": "^2.29.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
|
@ -1256,6 +1280,17 @@
|
|||
"integrity": "sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz",
|
||||
"integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==",
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/youch": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/youch/-/youch-3.3.3.tgz",
|
||||
|
|
|
@ -9,5 +9,10 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"wrangler": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"yaml": "^2.4.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
23
src/proxy.js
23
src/proxy.js
|
@ -1,23 +0,0 @@
|
|||
export default {
|
||||
async fetch(request, env, ctx) {
|
||||
const url = new URL(request.url);
|
||||
|
||||
const proxyUrl = url.searchParams.get('proxyUrl'); // get a query param value (?proxyUrl=...)
|
||||
const modify = url.searchParams.has('modify'); // check if a query param is set (?proxyUrl=...&modify)
|
||||
|
||||
if (!proxyUrl) {
|
||||
return new Response('Bad request: Missing `proxyUrl` query param', { status: 400 });
|
||||
}
|
||||
|
||||
// make subrequests with the global `fetch()` function
|
||||
let res = await fetch(proxyUrl, request);
|
||||
|
||||
// optionally, modify the respone
|
||||
if (modify) {
|
||||
res = new Response(res.body, res)
|
||||
res.headers.set('X-My-Header', 'My Header Value');
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
export default {
|
||||
async fetch(request, env, ctx) {
|
||||
const url = new URL(request.url);
|
||||
const redirectUrl = url.searchParams.get('redirectUrl'); // get a query param value (?redirectUrl=...)
|
||||
|
||||
if (!redirectUrl) {
|
||||
return new Response('Bad request: Missing `redirectUrl` query param', { status: 400 });
|
||||
}
|
||||
|
||||
// The Response class has static methods to create common Response objects as a convenience
|
||||
return Response.redirect(redirectUrl);
|
||||
},
|
||||
};
|
|
@ -1,90 +0,0 @@
|
|||
// An example 'toy' implementation of a router using URLPattern: https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API
|
||||
// If you're interested in more production-ready routing, you may want to check out one of the following:
|
||||
|
||||
// itty-router: https://www.npmjs.com/package/itty-router
|
||||
// Hono: https://www.npmjs.com/package/hono
|
||||
|
||||
class Router {
|
||||
routes = [];
|
||||
|
||||
handle(request) {
|
||||
for (const route of this.routes) {
|
||||
const match = route[0](request);
|
||||
if (match) {
|
||||
return route[1]({ ...match, request });
|
||||
}
|
||||
}
|
||||
const match = this.routes.find(([matcher]) => matcher(request));
|
||||
if (match) {
|
||||
return match[1](request);
|
||||
}
|
||||
}
|
||||
|
||||
register(handler, path, method) {
|
||||
const urlPattern = new URLPattern({ pathname: path });
|
||||
this.routes.push([
|
||||
(request) => {
|
||||
if (method === undefined || request.method.toLowerCase() === method) {
|
||||
const match = urlPattern.exec({
|
||||
pathname: new URL(request.url).pathname,
|
||||
});
|
||||
if (match) {
|
||||
return { params: match.pathname.groups };
|
||||
}
|
||||
}
|
||||
},
|
||||
(args) => handler(args),
|
||||
]);
|
||||
}
|
||||
|
||||
options(path, handler) {
|
||||
this.register(handler, path, "options");
|
||||
}
|
||||
head(path, handler) {
|
||||
this.register(handler, path, "head");
|
||||
}
|
||||
get(path, handler) {
|
||||
this.register(handler, path, "get");
|
||||
}
|
||||
post(path, handler) {
|
||||
this.register(handler, path, "post");
|
||||
}
|
||||
put(path, handler) {
|
||||
this.register(handler, path, "put");
|
||||
}
|
||||
patch(path, handler) {
|
||||
this.register(handler, path, "patch");
|
||||
}
|
||||
delete(path, handler) {
|
||||
this.register(handler, path, "delete");
|
||||
}
|
||||
|
||||
all(path, handler) {
|
||||
this.register(handler, path);
|
||||
}
|
||||
}
|
||||
|
||||
// Setting up our application:
|
||||
|
||||
const router = new Router();
|
||||
|
||||
// GET collection index
|
||||
router.get("/api/todos", () => new Response("Todos Index!"));
|
||||
|
||||
// GET item
|
||||
router.get(
|
||||
"/api/todos/:id",
|
||||
({ params }) => new Response(`Todo #${params.id}`)
|
||||
);
|
||||
|
||||
// POST to the collection (we'll use async here)
|
||||
router.post("/api/todos", async ({ request }) => {
|
||||
const content = await request.json();
|
||||
|
||||
return new Response("Creating Todo: " + JSON.stringify(content));
|
||||
});
|
||||
|
||||
// 404 for everything else
|
||||
router.all("*", () => new Response("Not Found.", { status: 404 }));
|
||||
|
||||
export default router;
|
121
src/worker.js
121
src/worker.js
|
@ -8,40 +8,119 @@
|
|||
* Learn more at https://developers.cloudflare.com/workers/
|
||||
*/
|
||||
|
||||
import handleProxy from './proxy.js';
|
||||
import handleRedirect from './redirect.js';
|
||||
import apiRouter from './router.js';
|
||||
import YAML from 'yaml'
|
||||
import moment from 'moment-timezone'
|
||||
|
||||
|
||||
// Export a default object containing event handlers
|
||||
export default {
|
||||
// The fetch handler is invoked when this worker receives a HTTP(S) request
|
||||
// and should return a Response (optionally wrapped in a Promise)
|
||||
async fetch(request, env, ctx) {
|
||||
async fetch(req, env, ctx) {
|
||||
// You'll find it helpful to parse the request.url string into a URL object. Learn more at https://developer.mozilla.org/en-US/docs/Web/API/URL
|
||||
const url = new URL(request.url);
|
||||
const url = new URL(req.url);
|
||||
|
||||
// You can get pretty far with simple logic like if/switch-statements
|
||||
switch (url.pathname) {
|
||||
case '/redirect':
|
||||
return handleRedirect.fetch(request, env, ctx);
|
||||
// log the incoming request info
|
||||
console.info(`${new Date().toISOString()} 📥 ${req.method} ${req.url}`)
|
||||
|
||||
case '/proxy':
|
||||
return handleProxy.fetch(request, env, ctx);
|
||||
// CORS route (for now, any domain has access)
|
||||
if(req.method === "OPTIONS") {
|
||||
const headers = {
|
||||
'Access-Control-Allow-Origin': req.headers.get('Origin') || '*',
|
||||
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, DELETE',
|
||||
'Access-Control-Max-Age': '86400',
|
||||
'Access-Control-Allow-Credentials': true
|
||||
}
|
||||
let h
|
||||
if (h = req.headers.get('Access-Control-Request-Headers'))
|
||||
headers['Access-Control-Allow-Headers'] = 'Accept, Content-Type, Authorization, Signature, Digest, Date, Host'
|
||||
return new Response('', { status: 204, headers })
|
||||
}
|
||||
|
||||
if (url.pathname.startsWith('/api/')) {
|
||||
// You can also use more robust routing
|
||||
return apiRouter.handle(request);
|
||||
else if(req.method === "POST" && url.pathname.startsWith("/status.lol")) {
|
||||
const body = await req.json()
|
||||
return handleLol(body, env)
|
||||
}
|
||||
else if(req.method === "GET" && url.pathname.startsWith("/apget")) {
|
||||
const href = url.searchParams.get('url')
|
||||
if(!href) return new Response('"url" parameter required', { status: 400, headers: { "Content-Type": "text/plain" }})
|
||||
|
||||
return fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return new Response(
|
||||
`Try making requests to:
|
||||
<ul>
|
||||
<li><code><a href="/redirect?redirectUrl=https://example.com/">/redirect?redirectUrl=https://example.com/</a></code>,</li>
|
||||
<li><code><a href="/proxy?modify&proxyUrl=https://example.com/">/proxy?modify&proxyUrl=https://example.com/</a></code>, or</li>
|
||||
<li><code><a href="/api/todos">/api/todos</a></code></li>`,
|
||||
`This is a worker to accept POST requests at /status.lol from a status.lol webhook`,
|
||||
{ headers: { "Content-Type": "text/html" } }
|
||||
);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
async function handleLol(body, env) {
|
||||
/* incoming: {
|
||||
status_emoji: '🧪',
|
||||
status_text: 'This is a test.',
|
||||
posted: 1715234767,
|
||||
id: '663c67cf3344e',
|
||||
url: 'https://status.lol/deathau/663c67cf3344e'
|
||||
} */
|
||||
|
||||
// get the url (and username) from the incoming data
|
||||
const url = new URL(body.url);
|
||||
const username = url.pathname.substring(1).split('/')[0]
|
||||
|
||||
// get the full data of the status.lol post
|
||||
const statusres = (await fetch(`https://api.omg.lol/address/${username}/statuses/${body.id}`))
|
||||
if(!statusres.ok) return statusres
|
||||
const status = (await statusres.json()).response.status
|
||||
|
||||
// check that the incoming data matches what we got from status.lol
|
||||
if(status.id != body.id) {
|
||||
console.error("Error: Incoming status does not match retrieved status!", body, status)
|
||||
return new Response(JSON.stringify({body,status}, null, 2), {status:500})
|
||||
}
|
||||
|
||||
// create a "title" for the status
|
||||
const title = status.emoji + ' ' + status.content.match(/((\s*\S+){10})([\s\S]*)/)[1] + '...'
|
||||
|
||||
// this is the frontmatter data we're adding to the markdown file
|
||||
let frontmatter = {
|
||||
id: status.id,
|
||||
title,
|
||||
date: moment(status.created*1000).tz('Australia/Melbourne').format(),
|
||||
location: `/${(status.created * 1000).toString(36)}`,
|
||||
emoji: status.emoji,
|
||||
background: status.background,
|
||||
external_url: status.external_url,
|
||||
status_url: `https://${status.address}.status.lol/${status.id}`
|
||||
}
|
||||
// this is the content
|
||||
let content = status.content
|
||||
|
||||
// this is the actual markdown
|
||||
let markdown = `---\n${YAML.stringify(frontmatter)}---\n\n${content}\n`
|
||||
|
||||
// make sure we have the env variables defined
|
||||
if(env.FORGEJO_TOKEN && env.FORGEJO_URL && env.GIT_REPO) {
|
||||
// the url to push the file to the repo in `/weblog/status/{whatever the location is}.md`
|
||||
let pushurl = `${env.FORGEJO_URL}api/v1/repos/${env.GIT_REPO}/contents/weblog/status${frontmatter.location}.md`
|
||||
|
||||
// just return the result of this request
|
||||
return await fetch(pushurl, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
content: btoa(unescape(encodeURIComponent(markdown))), // the markdown content as base64
|
||||
message: `Added status ${frontmatter.location}` // the commit message
|
||||
}),
|
||||
headers: {
|
||||
'Authorization': `token ${env.FORGEJO_TOKEN}`, // require the auth token
|
||||
'Content-Type': 'application/json' // the content type is required for this api
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return new Response(markdown, { status: 400 })
|
||||
}
|
Loading…
Reference in a new issue