Skip to content

Instantly share code, notes, and snippets.

@txthinking
Last active July 11, 2023 04:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save txthinking/e69cfec36efe0f0d63a8ced181466515 to your computer and use it in GitHub Desktop.
Save txthinking/e69cfec36efe0f0d63a8ced181466515 to your computer and use it in GitHub Desktop.
Pastebin: with times limit, expiration for Brook GUI
//
// There are many pastebin service: https://soso.ooo/search.html?q=pastebin
// This too, and with times limit, expiration, and browser User-Agent limit, allow only Go and Dart UA for Brook GUI
//
// This script will create a webserver on http://127.0.0.1:2023
// You can modify the listen address at the bottom of this file.
//
// Because the data is stored in localStorage, there is a size limit of 10M, and a clearing operation will be performed automatically after exceeding.
// So, you can fork and modify me.
//
// How to run:
// download this file to local pastebin.js
// $ nami install deno
// $ deno run -A ./pastebin.js
//
// If you want https, you may prefer https://github.com/txthinking/nico
// $ nami install nico
// $ nico domain.com http://127.0.0.1:2023
//
// If you want daemon, you may prefer https://github.com/txthinking/joker
//
// The MIT License (MIT)
// Copyright (c) 2014-present Cloud <cloud@txthinking.com> https://www.txthinking.com
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import server from "https://raw.githubusercontent.com/txthinking/denolib/master/httpserver.js";
import { b2s, echo, ok, err, now } from "https://raw.githubusercontent.com/txthinking/denolib/master/f.js";
server.path("/", async (r) => {
var html = `<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@1.5.3/css/pico.min.css" />
<script src="https://unpkg.com/vue@3.2.29/dist/vue.global.prod.js"></script>
<title>Pastebin</title>
<style>
[data-theme="light"],
:root:not([data-theme="dark"]) {
--primary: #000000;
--primary-inverse: #ffffff;
}
@media (min-width: 992px) {
.dm {
display: flex;
justify-content: center;
}
.dm > article {
width: 500px;
}
}
</style>
<script>
window.addEventListener("DOMContentLoaded", async (e) => {
var app = {
data() {
return {
links: "brook://server?name=%F0%9F%87%BA%F0%9F%87%B8+heygirl&password=hello&server=example.com%3A9999&username=\\nbrook://server?name=hey+boy&password=hello&server=example2.com%3A9999&udpovertcp=true&username=",
times: 1,
expire: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString().split(":").slice(0, 2).join(":"),
ing: false,
zh: navigator.language.toLowerCase().startsWith("zh-"),
url: "",
};
},
methods: {
async generate() {
if (this.ing) {
return;
}
this.ing = true;
try {
var r = await fetch("/generate", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
links: this.links,
times: this.times,
expire: parseInt(new Date(this.expire).getTime() / 1000),
}),
});
if (r.status != 200) {
throw await r.text();
}
this.url = location.origin + "/links?id=" + (await r.text());
} catch (e) {
alert(e.toString());
}
this.ing = false;
},
},
};
Vue.createApp(app).mount("body");
});
</script>
</head>
<body style="margin: 0">
<nav class="container-fluid" style="background-color: #000000">
<ul>
<li>
<a href="https://gist.github.com/txthinking/e69cfec36efe0f0d63a8ced181466515"><strong style="color: #ffffff">Pastebin</strong></a>
</li>
</ul>
<ul>
<li><a target="_blank" style="color: #ffffff" href="https://www.txthinking.com/talks/">Talks</a></li>
<li><a target="_blank" style="color: #ffffff" href="https://www.youtube.com/txthinking">YouTube</a></li>
<li><a target="_blank" style="color: #ffffff" href="https://t.me/s/txthinking_news">News</a></li>
<li><a target="_blank" style="color: #ffffff" href="https://t.me/brookgroup">Telegram</a></li>
</ul>
</nav>
<main class="container-fluid">
<textarea v-model="links" style="height: 300px; width: 100%"></textarea>
</main>
<main class="container">
<div>{{zh ? "次数限制" : "Times limit"}}</div>
<input type="number" v-model="times" min="1" max="10000" />
<div>{{zh ? "过期时间" : "Expiration"}}</div>
<input type="datetime-local" v-model="expire" />
<button @click="generate">{{zh ? "生成" : "Generate"}}</button>
<mark v-if="url">{{url}}</mark>
</main>
<footer class="container" style="font-size: 14px">
<center>
<p style="font-size: 14px"><a style="font-size: 14px" href="https://www.txthinking.com">Made with Love</a></p>
</center>
</footer>
</body>
</html>`;
return new Response(html, { headers: { "Content-Type": "text/html; charset=utf-8" } });
});
server.path("/generate", async (r) => {
var j = await r.json();
var s = `${j.links}`.trim();
if (!s) {
return err("You forgot the links");
}
var t = parseInt(j.times);
if (!t || t <= 0) {
return err("times?");
}
var e = parseInt(j.expire);
if (!e || e <= now()) {
return err("expire?");
}
var o = {
links: s,
times: t,
expire: e,
createdat: now(),
};
var id = crypto.randomUUID().replace(/-/g, "");
try {
localStorage.setItem(id, JSON.stringify(o));
} catch (e) {
localStorage.clear();
return err(e.toString());
}
return ok(id);
});
server.path("/links", async (r) => {
var id = new URL(r.url).searchParams.get("id");
var s = localStorage.getItem(id);
if (!s) {
return err("This link has expired");
}
if (!r.headers.get("User-Agent") || (r.headers.get("User-Agent").indexOf("Go-http-client/") == -1 && r.headers.get("User-Agent").indexOf("Dart/") == -1)) {
if (r.headers.get("Accept-Language") && r.headers.get("Accept-Language").toLowerCase().indexOf("zh-")) {
return err("请使用 Brook 图形客户端导入:1. 菜单 2. 批量导入 3. 输入当前链接 4. 导入");
}
return err("Please use the Brook GUI to import: 1. Menu 2. Import Servers 3. Type this link 4. Import");
}
var j = JSON.parse(s);
if (j.expire <= now()) {
localStorage.removeItem(id);
return err("This link has expired");
}
j.times--;
if (j.times > 0) {
try {
localStorage.setItem(id, JSON.stringify(j));
} catch (e) {
localStorage.clear();
echo(e);
return err(e.toString(), 500);
}
}
if (j.times <= 0) {
localStorage.removeItem(id);
}
return ok(j.links);
});
server.run({ hostname: "127.0.0.1", port: 2023 });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment