Compare commits

...

38 Commits

Author SHA1 Message Date
e5f58c2898 Produce user info for the server identity for admin users.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 16m52s
2024-09-19 12:22:38 -04:00
f83863ef01 This doc was ancient, so paste some of the latest from the wiki in. It's something.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 19m18s
2024-09-18 20:16:35 -04:00
837f069cf5 Update CodeMirror. 2024-09-18 20:16:35 -04:00
9f057dc29a Add F-Droid and c-ares to the welcome page.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-09-18 20:01:36 -04:00
c4904f176c Distinguish the server identity in the identity app.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 16m46s
2024-09-18 19:09:44 -04:00
d3a5aba703 A brave new world where admin users can use the server identity.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 16m31s
2024-09-17 12:47:28 -04:00
9e283e427c Fix viewing apps by blob ID URL.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 16m18s
2024-09-16 12:45:06 -04:00
133ba31d66 c-ares 1.33.1. 2024-09-15 08:57:22 -04:00
241a65a92a sus. Disable a warning in c-ares during ltcg with gcc 13 on Haiku.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 19m2s
2024-09-15 12:52:28 -04:00
0b54795bab Merge branch 'main' of https://dev.tildefriends.net/cory/tildefriends
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 16m29s
2024-09-11 20:28:06 -04:00
6208193de5 Fix plumbing for replies on the search tab.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-09-11 20:25:55 -04:00
c53321532f Update CodeMirror. 2024-09-11 20:18:57 -04:00
34f25e3e06 How did I not have an index on type? Wow.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-09-11 19:53:07 -04:00
c46244366e I don't know what GHES is, but it says to use this version, so sure.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 15m50s
2024-09-11 19:52:43 -04:00
6518af04fc It finally built. Now let's upload some artifacts.
Some checks failed
Build Tilde Friends / Build-All (push) Failing after 16m15s
2024-09-11 19:33:37 -04:00
bf137ff1f7 Seriously? More volume mount config.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 20m30s
2024-09-11 19:00:30 -04:00
1877955b62 Syntax?
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-09-11 18:54:39 -04:00
50d0875de2 Mount volumes across another container?
Some checks are pending
Build Tilde Friends / Build-All (push) Waiting to run
2024-09-11 18:49:06 -04:00
bf151e6b7d I am confused.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-09-11 18:41:55 -04:00
82893402d0 Maybe we can sign here?
Some checks failed
Build Tilde Friends / Build-All (push) Failing after 17m55s
2024-09-10 21:54:36 -04:00
8049102787 More silent OpenSSL build for mingw.
Some checks failed
Build Tilde Friends / Build-All (push) Failing after 19m24s
2024-09-10 21:19:15 -04:00
f42cc3d9fd Find the Android SDKs you just installed.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-09-10 21:01:20 -04:00
5f9a5208db Appease the sdkmanager version number whatever.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 21m56s
2024-09-10 20:34:25 -04:00
6df506d238 Spelling.
Some checks failed
Build Tilde Friends / Build-All (push) Failing after 5m7s
2024-09-10 20:25:51 -04:00
2bd3354256 yaml better??
Some checks failed
Build Tilde Friends / Build-All (push) Failing after 2m32s
2024-09-10 20:22:06 -04:00
b55aaa1d18 Maybe CI android?? 2024-09-10 20:20:40 -04:00
34e19505bd No longer need this test. 2024-09-09 15:57:43 -04:00
6e06ec0904 Clean up connections that don't handshake in time.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 11m26s
2024-09-09 15:25:10 -04:00
a5814074fe OpenSSL 3.3.2.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 7m26s
2024-09-04 20:24:32 -04:00
d7479df5a2 Latest CodeMirror.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 8m23s
2024-09-04 20:11:28 -04:00
34508aa0ae dist slightly more in parallel. Exclude dotfiles from data.zip.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-09-04 20:07:26 -04:00
ae096b2c9c Try harder to make webview localStorage work on different versions. I suspect that's what #73 is about.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 7m43s
2024-09-04 12:50:12 -04:00
95d036e34a Build an AppImage. Why not?
Some checks failed
Build Tilde Friends / Build-All (push) Failing after 6m27s
2024-08-28 20:55:52 -04:00
4af5e8ec42 I guess prettier says this, now.
Some checks failed
Build Tilde Friends / Build-All (push) Failing after 6m30s
2024-08-28 20:24:26 -04:00
2a5f71bd5d This now lives in the fdroiddata repository.
Some checks failed
Build Tilde Friends / Build-All (push) Failing after 7m26s
2024-08-28 19:59:49 -04:00
97fb63dda1 Actually 0.0.23-wip. 2024-08-28 19:59:34 -04:00
87d42e3b3b 0.0.23-wip again. Let's gooooo.
Some checks failed
Build Tilde Friends / Build-All (push) Failing after 7m45s
2024-08-28 19:49:36 -04:00
0394129a4c nix => 0.0.22 again.
Some checks are pending
Build Tilde Friends / Build-All (push) Waiting to run
2024-08-28 19:42:31 -04:00
32 changed files with 700 additions and 337 deletions

View File

