mirror of
https://codeberg.org/valpackett/tiddlypwa.git
synced 2025-12-15 15:10:32 -08:00
Compare commits
9 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9192a4d45a | ||
|
|
7ff63eb696 | ||
|
|
8c55b22aa7 | ||
|
|
2961396310 | ||
|
|
05d24a9efd | ||
|
|
ef786f611a | ||
|
|
71509921d6 | ||
|
|
1541ba5589 | ||
|
|
9693281cb4 |
16 changed files with 189 additions and 47 deletions
22
.forgejo/workflows/check.yaml
Normal file
22
.forgejo/workflows/check.yaml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
on:
|
||||
push:
|
||||
branches: [trunk]
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
jobs:
|
||||
test:
|
||||
runs-on: val-arm64
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://code.forgejo.org/actions/checkout@v4
|
||||
- name: Get Deno
|
||||
run: curl -fsSL https://deno.land/install.sh | sh
|
||||
- name: Run server tests
|
||||
run: /root/.deno/bin/deno test server
|
||||
- name: Try hash-admin-password
|
||||
run: echo uwu | /root/.deno/bin/deno run server/hash-admin-password.ts
|
||||
- name: Check client build
|
||||
run: /root/.deno/bin/deno run --allow-env --allow-read --allow-write=output npm:tiddlywiki@5.3.5 --build
|
||||
- name: Check formatting
|
||||
run: /root/.deno/bin/deno fmt --check --ignore="**/*.css" plugins server
|
||||
22
.forgejo/workflows/upload.yaml
Normal file
22
.forgejo/workflows/upload.yaml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
on:
|
||||
push:
|
||||
branches: [release]
|
||||
jobs:
|
||||
test:
|
||||
runs-on: val-arm64
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://code.forgejo.org/actions/checkout@v4
|
||||
- name: Fetch theme
|
||||
run: mkdir notebook && curl -L https://github.com/valpackett/Notebook/archive/e2c61ccd6e9db5cfcbc77e54eccc8b5961da5831.tar.gz | tar -xvzf - -C notebook --strip-components=1
|
||||
- name: Build client
|
||||
run: npx tiddlywiki@5.3.5 --build
|
||||
env:
|
||||
TIDDLYWIKI_THEME_PATH: notebook/themes
|
||||
TIDDLYWIKI_PLUGIN_PATH: notebook/plugins
|
||||
- name: Upload result
|
||||
run: sh bunny.sh
|
||||
env:
|
||||
BUNNY_BUCKET: ${{ secrets.BUNNY_BUCKET }}
|
||||
BUNNY_KEY: ${{ secrets.BUNNY_KEY }}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
steps:
|
||||
test:
|
||||
image: denoland/deno:alpine-1.45.4
|
||||
commands:
|
||||
- deno test
|
||||
- DENO_FUTURE=1 deno test
|
||||
- echo uwu | DENO_FUTURE=1 deno run server/hash-admin-password.ts
|
||||
- deno fmt --check
|
||||
- DENO_FUTURE=1 deno run --allow-env --allow-read --allow-write=output npm:tiddlywiki@5.3.5 --build
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
[](https://ci.codeberg.org/valpackett/tiddlypwa)
|
||||
[](https://app.netlify.com/sites/tiddly-packett-cool/deploys)
|
||||
[](https://www.patreon.com/valpackett)
|
||||
|
||||
# TiddlyPWA
|
||||
|
|
|
|||
8
bunny.sh
Executable file
8
bunny.sh
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
cd output
|
||||
find * -type f -exec curl --fail --request PUT \
|
||||
--url https://storage.bunnycdn.com/$BUNNY_BUCKET/{} \
|
||||
--header "AccessKey: $BUNNY_KEY" \
|
||||
--header "Content-Type: application/octet-stream" \
|
||||
--data-binary @{} \;
|
||||
18
netlify.toml
18
netlify.toml
|
|
@ -1,18 +0,0 @@
|
|||
[build]
|
||||
publish = "output"
|
||||
command = """
|
||||
mkdir notebook; curl -L https://github.com/valpackett/Notebook/archive/e2c61ccd6e9db5cfcbc77e54eccc8b5961da5831.tar.gz | tar -xvzf - -C notebook --strip-components=1 &&
|
||||
TIDDLYWIKI_THEME_PATH=notebook/themes TIDDLYWIKI_PLUGIN_PATH=notebook/plugins npx tiddlywiki@5.3.5 --build
|
||||
"""
|
||||
|
||||
[[headers]]
|
||||
for = "/*"
|
||||
[headers.values]
|
||||
x-content-type-options = "nosniff"
|
||||
x-frame-options = "SAMEORIGIN"
|
||||
referrer-policy = "no-referrer-when-downgrade"
|
||||
|
||||
[[redirects]]
|
||||
from = "/w/:name/:file"
|
||||
to = "/app/:file"
|
||||
status = 200
|
||||
2
plugins/tiddlypwa/bootstrap.js
vendored
2
plugins/tiddlypwa/bootstrap.js
vendored
|
|
@ -15,7 +15,7 @@ Formatted with `deno fmt`.
|
|||
const dm = $tw.utils.domMaker;
|
||||
|
||||
module.exports.BootstrapModal = class {
|
||||
wrapper = dm('div', { class: 'tc-modal-wrapper', style: { 'z-index': 1500 } }); // below alerts, above hide-sidebar-btn
|
||||
wrapper = dm('div', { class: 'tc-modal-wrapper', style: { 'z-index': 4000 } }); // below alerts, above hide-sidebar-btn and others (e.g. side panel in Notebook theme has z-index=3000)
|
||||
constructor() {
|
||||
$tw.utils.addClass(document.body, 'tc-modal-prevent-scroll');
|
||||
this.wrapper.appendChild(dm('div', { class: 'tc-modal-backdrop' }));
|
||||
|
|
|
|||
36
plugins/tiddlypwa/filters.js
Normal file
36
plugins/tiddlypwa/filters.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*\
|
||||
title: $:/plugins/valpackett/tiddlypwa/filters.js
|
||||
type: application/javascript
|
||||
module-type: isfilteroperator
|
||||
|
||||
Filter function for TiddlyPWA-managed tiddler identification
|
||||
|
||||
Licensed under 0BSD, see license.tid.
|
||||
Formatted with `deno fmt`.
|
||||
\*/
|
||||
|
||||
'use strict';
|
||||
|
||||
exports.tiddlypwa = function (source, prefix, options) {
|
||||
const results = [];
|
||||
|
||||
const pwaStorage = $tw.syncer.syncadaptor;
|
||||
if (!pwaStorage || !pwaStorage.tiddlersInFile) {
|
||||
return ['Error: TiddlyPWA not available'];
|
||||
}
|
||||
|
||||
if (prefix === '!') {
|
||||
source(function (tiddler, title) {
|
||||
if (pwaStorage.tiddlersInFile.has(title)) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
source(function (tiddler, title) {
|
||||
if (!pwaStorage.tiddlersInFile.has(title)) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
11
plugins/tiddlypwa/filters.tid
Normal file
11
plugins/tiddlypwa/filters.tid
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
title: $:/plugins/valpackett/tiddlypwa/filters
|
||||
|
||||
!!! Filter Operators
|
||||
|
||||
TiddlyPWA provides filter operator to identify tiddlers it manages:
|
||||
|
||||
* `[is[tiddlypwa]]` - tiddlers created/loaded by TiddlyPWA (from local DB or server)
|
||||
* `[!is[tiddlypwa]]` - inversion of the above, i.e. tiddlers loaded from app wiki html file
|
||||
|
||||
This may be useful to find system tiddlers created by you, or to check shadow tiddler origins
|
||||
(app file vs DB/server) or when exporting (to e.g. backup everything stored on server including system tiddlers).
|
||||
|
|
@ -3,7 +3,7 @@ tags: $:/tags/PageTemplate
|
|||
|
||||
<$reveal type="nomatch" state="$:/temp/HideUpdate" text="yes">
|
||||
<$reveal type="match" state="$:/status/TiddlyPWAUpdateAvailable" text="yes">
|
||||
<div class="tc-plugin-reload-warning" role="alert" style="z-index:2000">
|
||||
<div class="tc-plugin-reload-warning" role="alert" style="z-index:5000">
|
||||
|
||||
~TiddlyWiki has been updated, please <$button message="tiddlypwa-browser-refresh">reload the page</$button>!
|
||||
<$button set="$:/temp/HideUpdate" setTo="yes" class="tc-btn-invisible">{{$:/core/images/close-button}}</$button>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
"author": "Val Packett",
|
||||
"version": "0.2.2",
|
||||
"core-version": ">=5.2.0",
|
||||
"list": "readme license",
|
||||
"list": "readme filters license",
|
||||
"source": "https://codeberg.org/valpackett/tiddlypwa",
|
||||
"demo": "https://tiddly.packett.cool/",
|
||||
"plugin-type": "plugin"
|
||||
|
|
|
|||
|
|
@ -16,3 +16,5 @@ please do [[throw some monies her way|https://www.patreon.com/valpackett]] :3
|
|||
<$action-navigate $to="$:/ControlPanel"/>
|
||||
Configure the plugin in the Control Panel
|
||||
</$button>
|
||||
|
||||
See ''filters'' tab for filter operators.
|
||||
|
|
@ -1,10 +1,20 @@
|
|||
title: $:/plugins/valpackett/tiddlypwa/upload-app-form
|
||||
|
||||
\procedure input(tiddler,id,tag:input,default)
|
||||
<$tiddler tiddler=<<tiddler>> >
|
||||
<$edit-text inputActions=<<inputActions>> id=<<id>> tag=<<tag>> default=<<default>> />
|
||||
</$tiddler>
|
||||
\end
|
||||
|
||||
\procedure inputActions()
|
||||
<$action-setfield text={{{ [<actionValue>trim[]] }}} />
|
||||
\end
|
||||
|
||||
<div class="tc-control-panel">
|
||||
|
||||
|tc-table-no-border tc-max-width tc-first-col-min-width|k
|
||||
| URL|<$edit-text id="tpwa-endpoint-url" tiddler="$:/temp/TiddlyPWAServerURL" tag="input" default="https://" /> |
|
||||
| Token|<$edit-text tiddler="$:/temp/TiddlyPWAServerToken" tag="input" default="" /> |
|
||||
| URL|<<input id:"tpwa-endpoint-url" tiddler:"$:/temp/TiddlyPWAServerURL" default:"https://" >> |
|
||||
| Token|<<input tiddler:"$:/temp/TiddlyPWAServerToken">> |
|
||||
| |<$button><$action-sendmessage $message="tiddlypwa-upload-app-wiki" publishFilter={{$:/plugins/valpackett/tiddlypwa/app-filter}} uploadUrl={{$:/temp/TiddlyPWAServerURL}} uploadToken={{$:/temp/TiddlyPWAServerToken}} />Upload</$button> |
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -64,7 +64,7 @@ function supportsEncoding(headers: Headers, enc: string): boolean {
|
|||
|
||||
function processEtag(etag: Uint8Array, headers: Headers): [boolean, string] {
|
||||
const supportsBrotli = supportsEncoding(headers, 'br');
|
||||
return [supportsBrotli, '"' + base64.encode(etag) + (supportsBrotli ? '-b' : '-x') + '"'];
|
||||
return [supportsBrotli, '"' + base64.encode(etag.buffer as ArrayBuffer) + (supportsBrotli ? '-b' : '-x') + '"'];
|
||||
}
|
||||
|
||||
function notifyMonitors(token: string, browserToken: string) {
|
||||
|
|
@ -132,11 +132,11 @@ export class TiddlyPWASyncApp {
|
|||
// console.log('ServHas', base64nourl.encode(thash as Uint8Array), mtime, modsince, mtime < modsince);
|
||||
ctrl.enqueue(
|
||||
(firstWritten ? '\n,' : '\n') + JSON.stringify({
|
||||
thash: thash ? base64nourl.encode(thash as Uint8Array) : null,
|
||||
iv: iv ? base64nourl.encode(iv as Uint8Array) : null,
|
||||
ct: ct ? base64nourl.encode(ct as Uint8Array) : null,
|
||||
sbiv: sbiv ? base64nourl.encode(sbiv as Uint8Array) : null,
|
||||
sbct: sbct ? base64nourl.encode(sbct as Uint8Array) : null,
|
||||
thash: thash ? base64nourl.encode(thash.buffer as ArrayBuffer) : null,
|
||||
iv: iv ? base64nourl.encode(iv.buffer as ArrayBuffer) : null,
|
||||
ct: ct ? base64nourl.encode(ct.buffer as ArrayBuffer) : null,
|
||||
sbiv: sbiv ? base64nourl.encode(sbiv.buffer as ArrayBuffer) : null,
|
||||
sbct: sbct ? base64nourl.encode(sbct.buffer as ArrayBuffer) : null,
|
||||
mtime,
|
||||
deleted,
|
||||
}),
|
||||
|
|
@ -174,7 +174,7 @@ export class TiddlyPWASyncApp {
|
|||
if (note !== undefined && typeof note !== 'string') {
|
||||
return Response.json({ error: 'EPROTO' }, { headers: respHdrs, status: 400 });
|
||||
}
|
||||
const token = base64.encode(crypto.getRandomValues(new Uint8Array(32)));
|
||||
const token = base64.encode(crypto.getRandomValues(new Uint8Array(32)).buffer);
|
||||
this.db.createWiki(token, note);
|
||||
return Response.json({ token }, { headers: respHdrs, status: 201 });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ tags: [[TiddlyPWA Docs]]
|
|||
Opening the `/` home page of the hosted sync server will let you access the admin console with the admin password.
|
||||
There, you can create "wikis", i.e. storage slots that can store synchronized tiddlers ''and'' an app wiki, i.e. the TiddlyWiki HTML file with plugins and themes in it.
|
||||
|
||||
Hosting the app wiki on the sync server is not obligatory, but ''highly recommend'' because it simplifies setup on multiple devices and allows easy plugin/theme installation.
|
||||
Hosting the app wiki on the sync server is not obligatory, but ''highly recommended'' because it simplifies setup on multiple devices and allows easy plugin/theme installation.
|
||||
|
||||
Once you have created a storage slot, you get a token that you can use for syncing the tiddlers and uploading the app wiki.
|
||||
|
||||
|
|
@ -16,4 +16,4 @@ Once you have created a storage slot, you get a token that you can use for synci
|
|||
Upon uploading the app wiki, you'll see the URL for the hosted app wiki. Now you should bookmark that URL and use it to access the wiki.
|
||||
It will also be accessible from the admin panel, by clicking the app wiki size.
|
||||
|
||||
Enjoy!
|
||||
Enjoy!
|
||||
|
|
|
|||
|
|
@ -26,12 +26,16 @@ or use a unix domain socket passing the path as `--socket` (you'll need to both
|
|||
|
||||
You can pass the `--dotenv` flag to make the app read variables from a `.env` file (which is mostly used in development with the included file that contains a hash of the `test` password.)
|
||||
|
||||
You can customize the location of the Deno module cache using the `DENO_DIR` environment variable.
|
||||
|
||||
You really need to have TLS (HTTPS) working, so run this behind a reverse proxy like [[Caddy|https://caddyserver.com/]], [[H2O|https://h2o.examp1e.net/]] or [[Nginx|https://nginx.org/en/]].
|
||||
Caddy is famous for fully integrated [[automatic HTTPS|https://caddyserver.com/docs/automatic-https]] support, supporting [[Let's Encrypt|https://letsencrypt.org/]] on the public web as well working with [[Tailscale|https://tailscale.com/]]'s HTTPS support.
|
||||
(Also [[caddy-tailscale|https://github.com/tailscale/caddy-tailscale]] exists for hosting a bunch of as separate Tailscale hosts!)
|
||||
(Though [[Tailscale Serve|https://tailscale.com/kb/1312/serve]] can do basic TLS reverse proxying inside of tailscaled itself; also [[caddy-tailscale|https://github.com/tailscale/caddy-tailscale]] exists for hosting a bunch of as separate Tailscale hosts!)
|
||||
|
||||
When running behind a reverse proxy that rewrites paths, you can customize the base path used for wiki using the `--basepath` flag. It should match the respective Caddy rewrite directive / H2O path / Nginx location, without a trailing backslash. By default, the server assumes no path rewriting takes place.
|
||||
|
||||
If you're running Linux with systemd, see below for an example unit file.
|
||||
|
||||
{{AfterServerHosting}}
|
||||
|
||||
!! Updating
|
||||
|
|
@ -45,8 +49,11 @@ To refresh the cached version of the server scripts, you can use this command:
|
|||
Thanks to Deno providing [[sandboxing|https://docs.deno.com/runtime/manual/basics/permissions]] by default, the server process does not get permission to access anything other than what was specified in the `--allow-*` flags.
|
||||
You can be confident that unless there's a horrible bug in the Deno runtime, the code is unable to touch anything outside of the database directory, nor is it able to contact external network services, nor launch processes.
|
||||
|
||||
If you're paranoid enough to audit all the server code, you can use the [[deno info|https://docs.deno.com/runtime/manual/tools/dependency_inspector]] dependency inspector and/or [[deno vendor|https://docs.deno.com/runtime/manual/tools/vendor]] which conveniently places everything in a friendly directory tree instead of the cache, making it a lot more convenient to review.
|
||||
You can then add `--no-remote --import-map path/to/vendor/import_map.json` flags to the `deno run` invocation (and you can still refer to the URL!) to strongly guarantee that Deno will only run code from the directory you reviewed.
|
||||
If you're running it on Linux with systemd, systemd options can (and should) be used to enforce an additional layer of sandboxing.
|
||||
The example unit file below uses them.
|
||||
|
||||
If you're paranoid enough to audit all the server code, you can use the [[deno info|https://docs.deno.com/runtime/manual/tools/dependency_inspector]] dependency inspector.
|
||||
You can also check out Deno's `--vendor` and `--cached-only` flags.
|
||||
|
||||
!! Single-Binary Deployment
|
||||
|
||||
|
|
@ -64,3 +71,56 @@ deno compile -o tiddlypwa-sync-server \
|
|||
|
||||
ADMIN_PASSWORD_HASH=Zn…PQ ADMIN_PASSWORD_SALT=q6…0o DB_PATH=/var/db/tiddly/pwa.db ./tiddlypwa-sync-server
|
||||
```
|
||||
|
||||
!! Example systemd unit file
|
||||
|
||||
```
|
||||
[Unit]
|
||||
Description=TiddlyPWA sync server
|
||||
Documentation=https://tiddly.packett.cool/
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=tiddlypwa
|
||||
Group=tiddlypwa
|
||||
DynamicUser=yes
|
||||
StateDirectory=tiddlypwa
|
||||
CacheDirectory=tiddlypwa
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
PrivateUsers=yes
|
||||
PrivateDevices=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
ProtectHostname=yes
|
||||
ProtectControlGroups=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelLogs=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectClock=yes
|
||||
ProtectProc=noaccess
|
||||
ProcSubset=pid
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
RestrictNamespaces=yes
|
||||
RestrictRealtime=yes
|
||||
LockPersonality=yes
|
||||
SystemCallFilter=@system-service
|
||||
SystemCallErrorNumber=EPERM
|
||||
SystemCallArchitectures=native
|
||||
CapabilityBoundingSet=
|
||||
EnvironmentFile=/etc/tiddlypwa.env
|
||||
Environment=DB_PATH=/var/lib/tiddlypwa/pwa.db
|
||||
Environment=DENO_DIR=/var/cache/tiddlypwa/deno
|
||||
ExecStart=/usr/bin/deno --unstable-broadcast-channel --allow-env \
|
||||
--allow-read=/var/lib/tiddlypwa --allow-write=/var/lib/tiddlypwa \
|
||||
--allow-net=:7770 \
|
||||
https://codeberg.org/valpackett/tiddlypwa/raw/branch/release/server/run.ts --port 7770
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Put the above into `/etc/systemd/system/tiddlypwa.service` and write the admin hash/salt variables to `/etc/tiddlypwa.env`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue