mirror of
https://github.com/aykhans/my-self-host-services.git
synced 2026-01-13 19:31:21 +00:00
general refactoring
This commit is contained in:
@@ -33,3 +33,6 @@ UPTIME_KUMA_DOMAIN=
|
|||||||
|
|
||||||
############# Stalwart #############
|
############# Stalwart #############
|
||||||
STALWART_DOMAIN=
|
STALWART_DOMAIN=
|
||||||
|
|
||||||
|
############# Textarea #############
|
||||||
|
TEXTAREA_DOMAIN=
|
||||||
|
|||||||
@@ -131,4 +131,12 @@
|
|||||||
reverse_proxy http://stalwart:8080
|
reverse_proxy http://stalwart:8080
|
||||||
}
|
}
|
||||||
|
|
||||||
|
############## textarea ##############
|
||||||
|
{$TEXTAREA_DOMAIN} {
|
||||||
|
root * /volume/textarea
|
||||||
|
file_server {
|
||||||
|
browse off
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
import Caddyfile.private
|
import Caddyfile.private
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ services:
|
|||||||
- ./data/data:/data
|
- ./data/data:/data
|
||||||
- ./data/config:/config
|
- ./data/config:/config
|
||||||
- ./data/log:/var/log/caddy
|
- ./data/log:/var/log/caddy
|
||||||
|
- ../volume:/volume
|
||||||
- ../private_volume:/private_volume
|
- ../private_volume:/private_volume
|
||||||
env_file:
|
env_file:
|
||||||
- ./.env
|
- ./.env
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
feeds:
|
feeds:
|
||||||
- url: https://selfh.st/rss/
|
- url: https://selfh.st/rss/
|
||||||
title: selfh.st
|
title: selfh.st
|
||||||
|
- url: https://antonz.org/index.xml
|
||||||
|
title: antonz.org
|
||||||
|
|
||||||
- size: small
|
- size: small
|
||||||
widgets:
|
widgets:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ networks:
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
stalwart:
|
stalwart:
|
||||||
image: stalwartlabs/stalwart:v0.14
|
image: stalwartlabs/stalwart:v0.15
|
||||||
container_name: stalwart
|
container_name: stalwart
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
labels:
|
labels:
|
||||||
|
|||||||
110
volume/textarea/index.html
Normal file
110
volume/textarea/index.html
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Textarea</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
color-scheme: light dark;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #161616;
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
background-color: #000;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
outline: none;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 18px max(18px, calc(50vw - 400px));
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100vh;
|
||||||
|
font: 18px / 1.5 system-ui;
|
||||||
|
tab-size: 4;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
text-wrap-style: pretty;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<article contenteditable="plaintext-only" spellcheck></article>
|
||||||
|
<script>
|
||||||
|
const article = document.querySelector('article')
|
||||||
|
article.addEventListener('input', debounce(500, save))
|
||||||
|
addEventListener('DOMContentLoaded', load)
|
||||||
|
addEventListener('hashchange', load)
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
try {
|
||||||
|
if (location.hash === '') {
|
||||||
|
await set(localStorage.getItem('hash') ?? '')
|
||||||
|
if (article.textContent) history.replaceState({}, '', await get())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await set(location.hash)
|
||||||
|
} catch (e) {
|
||||||
|
article.textContent = ''
|
||||||
|
article.removeAttribute('style')
|
||||||
|
}
|
||||||
|
updateTitle()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
const hash = await get()
|
||||||
|
if (location.hash !== hash) history.replaceState({}, '', hash)
|
||||||
|
try { localStorage.setItem('hash', hash) } catch (e) {}
|
||||||
|
updateTitle()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function set(hash) {
|
||||||
|
const [content, style] = (await decompress(hash.slice(1))).split('\x00')
|
||||||
|
article.textContent = content
|
||||||
|
if (style) article.setAttribute('style', style)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function get() {
|
||||||
|
const style = article.getAttribute('style')
|
||||||
|
const content = article.textContent + (style !== null ? '\x00' + style : '')
|
||||||
|
return '#' + await compress(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTitle() {
|
||||||
|
const match = article.textContent.match(/^\n*#(.+)\n/)
|
||||||
|
document.title = match?.[1] ?? 'Textarea'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function compress(string) {
|
||||||
|
const byteArray = new TextEncoder().encode(string)
|
||||||
|
const stream = new CompressionStream('deflate-raw')
|
||||||
|
const writer = stream.writable.getWriter()
|
||||||
|
writer.write(byteArray)
|
||||||
|
writer.close()
|
||||||
|
const buffer = await new Response(stream.readable).arrayBuffer()
|
||||||
|
return new Uint8Array(buffer).toBase64().replace(/\+/g, "-").replace(/\//g, "_")
|
||||||
|
}
|
||||||
|
|
||||||
|
async function decompress(b64) {
|
||||||
|
const byteArray = Uint8Array.fromBase64(b64.replace(/-/g, "+").replace(/_/g, "/"))
|
||||||
|
const stream = new DecompressionStream('deflate-raw')
|
||||||
|
const writer = stream.writable.getWriter()
|
||||||
|
writer.write(byteArray)
|
||||||
|
writer.close()
|
||||||
|
const buffer = await new Response(stream.readable).arrayBuffer()
|
||||||
|
return new TextDecoder().decode(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
function debounce(ms, fn) {
|
||||||
|
let timer
|
||||||
|
return (...args) => {
|
||||||
|
clearTimeout(timer)
|
||||||
|
timer = setTimeout(() => fn(...args), ms)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user