@ -1,40 +0,0 @@
Categories:
- Internet
License: MIT
AutoName: tildefriends
AuthorName: Cory McWilliams
AuthorEmail: cory@tildefriends.net
RepoType: git
Repo: https://dev.tildefriends.net/cory/tildefriends.git
Builds:
- versionName: 0.0.21-wip
versionCode: 22
commit: 09b6a00731d45fa160b23a2c44be6def98d92d6a
subdir: src/android
submodules: true
sudo:
- apt-get update
- apt-get install -y ant make zip
androidupdate:
- no
scandelete:
- deps/libuv/docs/src/static/diagrams.key/Index.zip
- deps/openssl_src/cloudflare-quiche/*
- deps/openssl_src/fuzz/*
- deps/openssl_src/gost-engine/*
- deps/openssl_src/test/*
- deps/openssl_src/tlslite-ng/*
prebuild:
- sdkmanager "platforms;android-34" "build-tools;34.0.0"
build:
- mkdir bin/
- ANDROID_SDK=$$SDK$$ ANDROID_NDK=$$NDK$$ ANDROID_NDK_ROOT=$$NDK$$ make -C ../../ -j`nproc` fdroid
ndk: r26d
AutoUpdateMode: Version ^v[0-9\.]+$
UpdateCheckMode: Tags
CurrentVersion: 0.0.21-wip
CurrentVersionCode: 22

View File

@ -5,11 +5,31 @@ on: [push]
jobs: jobs:
Build-All: Build-All:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container:
valid_volumes: ['/opt/keys']
volumes:
- /opt/keys:/opt/keys
steps: steps:
- name: check out code - name: check out code
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- run: ln -s /opt/keys .keys
- name: Setup JDK
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Setup Android SDK
uses: android-actions/setup-android@v3
with:
packages: 'tools platform-tools build-tools;34.0.0 platforms;android-34 ndk;26.3.11579264'
- run: sudo apt update && sudo apt install -y doxygen graphviz mingw-w64 - run: sudo apt update && sudo apt install -y doxygen graphviz mingw-w64
- run: make all -j`nproc` docs - run: ANDROID_SDK=$HOME/.android/sdk make -j`nproc` all docs
- run: docker build . - run: docker build .
- uses: actions/upload-artifact@v3
with:
path: out/TildeFriends-release.fdroid.apk
- uses: actions/upload-artifact@v3
with:
path: out/winrelease/tildefriends.exe

View File

@ -3,9 +3,9 @@
MAKEFLAGS += --warn-undefined-variables MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules MAKEFLAGS += --no-builtin-rules
VERSION_CODE := 26 VERSION_CODE := 27
VERSION_NUMBER := 0.0.22 VERSION_NUMBER := 0.0.23-wip
VERSION_NAME := Get born soon. VERSION_NAME := Me upon my pony on my boat.
SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3460100.zip SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3460100.zip
BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar
@ -35,7 +35,8 @@ BUILD_TYPES := debug release
CFLAGS += -Dstatic_assert=_Static_assert CFLAGS += -Dstatic_assert=_Static_assert
LDFLAGS += \ LDFLAGS += \
-lbsd \ -lbsd \
-lnetwork -lnetwork \
-Wno-stringop-overflow
else ifeq ($(UNAME_S),OpenBSD) else ifeq ($(UNAME_S),OpenBSD)
BUILD_TYPES := debug release BUILD_TYPES := debug release
CFLAGS += \ CFLAGS += \
@ -842,7 +843,7 @@ PACKAGE_DIRS := \
deps/prettier/ \ deps/prettier/ \
deps/lit/ deps/lit/
RAW_FILES := $(sort $(filter-out apps/blog% apps/issues% apps/welcome% apps/journal% %.map, $(shell find $(PACKAGE_DIRS) -type f))) RAW_FILES := $(sort $(filter-out apps/blog% apps/issues% apps/welcome% apps/journal% %.map, $(shell find $(PACKAGE_DIRS) -type f -not -name '.*')))
out/apk/TildeFriends-arm-debug.unsigned.apk: BUILD_TYPE := debug out/apk/TildeFriends-arm-debug.unsigned.apk: BUILD_TYPE := debug
out/apk/TildeFriends-arm-release.unsigned.apk: BUILD_TYPE := release out/apk/TildeFriends-arm-release.unsigned.apk: BUILD_TYPE := release
@ -1066,14 +1067,27 @@ $(IOS_DEPS):
$(filter $(BUILD_DIR)/ios%,$(APP_OBJS)): | $(IOS_DEPS) $(filter $(BUILD_DIR)/ios%,$(APP_OBJS)): | $(IOS_DEPS)
endif endif
out/Tilde_Friends-x86_64.AppImage: out/release/tildefriends out/data.zip
@mkdir -p out/AppDir/usr/bin
@mkdir -p out/AppDir/usr/share/applications
@mkdir -p out/AppDir/usr/share/icons/hicolor/scalable/apps
@echo "[Desktop Entry]\nName=Tilde Friends\nExec=tildefriends\nIcon=tildefriends\nType=Application\nCategories=Network" > out/AppDir/usr/share/applications/tildefriends.desktop
@cp src/ios/tildefriends.svg out/AppDir/usr/share/icons/hicolor/scalable/apps/
@cat out/release/tildefriends out/data.zip > out/AppDir/usr/bin/tildefriends
@chmod +x out/AppDir/usr/bin/tildefriends
@unset SOURCE_DATE_EPOCH; cd out; linuxdeploy-x86_64.AppImage --appdir AppDir --output appimage; cd ..
appimage: out/Tilde_Friends-x86_64.AppImage
.PHONY: appimage
clean: clean:
rm -rf $(BUILD_DIR) rm -rf $(BUILD_DIR)
.PHONY: clean .PHONY: clean
dist: release-apk iosrelease-ipa aab $(if $(HAVE_WIN), out/winrelease/tildefriends.standalone.exe) out/TildeFriends-release.fdroid.apk tarball:
@echo [archive] dist/tildefriends-$(VERSION_NUMBER).tar.xz @echo [archive] out/tildefriends-$(VERSION_NUMBER).tar.xz
@rm -rf out/tildefriends-$(VERSION_NUMBER) @rm -rf out/tildefriends-$(VERSION_NUMBER)
@mkdir -p dist/ out/tildefriends-$(VERSION_NUMBER) @mkdir -p out/tildefriends-$(VERSION_NUMBER)
@git ls-files --recurse-submodules | tar -c -T- | tar -x -C out/tildefriends-$(VERSION_NUMBER) @git ls-files --recurse-submodules | tar -c -T- | tar -x -C out/tildefriends-$(VERSION_NUMBER)
@tar \ @tar \
--exclude=apps/welcome* \ --exclude=apps/welcome* \
@ -1090,9 +1104,15 @@ dist: release-apk iosrelease-ipa aab $(if $(HAVE_WIN), out/winrelease/tildefrien
--exclude=deps/sqlite/shell.c \ --exclude=deps/sqlite/shell.c \
--exclude=deps/zlib/contrib/vstudio \ --exclude=deps/zlib/contrib/vstudio \
--exclude=deps/zlib/doc \ --exclude=deps/zlib/doc \
-caf dist/tildefriends-$(VERSION_NUMBER).tar.xz \ -caf out/tildefriends-$(VERSION_NUMBER).tar.xz \
-C out/ \ -C out/ \
tildefriends-$(VERSION_NUMBER) tildefriends-$(VERSION_NUMBER)
.PHONY: tarball
dist: release-apk iosrelease-ipa aab $(if $(HAVE_WIN), out/winrelease/tildefriends.standalone.exe) out/TildeFriends-release.fdroid.apk appimage
@mkdir -p dist/
@echo "[cp] tildefriends-$(VERSION_NUMBER).tar.xz"
@cp out/tildefriends-$(VERSION_NUMBER).tar.xz dist/tildefriends-$(VERSION_NUMBER).tar.xz
@echo "[cp] TildeFriends-x86-$(VERSION_NUMBER).apk" @echo "[cp] TildeFriends-x86-$(VERSION_NUMBER).apk"
@cp out/TildeFriends-x86-release.zopfli.apk dist/TildeFriends-x86-$(VERSION_NUMBER).apk @cp out/TildeFriends-x86-release.zopfli.apk dist/TildeFriends-x86-$(VERSION_NUMBER).apk
@echo "[cp] TildeFriends-arm-$(VERSION_NUMBER).apk" @echo "[cp] TildeFriends-arm-$(VERSION_NUMBER).apk"
@ -1101,8 +1121,12 @@ dist: release-apk iosrelease-ipa aab $(if $(HAVE_WIN), out/winrelease/tildefrien
@cp out/tildefriends-release.ipa dist/TildeFriends-$(VERSION_NUMBER).ipa @cp out/tildefriends-release.ipa dist/TildeFriends-$(VERSION_NUMBER).ipa
@test $(HAVE_WIN) && echo "[cp] tildefriends-$(VERSION_NUMBER).exe" @test $(HAVE_WIN) && echo "[cp] tildefriends-$(VERSION_NUMBER).exe"
@test $(HAVE_WIN) && cp out/winrelease/tildefriends.standalone.exe dist/tildefriends-$(VERSION_NUMBER).exe @test $(HAVE_WIN) && cp out/winrelease/tildefriends.standalone.exe dist/tildefriends-$(VERSION_NUMBER).exe
@echo "[cp] TildeFriends-$(VERSION_NUMBER).aab"
@cp out/TildeFriends.aab dist/TildeFriends-$(VERSION_NUMBER).aab @cp out/TildeFriends.aab dist/TildeFriends-$(VERSION_NUMBER).aab
@echo "[cp] TildeFriends-$(VERSION_NUMBER).fdroid.apk"
@cp out/TildeFriends-release.fdroid.apk dist/TildeFriends-$(VERSION_NUMBER).fdroid.apk @cp out/TildeFriends-release.fdroid.apk dist/TildeFriends-$(VERSION_NUMBER).fdroid.apk
@echo "[cp] TildeFriends-x86_64-$(VERSION_NUMBER).AppImage"
@cp out/Tilde_Friends-x86_64.AppImage dist/TildeFriends-x86_64-$(VERSION_NUMBER).AppImage
.PHONY: dist .PHONY: dist
dist-test: dist dist-test: dist

View File

@ -28,7 +28,10 @@ function global_settings_set(key, value) {
} }
function title_case(name) { function title_case(name) {
return name.split('_').map(x => x.charAt(0).toUpperCase() + x.substring(1)).join(' '); return name
.split('_')
.map((x) => x.charAt(0).toUpperCase() + x.substring(1))
.join(' ');
} }
window.addEventListener('load', function () { window.addEventListener('load', function () {

View File

@ -1,5 +1,5 @@
{ {
"type": "tildefriends-app", "type": "tildefriends-app",
"emoji": "🪪", "emoji": "🪪",
"previous": "&de7q4A59auHP/34bXgeNH05JZoxsGr5TjwXPvehWH30=.sha256" "previous": "&zxsmzdLKsiG/WZt/Gw7JOxepgypoktNNbIyWiyFiJVc=.sha256"
} }

View File

@ -18,6 +18,7 @@ tfrpc.register(async function reload() {
async function main() { async function main() {
let ids = await ssb.getIdentities(); let ids = await ssb.getIdentities();
let server_id = await ssb.getServerIdentity();
await app.setDocument( await app.setDocument(
` `
<head> <head>
@ -123,7 +124,7 @@ async function main() {
) => `<li style="overflow: hidden; text-wrap: nowrap; text-overflow: ellipsis"> ) => `<li style="overflow: hidden; text-wrap: nowrap; text-overflow: ellipsis">
<button onclick="handler.export_id(event)" data-id="${id}" class="w3-button w3-theme">Export Identity</button> <button onclick="handler.export_id(event)" data-id="${id}" class="w3-button w3-theme">Export Identity</button>
<button onclick="handler.delete_id(event)" data-id="${id}" class="w3-button w3-theme">Delete Identity</button> <button onclick="handler.delete_id(event)" data-id="${id}" class="w3-button w3-theme">Delete Identity</button>
${id} ${id}${id == server_id ? ' <div class="w3-tag w3-theme-l4 w3-round">🖥 local server</div>' : ''}
</li>` </li>`
) )
.join('\n') + .join('\n') +

View File

@ -1,5 +1,5 @@
{ {
"type": "tildefriends-app", "type": "tildefriends-app",
"emoji": "🐌", "emoji": "🐌",
"previous": "&xsmsLytB3VvoHphiFHZGGEvrCfTEVGXrGwobGTIYFPQ=.sha256" "previous": "&6oHPQCA26v+4nBXv+YUdCT43j2DpXDspxhHSSRydkiw=.sha256"
} }

View File

@ -5,6 +5,7 @@ import {styles} from './tf-styles.js';
class TfTabSearchElement extends LitElement { class TfTabSearchElement extends LitElement {
static get properties() { static get properties() {
return { return {
drafts: {type: Object},
whoami: {type: String}, whoami: {type: String},
users: {type: Object}, users: {type: Object},
following: {type: Array}, following: {type: Array},
@ -22,6 +23,10 @@ class TfTabSearchElement extends LitElement {
this.users = {}; this.users = {};
this.following = []; this.following = [];
this.expanded = {}; this.expanded = {};
this.drafts = {};
tfrpc.rpc.localStorageGet('drafts').then(function (d) {
self.drafts = JSON.parse(d || '{}');
});
} }
async search(query) { async search(query) {
@ -70,6 +75,18 @@ class TfTabSearchElement extends LitElement {
} }
} }
draft(event) {
let id = event.detail.id || '';
let previous = this.drafts[id];
if (event.detail.draft !== undefined) {
this.drafts[id] = event.detail.draft;
} else {
delete this.drafts[id];
}
this.drafts = Object.assign({}, this.drafts);
tfrpc.rpc.localStorageSet('drafts', JSON.stringify(this.drafts));
}
render() { render() {
if (this.query !== this.last_query) { if (this.query !== this.last_query) {
this.last_query = this.query; this.last_query = this.query;
@ -81,7 +98,7 @@ class TfTabSearchElement extends LitElement {
<input type="text" class="w3-input w3-theme-d1" id="search" value=${this.query} style="flex: 1" @keydown=${this.search_keydown}></input> <input type="text" class="w3-input w3-theme-d1" id="search" value=${this.query} style="flex: 1" @keydown=${this.search_keydown}></input>
<button class="w3-button w3-theme-d1" @click=${(event) => self.search(self.renderRoot.getElementById('search').value)}>Search</button> <button class="w3-button w3-theme-d1" @click=${(event) => self.search(self.renderRoot.getElementById('search').value)}>Search</button>
</div> </div>
<tf-news id="news" whoami=${this.whoami} .messages=${this.messages} .users=${this.users} .expanded=${this.expanded} @tf-expand=${this.on_expand}></tf-news> <tf-news id="news" whoami=${this.whoami} .messages=${this.messages} .users=${this.users} .expanded=${this.expanded} .drafts=${this.drafts} @tf-expand=${this.on_expand} @tf-draft=${this.draft}></tf-news>
`; `;
} }
} }

View File

@ -1,5 +1,5 @@
{ {
"type": "tildefriends-app", "type": "tildefriends-app",
"emoji": "👋", "emoji": "👋",
"previous": "&W5aJp2DgOW5rQ0AOIC9Ut3DpsahPrO6PjkJ1PQbNRdM=.sha256" "previous": "&7Pqk5nBAcbjzp0etv6WgiyTD3UF++ID0mW6qIbhwt3s=.sha256"
} }

124
apps/welcome/f-droid.svg Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Original Author: Unknown (if you are the original creator of the F-Droid button, please contact laura@ind.ie so I can credit you!) -->
<!-- Author: Created by Laura Kalbag and Released with ❤ by ind.ie (laura@ind.ie) -->
<!-- License: This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/. (As attribution is included in this file, you needn't include additional attribution on your site.) -->
<!-- How to use: The original blog post about this SVG button and how I use SVG on the Ind.ie website is at https://ind.ie/blog/f-droid-button/ -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="button_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="1490 188 300 104" enable-background="new 1490 188 300 104" xml:space="preserve">
<g id="get_it_on_f-droid_2_">
<path id="button" d="M1780,292h-280c-5.5,0-10-4.5-10-10v-84c0-5.5,4.5-10,10-10h280c5.5,0,10,4.5,10,10v84
C1790,287.5,1785.5,292,1780,292z"/>
<g id="f-droid_2_">
<path fill="#FFFFFF" d="M1621.2,236.8v3.6v1.2v5h-0.3h-0.6h-2.2c-0.7,0-1-0.3-1.2-1c-0.1-0.4-0.2-1.6-0.3-3.4h-13.5v11.1h13.2v5.5
h-13.2v10.1c0.9,0.2,1.7,0.3,2.6,0.4c0.9,0.2,1.3,0.7,1.3,1.6v3h-3.9h-7.1h-3.9v-3c0-0.9,0.4-1.5,1.3-1.6c0.9-0.2,1.7-0.3,2.6-0.4
V242c-0.9-0.2-1.7-0.3-2.6-0.4c-0.9-0.2-1.3-0.7-1.3-1.6v-3h3.9h22.7h1.2L1621.2,236.8L1621.2,236.8z"/>
<path fill="#FFFFFF" d="M1637.2,256v5.4h-13.5V256H1637.2z"/>
<path fill="#FFFFFF" d="M1676.7,255.6c0,5-1.7,10-5.3,13.5c-3.7,3.6-8.8,5.3-13.9,5.3h-13.9h-3.9v-3c0-0.9,0.4-1.5,1.3-1.6
c0.9-0.2,1.7-0.3,2.6-0.4V242c-0.9-0.2-1.7-0.3-2.6-0.4c-0.9-0.2-1.3-0.7-1.3-1.6v-3h3.9h13.9c5.1,0,10.2,1.6,13.9,5.3
C1675.1,245.6,1676.7,250.7,1676.7,255.6C1676.7,258.4,1676.7,252.9,1676.7,255.6z M1669.6,255.6c0-3.4-0.8-6.9-3.1-9.5
s-5.6-3.7-8.9-3.7h-6.8v26.4h6.8c3.4,0,6.7-1.1,8.9-3.7C1668.8,262.5,1669.6,259,1669.6,255.6z"/>
<path fill="#FFFFFF" d="M1699.7,248.1l-0.9,4.8c-0.2,1.1-1.3,0.9-2.1,0.7c-1-0.3-2.2-0.3-3.1,0c-2.2,0.5-3.7,2.3-4.5,4.2v11.4
c2.3,0.4,2.3,0.4,2.6,0.4c0.9,0.2,1.3,0.7,1.3,1.6v3h-3.9l0,0h-6.5h-3.9v-3c0-0.9,0.4-1.5,1.3-1.6c0.4-0.1,0.4-0.1,2.6-0.4v-16.3
c-2.3-0.4-2.3-0.4-2.6-0.4c-0.9-0.2-1.3-0.7-1.3-1.6v-3h3.9l0,0h3.8c1.1,0,1.7,0.4,1.9,1.6l0.3,3.2c1.1-1.9,2.6-3.7,4.7-4.7
C1695.2,247,1697.8,246.9,1699.7,248.1z"/>
<path fill="#FFFFFF" d="M1719.4,248.3c5.7,2.3,8,8,7.8,13.7c-0.3,5.6-3.4,10.7-9.1,12.3c-5.4,1.5-11.9,0-15.1-4.9
c-3.1-4.6-3.2-11.6-0.3-16.4C1706,247.6,1713.6,246,1719.4,248.3C1721,248.9,1717.8,247.6,1719.4,248.3z M1718.9,267.6
c2-2.8,2-7.1,1.2-10.2c-0.3-1.5-1-2.9-2.2-3.9c-1.3-1-3-1.4-4.6-1.2c-3.5,0.2-5.3,2.7-5.8,5.9c-0.5,3.1-0.5,7.5,1.8,10
C1711.8,270.7,1716.8,270.5,1718.9,267.6C1720,266.2,1717.9,269.1,1718.9,267.6z"/>
<path fill="#FFFFFF" d="M1743.3,271.4v3h-3.9h-6.5h-4v-3c0-0.9,0.4-1.5,1.3-1.6c0.9-0.2,1.7-0.3,2.6-0.4v-16.4
c-0.9-0.2-1.7-0.3-2.6-0.4c-0.9-0.2-1.3-0.7-1.3-1.6v-3h3.9h6.5v21.5c0.9,0.2,1.7,0.3,2.6,0.4
C1742.9,269.9,1743.3,270.5,1743.3,271.4z M1740,241.6c-1,2-3.4,3-5.4,2.2c-2-0.9-3.1-3.3-2.2-5.3c0.9-2.1,3.3-3,5.4-2.2
C1739.9,237.1,1741,239.5,1740,241.6C1739.8,242,1740.3,241,1740,241.6z"/>
<path fill="#FFFFFF" d="M1773.4,274.3h-3.7h-3.9c-0.8,0-1.5-0.3-1.7-1.2l-0.5-2.4c-2.4,2.8-5.9,4.5-9.8,4.1
c-3.8-0.4-6.5-3.2-7.8-6.6c-2.4-6.6-1.3-16.3,5.6-19.8c3.8-1.9,8.5-1.5,11.6,1.5V241c-0.9-0.2-1.7-0.3-2.6-0.4
c-0.9-0.2-1.3-0.7-1.3-1.6v-3h3.9h6.5v33.5c0.8,0.2,1.6,0.3,2.4,0.4c0.9,0.2,1.3,0.7,1.3,1.6V274.3z M1763.3,254.6
c-1.7-2-4.2-2.9-6.7-2.3c-2.5,0.6-3.9,2.8-4.4,5.1c-0.5,2.3-0.5,5-0.1,7.4c0.4,2.3,1.7,4.4,4.1,4.9c2.9,0.5,5.4-1,7.2-3.1V254.6z"
/>
</g>
<g id="get_it_on_2_">
<path fill="#FFFFFF" d="M1597.5,218.2v-2.9h7.6v6.9c-3.7,3.6-11.2,3.9-14.5-0.3c-3.3-4.2-2.4-11.5,2.6-14c2.2-1.1,5.1-1.1,7.3-0.4
s3.8,2.4,4.3,4.7l-3.5,0.7c-0.7-2.5-3.5-3.3-5.8-2.5c-2.2,0.7-3.1,2.8-3.2,4.9c-0.1,2.2,0.3,4.6,2.1,5.9c2.1,1.6,5.1,0.9,7-0.6
v-2.3H1597.5z"/>
<path fill="#FFFFFF" d="M1608.3,224.7v-17.3h13v2.9h-9.4v3.8h8.8v2.9h-8.8v4.8h9.8v2.9L1608.3,224.7L1608.3,224.7z"/>
<path fill="#FFFFFF" d="M1628.6,224.7v-14.4h-5.1v-2.9h13.9v2.9h-5.1v14.5L1628.6,224.7L1628.6,224.7z"/>
<path fill="#FFFFFF" d="M1646.3,224.7v-17.3h3.5v17.3H1646.3z"/>
<path fill="#FFFFFF" d="M1657.1,224.7v-14.4h-5.1v-2.9h13.9v2.9h-5.1v14.5L1657.1,224.7L1657.1,224.7z"/>
<path fill="#FFFFFF" d="M1674.1,216.1c0-1.7,0.3-3.3,0.8-4.4c0.8-1.7,2.2-3.2,3.9-4c2.8-1.1,6.5-1,9,0.9c2.5,1.8,3.4,4.9,3.3,7.8
c-0.1,2.9-1.1,5.8-3.8,7.5c-2.5,1.6-6.1,1.6-8.7,0.3C1675.4,222.7,1674.1,219.4,1674.1,216.1z M1677.8,216c0,2.3,0.7,4.8,3.1,5.6
c2.2,0.9,4.8,0,5.8-2c1-1.9,1-4.9,0.3-6.8c-0.9-2.1-3.1-3.1-5.3-2.7C1678.7,210.6,1677.8,213.4,1677.8,216z"/>
<path fill="#FFFFFF" d="M1693.9,224.7v-17.3h3.4l7.2,11.6v-11.6h3.3v17.3h-3.6l-7.1-11.4v11.4H1693.9z"/>
</g>
<g id="droid_2_">
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="413.9844" y1="440.5436" x2="413.9844" y2="452.6552" gradientTransform="matrix(3.7209 0 0 -3.7209 0 1914.4187)">
<stop offset="0" style="stop-color:#2B6099"/>
<stop offset="0.1299" style="stop-color:#2F69A1"/>
<stop offset="0.3451" style="stop-color:#3B83B6"/>
<stop offset="0.5" style="stop-color:#4699C8"/>
<stop offset="0.9944" style="stop-color:#479ECB"/>
</linearGradient>
<path fill="url(#SVGID_1_)" d="M1570.1,275.2h-59.3c-2.9,0-5.2-2.3-5.2-5.2v-34.7c0-2.9,2.4-5.2,5.2-5.2h59.3
c2.9,0,5.2,2.3,5.2,5.2V270C1575.3,272.8,1572.9,275.2,1570.1,275.2z"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="413.9844" y1="440.5436" x2="413.9844" y2="452.6552" gradientTransform="matrix(3.7209 0 0 -3.7209 0 1914.4187)">
<stop offset="0" style="stop-color:#2B6099"/>
<stop offset="0.5" style="stop-color:#58A4CD"/>
<stop offset="0.7865" style="stop-color:#7FB8D9"/>
<stop offset="1" style="stop-color:#9EC9E2"/>
</linearGradient>
<path fill="url(#SVGID_2_)" d="M1570.1,231.9c1.9,0,3.5,1.6,3.5,3.5V270c0,1.9-1.6,3.5-3.5,3.5h-59.3c-1.9,0-3.5-1.6-3.5-3.5
v-34.7c0-1.9,1.6-3.5,3.5-3.5H1570.1 M1570.1,230.1h-59.3c-2.9,0-5.2,2.3-5.2,5.2V270c0,2.9,2.4,5.2,5.2,5.2h59.3
c2.9,0,5.2-2.3,5.2-5.2v-34.7C1575.3,232.5,1572.9,230.1,1570.1,230.1L1570.1,230.1z"/>
</g>
<g>
<path fill="#295384" d="M1540.4,236.6c8.9,0,16.1,7.2,16.1,16c0,8.8-7.2,16-16.1,16s-16.1-7.2-16.1-16S1531.5,236.6,1540.4,236.6
M1540.4,235.3c-9.6,0-17.4,7.8-17.4,17.3s7.8,17.3,17.4,17.3s17.4-7.8,17.4-17.3S1550.1,235.3,1540.4,235.3L1540.4,235.3z"/>
</g>
<path fill="#295384" d="M1535.4,251.1c1.9-5.7,10.7-3.9,10.2,2.2c-0.5,5.6-8.5,6.2-10.2,1.2c0,0,0,0.1,0,0l0,0
c-2.4,0.2-4.7,0.4-7.1,0.6c1.7,10.1,15.3,13.1,21.6,5.3c6.5-8,0.3-20.2-10-19.8c-5.6,0.3-10.5,4.3-11.5,9.8"/>
<path fill="#7B952D" d="M1580.3,198.7l-2-1.6c-0.3-0.3-1-0.3-1.2,0.1l-6.8,7.9c-0.3,0.3-0.3,1,0.1,1.2l2,1.6
c0.3,0.3,1,0.3,1.2-0.1l6.8-7.9C1580.7,199.6,1580.6,199,1580.3,198.7z"/>
<path fill="#7B952D" d="M1500.6,198.1l2-1.6c0.3-0.3,1-0.3,1.2,0.1l6.8,7.9c0.3,0.3,0.3,1-0.1,1.2l-2,1.6c-0.3,0.3-1,0.3-1.2-0.1
l-6.8-7.9C1500.2,199,1500.3,198.5,1500.6,198.1z"/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="413.9844" y1="453.354" x2="413.9844" y2="459.4099" gradientTransform="matrix(3.7209 0 0 -3.7209 0 1914.4187)">
<stop offset="0" style="stop-color:#BCDB52"/>
<stop offset="0.3206" style="stop-color:#C5E358"/>
<stop offset="0.5" style="stop-color:#CDEA5C"/>
<stop offset="0.9944" style="stop-color:#DCF285"/>
</linearGradient>
<path fill="url(#SVGID_3_)" stroke="#7B952D" stroke-miterlimit="10" d="M1572.7,227.5h-64.5c-1,0-1.7-0.8-1.7-1.7v-19.1
c0-1,0.8-1.7,1.7-1.7h64.5c1,0,1.7,0.8,1.7,1.7v19.1C1574.4,226.7,1573.6,227.5,1572.7,227.5z"/>
<g>
<ellipse fill="#FFFFFF" cx="1523" cy="215.4" rx="6.1" ry="6.1"/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="409.8439" y1="458.401" x2="408.7539" y2="454.8358" gradientTransform="matrix(3.7209 0 0 -3.7209 0 1914.4187)">
<stop offset="0" style="stop-color:#8BA53D"/>
<stop offset="8.484717e-02" style="stop-color:#94AE45"/>
<stop offset="0.2259" style="stop-color:#AEC65C"/>
<stop offset="0.4048" style="stop-color:#D7ED81"/>
<stop offset="0.4246" style="stop-color:#DCF285"/>
</linearGradient>
<path fill="url(#SVGID_4_)" d="M1523,222.3c-3.8,0-7-3.1-7-6.9c0-3.8,3.1-6.9,7-6.9c3.8,0,7,3.1,7,6.9
C1529.9,219.2,1526.9,222.3,1523,222.3z M1523,210.2c-2.9,0-5.2,2.3-5.2,5.2s2.4,5.2,5.2,5.2s5.2-2.3,5.2-5.2
S1525.9,210.2,1523,210.2z"/>
</g>
<g>
<ellipse fill="#FFFFFF" cx="1557.8" cy="215.4" rx="6.1" ry="6.1"/>
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="419.2189" y1="458.401" x2="418.129" y2="454.8359" gradientTransform="matrix(3.7209 0 0 -3.7209 0 1914.4187)">
<stop offset="0" style="stop-color:#8BA53D"/>
<stop offset="8.484717e-02" style="stop-color:#94AE45"/>
<stop offset="0.2259" style="stop-color:#AEC65C"/>
<stop offset="0.4048" style="stop-color:#D7ED81"/>
<stop offset="0.4246" style="stop-color:#DCF285"/>
</linearGradient>
<path fill="url(#SVGID_5_)" d="M1557.8,222.3c-3.8,0-7-3.1-7-6.9c0-3.8,3.1-6.9,7-6.9c3.8,0,7,3.1,7,6.9
C1564.8,219.2,1561.8,222.3,1557.8,222.3z M1557.8,210.2c-2.9,0-5.2,2.3-5.2,5.2s2.4,5.2,5.2,5.2c2.9,0,5.2-2.3,5.2-5.2
S1560.8,210.2,1557.8,210.2z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -68,6 +68,9 @@
href="https://dev.tildefriends.net/" href="https://dev.tildefriends.net/"
><i class="fa fa-mug-hot"></i> Code</a ><i class="fa fa-mug-hot"></i> Code</a
> >
<p>
<a href="https://f-droid.org/en/packages/com.unprompted.tildefriends.fdroid/"><img src="f-droid.svg" style="height: 3em"></a>
</p>
</div> </div>
<div class="w3-col l4 m6"> <div class="w3-col l4 m6">
<img src="tildefriends.png" class="w3-image w3-right w3-hide-small" /> <img src="tildefriends.png" class="w3-image w3-right w3-hide-small" />
@ -270,6 +273,13 @@
<i class="fa fa-fire w3-text-cyan w3-jumbo"></i> <i class="fa fa-fire w3-text-cyan w3-jumbo"></i>
<p>Lit</p> <p>Lit</p>
</a> </a>
<a href="https://github.com/c-ares/c-ares" class="w3-col s3">
<i class="fa fa-book-atlas w3-text-purple w3-jumbo"></i>
<p>c-ares</p>
</a>
</div>
<div class="w3-row" style="margin-top: 64px">
<a href="https://www.gnu.org/software/make/" class="w3-col s3"> <a href="https://www.gnu.org/software/make/" class="w3-col s3">
<i class="fa fa-hammer w3-text-teal w3-jumbo"></i> <i class="fa fa-hammer w3-text-teal w3-jumbo"></i>
<p>GNU Make</p> <p>GNU Make</p>

View File

@ -77,7 +77,8 @@ const k_global_settings = {
peer_exchange: { peer_exchange: {
type: 'boolean', type: 'boolean',
default_value: false, default_value: false,
description: 'Enable discovery of, sharing of, and connecting to internet peer strangers, including announcing this instance.', description:
'Enable discovery of, sharing of, and connecting to internet peer strangers, including announcing this instance.',
}, },
account_registration: { account_registration: {
type: 'boolean', type: 'boolean',
@ -1350,8 +1351,4 @@ function storePermission(user, packageOwner, packageName, permission, allow) {
} }
} }
export { export {gGlobalSettings as globalSettings, invoke, getSessionProcessBlob};
gGlobalSettings as globalSettings,
invoke,
getSessionProcessBlob,
};

View File

@ -28,7 +28,7 @@ pkgs.stdenv.mkDerivation rec {
owner = "cory"; owner = "cory";
repo = "tildefriends"; repo = "tildefriends";
rev = "v${version}"; rev = "v${version}";
hash = "sha256-0x4LJXtMUxBsWEOoWAaQlStTfvqUf4Qs/5vJZ2EvBpY="; hash = "sha256-Su+y++zVXmYNbwfhCP6w5e36oxW5fkURPFzFLjbyFEI=";
fetchSubmodules = true; fetchSubmodules = true;
}; };

2
deps/c-ares vendored

@ -1 +1 @@
Subproject commit caffa5ffb3826cf0926405793361bbad11db3268 Subproject commit 27b98d96eff6122fb981e338bddef3d6a57d8d44

File diff suppressed because one or more lines are too long

184
deps/codemirror_src/package-lock.json generated vendored
View File

@ -19,9 +19,9 @@
} }
}, },
"node_modules/@codemirror/autocomplete": { "node_modules/@codemirror/autocomplete": {
"version": "6.18.0", "version": "6.18.1",
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.0.tgz", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.1.tgz",
"integrity": "sha512-5DbOvBbY4qW5l57cjDsmmpDh3/TeK1vXfTHa+BUMrRzdWdcxKZ4U4V7vQaTtOpApNU4kLS4FQ6cINtLg245LXA==", "integrity": "sha512-iWHdj/B1ethnHRTwZj+C1obmmuCzquH29EbcKr0qIjA9NfDeBDJ7vs+WOHsFeLeflE4o+dHfYndJloMKHUkWUA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@codemirror/language": "^6.0.0", "@codemirror/language": "^6.0.0",
@ -37,9 +37,9 @@
} }
}, },
"node_modules/@codemirror/commands": { "node_modules/@codemirror/commands": {
"version": "6.6.0", "version": "6.6.2",
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.6.0.tgz", "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.6.2.tgz",
"integrity": "sha512-qnY+b7j1UNcTS31Eenuc/5YJB6gQOzkUoNmJQc0rznwqSRpeaWWpjkWy2C/MPTcePpsKJEM26hXrOXl1+nceXg==", "integrity": "sha512-Fq7eWOl1Rcbrfn6jD8FPCj9Auaxdm5nIK5RYOeW7ughnd/rY5AmPg6b+CfsG39ZHdwiwe8lde3q8uR7CF5S0yQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@codemirror/language": "^6.0.0", "@codemirror/language": "^6.0.0",
@ -49,16 +49,16 @@
} }
}, },
"node_modules/@codemirror/lang-css": { "node_modules/@codemirror/lang-css": {
"version": "6.2.1", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.2.1.tgz", "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.0.tgz",
"integrity": "sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==", "integrity": "sha512-CyR4rUNG9OYcXDZwMPvJdtb6PHbBDKUc/6Na2BIwZ6dKab1JQqKa4di+RNRY9Myn7JB81vayKwJeQ7jEdmNVDA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@codemirror/autocomplete": "^6.0.0", "@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.0.0", "@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0", "@codemirror/state": "^6.0.0",
"@lezer/common": "^1.0.2", "@lezer/common": "^1.0.2",
"@lezer/css": "^1.0.0" "@lezer/css": "^1.1.7"
} }
}, },
"node_modules/@codemirror/lang-html": { "node_modules/@codemirror/lang-html": {
@ -239,9 +239,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@lezer/css": { "node_modules/@lezer/css": {
"version": "1.1.8", "version": "1.1.9",
"resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.8.tgz", "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.9.tgz",
"integrity": "sha512-7JhxupKuMBaWQKjQoLtzhGj83DdnZY9MckEOG5+/iLKNK2ZJqKc6hf6uc0HjwCX7Qlok44jBNqZhHKDhEhZYLA==", "integrity": "sha512-TYwgljcDv+YrV0MZFFvYFQHCfGgbPMR6nuqLabBdmZoFH3EP1gvw8t0vae326Ne3PszQkbXfVBjCnf3ZVCr0bA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@lezer/common": "^1.2.0", "@lezer/common": "^1.2.0",
@ -270,9 +270,9 @@
} }
}, },
"node_modules/@lezer/javascript": { "node_modules/@lezer/javascript": {
"version": "1.4.17", "version": "1.4.18",
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.17.tgz", "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.18.tgz",
"integrity": "sha512-bYW4ctpyGK+JMumDApeUzuIezX01H76R1foD6LcRX224FWfyYit/HYxiPGDjXXe/wQWASjCvVGoukTH68+0HIA==", "integrity": "sha512-Y8BeHOt4LtcxJgXwadtfSeWPrh0XzklcCHnCVT+vOsxqH4gWmunP2ykX+VVOlM/dusyVyiNfG3lv0f10UK+mgA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@lezer/common": "^1.2.0", "@lezer/common": "^1.2.0",
@ -371,9 +371,9 @@
} }
}, },
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.3.tgz",
"integrity": "sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==", "integrity": "sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -384,9 +384,9 @@
] ]
}, },
"node_modules/@rollup/rollup-android-arm64": { "node_modules/@rollup/rollup-android-arm64": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.3.tgz",
"integrity": "sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==", "integrity": "sha512-zrt8ecH07PE3sB4jPOggweBjJMzI1JG5xI2DIsUbkA+7K+Gkjys6eV7i9pOenNSDJH3eOr/jLb/PzqtmdwDq5g==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -397,9 +397,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-arm64": { "node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.3.tgz",
"integrity": "sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==", "integrity": "sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -410,9 +410,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-x64": { "node_modules/@rollup/rollup-darwin-x64": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.3.tgz",
"integrity": "sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==", "integrity": "sha512-L1M0vKGO5ASKntqtsFEjTq/fD91vAqnzeaF6sfNAy55aD+Hi2pBI5DKwCO+UNDQHWsDViJLqshxOahXyLSh3EA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -423,9 +423,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-gnueabihf": { "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.3.tgz",
"integrity": "sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==", "integrity": "sha512-btVgIsCjuYFKUjopPoWiDqmoUXQDiW2A4C3Mtmp5vACm7/GnyuprqIDPNczeyR5W8rTXEbkmrJux7cJmD99D2g==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -436,9 +436,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-musleabihf": { "node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.3.tgz",
"integrity": "sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==", "integrity": "sha512-zmjbSphplZlau6ZTkxd3+NMtE4UKVy7U4aVFMmHcgO5CUbw17ZP6QCgyxhzGaU/wFFdTfiojjbLG3/0p9HhAqA==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -449,9 +449,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-gnu": { "node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.3.tgz",
"integrity": "sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==", "integrity": "sha512-nSZfcZtAnQPRZmUkUQwZq2OjQciR6tEoJaZVFvLHsj0MF6QhNMg0fQ6mUOsiCUpTqxTx0/O6gX0V/nYc7LrgPw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -462,9 +462,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-musl": { "node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.3.tgz",
"integrity": "sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==", "integrity": "sha512-MnvSPGO8KJXIMGlQDYfvYS3IosFN2rKsvxRpPO2l2cum+Z3exiExLwVU+GExL96pn8IP+GdH8Tz70EpBhO0sIQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -475,9 +475,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": { "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.3.tgz",
"integrity": "sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==", "integrity": "sha512-+W+p/9QNDr2vE2AXU0qIy0qQE75E8RTwTwgqS2G5CRQ11vzq0tbnfBd6brWhS9bCRjAjepJe2fvvkvS3dno+iw==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@ -488,9 +488,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-riscv64-gnu": { "node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.3.tgz",
"integrity": "sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==", "integrity": "sha512-yXH6K6KfqGXaxHrtr+Uoy+JpNlUlI46BKVyonGiaD74ravdnF9BUNC+vV+SIuB96hUMGShhKV693rF9QDfO6nQ==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@ -501,9 +501,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-s390x-gnu": { "node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.3.tgz",
"integrity": "sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==", "integrity": "sha512-R8cwY9wcnApN/KDYWTH4gV/ypvy9yZUHlbJvfaiXSB48JO3KpwSpjOGqO4jnGkLDSk1hgjYkTbTt6Q7uvPf8eg==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@ -514,9 +514,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-gnu": { "node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.3.tgz",
"integrity": "sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==", "integrity": "sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -527,9 +527,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-musl": { "node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.3.tgz",
"integrity": "sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==", "integrity": "sha512-S0Yq+xA1VEH66uiMNhijsWAafffydd2X5b77eLHfRmfLsRSpbiAWiRHV6DEpz6aOToPsgid7TI9rGd6zB1rhbg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -540,9 +540,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-arm64-msvc": { "node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.3.tgz",
"integrity": "sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==", "integrity": "sha512-9isNzeL34yquCPyerog+IMCNxKR8XYmGd0tHSV+OVx0TmE0aJOo9uw4fZfUuk2qxobP5sug6vNdZR6u7Mw7Q+Q==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -553,9 +553,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-ia32-msvc": { "node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.3.tgz",
"integrity": "sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==", "integrity": "sha512-nMIdKnfZfzn1Vsk+RuOvl43ONTZXoAPUUxgcU0tXooqg4YrAqzfKzVenqqk2g5efWh46/D28cKFrOzDSW28gTA==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@ -566,9 +566,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-x64-msvc": { "node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.3.tgz",
"integrity": "sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==", "integrity": "sha512-fOvu7PCQjAj4eWDEuD8Xz5gpzFqXzGlxHZozHP4b9Jxv9APtdxL6STqztDzMLuRXEc4UpXGGhx029Xgm91QBeA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -579,9 +579,9 @@
] ]
}, },
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.5", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/resolve": { "node_modules/@types/resolve": {
@ -782,9 +782,9 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.21.0", "version": "4.21.3",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.0.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.3.tgz",
"integrity": "sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==", "integrity": "sha512-7sqRtBNnEbcBtMeRVc6VRsJMmpI+JU1z9VTvW8D4gXIYQFz0aLcsE6rRkyghZkLfEgUZgVvOG7A5CVz/VW5GIA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/estree": "1.0.5" "@types/estree": "1.0.5"
@ -797,25 +797,31 @@
"npm": ">=8.0.0" "npm": ">=8.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.21.0", "@rollup/rollup-android-arm-eabi": "4.21.3",
"@rollup/rollup-android-arm64": "4.21.0", "@rollup/rollup-android-arm64": "4.21.3",
"@rollup/rollup-darwin-arm64": "4.21.0", "@rollup/rollup-darwin-arm64": "4.21.3",
"@rollup/rollup-darwin-x64": "4.21.0", "@rollup/rollup-darwin-x64": "4.21.3",
"@rollup/rollup-linux-arm-gnueabihf": "4.21.0", "@rollup/rollup-linux-arm-gnueabihf": "4.21.3",
"@rollup/rollup-linux-arm-musleabihf": "4.21.0", "@rollup/rollup-linux-arm-musleabihf": "4.21.3",
"@rollup/rollup-linux-arm64-gnu": "4.21.0", "@rollup/rollup-linux-arm64-gnu": "4.21.3",
"@rollup/rollup-linux-arm64-musl": "4.21.0", "@rollup/rollup-linux-arm64-musl": "4.21.3",
"@rollup/rollup-linux-powerpc64le-gnu": "4.21.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.21.3",
"@rollup/rollup-linux-riscv64-gnu": "4.21.0", "@rollup/rollup-linux-riscv64-gnu": "4.21.3",
"@rollup/rollup-linux-s390x-gnu": "4.21.0", "@rollup/rollup-linux-s390x-gnu": "4.21.3",
"@rollup/rollup-linux-x64-gnu": "4.21.0", "@rollup/rollup-linux-x64-gnu": "4.21.3",
"@rollup/rollup-linux-x64-musl": "4.21.0", "@rollup/rollup-linux-x64-musl": "4.21.3",
"@rollup/rollup-win32-arm64-msvc": "4.21.0", "@rollup/rollup-win32-arm64-msvc": "4.21.3",
"@rollup/rollup-win32-ia32-msvc": "4.21.0", "@rollup/rollup-win32-ia32-msvc": "4.21.3",
"@rollup/rollup-win32-x64-msvc": "4.21.0", "@rollup/rollup-win32-x64-msvc": "4.21.3",
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"node_modules/rollup/node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"license": "MIT"
},
"node_modules/safe-buffer": { "node_modules/safe-buffer": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@ -894,9 +900,9 @@
} }
}, },
"node_modules/terser": { "node_modules/terser": {
"version": "5.31.6", "version": "5.33.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.33.0.tgz",
"integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", "integrity": "sha512-JuPVaB7s1gdFKPKTelwUyRq5Sid2A3Gko2S0PncwdBq7kN9Ti9HPWDQ06MPsEDGsZeVESjKEnyGy68quBk1w6g==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {

2
deps/openssl_src vendored

@ -1 +1 @@
Subproject commit db2ac4f6ebd8f3d7b2a60882992fbea1269114e2 Subproject commit fb7fab9fa6f4869eaa8fbb97e0d593159f03ffe4

63
docs/cheatsheet.md Normal file
View File

@ -0,0 +1,63 @@
# Tilde Friends Cheat Sheet
Making apps for the impatient tilde friend.
## Prerequisites
- either run your own instance or use [tildefriends.net](https://www.tildefriends.net/)
- register and login
- [optional] use the `ssb` app to create yourself an SSB identity
## Development Process
1. hit the `edit` link from any app or new app URL
2. make sure the path in the text box is under your username: `/~username/app/`
3. write server-side code in `app.js`
4. click the `save` button or press the save hotkey (Alt+S or _[browser-specific modifiers]_+S)
5. see the app reload on the right side
## Output
- **`app.setDocument(html)`** - send HTML to the browser
- **`print(...)`** - send values to the browser's developer console
## Persistence
- **`app.localStorageGet(key)`** -> **`value`**
- **`app.localStorageSet(key, value)`**
- **`database()`**, **`shared_database(key)`**, **`my_shared_database(package, key)`**
- **`db.get(key)`** -> **`value`**
- **`db.set(key, value)`**
- **`db.exchange(key, expected, value)`** -> **`exchanged`**
- **`db.remove(key)`**
- **`db.getAll()`** -> **`[key1, ...]`**
- **`db.getLike(pattern)`** -> **`{key1: value1, ...}`**
## SSB
- **`ssb.createIdentity()`** -> **`id`**
- **`ssb.getIdentities()`** -> **`[id1, ...]`**
- **`ssb.appendMessageWithIdentity(id, content)`** -> **`message_id`**
- **`ssb.blobStore(blob)`** -> **`blob_id`**
- **`ssb.blobGet(id)`** -> **`blob`**
- **`ssb.sqlAsync(query, args, row_callback)`**
## TF-RPC
Stock helper code for calling functions across the web server and browser boundary.
- on the server: `import * as tfrpc from '/tfrpc.js';`
- in the browser: `import * as tfrpc from '/static/tfrpc.js';`
- either direction:
- register a function: `tfrpc.register(function my_function() {});`
- call a remote function: `let promise = tfrpc.rpc.my_function();`
## Share
- give out web links: [https://www.tildefriends.net/~cory/screwble/](https://www.tildefriends.net/~cory/screwble/)
- use the `Attach App` button when composing a post in [the SSB app](https://www.tildefriends.net/~core/ssb/)
## More Docs
- [api reference](https://www.tildefriends.net/~cory/api/)
- [source code](https://dev.tildefriends.net/cory/tildefriends/releases)

View File

@ -1,209 +1,166 @@
# Philosophy # Tilde Friends Developer's Guide
Tilde Friends is a platform for making, running, and sharing web applications. A Tilde Friends application starts with code that runs on a Tilde Friends server, possibly far away from where you wrote it, in a little JavaScript environment, in its own restricted process, with the only access to the outside world being the ability to send messages to the server. This document gives some recipes showing how that can be used to build a functional user-facing application in light of the unique constraints present.
When you visit Tilde Friends in a web browser, you are presented with a ## Example 1: Hello, world!
terminal interface, typically with a big text output box covering most of the
page and an input box at the bottom, into which text or commands can be
entered. A script runs to produce text output and consume user input.
The script is a Tilde Friends application, and it runs on the server, which Of course we must start with a classic.
means that unlike client-side JavaScript, it can have the ability to read and
write files on the server or create network connections to other machines.
Unlike node.js or other server-side runtime environments, applications are
limited for security reasons to not interfere with each other or bring the
entire server down.
Above the terminal, an "Edit" link brings a visitor to the source code for the ### app.js
current Tilde Friends application, which they can then edit, save as their own,
and run.
# Architecture ```
app.setDocument('<h1 style="color: #fff">Hello, world!</h1>');
```
Tilde Friends is a C++ application with a JavaScript runtime that provides ### Output
restricted access to filesystem, network, and other system resources. The core
process runs a core set of scripts that implement a web server, typically
starting a new process for each visitor's session which runs scripts for the
active application and stopping it when the visitor leaves.
Only the core process has access to most system resources, but session <iframe srcdoc="&lt;h1 style=&quot;color: #fff&quot;&gt;Hello, world!&lt;/h1&gt;"></iframe>
processes can be given accesss through the core process.
Service processes are identical to session processes, but they are not tied to ### Explanation
a user session.
## Communication At a glance, this might seem mundane, but for it to work:
In the same way that web browsers expose APIs for scripts running in the - the server starts a real process for your app and loads your code into it
browser to modify the document, play sounds and video, and draw, Tilde Friends - your code runs
exposes APIs for scripts running on a Tilde Friends server to interact with a - `app.setDocument()` sends a message back to the server
visitor's web browser, read and write files on the server, and otherwise - the server interprets the message and redirects it to the browser
interact with the world. - `core/client.js` in the browser receives the message and puts your HTML into an iframe
- your HTML is presented by the browser in an iframe sandbox
There are several distinct classes of APIs. But you don't have to think about all that. Call a function, and you see the result.
First, there are low-level functions exposed from C++ to JavaScript. Most of ## Example 2: Hit Counter
these are only available to the core process. These typically only go through
a basic JavaScript to C++ transition and are relatively fast and immediate.
// Displays some text to the server's console. Let's take advantage of code running on the server and create a little hit counter using a key value store shared between all visitors.
print("Hello, world!");
There is a mechanism for communicating between processes. Functions can be ### app.js
exported and called across process boundaries. When this is done, any
arguments are serialized to a network protocol, deserialized by the other
process, the function called, and finally any return value is passed back in
the same way. Any functions referenced by the arguments or return value are
also exported and can be subsequently called across process boundaries.
Functions called across process boundaries are always asynchronous, returning a
Promise. Care must be taken for security reasons to not pass dangerous
functions ("deleteAllMydata()") to untrusted processes, and it is best for
performance reasons to minimize the data size transferred between processes.
// Send an "add" function to any other running processes. When called, it ```
// will run in this process. async function main() {
core.broadcast({add: function(x, y) { return x + y; }}); let db = await shared_database('visitors');
let count = parseInt((await db.get('visitors')) ?? '0') + 1;
await db.set('visitors', count.toString());
await app.setDocument(`
<h1 style="color: #fff">Welcome, visitor #${count}!</h1>
`);
}
// Receive the above message and call the function. main();
core.register("onMessage", function(sender, message) { ```
message.add(3, 4).then(x => terminal.print(x.toString()));
### Output
<iframe srcdoc="&lt;h1 style=&quot;color: #fff&quot;&gt;Welcome, visitor #1!&lt;/h1&gt;"></iframe>
### Explanation
Just as pure browser apps have access to `localStorage`, Tilde Friends apps have access to key-value storage on the server.
The interface is a bit clunky and will likely change someday, but this example gets a database object, from which you can get and set string values by key. There are various on `shared_database` that let you store data that is private to the user or shared by different criteria.
Also, even though any browser-side code is sandboxed, it is allowed to access browser local storage by going through Tilde Friends API, because sometimes that is useful.
## Example 3: Files
Suppose you don't want to create your entire app in a single server-side file as we've done with the previous examples. There are some tools to allow you to begin to organize.
### app.js
```
async function main() {
let html = utf8Decode(await getFile('index.html'));
app.setDocument(html);
}
main();
```
### index.html
```
<html>
<head>
<script type="module" src="script.js"></script>
</head>
<body style="color: #fff">
<h1>File Test</h1>
</body>
</html>
```
### script.js
```
window.addEventListener('load', function() {
document.body.appendChild(document.createTextNode('Hello, world');
}); });
```
Finally, there is a core web interface that runs on the client's browser that ### Output
extends access to a running Tilde Friends script.
// Displays a message in the client's browser. <iframe srcdoc="&lt;body style=&quot;color: #fff&quot;&gt;<h1>File Test</h1>Hello, world!&lt;/body&gt;"></iframe>
terminal.print("Hello, world!");
## API Documentation ### Explanation
The Tilde Friends API is very much evolving. On the server, `utf8Decode(await getFile(fileName))` lets you load a file from your app. In the browser, your app files are made available by HTTP, so you can `<script src="my_script.js"></script>` and such to access them.
All currently registered methods can be explored in the ## Example 4: Remote Procedure Call
[documentation](https://www.tildefriends.net/~cory/documentation) app.
All browser-facing methods are implemented in [client.js](core/client.js). While making calls between the client and the server, it is possible to pass functions across that boundary. `tfrpc.js` is a tiny script which builds on that feature to try to hide some of the complexities.
Most process-related methods are implemented in [core.js](core/core.js).
Higher-level behaviors are often implemented within library-style apps ### app.js
themselves and are beyond the scope of this document.
### Terminal ```
import * as tf from '/tfrpc.js';
All interaction with a human user is through a terminal-like interface. Though function sum() {
it is somewhat limiting, it makes simple things easy, and it is possible to let s = 0
construct complicated interfaces by creating and interacting with an iframe. for (let x of arguments) {
s += x;
}
return s;
}
tf.register(sum);
#### terminal.print(arguments...) async function main() {
app.setDocument(utf8Decode(await getFile('index.html')));
}
main();
```
Print to the terminal. Arguments and lists are recursively expanded. Numerous ### index.html
special values are supported as implemented in client.cs.
// Create a link. ```
terminal.print({href: "http://www.tildefriends.net/", value: "Tilde Friends!"}); <html>
<body>
<h1 id='result'>Calculating...</h1>
</body>
<script type="module" src="script.js"></script>
</html>
```
// Create an iframe. ### script.js
terminal.print({iframe: "&lt;b&gt;Hello, world!&lt;/b&gt;", width: 640, height: 480});
// Use style. ```
terminal.print({style: "color: #f00", value: "Hello, world!"}); import * as tf from '/static/tfrpc.js';
// Create a link that when clicked will act as if the user typed a command. window.addEventListener('load', async function() {
terminal.print({command: "exit", value: "Get out of here."}); document.getElementById('result').innerText = await tf.rpc.sum(1, 2, 3);
});
```
#### terminal.clear() ### Output
Clears the terminal output. <iframe srcdoc="&lt;body style=&quot;color: #fff&quot;&gt;<h1>6</h1>&lt;/body&gt;"></iframe>
#### terminal.readLine() ### Explanation
Read a line of input from the user. Here the browser makes an asynchronous call to the server to do some basic math and update its DOM with the result.
#### terminal.setEcho(echo) With your favorite Vue/Lit/React/... library on the client-side and your favorite Tilde Friends API calls registered with tfrpc, it becomes pretty easy to start extracting interesting information from, say, SQL queries over Secure Scuttlebutt data, and generating complicated, dynamic user interface. These are the building blocks I used to make the current Tilde Friends SSB client interface.
Controls whether the terminal will automatically echo user input. Defaults to true. ## Conclusion
#### terminal.setPrompt(prompt) Tilde Friends is currently a pile of all the parts that I thought I needed to build interesting web applications, tied together by code that tries to walk the fine line between being secure enough to let us safely run code on the same device and being usable enough that you can open a tab in your browser and start building just by typing code.
Sets the terminal prompt. The default is "&gt;". I don't claim it thoroughly accomplishes either yet, but I believe it is at a stage where it is showing how promising this approach can be, and I am excited for you to take it for a spin and share.
#### terminal.setTitle(title)
Sets the browser window/tab title.
#### terminal.split(terminalList)
Reconfigures the terminal layout, potentially into multiple split panes.
terminal.split([
{
type: "horizontal",
children: [
{name: "left", basis: "2in", grow: 0, shrink: 0},
{name: "middle", grow: 1},
{name: "right", basis: "2in", grow: 0, shrink: 0},
],
},
]);
#### terminal.select(name)
Directs subsequent output to the named terminal.
#### terminal.postMessageToIframe(iframeName, message)
Sends a message to the iframe that was created with the given name, using the
browser's window.postMessage.
### Database
Tilde Friends uses lmdb as a basic key value store. Keys and values are all
expected to be of type String. Each application gets its own isolated
database.
#### database.get(key)
Retrieve the database value associated with the given key.
#### database.set(key, value)
Sets the database value for the given key, overwriting any existing value.
#### database.remove(key)
Remove the database entry for the given key.
#### database.getAlll()
Retrieve a list of all key names.
### Network
Network access is generally not extended to untrusted users.
It is necessary to grant network permissions to an app owner through the
administration app.
Apps that require network access must declare it like this:
//! { "permissions": ["network"] }
#### network.newConnection()
Creates a Connection object.
#### connection.connect(host, port)
Opens a TCP connection to host:port.
#### connection.read(readCallback)
Begins reading and calls readCallback(data) for all data received.
#### connection.write(data)
Writes data to the connection.
#### connection.close()
Closes the connection.

64
docs/vision.md Normal file
View File

@ -0,0 +1,64 @@
# Tilde Friends Vision
Tilde Friends is a tool for making and sharing.
It is both a peer-to-peer social network client, participating in Secure
Scuttlebutt, and an environment for creating and running web applications.
## Why
This is a thing that I wanted to exist and wanted to work on. No other reason.
There is not a business model. I believe it is interesting and unique.
## Goals
1. Make it **easy and fun** to run all sorts of web applications.
2. Provide **security** that is easy to understand and protects your data.
3. Make **creating and sharing** web applications accessible to anyone with a
browser.
## Ways to Use Tilde Friends
1. **Social Network User**: This is a social network first. You are just here,
because your friends are. Or you like how we limit your message length or
short videos or whatever the trend is. If you are ambitious, you click links
and see interactive experiences (apps) that you wouldn't see elsewhere.
2. **Web Visitor**: You get links from a friend to meeting invites, polls, games,
lists, wiki pages, ..., and you interact with them as though they were
cloud-hosted by a megacorporation. They just work, and you don't think twice.
3. **Group leader**: You host or use a small public instance, installing apps for
a group of friends to use as web visitors.
4. **Developer**: You like to write code and make or improve apps for fun or to
solve problems. When you encounter a Tilde Friends app on a strange server,
you know you can trivially modify it or download it to your own instance.
## Future Goals / Endgame
1. Mobile apps. This can run on your old phone. Maybe you won't be hosting
the web interface publicly, but you can sync, install and edit apps, and
otherwise get the full experience from a tiny touch screen.
2. The universal application runtime. The web browser is the universal
platform, but even for the simplest application that you might want to host
for your friends, cloud hosting, containers, and complicated dependencies might
all enter the mix. Tilde Friends, though it is yet another thing to host,
includes everything you need out of the box to run a vast variety of interesting
apps.
Tilde Friends will be built out, gradually providing safe access to host
resources and client resources the same way web browsers extended access to
resources like GPU, persistent storage, cameras, ... over the years.
Not much effort has been put forward yet to having a robust, long-lasting API,
but since the client side longevity is already handled by web browsers, it
seems possible that the server-side API can be managed in a similar way.
3. An awesome development environment. Right now it runs JavaScript from the
first embeddable text editor I could poorly configure enough to edit code,
but it could incorporate a debugger, source control integration a la ssb-git,
merge tools, and transpiling from all sorts of different languages.

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unprompted.tildefriends" package="com.unprompted.tildefriends"
android:versionCode="26" android:versionCode="27"
android:versionName="0.0.22"> android:versionName="0.0.23-wip">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<application <application

View File

@ -165,6 +165,8 @@ public class TildeFriendsActivity extends Activity {
web_view.getSettings().setDatabaseEnabled(true); web_view.getSettings().setDatabaseEnabled(true);
web_view.getSettings().setDomStorageEnabled(true); web_view.getSettings().setDomStorageEnabled(true);
set_database_path();
web_view.setDownloadListener(new DownloadListener() { web_view.setDownloadListener(new DownloadListener() {
public void onDownloadStart(String url, String userAgent, String content_disposition, String mime_type, long content_length) { public void onDownloadStart(String url, String userAgent, String content_disposition, String mime_type, long content_length) {
Log.w("tildefriends", "Let's download: " + url + " (" + content_disposition + ")"); Log.w("tildefriends", "Let's download: " + url + " (" + content_disposition + ")");
@ -458,4 +460,12 @@ public class TildeFriendsActivity extends Activity {
s_activity.service_connection = null; s_activity.service_connection = null;
} }
} }
@SuppressWarnings("deprecation")
private void set_database_path()
{
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
web_view.getSettings().setDatabasePath(getDatabasePath("webview").getPath());
}
}
} }

View File

@ -1689,6 +1689,7 @@ void tf_httpd_register(JSContext* context)
tf_http_add_handler(http, "/static/*", _httpd_endpoint_static, NULL, task); tf_http_add_handler(http, "/static/*", _httpd_endpoint_static, NULL, task);
tf_http_add_handler(http, "/.well-known/*", _httpd_endpoint_static, NULL, task); tf_http_add_handler(http, "/.well-known/*", _httpd_endpoint_static, NULL, task);
tf_http_add_handler(http, "/~*/*/", _httpd_endpoint_static, NULL, task); tf_http_add_handler(http, "/~*/*/", _httpd_endpoint_static, NULL, task);
tf_http_add_handler(http, "/&*.sha256/", _httpd_endpoint_static, NULL, task);
tf_http_add_handler(http, "/robots.txt", _httpd_endpoint_robots_txt, NULL, NULL); tf_http_add_handler(http, "/robots.txt", _httpd_endpoint_robots_txt, NULL, NULL);
tf_http_add_handler(http, "/debug", _httpd_endpoint_debug, NULL, task); tf_http_add_handler(http, "/debug", _httpd_endpoint_debug, NULL, task);

View File

@ -80,6 +80,7 @@ enum
k_seed_expire_seconds = 10 * 60, k_seed_expire_seconds = 10 * 60,
k_seed_check_interval_seconds = 5 * 60, k_seed_check_interval_seconds = 5 * 60,
k_udp_discovery_expires_seconds = 10, k_udp_discovery_expires_seconds = 10,
k_handshake_timeout_ms = 15000,
}; };
typedef struct _tf_ssb_broadcast_t tf_ssb_broadcast_t; typedef struct _tf_ssb_broadcast_t tf_ssb_broadcast_t;
@ -294,6 +295,7 @@ typedef struct _tf_ssb_connection_t
uv_tcp_t tcp; uv_tcp_t tcp;
uv_connect_t connect; uv_connect_t connect;
uv_async_t async; uv_async_t async;
uv_timer_t handshake_timer;
bool closing; bool closing;
tf_ssb_connection_t* tunnel_connection; tf_ssb_connection_t* tunnel_connection;
@ -1258,6 +1260,10 @@ static void _tf_ssb_connection_verify_identity(tf_ssb_connection_t* connection,
JS_SetPropertyStr(context, connection->object, "is_client", JS_TRUE); JS_SetPropertyStr(context, connection->object, "is_client", JS_TRUE);
connection->state = k_tf_ssb_state_verified; connection->state = k_tf_ssb_state_verified;
if (connection->handshake_timer.data)
{
uv_timer_stop(&connection->handshake_timer);
}
_tf_ssb_notify_connections_changed(connection->ssb, k_tf_ssb_change_connect, connection); _tf_ssb_notify_connections_changed(connection->ssb, k_tf_ssb_change_connect, connection);
} }
@ -1486,6 +1492,10 @@ static void _tf_ssb_connection_verify_client_identity(tf_ssb_connection_t* conne
JS_SetPropertyStr(context, connection->object, "is_client", JS_FALSE); JS_SetPropertyStr(context, connection->object, "is_client", JS_FALSE);
connection->state = k_tf_ssb_state_server_verified; connection->state = k_tf_ssb_state_server_verified;
if (connection->handshake_timer.data)
{
uv_timer_stop(&connection->handshake_timer);
}
_tf_ssb_notify_connections_changed(connection->ssb, k_tf_ssb_change_connect, connection); _tf_ssb_notify_connections_changed(connection->ssb, k_tf_ssb_change_connect, connection);
} }
@ -1931,8 +1941,13 @@ static void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const ch
{ {
uv_close((uv_handle_t*)&connection->tcp, _tf_ssb_connection_on_close); uv_close((uv_handle_t*)&connection->tcp, _tf_ssb_connection_on_close);
} }
if (connection->handshake_timer.data && !uv_is_closing((uv_handle_t*)&connection->handshake_timer))
{
uv_close((uv_handle_t*)&connection->handshake_timer, _tf_ssb_connection_on_close);
}
if (JS_IsUndefined(connection->object) && !connection->async.data && !connection->tcp.data && !connection->connect.data && connection->ref_count == 0) if (JS_IsUndefined(connection->object) && !connection->async.data && !connection->tcp.data && !connection->connect.data && !connection->handshake_timer.data &&
connection->ref_count == 0)
{ {
tf_free(connection->message_requests); tf_free(connection->message_requests);
connection->message_requests = NULL; connection->message_requests = NULL;
@ -1956,7 +1971,7 @@ static void _tf_ssb_connection_on_close(uv_handle_t* handle)
{ {
tf_ssb_connection_t* connection = handle->data; tf_ssb_connection_t* connection = handle->data;
handle->data = NULL; handle->data = NULL;
if (connection) if (connection && connection->closing)
{ {
_tf_ssb_connection_destroy(connection, "handle closed"); _tf_ssb_connection_destroy(connection, "handle closed");
} }
@ -2643,6 +2658,15 @@ static void _tf_ssb_connection_process_message_async(uv_async_t* async)
} }
} }
static void _tf_ssb_connection_handshake_timer_callback(uv_timer_t* timer)
{
tf_ssb_connection_t* connection = timer->data;
if (connection && connection->state != k_tf_ssb_state_verified && connection->state != k_tf_ssb_state_server_verified)
{
_tf_ssb_connection_destroy(connection, "handshake timeout");
}
}
tf_ssb_connection_t* tf_ssb_connection_create(tf_ssb_t* ssb, const char* host, const struct sockaddr_in* addr, const uint8_t* public_key) tf_ssb_connection_t* tf_ssb_connection_create(tf_ssb_t* ssb, const char* host, const struct sockaddr_in* addr, const uint8_t* public_key)
{ {
for (tf_ssb_connection_t* connection = ssb->connections; connection; connection = connection->next) for (tf_ssb_connection_t* connection = ssb->connections; connection; connection = connection->next)
@ -2677,6 +2701,10 @@ tf_ssb_connection_t* tf_ssb_connection_create(tf_ssb_t* ssb, const char* host, c
connection->async.data = connection; connection->async.data = connection;
uv_async_init(ssb->loop, &connection->async, _tf_ssb_connection_process_message_async); uv_async_init(ssb->loop, &connection->async, _tf_ssb_connection_process_message_async);
connection->handshake_timer.data = connection;
uv_timer_init(ssb->loop, &connection->handshake_timer);
uv_timer_start(&connection->handshake_timer, _tf_ssb_connection_handshake_timer_callback, k_handshake_timeout_ms, 0);
connection->object = JS_NewObjectClass(ssb->context, _connection_class_id); connection->object = JS_NewObjectClass(ssb->context, _connection_class_id);
JS_SetOpaque(connection->object, connection); JS_SetOpaque(connection->object, connection);
char public_key_str[k_id_base64_len] = { 0 }; char public_key_str[k_id_base64_len] = { 0 };
@ -2737,6 +2765,10 @@ tf_ssb_connection_t* tf_ssb_connection_tunnel_create(tf_ssb_t* ssb, const char*
tunnel->async.data = tunnel; tunnel->async.data = tunnel;
uv_async_init(ssb->loop, &tunnel->async, _tf_ssb_connection_process_message_async); uv_async_init(ssb->loop, &tunnel->async, _tf_ssb_connection_process_message_async);
tunnel->handshake_timer.data = tunnel;
uv_timer_init(ssb->loop, &tunnel->handshake_timer);
uv_timer_start(&tunnel->handshake_timer, _tf_ssb_connection_handshake_timer_callback, k_handshake_timeout_ms, 0);
tunnel->object = JS_NewObjectClass(ssb->context, _connection_class_id); tunnel->object = JS_NewObjectClass(ssb->context, _connection_class_id);
JS_SetOpaque(tunnel->object, tunnel); JS_SetOpaque(tunnel->object, tunnel);
JS_SetPropertyStr(context, tunnel->object, "id", JS_NewString(context, target_id)); JS_SetPropertyStr(context, tunnel->object, "id", JS_NewString(context, target_id));
@ -2859,6 +2891,10 @@ static void _tf_ssb_on_connection(uv_stream_t* stream, int status)
return; return;
} }
connection->handshake_timer.data = connection;
uv_timer_init(ssb->loop, &connection->handshake_timer);
uv_timer_start(&connection->handshake_timer, _tf_ssb_connection_handshake_timer_callback, k_handshake_timeout_ms, 0);
struct sockaddr_storage addr = { 0 }; struct sockaddr_storage addr = { 0 };
int size = sizeof(addr); int size = sizeof(addr);
if (uv_tcp_getpeername(&connection->tcp, (struct sockaddr*)&addr, &size) == 0) if (uv_tcp_getpeername(&connection->tcp, (struct sockaddr*)&addr, &size) == 0)
@ -2870,10 +2906,10 @@ static void _tf_ssb_on_connection(uv_stream_t* stream, int status)
connection->next = ssb->connections; connection->next = ssb->connections;
ssb->connections = connection; ssb->connections = connection;
ssb->connections_count++; ssb->connections_count++;
_tf_ssb_notify_connections_changed(ssb, k_tf_ssb_change_create, connection);
connection->state = k_tf_ssb_state_server_wait_hello; connection->state = k_tf_ssb_state_server_wait_hello;
_tf_ssb_connection_read_start(connection); _tf_ssb_connection_read_start(connection);
_tf_ssb_notify_connections_changed(ssb, k_tf_ssb_change_create, connection);
} }
static void _tf_ssb_send_broadcast(tf_ssb_t* ssb, struct sockaddr_in* address, struct sockaddr_in* netmask) static void _tf_ssb_send_broadcast(tf_ssb_t* ssb, struct sockaddr_in* address, struct sockaddr_in* netmask)

View File

@ -133,6 +133,7 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_sequence_index ON messages (author, sequence)"); _tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_sequence_index ON messages (author, sequence)");
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_timestamp_index ON messages (author, timestamp)"); _tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_timestamp_index ON messages (author, timestamp)");
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_timestamp_index ON messages (timestamp)"); _tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_timestamp_index ON messages (timestamp)");
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_type_timestamp_index ON messages (content ->> 'type', timestamp)");
_tf_ssb_db_exec(db, _tf_ssb_db_exec(db,
"CREATE TABLE IF NOT EXISTS blobs (" "CREATE TABLE IF NOT EXISTS blobs ("
" id TEXT PRIMARY KEY," " id TEXT PRIMARY KEY,"
@ -1945,3 +1946,24 @@ bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id)
} }
return verified; return verified;
} }
bool tf_ssb_db_user_has_permission(tf_ssb_t* ssb, const char* id, const char* permission)
{
bool has_permission = false;
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
sqlite3_stmt* statement = NULL;
if (sqlite3_prepare(db,
"SELECT COUNT(*) FROM properties, json_each(properties.value -> 'permissions' -> ?) AS permission WHERE properties.id = 'core' AND properties.key = 'settings' AND "
"permission.value = ?",
-1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, permission, -1, NULL) == SQLITE_OK &&
sqlite3_step(statement) == SQLITE_ROW)
{
has_permission = sqlite3_column_int64(statement, 0) > 0;
}
sqlite3_finalize(statement);
}
tf_ssb_release_db_reader(ssb, db);
return has_permission;
}

View File

@ -416,6 +416,15 @@ void tf_ssb_db_resolve_index_async(tf_ssb_t* ssb, const char* host, void (*callb
*/ */
bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id); bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id);
/**
** Check if a user has a specific permission.
** @param ssb The SSB instance.
** @param id The user ID.
** @param permission The name of the permission.
** @return true If the user has the requested permission.
*/
bool tf_ssb_db_user_has_permission(tf_ssb_t* ssb, const char* id, const char* permission);
/** /**
** An SQLite authorizer callback. See https://www.sqlite.org/c3ref/set_authorizer.html for use. ** An SQLite authorizer callback. See https://www.sqlite.org/c3ref/set_authorizer.html for use.
** @param user_data User data registered with the authorizer. ** @param user_data User data registered with the authorizer.

View File

@ -381,6 +381,14 @@ static void _tf_ssb_getIdentities_visit(const char* identity, void* user_data)
static void _tf_ssb_get_identities_work(tf_ssb_t* ssb, void* user_data) static void _tf_ssb_get_identities_work(tf_ssb_t* ssb, void* user_data)
{ {
identities_visit_t* work = user_data; identities_visit_t* work = user_data;
if (tf_ssb_db_user_has_permission(ssb, work->user, "administration"))
{
char id[k_id_base64_len] = "";
if (tf_ssb_whoami(ssb, id, sizeof(id)))
{
_tf_ssb_getIdentities_visit(*id == '@' ? id + 1 : id, work);
}
}
tf_ssb_db_identity_visit(ssb, work->user, _tf_ssb_getIdentities_visit, user_data); tf_ssb_db_identity_visit(ssb, work->user, _tf_ssb_getIdentities_visit, user_data);
} }
@ -445,6 +453,10 @@ static void _tf_ssb_get_private_key_work(tf_ssb_t* ssb, void* user_data)
{ {
get_private_key_t* work = user_data; get_private_key_t* work = user_data;
work->got_private_key = tf_ssb_db_identity_get_private_key(ssb, work->user, work->id, work->private_key, sizeof(work->private_key)); work->got_private_key = tf_ssb_db_identity_get_private_key(ssb, work->user, work->id, work->private_key, sizeof(work->private_key));
if (!work->got_private_key && tf_ssb_db_user_has_permission(ssb, work->user, "administration"))
{
work->got_private_key = tf_ssb_db_identity_get_private_key(ssb, ":admin", work->id, work->private_key, sizeof(work->private_key));
}
} }
static void _tf_ssb_get_private_key_after_work(tf_ssb_t* ssb, int status, void* user_data) static void _tf_ssb_get_private_key_after_work(tf_ssb_t* ssb, int status, void* user_data)
@ -625,6 +637,14 @@ static void _tf_ssb_getIdentityInfo_visit(const char* identity, void* data)
static void _tf_ssb_getIdentityInfo_work(tf_ssb_t* ssb, void* user_data) static void _tf_ssb_getIdentityInfo_work(tf_ssb_t* ssb, void* user_data)
{ {
identity_info_work_t* request = user_data; identity_info_work_t* request = user_data;
char id[k_id_base64_len] = "";
if (tf_ssb_db_user_has_permission(ssb, request->name, "administration"))
{
if (tf_ssb_whoami(ssb, id, sizeof(id)))
{
_tf_ssb_getIdentityInfo_visit(*id == '@' ? id + 1 : id, request);
}
}
tf_ssb_db_identity_visit(ssb, request->name, _tf_ssb_getIdentityInfo_visit, request); tf_ssb_db_identity_visit(ssb, request->name, _tf_ssb_getIdentityInfo_visit, request);
sqlite3* db = tf_ssb_acquire_db_reader(ssb); sqlite3* db = tf_ssb_acquire_db_reader(ssb);
@ -637,12 +657,15 @@ static void _tf_ssb_getIdentityInfo_work(tf_ssb_t* ssb, void* user_data)
" messages.content ->> 'name' AS name " " messages.content ->> 'name' AS name "
" FROM messages " " FROM messages "
" JOIN identities ON messages.author = ('@' || identities.public_key) " " JOIN identities ON messages.author = ('@' || identities.public_key) "
" WHERE identities.user = ? AND json_extract(messages.content, '$.type') = 'about' AND content ->> 'about' = messages.author AND name IS NOT NULL) " " WHERE "
" (identities.user = ? OR identities.public_key = ?) AND "
" json_extract(messages.content, '$.type') = 'about' AND "
" content ->> 'about' = messages.author AND name IS NOT NULL) "
"WHERE author_rank = 1 ", "WHERE author_rank = 1 ",
-1, &statement, NULL); -1, &statement, NULL);
if (request->result == SQLITE_OK) if (request->result == SQLITE_OK)
{ {
if (sqlite3_bind_text(statement, 1, request->name, -1, NULL) == SQLITE_OK) if (sqlite3_bind_text(statement, 1, request->name, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, *id == '@' ? id + 1 : id, -1, NULL) == SQLITE_OK)
{ {
int r = SQLITE_OK; int r = SQLITE_OK;
while ((r = sqlite3_step(statement)) == SQLITE_ROW) while ((r = sqlite3_step(statement)) == SQLITE_ROW)
@ -777,6 +800,10 @@ static void _tf_ssb_append_message_with_identity_get_key_work(tf_ssb_t* ssb, voi
{ {
append_message_t* work = user_data; append_message_t* work = user_data;
work->got_private_key = tf_ssb_db_identity_get_private_key(ssb, work->user, work->id, work->private_key, sizeof(work->private_key)); work->got_private_key = tf_ssb_db_identity_get_private_key(ssb, work->user, work->id, work->private_key, sizeof(work->private_key));
if (!work->got_private_key && tf_ssb_db_user_has_permission(ssb, work->user, "administration"))
{
work->got_private_key = tf_ssb_db_identity_get_private_key(ssb, ":admin", work->id, work->private_key, sizeof(work->private_key));
}
tf_ssb_db_get_latest_message_by_author(ssb, work->id, &work->previous_sequence, work->previous_id, sizeof(work->previous_id)); tf_ssb_db_get_latest_message_by_author(ssb, work->id, &work->previous_sequence, work->previous_id, sizeof(work->previous_id));
} }

View File

@ -1,2 +1,2 @@
#define VERSION_NUMBER "0.0.22" #define VERSION_NUMBER "0.0.23-wip"
#define VERSION_NAME "Get born soon." #define VERSION_NAME "Me upon my pony on my boat."

View File

@ -25,6 +25,30 @@ try:
#options.add_argument('--headless') #options.add_argument('--headless')
driver = webdriver.Firefox(options = options, service = service) driver = webdriver.Firefox(options = options, service = service)
wait = WebDriverWait(driver, 10) wait = WebDriverWait(driver, 10)
driver.get('http://localhost:8888')
driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.LINK_TEXT, 'login').click()
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'register_label').click()
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'name').send_keys('adminuser')
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'password').send_keys('admin_password')
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'confirm').send_keys('admin_password')
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'loginButton').click()
wait.until(expected_conditions.presence_of_element_located((By.ID, 'document')))
driver.switch_to.frame(driver.find_element(By.ID, 'document'))
wait.until(expected_conditions.presence_of_element_located((By.LINK_TEXT, 'identity')))
driver.switch_to.default_content()
driver.get('http://localhost:8888/~core/admin/')
wait.until(expected_conditions.presence_of_element_located((By.ID, 'document')))
driver.switch_to.frame(driver.find_element(By.ID, 'document'))
wait.until(expected_conditions.presence_of_element_located((By.ID, 'gs_room_name'))).send_keys('test room')
wait.until(expected_conditions.presence_of_element_located((By.XPATH, '//*[@id="gs_room_name"]/following-sibling::button'))).click()
driver.switch_to.alert.accept()
driver.switch_to.default_content()
driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.ID, 'identity').click()
driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.ID, 'logout').click()
driver.get('http://localhost:8888') driver.get('http://localhost:8888')
driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.LINK_TEXT, 'login').click() driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.LINK_TEXT, 'login').click()
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'register_label').click() driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'register_label').click()
@ -82,13 +106,6 @@ try:
driver.switch_to.frame(wait.until(expected_conditions.presence_of_element_located((By.ID, 'document')))) driver.switch_to.frame(wait.until(expected_conditions.presence_of_element_located((By.ID, 'document'))))
id1 = wait.until(expected_conditions.presence_of_element_located((By.TAG_NAME, 'li'))).text.split(' ')[-1] id1 = wait.until(expected_conditions.presence_of_element_located((By.TAG_NAME, 'li'))).text.split(' ')[-1]
driver.get('http://localhost:8888/~core/admin/')
wait.until(expected_conditions.presence_of_element_located((By.ID, 'document')))
driver.switch_to.frame(driver.find_element(By.ID, 'document'))
wait.until(expected_conditions.presence_of_element_located((By.ID, 'gs_room_name'))).send_keys('test room')
wait.until(expected_conditions.presence_of_element_located((By.XPATH, '//*[@id="gs_room_name"]/following-sibling::button'))).click()
driver.switch_to.alert.accept()
driver.get('http://localhost:8888') driver.get('http://localhost:8888')
wait.until(expected_conditions.presence_of_element_located((By.ID, 'document'))) wait.until(expected_conditions.presence_of_element_located((By.ID, 'document')))
driver.switch_to.frame(driver.find_element(By.ID, 'document')) driver.switch_to.frame(driver.find_element(By.ID, 'document'))

View File

@ -3,7 +3,6 @@
if [ -z $ANDROID_NDK_ROOT ]; then if [ -z $ANDROID_NDK_ROOT ]; then
ANDROID_NDK_ROOT=~/Android/Sdk/ndk/26.1.10909125 ANDROID_NDK_ROOT=~/Android/Sdk/ndk/26.1.10909125
fi fi
OPENSSL_VERSION=3.3.1
API_LEVEL=24 API_LEVEL=24
@ -11,7 +10,7 @@ BUILD_DIR=out/openssl_android_build
BUILD_TARGETS="x86_64 x86 arm64-v8a armeabi-v7a" BUILD_TARGETS="x86_64 x86 arm64-v8a armeabi-v7a"
WORK_DIR=out/openssl-${OPENSSL_VERSION}-android WORK_DIR=out/openssl-android
rm -rf $WORK_DIR rm -rf $WORK_DIR
cp -arf deps/openssl_src/ $WORK_DIR cp -arf deps/openssl_src/ $WORK_DIR

View File

@ -1,14 +1,12 @@
#!/bin/bash #!/bin/bash
OPENSSL_VERSION=3.3.1
API_LEVEL=28 API_LEVEL=28
BUILD_DIR=out/openssl_ios_build BUILD_DIR=out/openssl_ios_build
BUILD_TARGETS="ios64-xcrun iossimulator-xcrun" BUILD_TARGETS="ios64-xcrun iossimulator-xcrun"
WORK_DIR=out/openssl-${OPENSSL_VERSION}-ios WORK_DIR=out/openssl-ios
rm -rf $WORK_DIR rm -rf $WORK_DIR
cp -af deps/openssl_src/ $WORK_DIR cp -af deps/openssl_src/ $WORK_DIR

View File

@ -1,14 +1,12 @@
#!/bin/bash #!/bin/bash
OPENSSL_VERSION=3.3.1
API_LEVEL=24 API_LEVEL=24
BUILD_DIR=out/openssl_mingw64_build BUILD_DIR=out/openssl_mingw64_build
BUILD_TARGETS="mingw64" BUILD_TARGETS="mingw64"
WORK_DIR=out/openssl-${OPENSSL_VERSION}-mingw64 WORK_DIR=out/openssl-mingw64
rm -rf $WORK_DIR rm -rf $WORK_DIR
cp -arf deps/openssl_src/ $WORK_DIR cp -arf deps/openssl_src/ $WORK_DIR
@ -65,9 +63,9 @@ build_the_thing() {
-DOPENSSL_SMALL_FOOTPRINT" -DOPENSSL_SMALL_FOOTPRINT"
echo "./Configure $SSL_TARGET $OPTIONS $GLOBAL_OPTIONS" && \ echo "./Configure $SSL_TARGET $OPTIONS $GLOBAL_OPTIONS" && \
./Configure $SSL_TARGET $OPTIONS $GLOBAL_OPTIONS && \ ./Configure $SSL_TARGET $OPTIONS $GLOBAL_OPTIONS && \
make clean && \ make -s clean && \
make build_generated && \ make -s build_generated && \
make libcrypto.a libssl.a || exit 128 make -s libcrypto.a libssl.a || exit 128
} }
for build_target in $BUILD_TARGETS for build_target in $BUILD_TARGETS