From 08fdc69d1c7f7a2c7b2a0218c31997ba2786b726 Mon Sep 17 00:00:00 2001 From: SharzyL Date: Mon, 11 Mar 2024 00:44:36 +0800 Subject: [PATCH] [major] bundle static files with js, discard config.json --- Makefile | 74 ---------------------- README.md | 31 ++++----- config.json | 5 -- config.preview.json | 5 -- frontend/{index.js => index.client.js} | 0 frontend/index.html | 26 +++----- frontend/tos.md | 26 ++++---- package.json | 3 +- src/auth.js | 47 ++++++++------ src/index.js | 88 ++++++++++++-------------- src/markdown.js | 2 +- src/staticPages.js | 40 ++++++++++-- webpack.config.js | 7 +- wrangler.toml | 44 +++++++++---- 14 files changed, 174 insertions(+), 224 deletions(-) delete mode 100644 Makefile delete mode 100644 config.json delete mode 100644 config.preview.json rename frontend/{index.js => index.client.js} (100%) diff --git a/Makefile b/Makefile deleted file mode 100644 index 9f1d985..0000000 --- a/Makefile +++ /dev/null @@ -1,74 +0,0 @@ -CONF = config.json -CONF_PREVIEW = config.preview.json -BUILD_DIR = dist -JS_DIR = src -JS_LOCK = yarn.lock - -target_html_files = index.html tos.html - -# script path -html_render_script = scripts/render.js -md2html = scripts/md2html.sh -deploy_static_script = scripts/deploy-static.sh - -# stub directories to record when files are uploaded -RENDERED_DIR = dist/rendered -RENDERED_PREVIEW_DIR = dist/rendered_preview -DEPLOY_DIR = dist/preview -DEPLOY_PREVIEW_DIR = dist/deploy_preview - -rendered_html = $(addprefix $(RENDERED_DIR)/,$(target_html_files)) -rendered_preview_html = $(addprefix $(RENDERED_PREVIEW_DIR)/,$(target_html_files)) -deploy_html = $(addprefix $(DEPLOY_DIR)/, $(target_html_files)) -deploy_preview_html = $(addprefix $(DEPLOY_PREVIEW_DIR)/, $(target_html_files)) - -html: $(all_html) - -test: - ./test/test.sh - -deploy: $(deploy_html) $(source_js_files) $(JS_LOCK) - yarn wrangler publish - -preview: $(deploy_preview_html) $(source_js_files) $(JS_LOCK) - yarn wrangler publish --env preview - -clean: - rm -f $(all_html) $(all_html_deploy) $(all_html_preview) $(js_deploy) $(js_preview) - -$(BUILD_DIR)/tos.html.liquid: frontend/tos.md $(md2html) - mkdir -p $(BUILD_DIR) - $(md2html) $< $@ "Terms and Conditions" - -$(BUILD_DIR)/index.html.liquid: frontend/index.html frontend/index.js frontend/style.css - @# no generation needed, simply copy - mkdir -p $(BUILD_DIR) - cp $< $@ - -# convert liquid template to html file -$(rendered_html): $(RENDERED_DIR)/%.html: $(BUILD_DIR)/%.html.liquid $(CONF) $(html_render_script) - mkdir -p $(dir $@) - node $(html_render_script) -c $(CONF) -o $@ $< - @# remove indents to reduce size - perl -pi -e 's/^\s+//g' $@ - -# convert liquid template to html file -$(rendered_preview_html): $(RENDERED_PREVIEW_DIR)/%.html: $(BUILD_DIR)/%.html.liquid $(CONF_PREVIEW) $(html_render_script) - mkdir -p $(dir $@) - node $(html_render_script) -c $(CONF_PREVIEW) -o $@ $< - @# remove indents to reduce size - perl -pi -e 's/^\s+//g' $@ - -# deploy html file to Cloudflare -$(deploy_html): $(DEPLOY_DIR)/%.html: $(RENDERED_DIR)/%.html $(deploy_static_script) - $(deploy_static_script) $< - @mkdir -p $(dir $@) - @touch $@ - -# deploy html file to Cloudflare preview env -$(deploy_preview_html): $(DEPLOY_PREVIEW_DIR)/%.html: $(RENDERED_PREVIEW_DIR)/%.html $(deploy_static_script) - $(deploy_static_script) --preview $< - @mkdir -p $(dir $@) - @touch $@ - -.PHONY: html test deploy preview clean diff --git a/README.md b/README.md index 21ce337..52ce62f 100644 --- a/README.md +++ b/README.md @@ -32,38 +32,29 @@ This is a pastebin that can be deployed on Cloudflare workers. Try it on [shz.al You are free to deploy the pastebin on your own domain if you host your domain on Cloudflare. -Requirements: -1. \*nix environment with bash and basic cli programs. If you are using Windows, try cygwin, WSL or something. -2. GNU make. -3. `node` and `yarn`. +1. Install `node` and `yarn`. -Create two KV namespaces on Cloudflare workers dashboard (one for production, one for test). Remember their IDs. If you do not need testing, simply create one. +2. Create a KV namespace on Cloudflare workers dashboard, remember its ID. -Clone the repository and enter the directory. Login to your Cloudflare account with `wrangler login`. Modify entries in `wrangler.toml` according to your own account information (`account_id`, routes, kv namespace ids are what you need to modify). The `env.preview` section can be safely removed if you do not need a testing deployment. Refer to [Cloudflare doc](https://developers.cloudflare.com/workers/cli-wrangler/configuration) on how to find out these parameters. +3. Clone the repository and enter the directory. Login to your Cloudflare account with `wrangler login`. -Modify the contents in `config.json` (which controls the generation of static pages): `BASE_URL` is the URL of your site (no trailing slash); `FAVICON` is the URL to the favicon you want to use on your site. If you need testing, also modify `config.preview.json`. +4. Modify entries in `wrangler.toml`. Its comments will tell you how. -Deploy and enjoy! +5. Deploy and enjoy! ```shell $ yarn install -$ make deploy +$ yarn deploy ``` ## Auth -If you want a private deployment (only you can upload paste, but everyone can read the paste), add the following entry to your `config.json` (other configurations also contained in the outmost brace): +If you want a private deployment (only you can upload paste, but everyone can read the paste), add the following entry to your `wrangler.toml`. -```json -{ - "basicAuth": { - "enabled": true, - "passwd": { - "admin1": "this-is-passwd-1", - "admin2": "this-is-passwd-2" - } - } -} +```toml +[vars.BASIC_AUTH] +user1 = "passwd1" +user2 = "passwd2" ``` Now every access to PUT or POST request, and every access to the index page, requires an HTTP basic auth with the user-password pair listed above. For example: diff --git a/config.json b/config.json deleted file mode 100644 index a7b4f37..0000000 --- a/config.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "BASE_URL": "https://shz.al", - "REPO": "https://github.com/SharzyL/pastebin-worker", - "FAVICON": "https://sharzy.in/favicon-32x32.png" -} diff --git a/config.preview.json b/config.preview.json deleted file mode 100644 index 3b6738e..0000000 --- a/config.preview.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "BASE_URL": "https://pb-preview.shz.al", - "REPO": "https://github.com/SharzyL/pastebin-worker", - "FAVICON": "https://sharzy.in/favicon-32x32.png" -} diff --git a/frontend/index.js b/frontend/index.client.js similarity index 100% rename from frontend/index.js rename to frontend/index.client.js diff --git a/frontend/index.html b/frontend/index.html index 3de5eb3..e974f2e 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -5,7 +5,7 @@ @@ -14,6 +14,7 @@

Yet Another Pastebin

This is an open source pastebin deployed on Cloudflare Workers.

Usage: paste any text here, submit, then share it with URL.

+

Warning: only for temporary share, files will be deleted without notice!

Refer to GitHub for more details.

@@ -40,17 +41,9 @@

Yet Another Pastebin

Settings

- - - - - - - - - - + +
@@ -106,16 +99,13 @@

Uploaded paste

diff --git a/frontend/tos.md b/frontend/tos.md index 92c5c5c..4c16fec 100644 --- a/frontend/tos.md +++ b/frontend/tos.md @@ -1,20 +1,20 @@ # TERMS AND CONDITIONS -> TL;DR: **No fucking warranty at all, EXCEPT AS ENFORCED BY LAW**. +> TL;DR: **No warranty at all, EXCEPT AS ENFORCED BY LAW**. Last updated: 2021-08-08 ## 1. Introduction -Welcome to the pastebin maintained by **Sharzy** (“Company”, “we”, “our”, “us”)! +Welcome to the pastebin maintained by **{{TOS_MAINTAINER}}** (“Company”, “we”, “our”, “us”)! -These Terms of Service (“Terms”, “Terms of Service”) govern your use of our website located at **shz.al** (together or individually “Service”) operated by **Sharzy**. +These Terms of Service (“Terms”, “Terms of Service”) govern your use of our website located at **{{BASE_URL}}** (together or individually “Service”) operated by **{{TOS_MAINTAINER}}**. Our Privacy Policy also governs your use of our Service and explains how we collect, safeguard and disclose information that results from your use of our web pages. Your agreement with us includes these Terms and our Privacy Policy (“Agreements”). You acknowledge that you have read and understood Agreements, and agree to be bound of them. -If you do not agree with (or cannot comply with) Agreements, then you may not use the Service, but please let us know by emailing at **shz.al@sharzy.in** so we can try to find a solution. These Terms apply to all visitors, users and others who wish to access or use Service. +If you do not agree with (or cannot comply with) Agreements, then you may not use the Service, but please let us know by emailing at **{{TOS_MAIL}}** so we can try to find a solution. These Terms apply to all visitors, users and others who wish to access or use Service. ## 2. Content @@ -24,9 +24,9 @@ By posting Content on or through Service, You represent and warrant that: (i) Co You retain any and all of your rights to any Content you submit, post or display on or through Service and you are responsible for protecting those rights. We take no responsibility and assume no liability for Content you or any third party posts on or through Service. However, by posting Content using Service you grant us the right and license to use, modify, publicly perform, publicly display, reproduce, and distribute such Content on and through Service. You agree that this license includes the right for us to make your Content available to other users of Service, who may also use your Content subject to these Terms. -Sharzy has the right but not the obligation to monitor and edit all Content provided by users. +{{TOS_MAINTAINER}} has the right but not the obligation to monitor and edit all Content provided by users. -In addition, Content found on or through this Service are the property of Sharzy or used with permission. You may not distribute, modify, transmit, reuse, download, repost, copy, or use said Content, whether in whole or in part, for commercial purposes or for personal gain, without express advance written permission from us. +In addition, Content found on or through this Service are the property of {{TOS_MAINTAINER}} or used with permission. You may not distribute, modify, transmit, reuse, download, repost, copy, or use said Content, whether in whole or in part, for commercial purposes or for personal gain, without express advance written permission from us. ## 3. Prohibited Uses @@ -70,13 +70,13 @@ We may use third-party Service Providers to monitor and analyze the use of our S ## 5. Intellectual Property -Service and its original content (excluding Content provided by users), features and functionality are and will remain the exclusive property of Sharzy and its licensors. Service is protected by copyright, trademark, and other laws of and foreign countries. Our trademarks may not be used in connection with any product or service without the prior written consent of Sharzy. +Service and its original content (excluding Content provided by users), features and functionality are and will remain the exclusive property of {{TOS_MAINTAINER}} and its licensors. Service is protected by copyright, trademark, and other laws of and foreign countries. Our trademarks may not be used in connection with any product or service without the prior written consent of {{TOS_MAINTAINER}}. ## 6. Copyright Policy We respect the intellectual property rights of others. It is our policy to respond to any claim that Content posted on Service infringes on the copyright or other intellectual property rights (“Infringement”) of any person or entity. -If you are a copyright owner, or authorized on behalf of one, and you believe that the copyrighted work has been copied in a way that constitutes copyright infringement, please submit your claim via email to shz.al@sharzy.in, with the subject line: “Copyright Infringement” and include in your claim a detailed description of the alleged Infringement as detailed below, under “DMCA Notice and Procedure for Copyright Infringement Claims” +If you are a copyright owner, or authorized on behalf of one, and you believe that the copyrighted work has been copied in a way that constitutes copyright infringement, please submit your claim via email to {{TOS_MAIL}} with the subject line: “Copyright Infringement” and include in your claim a detailed description of the alleged Infringement as detailed below, under “DMCA Notice and Procedure for Copyright Infringement Claims” You may be held accountable for damages (including costs and attorneys’ fees) for misrepresentation or bad-faith claims on the infringement of any Content found on and/or through Service on your copyright. @@ -96,17 +96,17 @@ You may submit a notification pursuant to the Digital Millennium Copyright Act ( 7.6. a statement by you, made under penalty of perjury, that the above information in your notice is accurate and that you are the copyright owner or authorized to act on the copyright owner’s behalf. -You can contact our Copyright Agent via email at shz.al@sharzy.in. +You can contact our Copyright Agent via email at {{TOS_MAIL}}. ## 8. Error Reporting and Feedback -You may provide us either directly at shz.al@sharzy.in or via third party sites and tools with information and feedback concerning errors, suggestions for improvements, ideas, problems, complaints, and other matters related to our Service (“Feedback”). You acknowledge and agree that: (i) you shall not retain, acquire or assert any intellectual property right or other right, title or interest in or to the Feedback; (ii) Company may have development ideas similar to the Feedback; (iii) Feedback does not contain confidential information or proprietary information from you or any third party; and (iv) Company is not under any obligation of confidentiality with respect to the Feedback. In the event the transfer of the ownership to the Feedback is not possible due to applicable mandatory laws, you grant Company and its affiliates an exclusive, transferable, irrevocable, free-of-charge, sub-licensable, unlimited and perpetual right to use (including copy, modify, create derivative works, publish, distribute and commercialize) Feedback in any manner and for any purpose. +You may provide us either directly at {{TOS_MAIL}} or via third party sites and tools with information and feedback concerning errors, suggestions for improvements, ideas, problems, complaints, and other matters related to our Service (“Feedback”). You acknowledge and agree that: (i) you shall not retain, acquire or assert any intellectual property right or other right, title or interest in or to the Feedback; (ii) Company may have development ideas similar to the Feedback; (iii) Feedback does not contain confidential information or proprietary information from you or any third party; and (iv) Company is not under any obligation of confidentiality with respect to the Feedback. In the event the transfer of the ownership to the Feedback is not possible due to applicable mandatory laws, you grant Company and its affiliates an exclusive, transferable, irrevocable, free-of-charge, sub-licensable, unlimited and perpetual right to use (including copy, modify, create derivative works, publish, distribute and commercialize) Feedback in any manner and for any purpose. ## 9. Links To Other Web Sites -Our Service may contain links to third party web sites or services that are not owned or controlled by Sharzy. +Our Service may contain links to third party web sites or services that are not owned or controlled by {{TOS_MAINTAINER}}. -Sharzy has no control over, and assumes no responsibility for the content, privacy policies, or practices of any third party web sites or services. We do not warrant the offerings of any of these entities/individuals or their websites. +{{TOS_MAINTAINER}} has no control over, and assumes no responsibility for the content, privacy policies, or practices of any third party web sites or services. We do not warrant the offerings of any of these entities/individuals or their websites. YOU ACKNOWLEDGE AND AGREE THAT COMPANY SHALL NOT BE RESPONSIBLE OR LIABLE, DIRECTLY OR INDIRECTLY, FOR ANY DAMAGE OR LOSS CAUSED OR ALLEGED TO BE CAUSED BY OR IN CONNECTION WITH USE OF OR RELIANCE ON ANY SUCH CONTENT, GOODS OR SERVICES AVAILABLE ON OR THROUGH ANY SUCH THIRD PARTY WEB SITES OR SERVICES. @@ -164,5 +164,5 @@ BY USING SERVICE OR OTHER SERVICES PROVIDED BY US, YOU ACKNOWLEDGE THAT YOU HAVE ## 18. Contact Us -Please send your feedback, comments, requests for technical support by email: **shz.al@sharzy.in**. +Please send your feedback, comments, requests for technical support by email: **{{TOS_MAIL}}**. diff --git a/package.json b/package.json index fce26fa..3ec61e3 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "main": "dist/worker.js", "type": "module", "scripts": { - "deploy": "wrangler deploy" + "deploy": "wrangler deploy", + "dev": "wrangler dev --var BASE_URL:http://localhost:8787" }, "author": "SharzyL ", "license": "MIT", diff --git a/src/auth.js b/src/auth.js index 4f1ae81..963b7a1 100644 --- a/src/auth.js +++ b/src/auth.js @@ -1,5 +1,4 @@ import { WorkerError } from "./common.js"; -import conf from '../config.json' function parseBasicAuth(request) { const Authorization = request.headers.get('Authorization'); @@ -27,25 +26,35 @@ function parseBasicAuth(request) { }; } -export function verifyAuth(request) { - if ('basicAuth' in conf && conf.basicAuth.enabled === true) { - if (request.headers.has('Authorization')) { - const { user, pass } = parseBasicAuth(request) - const passwdMap = conf.basicAuth.passwd - if (passwdMap[user] === undefined) { - throw new WorkerError(401, "user not found for basic auth") - } else if (passwdMap[user] !== pass) { - throw new WorkerError(401, "incorrect passwd for basic auth") - } +// return true if auth passes or is not required, +// return auth page if auth is required +// throw WorkerError if auth failed +export function verifyAuth(request, env) { + // pass auth if 'BASIC_AUTH' is not present + console.log(env) + if (!('BASIC_AUTH' in env)) return null + + const passwdMap = new Map(Object.entries(env['BASIC_AUTH'])) + + // pass auth if 'BASIC_AUTH' is empty + if (passwdMap.size == 0) return null + + if (request.headers.has('Authorization')) { + const { user, pass } = parseBasicAuth(request) + if (passwdMap.get(user) === undefined) { + throw new WorkerError(401, "user not found for basic auth") + } else if (passwdMap.get(user) !== pass) { + throw new WorkerError(401, "incorrect passwd for basic auth") } else { - return new Response('HTTP basic auth is required', { - status: 401, - headers: { - // Prompts the user for credentials. - 'WWW-Authenticate': 'Basic realm="my scope", charset="UTF-8"', - }, - }); + return null } + } else { + return new Response('HTTP basic auth is required', { + status: 401, + headers: { + // Prompts the user for credentials. + 'WWW-Authenticate': 'Basic realm="my scope", charset="UTF-8"', + }, + }); } - return null } diff --git a/src/index.js b/src/index.js index bcad9c9..5a1ee36 100644 --- a/src/index.js +++ b/src/index.js @@ -2,27 +2,24 @@ import { WorkerError, parsePath, parseExpiration, genRandStr, decode, params, en import { handleOptions, corsWrapResponse } from './cors.js' import { makeHighlight } from "./highlight.js" import { parseFormdata, getBoundary } from "./parseFormdata.js" -import { staticPageMap } from './staticPages.js' +import { getStaticPage } from './staticPages.js' import { makeMarkdown } from "./markdown.js"; -import conf_production from '../config.json' -import conf_preview from '../config.preview.json' - -const conf = globalThis.ENVIRONMENT === "preview" ? conf_preview : conf_production import { getType } from "mime/lite.js" import {verifyAuth} from "./auth.js"; -addEventListener("fetch", (event) => { - const { request } = event - return event.respondWith(handleRequest(request)) -}) +export default { + async fetch(request, env, ctx) { + return await handleRequest(request, env, ctx) + } +} -async function handleRequest(request) { +async function handleRequest(request, env, ctx) { try { if (request.method === "OPTIONS") { return handleOptions(request) } else { - const response = await handleNormalRequest(request) + const response = await handleNormalRequest(request, env, ctx) if (response.status !== 302 && response.headers !== undefined) { // because Cloudflare do not allow modifying redirect headers response.headers.set("Access-Control-Allow-Origin", "*") } @@ -38,22 +35,22 @@ async function handleRequest(request) { } } -async function handleNormalRequest(request) { +async function handleNormalRequest(request, env, ctx) { if (request.method === "POST") { - return await handlePostOrPut(request, false) + return await handlePostOrPut(request, env, ctx, false) } else if (request.method === "GET") { - return await handleGet(request) + return await handleGet(request, env, ctx) } else if (request.method === "DELETE") { - return await handleDelete(request) + return await handleDelete(request, env, ctx) } else if (request.method === "PUT") { - return await handlePostOrPut(request, true) + return await handlePostOrPut(request, env, ctx, true) } else { throw new WorkerError(405, "method not allowed") } } -async function handlePostOrPut(request, isPut) { - const authResponse = verifyAuth(request) +async function handlePostOrPut(request, env, ctx, isPut) { + const authResponse = verifyAuth(request, env) if (authResponse !== null) { return authResponse } @@ -123,7 +120,7 @@ async function handlePostOrPut(request, isPut) { if (isPut) { const { short, passwd } = parsePath(url.pathname) - const item = await PB.getWithMetadata(short) + const item = await env.PB.getWithMetadata(short) if (item.value === null) { throw new WorkerError(404, `paste of name '${short}' is not found`) } else { @@ -132,7 +129,7 @@ async function handlePostOrPut(request, isPut) { throw new WorkerError(403, `incorrect password for paste '${short}`) } else { return makeResponse( - await createPaste(content, isPrivate, expirationSeconds, short, date, passwd, filename), + await createPaste(env, content, isPrivate, expirationSeconds, short, date, passwd, filename), ) } } @@ -140,37 +137,34 @@ async function handlePostOrPut(request, isPut) { let short = undefined if (name !== undefined) { short = "~" + name - if ((await PB.get(short)) !== null) + if ((await env.PB.get(short)) !== null) throw new WorkerError(409, `name '${name}' is already used`) } return makeResponse(await createPaste( - content, isPrivate, expirationSeconds, short, undefined, passwd, filename + env, content, isPrivate, expirationSeconds, short, undefined, passwd, filename )) } } -async function handleGet(request) { +async function handleGet(request, env, ctx) { const url = new URL(request.url) const { role, short, ext, passwd, filename } = parsePath(url.pathname) - if (staticPageMap.has(url.pathname)) { + const staticPageContent = getStaticPage(url.pathname, env) + if (staticPageContent) { // access to all static pages requires auth - const authResponse = verifyAuth(request) + const authResponse = verifyAuth(request, env) if (authResponse !== null) { return authResponse } - const item = await PB.get(staticPageMap.get(url.pathname)) - if (!item) { - throw new WorkerError(500, `no static page of path ‘${url.pathname}’ in KV storage`) - } - return new Response(item, { + return new Response(staticPageContent, { headers: { "content-type": "text/html;charset=UTF-8" } }) } // return the editor for admin URL if (passwd.length > 0) { - const item = await PB.get('index') + const item = await env.PB.get('index') return new Response(item, { headers: { "content-type": "text/html;charset=UTF-8" } }) @@ -180,7 +174,7 @@ async function handleGet(request) { const disp = url.searchParams.has("a") ? "attachment" : "inline" - const item = await PB.getWithMetadata(short, { type: "arrayBuffer" }) + const item = await env.PB.getWithMetadata(short, { type: "arrayBuffer" }) // determine filename with priority: url path > meta const returnFilename = filename || (item.metadata && item.metadata.filename) @@ -197,7 +191,7 @@ async function handleGet(request) { // handle article (render as markdown) if (role === "a") { - const md = await makeMarkdown(decode(item.value)) + const md = makeMarkdown(decode(item.value)) return new Response(md, { headers: { "content-type": `text/html;charset=UTF-8` }, }) @@ -212,9 +206,9 @@ async function handleGet(request) { } else { // handle default - headers = { "content-type": `${mime};charset=UTF-8` } + const headers = { "content-type": `${mime};charset=UTF-8` } if (returnFilename) { - encodedFilename = encodeRFC5987ValueChars(returnFilename) + const encodedFilename = encodeRFC5987ValueChars(returnFilename) headers["content-disposition"] = `${disp}; filename*=UTF-8''${encodedFilename}` } else { headers["content-disposition"] = `${disp}` @@ -223,23 +217,23 @@ async function handleGet(request) { } } -async function handleDelete(request) { +async function handleDelete(request, env, ctx) { const url = new URL(request.url) const { short, passwd } = parsePath(url.pathname) - const item = await PB.getWithMetadata(short) + const item = await env.PB.getWithMetadata(short) if (item.value === null) { throw new WorkerError(404, `paste of name '${short}' not found`) } else { if (passwd !== item.metadata.passwd) { throw new WorkerError(403, `incorrect password for paste '${short}`) } else { - await PB.delete(short) + await env.PB.delete(short) return new Response("the paste will be deleted in seconds") } } } -async function createPaste(content, isPrivate, expire, short, date, passwd, filename) { +async function createPaste(env, content, isPrivate, expire, short, date, passwd, filename) { date = date || new Date().toISOString() passwd = passwd || genRandStr(params.ADMIN_PATH_LEN) const short_len = isPrivate ? params.PRIVATE_RAND_LEN : params.RAND_LEN @@ -247,11 +241,11 @@ async function createPaste(content, isPrivate, expire, short, date, passwd, file if (short === undefined) { while (true) { short = genRandStr(short_len) - if ((await PB.get(short)) === null) break + if ((await env.PB.get(short)) === null) break } } - await PB.put(short, content, { + await env.PB.put(short, content, { expirationTtl: expire, metadata: { postedAt: date, @@ -259,18 +253,18 @@ async function createPaste(content, isPrivate, expire, short, date, passwd, file filename: filename, }, }) - let accessUrl = conf.BASE_URL + '/' + short - const adminUrl = conf.BASE_URL + '/' + short + params.SEP + passwd + let accessUrl = env.BASE_URL + '/' + short + const adminUrl = env.BASE_URL + '/' + short + params.SEP + passwd return { url: accessUrl, - suggestUrl: suggestUrl(content, filename, short), + suggestUrl: suggestUrl(content, filename, short, env.BASE_URL), admin: adminUrl, isPrivate: isPrivate, expire: expire, } } -function suggestUrl(content, filename, short) { +function suggestUrl(content, filename, short, baseUrl) { function isUrl(text) { try { new URL(text) @@ -281,9 +275,9 @@ function suggestUrl(content, filename, short) { } if (filename) { - return `${conf.BASE_URL}/${short}/${filename}` + return `${baseUrl}/${short}/${filename}` } else if (isUrl(decode(content))) { - return `${conf.BASE_URL}/u/${short}` + return `${baseUrl}/u/${short}` } else { return null } diff --git a/src/markdown.js b/src/markdown.js index 9af5de8..f775e5d 100644 --- a/src/markdown.js +++ b/src/markdown.js @@ -32,7 +32,7 @@ function getMetadata(options) { } } -export async function makeMarkdown(content) { +export function makeMarkdown(content) { const metadata = { title: defaultTitle, description: "" } const convertedHtml = unified() .use(remarkParse) diff --git a/src/staticPages.js b/src/staticPages.js index 9f608d2..1a3435a 100644 --- a/src/staticPages.js +++ b/src/staticPages.js @@ -1,7 +1,33 @@ -export const staticPageMap = new Map([ - ['/', 'index'], - ['/index', 'index'], - ['/index.html', 'index'], - ['/tos', 'tos'], - ['/tos.html', 'tos'], -]) +import { makeMarkdown } from './markdown.js' + +import indexHtml from '../frontend/index.html' +import styleCss from '../frontend/style.css' +import indexJs from '../frontend/index.client.js' +import tosMd from '../frontend/tos.md' +import apiMd from '../doc/api.md' + +function indexPage(env) { + return indexHtml + .replace("{{CSS}}", styleCss) + .replace("{{INDEX_JS}}", indexJs) + .replaceAll("{{BASE_URL}}", env.BASE_URL) + .replaceAll("{{REPO}}", env.REPO) + .replaceAll("{{FAVICON}}", env.FAVICON) +} + +export function getStaticPage(path, env) { + if (path == '/' || path == '/index' || path == '/index.html') { + return indexPage(env) + } else if (path == '/tos' || path == '/tos.html') { + const tosMdRenderred = tosMd + .replaceAll("{{TOS_MAINTAINER}}", env.TOS_MAINTAINER) + .replaceAll("{{TOS_MAIL}}", env.TOS_MAIL) + .replaceAll("{{BASE_URL}}", env.BASE_URL) + + return makeMarkdown(tosMdRenderred) + } else if (path == '/api' || path == '/api.html') { + return makeMarkdown(apiMd) + } else { + return null + } +} diff --git a/webpack.config.js b/webpack.config.js index 0f08b9e..5ff1113 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -4,7 +4,12 @@ let basicConfig = { worker: "./src/index.js", }, devtool: "inline-nosources-source-map", - plugins: [] + plugins: [], + module: { + rules: [ + { test: /frontend/, type: 'asset/source' }, + ], + }, } export default (env, argv) => { diff --git a/wrangler.toml b/wrangler.toml index 597a9cf..4d73529 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -1,23 +1,41 @@ name = "pb" compatibility_date = "2023-01-28" -account_id = "1ddaf86dbca12e8f4fcaa76f32bb707d" workers_dev = false main = "src/index.js" -# config for default production environment -vars = { ENVIRONMENT = "production" } -route = { pattern = "shz.al", custom_domain = true } -kv_namespaces = [ - { binding = "PB", id = "cc398e983a234aa19de5ea6af571a483" }, +rules = [ + { type = "Text", globs = [ "*.html", "*.client.js", "*.md", "*.css" ], fallthrough = true } ] -[env.preview] -vars = { ENVIRONMENT = "preview" } -route = { pattern = "pb-preview.shz.al", custom_domain = true } -minify = false +#---------------------------------------- +# lines below are what you should modify +#---------------------------------------- -kv_namespaces = [ - { binding = "PB", id = "f56ae0043abd4bb4ab61c52071bd9c7f" } -] +[[routes]] +# Refer to https://developers.cloudflare.com/workers/wrangler/configuration/#routes +pattern = "shz.al" +custom_domain = true + +[[kv_namespaces]] +binding = "PB" # do not touch this +id = "cc398e983a234aa19de5ea6af571a483" # id of your KV namespace + +[vars] +# must be consistent with your routes +BASE_URL = "https://shz.al" + +# url to repo, displayed in index page +REPO = "https://github.com/SharzyL/pastebin-worker" + +# url to favicon +FAVICON = "https://sharzy.in/favicon-32x32.png" + +# the name displayed in TOS +TOS_MAINTAINER = "Sharzy" + +# the email displayed in TOS +TOS_MAIL = "pb@shz.al" +[vars.BASIC_AUTH] +user1 = "passwd1"