Compare commits
62 Commits
c74f90ef04
...
v0.0.29
Author | SHA1 | Date | |
---|---|---|---|
36370f2dea | |||
db9bf7f7bd | |||
fa7aef0c37 | |||
b135ea17f6 | |||
4b1643bc47 | |||
240a8ce9c7 | |||
8928e8722b | |||
d692734e55 | |||
50197198b4 | |||
1ee1107c93 | |||
cf90533b6c | |||
f0211f621e | |||
d9693af89b | |||
13722232fb | |||
0bcb033349 | |||
e92c439724 | |||
7f34b585d3 | |||
d7e9fd918a | |||
9899c0c5e2 | |||
c50de0b0f0 | |||
bb7d2d7ae0 | |||
862d172ca8 | |||
3671051d0e | |||
223e20cbbc | |||
9af4312561 | |||
934e40240e | |||
edb1980387 | |||
bb7b04013f | |||
26a3007268 | |||
5de2b09596 | |||
3660577a23 | |||
98b4c7cf04 | |||
427a7b8d25 | |||
67b84830cd | |||
973cd53266 | |||
1afdbe6932 | |||
942f582329 | |||
951a80389a | |||
b7ecfc9925 | |||
59e389d793 | |||
2ec047cc00 | |||
ee33f54745 | |||
7a79534ca8 | |||
a74a9fc821 | |||
e2c388b9db | |||
0f573ce09e | |||
bc70e41b7c | |||
f500e14aa3 | |||
8b47938238 | |||
8912212d8e | |||
6a346bf940 | |||
b5bdae4611 | |||
6590da5793 | |||
eb2b426ec7 | |||
4864a0411f | |||
68590cae33 | |||
abf2bbaec2 | |||
0da7e2722f | |||
60d4b06057 | |||
4c3df34950 | |||
7737e60b52 | |||
71e816bc13 |
10
Doxyfile
10
Doxyfile
@ -943,7 +943,9 @@ WARN_LOGFILE =
|
|||||||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||||
# Note: If this tag is empty the current directory is searched.
|
# Note: If this tag is empty the current directory is searched.
|
||||||
|
|
||||||
INPUT = README.md docs/ src/
|
INPUT = README.md \
|
||||||
|
docs/ \
|
||||||
|
src/
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# This tag can be used to specify the character encoding of the source files
|
||||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||||
@ -2268,7 +2270,7 @@ GENERATE_AUTOGEN_DEF = NO
|
|||||||
# database with symbols found by doxygen stored in tables.
|
# database with symbols found by doxygen stored in tables.
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
|
|
||||||
GENERATE_SQLITE3 = NO
|
#GENERATE_SQLITE3 = NO
|
||||||
|
|
||||||
# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
|
# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
|
||||||
# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
|
# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
|
||||||
@ -2276,7 +2278,7 @@ GENERATE_SQLITE3 = NO
|
|||||||
# The default directory is: sqlite3.
|
# The default directory is: sqlite3.
|
||||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
||||||
|
|
||||||
SQLITE3_OUTPUT = sqlite3
|
#SQLITE3_OUTPUT = sqlite3
|
||||||
|
|
||||||
# The SQLITE3_OVERWRITE_DB tag is set to YES, the existing doxygen_sqlite3.db
|
# The SQLITE3_OVERWRITE_DB tag is set to YES, the existing doxygen_sqlite3.db
|
||||||
# database file will be recreated with each doxygen run. If set to NO, doxygen
|
# database file will be recreated with each doxygen run. If set to NO, doxygen
|
||||||
@ -2284,7 +2286,7 @@ SQLITE3_OUTPUT = sqlite3
|
|||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
||||||
|
|
||||||
SQLITE3_RECREATE_DB = YES
|
#SQLITE3_RECREATE_DB = YES
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# Configuration options related to the Perl module output
|
# Configuration options related to the Perl module output
|
||||||
|
25
GNUmakefile
25
GNUmakefile
@ -16,9 +16,9 @@ MAKEFLAGS += --no-builtin-rules
|
|||||||
## LD := Linker.
|
## LD := Linker.
|
||||||
## ANDROID_SDK := Path to the Android SDK.
|
## ANDROID_SDK := Path to the Android SDK.
|
||||||
|
|
||||||
VERSION_CODE := 33
|
VERSION_CODE := 34
|
||||||
VERSION_CODE_IOS := 9
|
VERSION_CODE_IOS := 11
|
||||||
VERSION_NUMBER := 0.0.28
|
VERSION_NUMBER := 0.0.29
|
||||||
VERSION_NAME := This program kills fascists.
|
VERSION_NAME := This program kills fascists.
|
||||||
|
|
||||||
IPHONEOS_VERSION_MIN=14.0
|
IPHONEOS_VERSION_MIN=14.0
|
||||||
@ -45,6 +45,10 @@ export TZ=UTC
|
|||||||
|
|
||||||
ifeq ($(UNAME_S),Darwin)
|
ifeq ($(UNAME_S),Darwin)
|
||||||
BUILD_TYPES := debug release iosdebug iosrelease iossimdebug iossimrelease
|
BUILD_TYPES := debug release iosdebug iosrelease iossimdebug iossimrelease
|
||||||
|
HAVE_ANDROID = 0
|
||||||
|
HAVE_LINUX_IOS = 0
|
||||||
|
HAVE_LINUX_MACOS = 0
|
||||||
|
HAVE_WIN = 0
|
||||||
else ifeq ($(UNAME_S),Linux)
|
else ifeq ($(UNAME_S),Linux)
|
||||||
BUILD_TYPES := debug release
|
BUILD_TYPES := debug release
|
||||||
HAVE_ANDROID = $(if $(shell which $(ANDROID_SDK)/platform-tools/adb),1)
|
HAVE_ANDROID = $(if $(shell which $(ANDROID_SDK)/platform-tools/adb),1)
|
||||||
@ -62,6 +66,10 @@ LDFLAGS += \
|
|||||||
-lnetwork \
|
-lnetwork \
|
||||||
-Wno-stringop-overflow
|
-Wno-stringop-overflow
|
||||||
USE_SYSTEM_SSL := 1
|
USE_SYSTEM_SSL := 1
|
||||||
|
HAVE_ANDROID = 0
|
||||||
|
HAVE_LINUX_IOS = 0
|
||||||
|
HAVE_LINUX_MACOS = 0
|
||||||
|
HAVE_WIN = 0
|
||||||
else ifeq ($(UNAME_S),OpenBSD)
|
else ifeq ($(UNAME_S),OpenBSD)
|
||||||
BUILD_TYPES := debug release
|
BUILD_TYPES := debug release
|
||||||
CFLAGS += \
|
CFLAGS += \
|
||||||
@ -985,7 +993,8 @@ PACKAGE_DIRS := \
|
|||||||
core \
|
core \
|
||||||
deps/codemirror \
|
deps/codemirror \
|
||||||
deps/prettier \
|
deps/prettier \
|
||||||
deps/lit
|
deps/lit \
|
||||||
|
deps/speedscope
|
||||||
|
|
||||||
RAW_FILES := $(sort $(filter-out apps/blog% apps/issues% apps/welcome% apps/journal% %.map, $(shell find $(PACKAGE_DIRS) -type f -not -name '.*')))
|
RAW_FILES := $(sort $(filter-out apps/blog% apps/issues% apps/welcome% apps/journal% %.map, $(shell find $(PACKAGE_DIRS) -type f -not -name '.*')))
|
||||||
|
|
||||||
@ -1153,7 +1162,13 @@ out/zsign_build/zsign: $(wildcard deps/zsign/*.cpp deps/zsign/*.h deps/zsign/*.t
|
|||||||
@cmake -B out/zsign_build deps/zsign
|
@cmake -B out/zsign_build deps/zsign
|
||||||
@cmake --build out/zsign_build -- COLOR=0 VERBOSE=0 MAKESILENT=-s
|
@cmake --build out/zsign_build -- COLOR=0 VERBOSE=0 MAKESILENT=-s
|
||||||
|
|
||||||
out/tildefriends-%.app/tildefriends: out/%/tildefriends out/tildefriends-%.app/Info.plist out/tildefriends-%.app/tildefriends.png out/data.zip $(if $(HAVE_LINUX_IOS),out/zsign_build/zsign)
|
ifeq ($(HAVE_LINUX_IOS),1)
|
||||||
|
ZSIGN_DEP = out/zsign_build/zsign
|
||||||
|
else
|
||||||
|
ZSIGN_DEP =
|
||||||
|
endif
|
||||||
|
|
||||||
|
out/tildefriends-%.app/tildefriends: out/%/tildefriends out/tildefriends-%.app/Info.plist out/tildefriends-%.app/tildefriends.png out/data.zip $(ZSIGN_DEP)
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
@cp -v $(filter-out out/zsign%,$<) $@
|
@cp -v $(filter-out out/zsign%,$<) $@
|
||||||
@cp -v out/data.zip $(@D)/
|
@cp -v out/data.zip $(@D)/
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
/* W3.CSS 5.01 March 14 2025 by Jan Egil and Borge Refsnes */
|
||||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||||
@ -108,6 +108,10 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||||
|
|
||||||
|
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||||
|
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||||
|
|
||||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||||
@ -149,9 +153,9 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||||
.w3-hover-none:hover{box-shadow:none!important}
|
.w3-hover-none:hover{box-shadow:none!important}
|
||||||
/* Colors */
|
/* Colors */
|
||||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
.w3-amber,.w3-hover-amber:hover,.w3-warning{color:#000!important;background-color:#ffc107!important}
|
||||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||||
.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
|
.w3-blue,.w3-hover-blue:hover,.w3-info,.w3-primary{color:#fff!important;background-color:#2196F3!important}
|
||||||
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
||||||
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
||||||
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
||||||
@ -166,15 +170,24 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
||||||
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
||||||
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
||||||
.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
|
.w3-red,.w3-hover-red:hover,.w3-danger{color:#fff!important;background-color:#f44336!important}
|
||||||
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
||||||
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
||||||
.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
|
.w3-yellow,.w3-hover-yellow:hover,.w3-note{color:#000!important;background-color:#ffeb3b!important}
|
||||||
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
||||||
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
||||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
|
||||||
|
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover,.w3-secondary{color:#000!important;background-color:#9e9e9e!important}
|
||||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||||
|
|
||||||
|
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||||
|
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||||
|
.w3-emerald,.w3-hover-emerald:hover,.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||||
|
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||||
|
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||||
|
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||||
|
|
||||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||||
@ -232,4 +245,4 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
/* W3.CSS 5.01 March 14 2025 by Jan Egil and Borge Refsnes */
|
||||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||||
@ -108,6 +108,10 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||||
|
|
||||||
|
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||||
|
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||||
|
|
||||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||||
@ -149,9 +153,9 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||||
.w3-hover-none:hover{box-shadow:none!important}
|
.w3-hover-none:hover{box-shadow:none!important}
|
||||||
/* Colors */
|
/* Colors */
|
||||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
.w3-amber,.w3-hover-amber:hover,.w3-warning{color:#000!important;background-color:#ffc107!important}
|
||||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||||
.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
|
.w3-blue,.w3-hover-blue:hover,.w3-info,.w3-primary{color:#fff!important;background-color:#2196F3!important}
|
||||||
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
||||||
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
||||||
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
||||||
@ -166,15 +170,24 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
||||||
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
||||||
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
||||||
.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
|
.w3-red,.w3-hover-red:hover,.w3-danger{color:#fff!important;background-color:#f44336!important}
|
||||||
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
||||||
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
||||||
.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
|
.w3-yellow,.w3-hover-yellow:hover,.w3-note{color:#000!important;background-color:#ffeb3b!important}
|
||||||
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
||||||
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
||||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
|
||||||
|
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover,.w3-secondary{color:#000!important;background-color:#9e9e9e!important}
|
||||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||||
|
|
||||||
|
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||||
|
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||||
|
.w3-emerald,.w3-hover-emerald:hover,.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||||
|
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||||
|
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||||
|
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||||
|
|
||||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||||
@ -232,4 +245,4 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
/* W3.CSS 5.01 March 14 2025 by Jan Egil and Borge Refsnes */
|
||||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||||
@ -108,6 +108,10 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||||
|
|
||||||
|
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||||
|
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||||
|
|
||||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||||
@ -149,9 +153,9 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||||
.w3-hover-none:hover{box-shadow:none!important}
|
.w3-hover-none:hover{box-shadow:none!important}
|
||||||
/* Colors */
|
/* Colors */
|
||||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
.w3-amber,.w3-hover-amber:hover,.w3-warning{color:#000!important;background-color:#ffc107!important}
|
||||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||||
.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
|
.w3-blue,.w3-hover-blue:hover,.w3-info,.w3-primary{color:#fff!important;background-color:#2196F3!important}
|
||||||
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
||||||
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
||||||
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
||||||
@ -166,15 +170,24 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
||||||
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
||||||
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
||||||
.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
|
.w3-red,.w3-hover-red:hover,.w3-danger{color:#fff!important;background-color:#f44336!important}
|
||||||
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
||||||
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
||||||
.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
|
.w3-yellow,.w3-hover-yellow:hover,.w3-note{color:#000!important;background-color:#ffeb3b!important}
|
||||||
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
||||||
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
||||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
|
||||||
|
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover,.w3-secondary{color:#000!important;background-color:#9e9e9e!important}
|
||||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||||
|
|
||||||
|
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||||
|
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||||
|
.w3-emerald,.w3-hover-emerald:hover,.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||||
|
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||||
|
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||||
|
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||||
|
|
||||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||||
@ -232,4 +245,4 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "🦀",
|
"emoji": "🦀",
|
||||||
"previous": "&jAAzd36Nmpw0sRA1Dx9wLiIwGX+q//+S/Han+RLlEOw=.sha256"
|
"previous": "&wOd/+1l5wpywBlfxC1Lm0i+HhYidrgSfrn9LRX7qy2w=.sha256"
|
||||||
}
|
}
|
||||||
|
@ -255,10 +255,12 @@ class TfComposeElement extends LitElement {
|
|||||||
let self = this;
|
let self = this;
|
||||||
let input = document.createElement('input');
|
let input = document.createElement('input');
|
||||||
input.type = 'file';
|
input.type = 'file';
|
||||||
input.onchange = function (event) {
|
input.addEventListener('change', function (event) {
|
||||||
|
input.parentNode.removeChild(input);
|
||||||
let file = event.target.files[0];
|
let file = event.target.files[0];
|
||||||
self.add_file(file);
|
self.add_file(file);
|
||||||
};
|
});
|
||||||
|
document.body.appendChild(input);
|
||||||
input.click();
|
input.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +139,8 @@ class TfProfileElement extends LitElement {
|
|||||||
let self = this;
|
let self = this;
|
||||||
let input = document.createElement('input');
|
let input = document.createElement('input');
|
||||||
input.type = 'file';
|
input.type = 'file';
|
||||||
input.onchange = function (event) {
|
input.addEventListener('change', function (event) {
|
||||||
|
input.parentNode.removeChild(input);
|
||||||
let file = event.target.files[0];
|
let file = event.target.files[0];
|
||||||
file
|
file
|
||||||
.arrayBuffer()
|
.arrayBuffer()
|
||||||
@ -154,7 +155,8 @@ class TfProfileElement extends LitElement {
|
|||||||
.catch(function (e) {
|
.catch(function (e) {
|
||||||
alert(e.message);
|
alert(e.message);
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
document.body.appendChild(input);
|
||||||
input.click();
|
input.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ const tf = css`
|
|||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const w3 = css`
|
const w3 = css`
|
||||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
/* W3.CSS 5.01 March 14 2025 by Jan Egil and Borge Refsnes */
|
||||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||||
@ -88,7 +88,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px}
|
.w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px}
|
||||||
.w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap}
|
.w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap}
|
||||||
.w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}
|
.w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}
|
||||||
.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||||
.w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none}
|
.w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none}
|
||||||
.w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none}
|
.w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none}
|
||||||
.w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%}
|
.w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%}
|
||||||
@ -136,7 +136,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important}
|
.w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important}
|
||||||
@media (max-width:1205px){.w3-auto{max-width:95%}}
|
@media (max-width:1205px){.w3-auto{max-width:95%}}
|
||||||
@media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px}
|
@media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px}
|
||||||
.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative}
|
.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative}
|
||||||
.w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center}
|
.w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center}
|
||||||
.w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}}
|
.w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}}
|
||||||
@media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}}
|
@media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}}
|
||||||
@ -158,6 +158,10 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||||
|
|
||||||
|
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||||
|
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||||
|
|
||||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||||
@ -199,9 +203,9 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||||
.w3-hover-none:hover{box-shadow:none!important}
|
.w3-hover-none:hover{box-shadow:none!important}
|
||||||
/* Colors */
|
/* Colors */
|
||||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
.w3-amber,.w3-hover-amber:hover,.w3-warning{color:#000!important;background-color:#ffc107!important}
|
||||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||||
.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
|
.w3-blue,.w3-hover-blue:hover,.w3-info,.w3-primary{color:#fff!important;background-color:#2196F3!important}
|
||||||
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
||||||
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
||||||
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
||||||
@ -216,15 +220,24 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
||||||
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
||||||
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
||||||
.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
|
.w3-red,.w3-hover-red:hover,.w3-danger{color:#fff!important;background-color:#f44336!important}
|
||||||
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
||||||
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
||||||
.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
|
.w3-yellow,.w3-hover-yellow:hover,.w3-note{color:#000!important;background-color:#ffeb3b!important}
|
||||||
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
||||||
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
||||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
|
||||||
|
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover,.w3-secondary{color:#000!important;background-color:#9e9e9e!important}
|
||||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||||
|
|
||||||
|
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||||
|
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||||
|
.w3-emerald,.w3-hover-emerald:hover,.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||||
|
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||||
|
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||||
|
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||||
|
|
||||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||||
|
@ -103,6 +103,23 @@ class TfTabConnectionsElement extends LitElement {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render_progress(name, value, max) {
|
||||||
|
if (max && value != max) {
|
||||||
|
return html`
|
||||||
|
<div class="w3-theme-d1 w3-small">
|
||||||
|
<div
|
||||||
|
class="w3-container w3-theme-l1"
|
||||||
|
style="width: ${Math.floor(
|
||||||
|
(100.0 * value) / max
|
||||||
|
)}%; text-wrap: nowrap"
|
||||||
|
>
|
||||||
|
${name} ${value} / ${max} (${Math.round((100.0 * value) / max)}%)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render_broadcast(connection) {
|
render_broadcast(connection) {
|
||||||
let self = this;
|
let self = this;
|
||||||
return html`
|
return html`
|
||||||
@ -154,6 +171,16 @@ class TfTabConnectionsElement extends LitElement {
|
|||||||
: undefined}
|
: undefined}
|
||||||
${connection.flags.one_shot ? '🔃' : undefined}
|
${connection.flags.one_shot ? '🔃' : undefined}
|
||||||
<tf-user id=${connection.id} .users=${this.users}></tf-user>
|
<tf-user id=${connection.id} .users=${this.users}></tf-user>
|
||||||
|
${this.render_progress(
|
||||||
|
'recv',
|
||||||
|
connection.progress.in.total - connection.progress.in.current,
|
||||||
|
connection.progress.in.total
|
||||||
|
)}
|
||||||
|
${this.render_progress(
|
||||||
|
'send',
|
||||||
|
connection.progress.out.total - connection.progress.out.current,
|
||||||
|
connection.progress.out.total
|
||||||
|
)}
|
||||||
${connection.tunnel !== undefined
|
${connection.tunnel !== undefined
|
||||||
? '🚇'
|
? '🚇'
|
||||||
: html`(${connection.host}:${connection.port})`}
|
: html`(${connection.host}:${connection.port})`}
|
||||||
@ -206,6 +233,21 @@ class TfTabConnectionsElement extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggle_accordian(id) {
|
||||||
|
let element = this.renderRoot.getElementById(id);
|
||||||
|
element.classList.toggle('w3-hide');
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_connections() {
|
||||||
|
return this.connections.filter((x) => x.tunnel === undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_broadcasts() {
|
||||||
|
return this.broadcasts
|
||||||
|
.filter((x) => x.address)
|
||||||
|
.filter((x) => this.connections.map((c) => c.id).indexOf(x.pubkey) == -1);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let self = this;
|
let self = this;
|
||||||
return html`
|
return html`
|
||||||
@ -220,27 +262,33 @@ class TfTabConnectionsElement extends LitElement {
|
|||||||
>
|
>
|
||||||
Connect
|
Connect
|
||||||
</button>
|
</button>
|
||||||
<h2>Broadcasts</h2>
|
<h2
|
||||||
<ul class="w3-ul w3-border">
|
class="w3-button w3-block w3-theme-d1"
|
||||||
${this.broadcasts
|
@click=${() => self.toggle_accordian('connections')}
|
||||||
.filter((x) => x.address)
|
>
|
||||||
.filter(
|
Connections (${this.valid_connections().length})
|
||||||
(x) => self.connections.map((c) => c.id).indexOf(x.pubkey) == -1
|
</h2>
|
||||||
)
|
<ul class="w3-ul w3-border" id="connections">
|
||||||
.map((x) => self.render_broadcast(x))}
|
${this.valid_connections().map(
|
||||||
|
(x) => html` <li class="w3-bar">${this.render_connection(x)}</li> `
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
<h2>Connections</h2>
|
<h2
|
||||||
<ul class="w3-ul w3-border">
|
class="w3-button w3-block w3-theme-d1"
|
||||||
${this.connections
|
@click=${() => self.toggle_accordian('broadcasts')}
|
||||||
.filter((x) => x.tunnel === undefined)
|
>
|
||||||
.map(
|
Broadcasts (${this.valid_broadcasts().length})
|
||||||
(x) => html`
|
</h2>
|
||||||
<li class="w3-bar">${this.render_connection(x)}</li>
|
<ul class="w3-ul w3-border w3-hide" id="broadcasts">
|
||||||
`
|
${this.valid_broadcasts().map((x) => self.render_broadcast(x))}
|
||||||
)}
|
|
||||||
</ul>
|
</ul>
|
||||||
<h2>Stored Connections</h2>
|
<h2
|
||||||
<ul class="w3-ul w3-border">
|
class="w3-button w3-block w3-theme-d1"
|
||||||
|
@click=${() => self.toggle_accordian('stored_connections')}
|
||||||
|
>
|
||||||
|
Stored Connections (${this.stored_connections.length})
|
||||||
|
</h2>
|
||||||
|
<ul class="w3-ul w3-border w3-hide" id="stored_connections">
|
||||||
${this.stored_connections.map(
|
${this.stored_connections.map(
|
||||||
(x) => html`
|
(x) => html`
|
||||||
<li>
|
<li>
|
||||||
@ -267,8 +315,13 @@ class TfTabConnectionsElement extends LitElement {
|
|||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
<h2>Local Accounts</h2>
|
<h2
|
||||||
<div class="w3-container">
|
class="w3-button w3-block w3-theme-d1"
|
||||||
|
@click=${() => self.toggle_accordian('local_accounts')}
|
||||||
|
>
|
||||||
|
Local Accounts (${this.identities.length})
|
||||||
|
</h2>
|
||||||
|
<div class="w3-container w3-hide" id="local_accounts">
|
||||||
${this.identities.map(
|
${this.identities.map(
|
||||||
(x) =>
|
(x) =>
|
||||||
html`<div
|
html`<div
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
/* W3.CSS 5.01 March 14 2025 by Jan Egil and Borge Refsnes */
|
||||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||||
@ -108,6 +108,10 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||||
|
|
||||||
|
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||||
|
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||||
|
|
||||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||||
@ -149,9 +153,9 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||||
.w3-hover-none:hover{box-shadow:none!important}
|
.w3-hover-none:hover{box-shadow:none!important}
|
||||||
/* Colors */
|
/* Colors */
|
||||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
.w3-amber,.w3-hover-amber:hover,.w3-warning{color:#000!important;background-color:#ffc107!important}
|
||||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||||
.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
|
.w3-blue,.w3-hover-blue:hover,.w3-info,.w3-primary{color:#fff!important;background-color:#2196F3!important}
|
||||||
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
||||||
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
||||||
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
||||||
@ -166,15 +170,24 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
||||||
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
||||||
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
||||||
.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
|
.w3-red,.w3-hover-red:hover,.w3-danger{color:#fff!important;background-color:#f44336!important}
|
||||||
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
||||||
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
||||||
.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
|
.w3-yellow,.w3-hover-yellow:hover,.w3-note{color:#000!important;background-color:#ffeb3b!important}
|
||||||
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
||||||
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
||||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
|
||||||
|
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover,.w3-secondary{color:#000!important;background-color:#9e9e9e!important}
|
||||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||||
|
|
||||||
|
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||||
|
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||||
|
.w3-emerald,.w3-hover-emerald:hover,.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||||
|
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||||
|
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||||
|
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||||
|
|
||||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||||
@ -232,4 +245,4 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||||
|
108
core/app.js
108
core/app.js
@ -1,53 +1,26 @@
|
|||||||
import * as core from './core.js';
|
import * as core from './core.js';
|
||||||
|
|
||||||
let g_next_id = 1;
|
|
||||||
let g_calls = {};
|
|
||||||
|
|
||||||
let gSessionIndex = 0;
|
let gSessionIndex = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function makeSessionId() {
|
|
||||||
return 'session_' + (gSessionIndex++).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function App() {
|
function App() {
|
||||||
this._on_output = null;
|
|
||||||
this._send_queue = [];
|
this._send_queue = [];
|
||||||
|
this.calls = {};
|
||||||
|
this._next_call_id = 1;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @param {*} callback
|
|
||||||
*/
|
|
||||||
App.prototype.readOutput = function (callback) {
|
|
||||||
this._on_output = callback;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @param {*} api
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
App.prototype.makeFunction = function (api) {
|
App.prototype.makeFunction = function (api) {
|
||||||
let self = this;
|
let self = this;
|
||||||
let result = function () {
|
let result = function () {
|
||||||
let id = g_next_id++;
|
let id = self._next_call_id++;
|
||||||
while (!id || g_calls[id]) {
|
while (!id || self.calls[id]) {
|
||||||
id = g_next_id++;
|
id = self._next_call_id++;
|
||||||
}
|
}
|
||||||
let promise = new Promise(function (resolve, reject) {
|
let promise = new Promise(function (resolve, reject) {
|
||||||
g_calls[id] = {resolve: resolve, reject: reject};
|
self.calls[id] = {resolve: resolve, reject: reject};
|
||||||
});
|
});
|
||||||
let message = {
|
let message = {
|
||||||
message: 'tfrpc',
|
action: 'tfrpc',
|
||||||
method: api[0],
|
method: api[0],
|
||||||
params: [...arguments],
|
params: [...arguments],
|
||||||
id: id,
|
id: id,
|
||||||
@ -59,10 +32,6 @@ App.prototype.makeFunction = function (api) {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @param {*} message
|
|
||||||
*/
|
|
||||||
App.prototype.send = function (message) {
|
App.prototype.send = function (message) {
|
||||||
if (this._send_queue) {
|
if (this._send_queue) {
|
||||||
if (this._on_output) {
|
if (this._on_output) {
|
||||||
@ -77,11 +46,6 @@ App.prototype.send = function (message) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @param {*} request
|
|
||||||
* @param {*} response
|
|
||||||
*/
|
|
||||||
exports.app_socket = async function socket(request, response) {
|
exports.app_socket = async function socket(request, response) {
|
||||||
let process;
|
let process;
|
||||||
let options = {};
|
let options = {};
|
||||||
@ -102,10 +66,16 @@ exports.app_socket = async function socket(request, response) {
|
|||||||
try {
|
try {
|
||||||
message = JSON.parse(event.data);
|
message = JSON.parse(event.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
print('ERROR', error, event.data, event.data.length, event.opCode);
|
print(
|
||||||
|
'WebSocket error:',
|
||||||
|
error,
|
||||||
|
event.data,
|
||||||
|
event.data.length,
|
||||||
|
event.opCode
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (message.action == 'hello') {
|
if (!process && message.action == 'hello') {
|
||||||
let packageOwner;
|
let packageOwner;
|
||||||
let packageName;
|
let packageName;
|
||||||
let blobId;
|
let blobId;
|
||||||
@ -122,7 +92,7 @@ exports.app_socket = async function socket(request, response) {
|
|||||||
if (!blobId) {
|
if (!blobId) {
|
||||||
response.send(
|
response.send(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
message: 'tfrpc',
|
action: 'tfrpc',
|
||||||
method: 'error',
|
method: 'error',
|
||||||
params: [message.path + ' not found'],
|
params: [message.path + ' not found'],
|
||||||
id: -1,
|
id: -1,
|
||||||
@ -163,7 +133,7 @@ exports.app_socket = async function socket(request, response) {
|
|||||||
options.packageOwner = packageOwner;
|
options.packageOwner = packageOwner;
|
||||||
options.packageName = packageName;
|
options.packageName = packageName;
|
||||||
options.url = message.url;
|
options.url = message.url;
|
||||||
let sessionId = makeSessionId();
|
let sessionId = 'session_' + (gSessionIndex++).toString();
|
||||||
if (blobId) {
|
if (blobId) {
|
||||||
if (message.edit_only) {
|
if (message.edit_only) {
|
||||||
response.send(
|
response.send(
|
||||||
@ -175,9 +145,24 @@ exports.app_socket = async function socket(request, response) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (process) {
|
if (process) {
|
||||||
process.app.readOutput(function (message) {
|
process.client_api.tfrpc = function (message) {
|
||||||
|
if (message.id) {
|
||||||
|
let calls = process?.app?.calls;
|
||||||
|
if (calls) {
|
||||||
|
let call = calls[message.id];
|
||||||
|
if (call) {
|
||||||
|
if (message.error !== undefined) {
|
||||||
|
call.reject(message.error);
|
||||||
|
} else {
|
||||||
|
call.resolve(message.result);
|
||||||
|
}
|
||||||
|
delete calls[message.id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
process.app._on_output = (message) =>
|
||||||
response.send(JSON.stringify(message), 0x1);
|
response.send(JSON.stringify(message), 0x1);
|
||||||
});
|
|
||||||
process.app.send();
|
process.app.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,26 +191,13 @@ exports.app_socket = async function socket(request, response) {
|
|||||||
if (process && process.timeout > 0) {
|
if (process && process.timeout > 0) {
|
||||||
setTimeout(ping, process.timeout);
|
setTimeout(ping, process.timeout);
|
||||||
}
|
}
|
||||||
} else if (message.action == 'resetPermission') {
|
|
||||||
if (process) {
|
|
||||||
process.resetPermission(message.permission);
|
|
||||||
}
|
|
||||||
} else if (message.action == 'setActiveIdentity') {
|
|
||||||
process.setActiveIdentity(message.identity);
|
|
||||||
} else if (message.action == 'createIdentity') {
|
|
||||||
await process.createIdentity();
|
|
||||||
} else if (message.message == 'tfrpc') {
|
|
||||||
if (message.id && g_calls[message.id]) {
|
|
||||||
if (message.error !== undefined) {
|
|
||||||
g_calls[message.id].reject(message.error);
|
|
||||||
} else {
|
|
||||||
g_calls[message.id].resolve(message.result);
|
|
||||||
}
|
|
||||||
delete g_calls[message.id];
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (process && process.eventHandlers['message']) {
|
if (process) {
|
||||||
await core.invoke(process.eventHandlers['message'], [message]);
|
if (process.client_api[message.action]) {
|
||||||
|
process.client_api[message.action](message);
|
||||||
|
} else if (process.eventHandlers['message']) {
|
||||||
|
await core.invoke(process.eventHandlers['message'], [message]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (event.opCode == 0x8) {
|
} else if (event.opCode == 0x8) {
|
||||||
|
@ -1325,7 +1325,7 @@ function _receive_websocket_message(message) {
|
|||||||
line.append(key, message.stats[key]);
|
line.append(key, message.stats[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (message && message.message === 'tfrpc' && message.method) {
|
} else if (message && message.action === 'tfrpc' && message.method) {
|
||||||
let api = k_api[message.method];
|
let api = k_api[message.method];
|
||||||
let id = message.id;
|
let id = message.id;
|
||||||
let params = message.params;
|
let params = message.params;
|
||||||
@ -1333,14 +1333,14 @@ function _receive_websocket_message(message) {
|
|||||||
Promise.resolve(api.func(...params))
|
Promise.resolve(api.func(...params))
|
||||||
.then(function (result) {
|
.then(function (result) {
|
||||||
send({
|
send({
|
||||||
message: 'tfrpc',
|
action: 'tfrpc',
|
||||||
id: id,
|
id: id,
|
||||||
result: result,
|
result: result,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch(function (error) {
|
||||||
send({
|
send({
|
||||||
message: 'tfrpc',
|
action: 'tfrpc',
|
||||||
id: id,
|
id: id,
|
||||||
error: error,
|
error: error,
|
||||||
});
|
});
|
||||||
|
124
core/core.js
124
core/core.js
@ -3,31 +3,22 @@ import * as http from './http.js';
|
|||||||
|
|
||||||
let gProcesses = {};
|
let gProcesses = {};
|
||||||
let gStatsTimer = false;
|
let gStatsTimer = false;
|
||||||
let kPingInterval = 60 * 1000;
|
let g_handler_index = 0;
|
||||||
|
|
||||||
/**
|
const k_ping_interval = 60 * 1000;
|
||||||
* TODOC
|
|
||||||
* @param {*} out
|
function printError(error) {
|
||||||
* @param {*} error
|
|
||||||
*/
|
|
||||||
function printError(out, error) {
|
|
||||||
if (error.stackTrace) {
|
if (error.stackTrace) {
|
||||||
out.print(error.fileName + ':' + error.lineNumber + ': ' + error.message);
|
print(error.fileName + ':' + error.lineNumber + ': ' + error.message);
|
||||||
out.print(error.stackTrace);
|
print(error.stackTrace);
|
||||||
} else {
|
} else {
|
||||||
for (let [k, v] of Object.entries(error)) {
|
for (let [k, v] of Object.entries(error)) {
|
||||||
out.print(k, v);
|
print(k, v);
|
||||||
}
|
}
|
||||||
out.print(error.toString());
|
print(error.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @param {*} handlers
|
|
||||||
* @param {*} argv
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function invoke(handlers, argv) {
|
function invoke(handlers, argv) {
|
||||||
let promises = [];
|
let promises = [];
|
||||||
if (handlers) {
|
if (handlers) {
|
||||||
@ -48,12 +39,6 @@ function invoke(handlers, argv) {
|
|||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @param {*} eventName
|
|
||||||
* @param {*} argv
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function broadcastEvent(eventName, argv) {
|
function broadcastEvent(eventName, argv) {
|
||||||
let promises = [];
|
let promises = [];
|
||||||
for (let process of Object.values(gProcesses)) {
|
for (let process of Object.values(gProcesses)) {
|
||||||
@ -64,11 +49,6 @@ function broadcastEvent(eventName, argv) {
|
|||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @param {*} message
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function broadcast(message) {
|
function broadcast(message) {
|
||||||
let sender = this;
|
let sender = this;
|
||||||
let promises = [];
|
let promises = [];
|
||||||
@ -85,12 +65,6 @@ function broadcast(message) {
|
|||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @param {String} eventName
|
|
||||||
* @param {*} argv
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function broadcastAppEventToUser(
|
function broadcastAppEventToUser(
|
||||||
user,
|
user,
|
||||||
packageOwner,
|
packageOwner,
|
||||||
@ -113,12 +87,6 @@ function broadcastAppEventToUser(
|
|||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @param {*} caller
|
|
||||||
* @param {*} process
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function getUser(caller, process) {
|
function getUser(caller, process) {
|
||||||
return {
|
return {
|
||||||
key: process.key,
|
key: process.key,
|
||||||
@ -129,12 +97,6 @@ function getUser(caller, process) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @param {*} user
|
|
||||||
* @param {*} process
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async function getApps(user, process) {
|
async function getApps(user, process) {
|
||||||
if (
|
if (
|
||||||
process.credentials &&
|
process.credentials &&
|
||||||
@ -161,28 +123,13 @@ async function getApps(user, process) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @param {*} from
|
|
||||||
* @param {*} to
|
|
||||||
* @param {*} message
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function postMessageInternal(from, to, message) {
|
function postMessageInternal(from, to, message) {
|
||||||
if (to.eventHandlers['message']) {
|
if (to.eventHandlers['message']) {
|
||||||
return invoke(to.eventHandlers['message'], [getUser(from, from), message]);
|
return invoke(to.eventHandlers['message'], [getUser(from, from), message]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
* @param {*} blobId
|
|
||||||
* @param {*} key
|
|
||||||
* @param {*} options
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async function getProcessBlob(blobId, key, options) {
|
async function getProcessBlob(blobId, key, options) {
|
||||||
// TODO(tasiaiso): break this down ?
|
|
||||||
let process = gProcesses[key];
|
let process = gProcesses[key];
|
||||||
if (!process && !(options && 'create' in options && !options.create)) {
|
if (!process && !(options && 'create' in options && !options.create)) {
|
||||||
let resolveReady;
|
let resolveReady;
|
||||||
@ -201,7 +148,7 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
}
|
}
|
||||||
process.lastActive = Date.now();
|
process.lastActive = Date.now();
|
||||||
process.lastPing = null;
|
process.lastPing = null;
|
||||||
process.timeout = kPingInterval;
|
process.timeout = k_ping_interval;
|
||||||
process.ready = new Promise(function (resolve, reject) {
|
process.ready = new Promise(function (resolve, reject) {
|
||||||
resolveReady = resolve;
|
resolveReady = resolve;
|
||||||
rejectReady = reject;
|
rejectReady = reject;
|
||||||
@ -461,10 +408,10 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
if (process.app) {
|
if (process.app) {
|
||||||
process.app.makeFunction(['error'])(error);
|
process.app.makeFunction(['error'])(error);
|
||||||
} else {
|
} else {
|
||||||
printError({print: print}, error);
|
printError(error);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
printError({print: print}, error);
|
printError(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
imports.ssb = Object.fromEntries(
|
imports.ssb = Object.fromEntries(
|
||||||
@ -649,17 +596,26 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
permissions: await imports.core.permissionsGranted(),
|
permissions: await imports.core.permissionsGranted(),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
process.resetPermission = async function resetPermission(permission) {
|
process.client_api = {
|
||||||
let user = process?.credentials?.session?.name;
|
createIdentity: function () {
|
||||||
await ssb.setUserPermission(
|
return process.createIdentity();
|
||||||
user,
|
},
|
||||||
options?.packageOwner,
|
resetPermission: async function resetPermission(message) {
|
||||||
options?.packageName,
|
let user = process?.credentials?.session?.name;
|
||||||
permission,
|
await ssb.setUserPermission(
|
||||||
undefined
|
user,
|
||||||
);
|
options?.packageOwner,
|
||||||
return process.sendPermissions();
|
options?.packageName,
|
||||||
|
message.permission,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
return process.sendPermissions();
|
||||||
|
},
|
||||||
|
setActiveIdentity: function setActiveIdentity(message) {
|
||||||
|
return process.setActiveIdentity(message.identity);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
ssb.registerImports(imports, process);
|
||||||
process.task.setImports(imports);
|
process.task.setImports(imports);
|
||||||
process.task.activate();
|
process.task.activate();
|
||||||
let source = await ssb.blobGet(blobId);
|
let source = await ssb.blobGet(blobId);
|
||||||
@ -686,7 +642,7 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
printError({print: print}, e);
|
printError(e);
|
||||||
}
|
}
|
||||||
broadcastEvent('onSessionBegin', [getUser(process, process)]);
|
broadcastEvent('onSessionBegin', [getUser(process, process)]);
|
||||||
if (process.app) {
|
if (process.app) {
|
||||||
@ -700,14 +656,10 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
sendStats();
|
sendStats();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (process.app) {
|
if (process?.app && process?.task?.onError) {
|
||||||
if (process?.task?.onError) {
|
process.task.onError(error);
|
||||||
process.task.onError(error);
|
|
||||||
} else {
|
|
||||||
printError({print: print}, error);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
printError({print: print}, error);
|
printError(error);
|
||||||
}
|
}
|
||||||
rejectReady(error);
|
rejectReady(error);
|
||||||
}
|
}
|
||||||
@ -727,9 +679,6 @@ ssb.addEventListener('connections', function () {
|
|||||||
broadcastEvent('onConnectionsChanged', []);
|
broadcastEvent('onConnectionsChanged', []);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
*/
|
|
||||||
async function loadSettings() {
|
async function loadSettings() {
|
||||||
let data = {};
|
let data = {};
|
||||||
try {
|
try {
|
||||||
@ -748,9 +697,6 @@ async function loadSettings() {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODOC
|
|
||||||
*/
|
|
||||||
function sendStats() {
|
function sendStats() {
|
||||||
let apps = Object.values(gProcesses)
|
let apps = Object.values(gProcesses)
|
||||||
.filter((process) => process.app)
|
.filter((process) => process.app)
|
||||||
@ -766,8 +712,6 @@ function sendStats() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let g_handler_index = 0;
|
|
||||||
|
|
||||||
exports.callAppHandler = async function callAppHandler(
|
exports.callAppHandler = async function callAppHandler(
|
||||||
response,
|
response,
|
||||||
app_blob_id,
|
app_blob_id,
|
||||||
|
27
core/w3.css
27
core/w3.css
@ -1,4 +1,4 @@
|
|||||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
/* W3.CSS 5.01 March 14 2025 by Jan Egil and Borge Refsnes */
|
||||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||||
@ -108,6 +108,10 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||||
|
|
||||||
|
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||||
|
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||||
|
|
||||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||||
@ -149,9 +153,9 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||||
.w3-hover-none:hover{box-shadow:none!important}
|
.w3-hover-none:hover{box-shadow:none!important}
|
||||||
/* Colors */
|
/* Colors */
|
||||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
.w3-amber,.w3-hover-amber:hover,.w3-warning{color:#000!important;background-color:#ffc107!important}
|
||||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||||
.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
|
.w3-blue,.w3-hover-blue:hover,.w3-info,.w3-primary{color:#fff!important;background-color:#2196F3!important}
|
||||||
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
||||||
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
||||||
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
||||||
@ -166,15 +170,24 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
||||||
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
||||||
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
||||||
.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
|
.w3-red,.w3-hover-red:hover,.w3-danger{color:#fff!important;background-color:#f44336!important}
|
||||||
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
||||||
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
||||||
.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
|
.w3-yellow,.w3-hover-yellow:hover,.w3-note{color:#000!important;background-color:#ffeb3b!important}
|
||||||
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
||||||
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
||||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
|
||||||
|
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover,.w3-secondary{color:#000!important;background-color:#9e9e9e!important}
|
||||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||||
|
|
||||||
|
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||||
|
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||||
|
.w3-emerald,.w3-hover-emerald:hover,.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||||
|
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||||
|
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||||
|
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||||
|
|
||||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||||
@ -232,4 +245,4 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
|||||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||||
|
@ -25,14 +25,14 @@
|
|||||||
}:
|
}:
|
||||||
pkgs.stdenv.mkDerivation rec {
|
pkgs.stdenv.mkDerivation rec {
|
||||||
pname = "tildefriends";
|
pname = "tildefriends";
|
||||||
version = "0.0.27.1";
|
version = "0.0.28";
|
||||||
|
|
||||||
src = pkgs.fetchFromGitea {
|
src = pkgs.fetchFromGitea {
|
||||||
domain = "dev.tildefriends.net";
|
domain = "dev.tildefriends.net";
|
||||||
owner = "cory";
|
owner = "cory";
|
||||||
repo = "tildefriends";
|
repo = "tildefriends";
|
||||||
rev = "v${version}";
|
rev = "v${version}";
|
||||||
hash = "sha256-3t1m9ZomQF3DteWyALJWrnCq0EAROEK8shKXh6Ao38c=";
|
hash = "sha256-vcLJCXgIrjC37t9oavK2QMRcMJljghuzKDYxQ4nyTcE=";
|
||||||
fetchSubmodules = true;
|
fetchSubmodules = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
2
deps/codemirror/cm6.js
vendored
2
deps/codemirror/cm6.js
vendored
File diff suppressed because one or more lines are too long
170
deps/codemirror_src/package-lock.json
generated
vendored
170
deps/codemirror_src/package-lock.json
generated
vendored
@ -144,9 +144,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/view": {
|
"node_modules/@codemirror/view": {
|
||||||
"version": "6.36.3",
|
"version": "6.36.4",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.3.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.4.tgz",
|
||||||
"integrity": "sha512-N2bilM47QWC8Hnx0rMdDxO2x2ImJ1FvZWXubwKgjeoOrWwEiFrtpA7SFHcuZ+o2Ze2VzbkgbzWVj4+V18LVkeg==",
|
"integrity": "sha512-ZQ0V5ovw/miKEXTvjgzRyjnrk9TwriUB1k4R5p7uNnHR9Hus+D1SXHGdJshijEzPFjU25xea/7nhIeSqYFKdbA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.5.0",
|
"@codemirror/state": "^6.5.0",
|
||||||
"style-mod": "^4.1.0",
|
"style-mod": "^4.1.0",
|
||||||
@ -344,9 +344,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.9.tgz",
|
||||||
"integrity": "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==",
|
"integrity": "sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -356,9 +356,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm64": {
|
"node_modules/@rollup/rollup-android-arm64": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.9.tgz",
|
||||||
"integrity": "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==",
|
"integrity": "sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -368,9 +368,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz",
|
||||||
"integrity": "sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==",
|
"integrity": "sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -380,9 +380,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-x64": {
|
"node_modules/@rollup/rollup-darwin-x64": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.9.tgz",
|
||||||
"integrity": "sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==",
|
"integrity": "sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -392,9 +392,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.9.tgz",
|
||||||
"integrity": "sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==",
|
"integrity": "sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -404,9 +404,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.9.tgz",
|
||||||
"integrity": "sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==",
|
"integrity": "sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -416,9 +416,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.9.tgz",
|
||||||
"integrity": "sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==",
|
"integrity": "sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -428,9 +428,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.9.tgz",
|
||||||
"integrity": "sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==",
|
"integrity": "sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -440,9 +440,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.9.tgz",
|
||||||
"integrity": "sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==",
|
"integrity": "sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -452,9 +452,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.9.tgz",
|
||||||
"integrity": "sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==",
|
"integrity": "sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -464,9 +464,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.9.tgz",
|
||||||
"integrity": "sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==",
|
"integrity": "sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
@ -476,9 +476,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.9.tgz",
|
||||||
"integrity": "sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==",
|
"integrity": "sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@ -488,9 +488,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.9.tgz",
|
||||||
"integrity": "sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==",
|
"integrity": "sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@ -500,9 +500,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.9.tgz",
|
||||||
"integrity": "sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==",
|
"integrity": "sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@ -512,9 +512,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.9.tgz",
|
||||||
"integrity": "sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==",
|
"integrity": "sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -524,9 +524,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.9.tgz",
|
||||||
"integrity": "sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==",
|
"integrity": "sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -536,9 +536,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.9.tgz",
|
||||||
"integrity": "sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==",
|
"integrity": "sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -548,9 +548,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.9.tgz",
|
||||||
"integrity": "sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==",
|
"integrity": "sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@ -560,9 +560,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz",
|
||||||
"integrity": "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==",
|
"integrity": "sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -582,9 +582,9 @@
|
|||||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
|
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
|
||||||
},
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.14.0",
|
"version": "8.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
||||||
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
@ -733,9 +733,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.34.8",
|
"version": "4.34.9",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.8.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.9.tgz",
|
||||||
"integrity": "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==",
|
"integrity": "sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "1.0.6"
|
"@types/estree": "1.0.6"
|
||||||
},
|
},
|
||||||
@ -747,25 +747,25 @@
|
|||||||
"npm": ">=8.0.0"
|
"npm": ">=8.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rollup/rollup-android-arm-eabi": "4.34.8",
|
"@rollup/rollup-android-arm-eabi": "4.34.9",
|
||||||
"@rollup/rollup-android-arm64": "4.34.8",
|
"@rollup/rollup-android-arm64": "4.34.9",
|
||||||
"@rollup/rollup-darwin-arm64": "4.34.8",
|
"@rollup/rollup-darwin-arm64": "4.34.9",
|
||||||
"@rollup/rollup-darwin-x64": "4.34.8",
|
"@rollup/rollup-darwin-x64": "4.34.9",
|
||||||
"@rollup/rollup-freebsd-arm64": "4.34.8",
|
"@rollup/rollup-freebsd-arm64": "4.34.9",
|
||||||
"@rollup/rollup-freebsd-x64": "4.34.8",
|
"@rollup/rollup-freebsd-x64": "4.34.9",
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": "4.34.8",
|
"@rollup/rollup-linux-arm-gnueabihf": "4.34.9",
|
||||||
"@rollup/rollup-linux-arm-musleabihf": "4.34.8",
|
"@rollup/rollup-linux-arm-musleabihf": "4.34.9",
|
||||||
"@rollup/rollup-linux-arm64-gnu": "4.34.8",
|
"@rollup/rollup-linux-arm64-gnu": "4.34.9",
|
||||||
"@rollup/rollup-linux-arm64-musl": "4.34.8",
|
"@rollup/rollup-linux-arm64-musl": "4.34.9",
|
||||||
"@rollup/rollup-linux-loongarch64-gnu": "4.34.8",
|
"@rollup/rollup-linux-loongarch64-gnu": "4.34.9",
|
||||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.34.8",
|
"@rollup/rollup-linux-powerpc64le-gnu": "4.34.9",
|
||||||
"@rollup/rollup-linux-riscv64-gnu": "4.34.8",
|
"@rollup/rollup-linux-riscv64-gnu": "4.34.9",
|
||||||
"@rollup/rollup-linux-s390x-gnu": "4.34.8",
|
"@rollup/rollup-linux-s390x-gnu": "4.34.9",
|
||||||
"@rollup/rollup-linux-x64-gnu": "4.34.8",
|
"@rollup/rollup-linux-x64-gnu": "4.34.9",
|
||||||
"@rollup/rollup-linux-x64-musl": "4.34.8",
|
"@rollup/rollup-linux-x64-musl": "4.34.9",
|
||||||
"@rollup/rollup-win32-arm64-msvc": "4.34.8",
|
"@rollup/rollup-win32-arm64-msvc": "4.34.9",
|
||||||
"@rollup/rollup-win32-ia32-msvc": "4.34.8",
|
"@rollup/rollup-win32-ia32-msvc": "4.34.9",
|
||||||
"@rollup/rollup-win32-x64-msvc": "4.34.8",
|
"@rollup/rollup-win32-x64-msvc": "4.34.9",
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
18
docs/upgrading.md
Normal file
18
docs/upgrading.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Upgrading
|
||||||
|
|
||||||
|
Tilde Friends can be upgraded simply by running a new executable against an
|
||||||
|
existing database.
|
||||||
|
|
||||||
|
Tilde Friends writes all data to a `db.sqlite` file, either in
|
||||||
|
`~/.local/share/tildefriends/` or in the working directory where it is run,
|
||||||
|
depending on the platform and whether each one already exists. Run with
|
||||||
|
`tildefriends run -d DB_PATH` to specify the path to the database explicitly.
|
||||||
|
|
||||||
|
This file can be copied and moved across machines as needed like any [sqlite3
|
||||||
|
database](https://www.sqlite.org/onefile.html).
|
||||||
|
|
||||||
|
Schema changes and compatibility breaks have been rare, by design. In general,
|
||||||
|
upgrading is not expected to require any manual intervention and likely does
|
||||||
|
not involve any automatic migration, either. Downgrading is not well-supported
|
||||||
|
but will probably just work excepting rare changes that will be called out in
|
||||||
|
the changelog.
|
11
metadata/en-US/changelogs/34.txt
Normal file
11
metadata/en-US/changelogs/34.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
* The connections tab now shows replication progress.
|
||||||
|
* Replication performance and thoroughness improvements.
|
||||||
|
* Bind only to localhost on mobile, configurable.
|
||||||
|
* Request blobs referenced by referenced blobs, and improve performance of that
|
||||||
|
query.
|
||||||
|
* Fix file upload on iOS.
|
||||||
|
* Add rough back/forward/refresh buttons on iOS.
|
||||||
|
* Other crash fixes and performance improvements.
|
||||||
|
* Updates:
|
||||||
|
* CodeMirror
|
||||||
|
* w3.css
|
Binary file not shown.
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Binary file not shown.
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 101 KiB |
6
package-lock.json
generated
6
package-lock.json
generated
@ -11,9 +11,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "3.5.2",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
|
||||||
"integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==",
|
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin/prettier.cjs"
|
"prettier": "bin/prettier.cjs"
|
||||||
},
|
},
|
||||||
|
@ -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="33"
|
android:versionCode="34"
|
||||||
android:versionName="0.0.28">
|
android:versionName="0.0.29">
|
||||||
<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
|
||||||
|
19
src/api.js.c
Normal file
19
src/api.js.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "api.js.h"
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#include <quickjs.h>
|
||||||
|
|
||||||
|
static JSValue _tf_api_register_imports(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
|
{
|
||||||
|
return JS_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tf_api_register(JSContext* context)
|
||||||
|
{
|
||||||
|
JSValue global = JS_GetGlobalObject(context);
|
||||||
|
JSValue ssb = JS_GetPropertyStr(context, global, "ssb");
|
||||||
|
JS_SetPropertyStr(context, ssb, "registerImports", JS_NewCFunction(context, _tf_api_register_imports, "registerImports", 2));
|
||||||
|
JS_FreeValue(context, ssb);
|
||||||
|
JS_FreeValue(context, global);
|
||||||
|
}
|
18
src/api.js.h
Normal file
18
src/api.js.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
** \defgroup api_js JS API
|
||||||
|
** Functions that are ultimately exposed to apps.
|
||||||
|
** @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** A JS context. */
|
||||||
|
typedef struct JSContext JSContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Register JS API functions.
|
||||||
|
** @param context The JS context.
|
||||||
|
*/
|
||||||
|
void tf_api_register(JSContext* context);
|
||||||
|
|
||||||
|
/** @} */
|
70
src/http.c
70
src/http.c
@ -84,6 +84,7 @@ typedef struct _tf_http_listener_t
|
|||||||
typedef struct _tf_http_t
|
typedef struct _tf_http_t
|
||||||
{
|
{
|
||||||
bool is_shutting_down;
|
bool is_shutting_down;
|
||||||
|
bool is_in_destroy;
|
||||||
|
|
||||||
tf_http_listener_t** listeners;
|
tf_http_listener_t** listeners;
|
||||||
int listeners_count;
|
int listeners_count;
|
||||||
@ -395,7 +396,7 @@ static void _http_add_body_bytes(tf_http_connection_t* connection, const void* d
|
|||||||
|
|
||||||
if (fin)
|
if (fin)
|
||||||
{
|
{
|
||||||
if (connection->request->on_message)
|
if (connection->request && connection->request->on_message)
|
||||||
{
|
{
|
||||||
tf_trace_begin(connection->http->trace, connection->trace_name ? connection->trace_name : "websocket");
|
tf_trace_begin(connection->http->trace, connection->trace_name ? connection->trace_name : "websocket");
|
||||||
connection->request->on_message(connection->request, connection->fragment_length ? connection->fragment_op_code : op_code,
|
connection->request->on_message(connection->request, connection->fragment_length ? connection->fragment_op_code : op_code,
|
||||||
@ -697,7 +698,7 @@ static void _http_on_connection(uv_stream_t* stream, int status)
|
|||||||
http->connections[http->connections_count++] = connection;
|
http->connections[http->connections_count++] = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tf_http_listen(tf_http_t* http, int port, tf_tls_context_t* tls, tf_http_cleanup_t* cleanup, void* user_data)
|
int tf_http_listen(tf_http_t* http, int port, bool local_only, tf_tls_context_t* tls, tf_http_cleanup_t* cleanup, void* user_data)
|
||||||
{
|
{
|
||||||
tf_http_listener_t* listener = tf_malloc(sizeof(tf_http_listener_t));
|
tf_http_listener_t* listener = tf_malloc(sizeof(tf_http_listener_t));
|
||||||
*listener = (tf_http_listener_t) {
|
*listener = (tf_http_listener_t) {
|
||||||
@ -715,25 +716,29 @@ int tf_http_listen(tf_http_t* http, int port, tf_tls_context_t* tls, tf_http_cle
|
|||||||
|
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
{
|
{
|
||||||
|
bool use_ipv6 = !tf_util_is_mobile();
|
||||||
|
|
||||||
#if defined(__HAIKU__)
|
#if defined(__HAIKU__)
|
||||||
/*
|
/*
|
||||||
** Binding to IPv6 here fails with an odd error, and the socket
|
** Binding to IPv6 here fails with an odd error, and the socket
|
||||||
** becomes unusable. Since we probably want localhost only
|
** becomes unusable. Since we probably want localhost only
|
||||||
** on this single-user OS, let's just assume IPv4.
|
** on this single-user OS, let's just assume IPv4.
|
||||||
*/
|
*/
|
||||||
struct sockaddr_in addr = {
|
use_ipv6 = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct sockaddr_in addr4 = {
|
||||||
.sin_family = AF_INET,
|
.sin_family = AF_INET,
|
||||||
.sin_addr = { .s_addr = INADDR_ANY },
|
.sin_addr = { .s_addr = local_only ? htonl(INADDR_LOOPBACK) : htonl(INADDR_ANY) },
|
||||||
.sin_port = ntohs(port),
|
.sin_port = ntohs(port),
|
||||||
};
|
};
|
||||||
#else
|
struct sockaddr_in6 addr6 = {
|
||||||
struct sockaddr_in6 addr = {
|
|
||||||
.sin6_family = AF_INET6,
|
.sin6_family = AF_INET6,
|
||||||
.sin6_addr = IN6ADDR_ANY_INIT,
|
.sin6_addr = local_only ? (struct in6_addr)IN6ADDR_LOOPBACK_INIT : (struct in6_addr)IN6ADDR_ANY_INIT,
|
||||||
.sin6_port = ntohs(port),
|
.sin6_port = ntohs(port),
|
||||||
};
|
};
|
||||||
#endif
|
struct sockaddr* addr = use_ipv6 ? (struct sockaddr*)&addr6 : (struct sockaddr*)&addr4;
|
||||||
r = uv_tcp_bind(&listener->tcp, (struct sockaddr*)&addr, 0);
|
r = uv_tcp_bind(&listener->tcp, addr, 0);
|
||||||
if (r)
|
if (r)
|
||||||
{
|
{
|
||||||
tf_printf("%s:%d: uv_tcp_bind: %s\n", __FILE__, __LINE__, uv_strerror(r));
|
tf_printf("%s:%d: uv_tcp_bind: %s\n", __FILE__, __LINE__, uv_strerror(r));
|
||||||
@ -786,7 +791,13 @@ static void _http_free_listener_on_close(uv_handle_t* handle)
|
|||||||
|
|
||||||
void tf_http_destroy(tf_http_t* http)
|
void tf_http_destroy(tf_http_t* http)
|
||||||
{
|
{
|
||||||
|
if (http->is_in_destroy)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
http->is_shutting_down = true;
|
http->is_shutting_down = true;
|
||||||
|
http->is_in_destroy = true;
|
||||||
|
|
||||||
for (int i = 0; i < http->connections_count; i++)
|
for (int i = 0; i < http->connections_count; i++)
|
||||||
{
|
{
|
||||||
@ -845,6 +856,10 @@ void tf_http_destroy(tf_http_t* http)
|
|||||||
|
|
||||||
tf_free(http);
|
tf_free(http);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
http->is_in_destroy = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* tf_http_status_text(int status)
|
const char* tf_http_status_text(int status)
|
||||||
@ -963,9 +978,42 @@ static void _http_write(tf_http_connection_t* connection, const void* data, size
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_http_request_send(tf_http_request_t* request, const void* data, size_t size)
|
void tf_http_request_websocket_send(tf_http_request_t* request, int op_code, const void* data, size_t size)
|
||||||
{
|
{
|
||||||
_http_write(request->connection, data, size);
|
uint8_t* copy = tf_malloc(size + 16);
|
||||||
|
bool fin = true;
|
||||||
|
size_t header = 1;
|
||||||
|
copy[0] = (fin ? (1 << 7) : 0) | (op_code & 0xf);
|
||||||
|
if (size < 126)
|
||||||
|
{
|
||||||
|
copy[1] = size;
|
||||||
|
header += 1;
|
||||||
|
}
|
||||||
|
else if (size < (1 << 16))
|
||||||
|
{
|
||||||
|
copy[1] = 126;
|
||||||
|
copy[2] = (size >> 8) & 0xff;
|
||||||
|
copy[3] = (size >> 0) & 0xff;
|
||||||
|
header += 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint32_t high = ((uint64_t)size >> 32) & 0xffffffff;
|
||||||
|
uint32_t low = (size >> 0) & 0xffffffff;
|
||||||
|
copy[1] = 127;
|
||||||
|
copy[2] = (high >> 24) & 0xff;
|
||||||
|
copy[3] = (high >> 16) & 0xff;
|
||||||
|
copy[4] = (high >> 8) & 0xff;
|
||||||
|
copy[5] = (high >> 0) & 0xff;
|
||||||
|
copy[6] = (low >> 24) & 0xff;
|
||||||
|
copy[7] = (low >> 16) & 0xff;
|
||||||
|
copy[8] = (low >> 8) & 0xff;
|
||||||
|
copy[9] = (low >> 0) & 0xff;
|
||||||
|
header += 9;
|
||||||
|
}
|
||||||
|
memcpy(copy + header, data, size);
|
||||||
|
_http_write(request->connection, copy, header + size);
|
||||||
|
tf_free(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, const void* body, size_t content_length)
|
void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, const void* body, size_t content_length)
|
||||||
|
@ -116,12 +116,13 @@ void tf_http_set_trace(tf_http_t* http, tf_trace_t* trace);
|
|||||||
** times to listen on multiple ports.
|
** times to listen on multiple ports.
|
||||||
** @param http The HTTP instance.
|
** @param http The HTTP instance.
|
||||||
** @param port The port on which to listen, or 0 to assign a free port.
|
** @param port The port on which to listen, or 0 to assign a free port.
|
||||||
|
** @param local_only Only access connections on localhost, otherwise any address.
|
||||||
** @param tls An optional TLS context to use for HTTPS requests.
|
** @param tls An optional TLS context to use for HTTPS requests.
|
||||||
** @param cleanup A function called when the HTTP instance is being cleaned up.
|
** @param cleanup A function called when the HTTP instance is being cleaned up.
|
||||||
** @param user_data User data passed to the cleanup callback.
|
** @param user_data User data passed to the cleanup callback.
|
||||||
** @return The port number on which the HTTP instance is now listening.
|
** @return The port number on which the HTTP instance is now listening.
|
||||||
*/
|
*/
|
||||||
int tf_http_listen(tf_http_t* http, int port, tf_tls_context_t* tls, tf_http_cleanup_t* cleanup, void* user_data);
|
int tf_http_listen(tf_http_t* http, int port, bool local_only, tf_tls_context_t* tls, tf_http_cleanup_t* cleanup, void* user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Add an HTTP request handler.
|
** Add an HTTP request handler.
|
||||||
@ -209,10 +210,11 @@ const char* tf_http_get_cookie(const char* cookie_header, const char* name);
|
|||||||
** Send a websocket message.
|
** Send a websocket message.
|
||||||
** @param request The HTTP request which was previously updated to a websocket
|
** @param request The HTTP request which was previously updated to a websocket
|
||||||
** session with tf_http_request_websocket_upgrade().
|
** session with tf_http_request_websocket_upgrade().
|
||||||
|
** @param op_code Websocket op code.
|
||||||
** @param data The message data.
|
** @param data The message data.
|
||||||
** @param size The size of data.
|
** @param size The size of data.
|
||||||
*/
|
*/
|
||||||
void tf_http_request_send(tf_http_request_t* request, const void* data, size_t size);
|
void tf_http_request_websocket_send(tf_http_request_t* request, int op_code, const void* data, size_t size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Upgrade an HTTP request to a websocket session.
|
** Upgrade an HTTP request to a websocket session.
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
#include "ssb.db.h"
|
#include "ssb.db.h"
|
||||||
|
#include "ssb.ebt.h"
|
||||||
#include "ssb.h"
|
#include "ssb.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
#include "tls.h"
|
#include "tls.h"
|
||||||
@ -152,44 +153,9 @@ static JSValue _httpd_response_send(JSContext* context, JSValueConst this_val, i
|
|||||||
tf_http_request_t* request = JS_GetOpaque(this_val, _httpd_request_class_id);
|
tf_http_request_t* request = JS_GetOpaque(this_val, _httpd_request_class_id);
|
||||||
int opcode = 0x1;
|
int opcode = 0x1;
|
||||||
JS_ToInt32(context, &opcode, argv[1]);
|
JS_ToInt32(context, &opcode, argv[1]);
|
||||||
uint64_t length = 0;
|
size_t length = 0;
|
||||||
size_t length_size = 0;
|
const char* message = JS_ToCStringLen(context, &length, argv[0]);
|
||||||
const char* message = JS_ToCStringLen(context, &length_size, argv[0]);
|
tf_http_request_websocket_send(request, opcode, message, length);
|
||||||
length = length_size;
|
|
||||||
uint8_t* copy = tf_malloc(length + 16);
|
|
||||||
bool fin = true;
|
|
||||||
size_t header = 1;
|
|
||||||
copy[0] = (fin ? (1 << 7) : 0) | (opcode & 0xf);
|
|
||||||
if (length < 126)
|
|
||||||
{
|
|
||||||
copy[1] = length;
|
|
||||||
header += 1;
|
|
||||||
}
|
|
||||||
else if (length < (1 << 16))
|
|
||||||
{
|
|
||||||
copy[1] = 126;
|
|
||||||
copy[2] = (length >> 8) & 0xff;
|
|
||||||
copy[3] = (length >> 0) & 0xff;
|
|
||||||
header += 3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
uint32_t high = (length >> 32) & 0xffffffff;
|
|
||||||
uint32_t low = (length >> 0) & 0xffffffff;
|
|
||||||
copy[1] = 127;
|
|
||||||
copy[2] = (high >> 24) & 0xff;
|
|
||||||
copy[3] = (high >> 16) & 0xff;
|
|
||||||
copy[4] = (high >> 8) & 0xff;
|
|
||||||
copy[5] = (high >> 0) & 0xff;
|
|
||||||
copy[6] = (low >> 24) & 0xff;
|
|
||||||
copy[7] = (low >> 16) & 0xff;
|
|
||||||
copy[8] = (low >> 8) & 0xff;
|
|
||||||
copy[9] = (low >> 0) & 0xff;
|
|
||||||
header += 9;
|
|
||||||
}
|
|
||||||
memcpy(copy + header, message, length);
|
|
||||||
tf_http_request_send(request, copy, header + length);
|
|
||||||
tf_free(copy);
|
|
||||||
JS_FreeCString(context, message);
|
JS_FreeCString(context, message);
|
||||||
return JS_UNDEFINED;
|
return JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
@ -604,6 +570,50 @@ static void _httpd_endpoint_trace(tf_http_request_t* request)
|
|||||||
tf_free(json);
|
tf_free(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _httpd_endpoint_ebt(tf_http_request_t* request)
|
||||||
|
{
|
||||||
|
if (_httpd_redirect(request))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tf_task_t* task = request->user_data;
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
JSContext* context = tf_ssb_get_context(ssb);
|
||||||
|
|
||||||
|
JSValue object = JS_NewObject(context);
|
||||||
|
|
||||||
|
tf_ssb_connection_t* connections[256];
|
||||||
|
int connection_count = tf_ssb_get_connections(ssb, connections, tf_countof(connections));
|
||||||
|
for (int i = 0; i < connection_count; i++)
|
||||||
|
{
|
||||||
|
char id[k_id_base64_len];
|
||||||
|
tf_ssb_connection_get_id(connections[i], id, sizeof(id));
|
||||||
|
|
||||||
|
char key[256];
|
||||||
|
JSValue clock = JS_NewObject(context);
|
||||||
|
snprintf(key, sizeof(key), "%d:%s", i, id);
|
||||||
|
|
||||||
|
tf_ssb_ebt_t* ebt = tf_ssb_connection_get_ebt(connections[i]);
|
||||||
|
tf_ssb_ebt_debug_clock(ebt, context, clock);
|
||||||
|
JS_SetPropertyStr(context, object, key, clock);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue json_value = JS_JSONStringify(context, object, JS_NULL, JS_NewInt32(context, 2));
|
||||||
|
const char* json = JS_ToCString(context, json_value);
|
||||||
|
JS_FreeValue(context, json_value);
|
||||||
|
|
||||||
|
const char* headers[] = {
|
||||||
|
"Content-Type",
|
||||||
|
"application/json; charset=utf-8",
|
||||||
|
"Access-Control-Allow-Origin",
|
||||||
|
"*",
|
||||||
|
};
|
||||||
|
tf_http_respond(request, 200, headers, tf_countof(headers) / 2, json, json ? strlen(json) : 0);
|
||||||
|
JS_FreeCString(context, json);
|
||||||
|
JS_FreeValue(context, object);
|
||||||
|
}
|
||||||
|
|
||||||
static void _httpd_endpoint_mem(tf_http_request_t* request)
|
static void _httpd_endpoint_mem(tf_http_request_t* request)
|
||||||
{
|
{
|
||||||
if (_httpd_redirect(request))
|
if (_httpd_redirect(request))
|
||||||
@ -2370,10 +2380,12 @@ void tf_httpd_register(JSContext* context)
|
|||||||
int64_t http_port = 0;
|
int64_t http_port = 0;
|
||||||
int64_t https_port = 0;
|
int64_t https_port = 0;
|
||||||
char out_http_port_file[512] = "";
|
char out_http_port_file[512] = "";
|
||||||
|
bool local_only = false;
|
||||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||||
tf_ssb_db_get_global_setting_int64(db, "http_port", &http_port);
|
tf_ssb_db_get_global_setting_int64(db, "http_port", &http_port);
|
||||||
tf_ssb_db_get_global_setting_int64(db, "https_port", &https_port);
|
tf_ssb_db_get_global_setting_int64(db, "https_port", &https_port);
|
||||||
tf_ssb_db_get_global_setting_string(db, "out_http_port_file", out_http_port_file, sizeof(out_http_port_file));
|
tf_ssb_db_get_global_setting_string(db, "out_http_port_file", out_http_port_file, sizeof(out_http_port_file));
|
||||||
|
tf_ssb_db_get_global_setting_bool(db, "http_local_only", &local_only);
|
||||||
tf_ssb_release_db_reader(ssb, db);
|
tf_ssb_release_db_reader(ssb, db);
|
||||||
|
|
||||||
if (https_port)
|
if (https_port)
|
||||||
@ -2420,6 +2432,7 @@ void tf_httpd_register(JSContext* context)
|
|||||||
tf_http_add_handler(http, "/hitches", _httpd_endpoint_hitches, NULL, task);
|
tf_http_add_handler(http, "/hitches", _httpd_endpoint_hitches, NULL, task);
|
||||||
tf_http_add_handler(http, "/mem", _httpd_endpoint_mem, NULL, task);
|
tf_http_add_handler(http, "/mem", _httpd_endpoint_mem, NULL, task);
|
||||||
tf_http_add_handler(http, "/trace", _httpd_endpoint_trace, NULL, task);
|
tf_http_add_handler(http, "/trace", _httpd_endpoint_trace, NULL, task);
|
||||||
|
tf_http_add_handler(http, "/ebt", _httpd_endpoint_ebt, NULL, task);
|
||||||
|
|
||||||
tf_http_add_handler(http, "/login/logout", _httpd_endpoint_logout, NULL, task);
|
tf_http_add_handler(http, "/login/logout", _httpd_endpoint_logout, NULL, task);
|
||||||
tf_http_add_handler(http, "/login/auto", _httpd_endpoint_login_auto, NULL, task);
|
tf_http_add_handler(http, "/login/auto", _httpd_endpoint_login_auto, NULL, task);
|
||||||
@ -2435,7 +2448,7 @@ void tf_httpd_register(JSContext* context)
|
|||||||
{
|
{
|
||||||
httpd_listener_t* listener = tf_malloc(sizeof(httpd_listener_t));
|
httpd_listener_t* listener = tf_malloc(sizeof(httpd_listener_t));
|
||||||
*listener = (httpd_listener_t) { 0 };
|
*listener = (httpd_listener_t) { 0 };
|
||||||
int assigned_port = tf_http_listen(http, http_port, NULL, _httpd_listener_cleanup, listener);
|
int assigned_port = tf_http_listen(http, http_port, local_only, NULL, _httpd_listener_cleanup, listener);
|
||||||
tf_printf(CYAN "~😎 Tilde Friends" RESET " " YELLOW VERSION_NUMBER RESET " is now up at " MAGENTA "http://127.0.0.1:%d/" RESET ".\n", assigned_port);
|
tf_printf(CYAN "~😎 Tilde Friends" RESET " " YELLOW VERSION_NUMBER RESET " is now up at " MAGENTA "http://127.0.0.1:%d/" RESET ".\n", assigned_port);
|
||||||
|
|
||||||
if (*out_http_port_file)
|
if (*out_http_port_file)
|
||||||
@ -2468,7 +2481,7 @@ void tf_httpd_register(JSContext* context)
|
|||||||
tf_tls_context_set_private_key(tls, private_key);
|
tf_tls_context_set_private_key(tls, private_key);
|
||||||
httpd_listener_t* listener = tf_malloc(sizeof(httpd_listener_t));
|
httpd_listener_t* listener = tf_malloc(sizeof(httpd_listener_t));
|
||||||
*listener = (httpd_listener_t) { .tls = tls };
|
*listener = (httpd_listener_t) { .tls = tls };
|
||||||
int assigned_port = tf_http_listen(http, https_port, tls, _httpd_listener_cleanup, listener);
|
int assigned_port = tf_http_listen(http, https_port, local_only, tls, _httpd_listener_cleanup, listener);
|
||||||
tf_printf(CYAN "~😎 Tilde Friends" RESET " " YELLOW VERSION_NUMBER RESET " is now up at " MAGENTA "https://127.0.0.1:%d/" RESET ".\n", assigned_port);
|
tf_printf(CYAN "~😎 Tilde Friends" RESET " " YELLOW VERSION_NUMBER RESET " is now up at " MAGENTA "https://127.0.0.1:%d/" RESET ".\n", assigned_port);
|
||||||
}
|
}
|
||||||
tf_free((char*)certificate);
|
tf_free((char*)certificate);
|
||||||
|
54
src/ios.m
54
src/ios.m
@ -4,12 +4,14 @@
|
|||||||
#import <WebKit/WKWebView.h>
|
#import <WebKit/WKWebView.h>
|
||||||
#import <WebKit/WKWebViewConfiguration.h>
|
#import <WebKit/WKWebViewConfiguration.h>
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
void tf_run_thread_start(const char* zip_path);
|
void tf_run_thread_start(const char* zip_path);
|
||||||
|
|
||||||
@interface ViewController : UIViewController <WKUIDelegate, WKNavigationDelegate>
|
@interface ViewController : UINavigationController <WKUIDelegate, WKNavigationDelegate>
|
||||||
@property (strong, nonatomic) WKWebView* web_view;
|
@property (strong, nonatomic) WKWebView* web_view;
|
||||||
@property bool initial_load_complete;
|
@property bool initial_load_complete;
|
||||||
@end
|
@end
|
||||||
@ -19,19 +21,67 @@ static void _start_initial_load(WKWebView* web_view)
|
|||||||
[web_view loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:12345/login/auto"]]];
|
[web_view loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:12345/login/auto"]]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@implementation ViewController : UIViewController
|
@implementation ViewController : UINavigationController
|
||||||
- (void)viewDidLoad
|
- (void)viewDidLoad
|
||||||
{
|
{
|
||||||
[super viewDidLoad];
|
[super viewDidLoad];
|
||||||
|
|
||||||
|
[self setToolbarHidden:false animated:false];
|
||||||
|
self.toolbar.items = @[
|
||||||
|
[[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStylePlain target:self action:@selector(goBack)],
|
||||||
|
[[UIBarButtonItem alloc] initWithTitle:@"Forward" style:UIBarButtonItemStylePlain target:self action:@selector(goForward)],
|
||||||
|
[[UIBarButtonItem alloc] initWithTitle:@"Refresh" style:UIBarButtonItemStylePlain target:self action:@selector(reload)]
|
||||||
|
];
|
||||||
|
|
||||||
WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init];
|
WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init];
|
||||||
self.web_view = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
|
self.web_view = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
|
||||||
self.web_view.UIDelegate = self;
|
self.web_view.UIDelegate = self;
|
||||||
self.web_view.navigationDelegate = self;
|
self.web_view.navigationDelegate = self;
|
||||||
self.web_view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
|
self.web_view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
|
||||||
|
self.web_view.translatesAutoresizingMaskIntoConstraints = false;
|
||||||
[self.view addSubview:self.web_view];
|
[self.view addSubview:self.web_view];
|
||||||
|
|
||||||
|
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.web_view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view
|
||||||
|
attribute:NSLayoutAttributeTop
|
||||||
|
multiplier:1.0
|
||||||
|
constant:0]];
|
||||||
|
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.web_view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view
|
||||||
|
attribute:NSLayoutAttributeBottom
|
||||||
|
multiplier:1.0
|
||||||
|
constant:-75]];
|
||||||
|
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.web_view attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view
|
||||||
|
attribute:NSLayoutAttributeLeft
|
||||||
|
multiplier:1.0
|
||||||
|
constant:0]];
|
||||||
|
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.web_view attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view
|
||||||
|
attribute:NSLayoutAttributeRight
|
||||||
|
multiplier:1.0
|
||||||
|
constant:0]];
|
||||||
|
|
||||||
_start_initial_load(self.web_view);
|
_start_initial_load(self.web_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)goBack
|
||||||
|
{
|
||||||
|
if (self.web_view.canGoBack)
|
||||||
|
{
|
||||||
|
[self.web_view goBack];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)goForward
|
||||||
|
{
|
||||||
|
if (self.web_view.canGoForward)
|
||||||
|
{
|
||||||
|
[self.web_view goForward];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)reload
|
||||||
|
{
|
||||||
|
[self.web_view reload];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)webView:(WKWebView*)webView didFinishNavigation:(WKNavigation*)navigation
|
- (void)webView:(WKWebView*)webView didFinishNavigation:(WKNavigation*)navigation
|
||||||
{
|
{
|
||||||
self.initial_load_complete = true;
|
self.initial_load_complete = true;
|
||||||
|
@ -13,13 +13,13 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.0.28</string>
|
<string>0.0.29</string>
|
||||||
<key>CFBundleSupportedPlatforms</key>
|
<key>CFBundleSupportedPlatforms</key>
|
||||||
<array>
|
<array>
|
||||||
<string>iPhoneOS</string>
|
<string>iPhoneOS</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>9</string>
|
<string>11</string>
|
||||||
<key>DTPlatformName</key>
|
<key>DTPlatformName</key>
|
||||||
<string>iphoneos</string>
|
<string>iphoneos</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
@ -1485,6 +1485,12 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = optind; i < argc; i++)
|
||||||
|
{
|
||||||
|
tf_printf("Unexpected argument: %s\n", argv[i]);
|
||||||
|
show_usage = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (show_usage)
|
if (show_usage)
|
||||||
{
|
{
|
||||||
tf_printf("\n%s run [options]\n\n", file);
|
tf_printf("\n%s run [options]\n\n", file);
|
||||||
|
@ -22,8 +22,6 @@ static int64_t s_tls_malloc_size;
|
|||||||
static int64_t s_js_malloc_size;
|
static int64_t s_js_malloc_size;
|
||||||
static int64_t s_sqlite_malloc_size;
|
static int64_t s_sqlite_malloc_size;
|
||||||
|
|
||||||
extern uint32_t fnv32a(const void* buffer, int length, uint32_t start);
|
|
||||||
|
|
||||||
static size_t _tf_mem_round_up(size_t size)
|
static size_t _tf_mem_round_up(size_t size)
|
||||||
{
|
{
|
||||||
return (size + 7) & ~7;
|
return (size + 7) & ~7;
|
||||||
@ -156,7 +154,7 @@ static void _tf_mem_summarize(void* ptr, size_t size, int frames_count, void* co
|
|||||||
{
|
{
|
||||||
summary_t* summary = user_data;
|
summary_t* summary = user_data;
|
||||||
tf_mem_allocation_t allocation = {
|
tf_mem_allocation_t allocation = {
|
||||||
.stack_hash = fnv32a(frames, sizeof(void*) * frames_count, 0),
|
.stack_hash = tf_util_fnv32a(frames, sizeof(void*) * frames_count, 0),
|
||||||
.count = 1,
|
.count = 1,
|
||||||
.size = size,
|
.size = size,
|
||||||
.frames_count = frames_count,
|
.frames_count = frames_count,
|
||||||
|
14
src/ssb.c
14
src/ssb.c
@ -579,6 +579,11 @@ static void _tf_ssb_connection_box_stream_send(tf_ssb_connection_t* connection,
|
|||||||
{
|
{
|
||||||
size_t send_size = size - offset > k_send_max ? k_send_max : size - offset;
|
size_t send_size = size - offset > k_send_max ? k_send_max : size - offset;
|
||||||
uint8_t* message_enc = tf_malloc(send_size + 34);
|
uint8_t* message_enc = tf_malloc(send_size + 34);
|
||||||
|
if (!message_enc)
|
||||||
|
{
|
||||||
|
tf_ssb_connection_close(connection, "out of memory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t nonce1[crypto_secretbox_NONCEBYTES];
|
uint8_t nonce1[crypto_secretbox_NONCEBYTES];
|
||||||
memcpy(nonce1, connection->send_nonce, sizeof(nonce1));
|
memcpy(nonce1, connection->send_nonce, sizeof(nonce1));
|
||||||
@ -3877,8 +3882,7 @@ JSValue tf_ssb_connection_get_object(tf_ssb_connection_t* connection)
|
|||||||
return connection ? connection->object : JS_UNDEFINED;
|
return connection ? connection->object : JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_ssb_add_message_added_callback(
|
void tf_ssb_add_message_added_callback(tf_ssb_t* ssb, tf_ssb_message_added_callback_t* callback, void (*cleanup)(tf_ssb_t* ssb, void* user_data), void* user_data)
|
||||||
tf_ssb_t* ssb, void (*callback)(tf_ssb_t* ssb, const char* id, void* user_data), void (*cleanup)(tf_ssb_t* ssb, void* user_data), void* user_data)
|
|
||||||
{
|
{
|
||||||
tf_ssb_message_added_callback_node_t* node = tf_malloc(sizeof(tf_ssb_message_added_callback_node_t));
|
tf_ssb_message_added_callback_node_t* node = tf_malloc(sizeof(tf_ssb_message_added_callback_node_t));
|
||||||
*node = (tf_ssb_message_added_callback_node_t) {
|
*node = (tf_ssb_message_added_callback_node_t) {
|
||||||
@ -3919,7 +3923,7 @@ void tf_ssb_notify_blob_stored(tf_ssb_t* ssb, const char* id)
|
|||||||
ssb->blobs_stored++;
|
ssb->blobs_stored++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* id, JSValue message_keys)
|
void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, JSValue message_keys)
|
||||||
{
|
{
|
||||||
tf_ssb_message_added_callback_node_t* next = NULL;
|
tf_ssb_message_added_callback_node_t* next = NULL;
|
||||||
ssb->messages_stored++;
|
ssb->messages_stored++;
|
||||||
@ -3928,7 +3932,7 @@ void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* id, JSValue message_
|
|||||||
next = node->next;
|
next = node->next;
|
||||||
tf_trace_begin(ssb->trace, "message added callback");
|
tf_trace_begin(ssb->trace, "message added callback");
|
||||||
PRE_CALLBACK(ssb, node->callback);
|
PRE_CALLBACK(ssb, node->callback);
|
||||||
node->callback(ssb, id, node->user_data);
|
node->callback(ssb, author, sequence, id, node->user_data);
|
||||||
POST_CALLBACK(ssb, node->callback);
|
POST_CALLBACK(ssb, node->callback);
|
||||||
tf_trace_end(ssb->trace);
|
tf_trace_end(ssb->trace);
|
||||||
}
|
}
|
||||||
@ -4576,7 +4580,7 @@ void tf_ssb_connection_adjust_read_backpressure(tf_ssb_connection_t* connection,
|
|||||||
void tf_ssb_connection_adjust_write_count(tf_ssb_connection_t* connection, int delta)
|
void tf_ssb_connection_adjust_write_count(tf_ssb_connection_t* connection, int delta)
|
||||||
{
|
{
|
||||||
connection->active_write_count += delta;
|
connection->active_write_count += delta;
|
||||||
if (!connection->is_closing)
|
if (!connection->is_closing && connection->active_write_count == 0)
|
||||||
{
|
{
|
||||||
uv_async_send(&connection->scheduled_async);
|
uv_async_send(&connection->scheduled_async);
|
||||||
}
|
}
|
||||||
|
402
src/ssb.db.c
402
src/ssb.db.c
@ -23,6 +23,17 @@ typedef struct _message_store_t message_store_t;
|
|||||||
|
|
||||||
static void _tf_ssb_db_store_message_after_work(tf_ssb_t* ssb, int status, void* user_data);
|
static void _tf_ssb_db_store_message_after_work(tf_ssb_t* ssb, int status, void* user_data);
|
||||||
|
|
||||||
|
static int _tf_ssb_db_try_exec(sqlite3* db, const char* statement)
|
||||||
|
{
|
||||||
|
char* error = NULL;
|
||||||
|
int result = sqlite3_exec(db, statement, NULL, NULL, &error);
|
||||||
|
if (result != SQLITE_OK)
|
||||||
|
{
|
||||||
|
tf_printf("Error running '%s': %s.\n", statement, error ? error : sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static void _tf_ssb_db_exec(sqlite3* db, const char* statement)
|
static void _tf_ssb_db_exec(sqlite3* db, const char* statement)
|
||||||
{
|
{
|
||||||
char* error = NULL;
|
char* error = NULL;
|
||||||
@ -60,11 +71,17 @@ static bool _tf_ssb_db_has_rows(sqlite3* db, const char* query)
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _tf_ssb_db_busy_handler(void* user_data, int count)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static void _tf_ssb_db_init_internal(sqlite3* db)
|
static void _tf_ssb_db_init_internal(sqlite3* db)
|
||||||
{
|
{
|
||||||
sqlite3_extended_result_codes(db, 1);
|
sqlite3_extended_result_codes(db, 1);
|
||||||
_tf_ssb_db_exec(db, "PRAGMA journal_mode = WAL");
|
_tf_ssb_db_exec(db, "PRAGMA journal_mode = WAL");
|
||||||
_tf_ssb_db_exec(db, "PRAGMA synchronous = NORMAL");
|
_tf_ssb_db_exec(db, "PRAGMA synchronous = NORMAL");
|
||||||
|
sqlite3_busy_handler(db, _tf_ssb_db_busy_handler, db);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_ssb_db_init_reader(sqlite3* db)
|
void tf_ssb_db_init_reader(sqlite3* db)
|
||||||
@ -108,21 +125,31 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
" flags INTEGER,"
|
" flags INTEGER,"
|
||||||
" UNIQUE(author, sequence)"
|
" UNIQUE(author, sequence)"
|
||||||
")");
|
")");
|
||||||
|
if (_tf_ssb_db_has_rows(db, "PRAGMA table_list('messages_stats')"))
|
||||||
|
{
|
||||||
|
if (_tf_ssb_db_has_rows(db, "SELECT 1 FROM messages_stats WHERE max_sequence IS NULL LIMIT 1"))
|
||||||
|
{
|
||||||
|
tf_printf("Rebuilding messages_stats.\n");
|
||||||
|
_tf_ssb_db_exec(db, "DROP TABLE messages_stats");
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!_tf_ssb_db_has_rows(db, "PRAGMA table_list('messages_stats')"))
|
if (!_tf_ssb_db_has_rows(db, "PRAGMA table_list('messages_stats')"))
|
||||||
{
|
{
|
||||||
|
_tf_ssb_db_exec(db, "BEGIN TRANSACTION");
|
||||||
_tf_ssb_db_exec(db,
|
_tf_ssb_db_exec(db,
|
||||||
"CREATE TABLE IF NOT EXISTS messages_stats ("
|
"CREATE TABLE IF NOT EXISTS messages_stats ("
|
||||||
" author TEXT PRIMARY KEY,"
|
" author TEXT PRIMARY KEY,"
|
||||||
" max_sequence INTEGER,"
|
" max_sequence INTEGER NOT NULL,"
|
||||||
" max_timestamp READ"
|
" max_timestamp REAL NOT NULL"
|
||||||
")");
|
")");
|
||||||
_tf_ssb_db_exec(
|
_tf_ssb_db_exec(
|
||||||
db, "INSERT OR REPLACE INTO messages_stats (author, max_sequence, max_timestamp) SELECT author, MAX(sequence), MAX(timestamp) FROM messages GROUP BY author");
|
db, "INSERT OR REPLACE INTO messages_stats (author, max_sequence, max_timestamp) SELECT author, MAX(sequence), MAX(timestamp) FROM messages GROUP BY author");
|
||||||
|
_tf_ssb_db_exec(db, "COMMIT TRANSACTION");
|
||||||
}
|
}
|
||||||
_tf_ssb_db_exec(db,
|
_tf_ssb_db_exec(db,
|
||||||
"CREATE TRIGGER IF NOT EXISTS messages_ai_stats AFTER INSERT ON messages BEGIN INSERT INTO messages_stats(author, max_sequence, max_timestamp) VALUES (new.author, "
|
"CREATE TRIGGER IF NOT EXISTS messages_ai_stats AFTER INSERT ON messages BEGIN INSERT INTO messages_stats(author, max_sequence, max_timestamp) VALUES (new.author, "
|
||||||
"new.sequence, new.timestamp) ON CONFLICT DO UPDATE SET max_sequence = MAX(max_sequence, excluded.max_sequence), max_timestamp = MAX(max_timestamp, "
|
"new.sequence, new.timestamp) ON CONFLICT DO UPDATE SET max_sequence = MAX(max_sequence, new.sequence), max_timestamp = MAX(max_timestamp, "
|
||||||
"excluded.max_timestamp); END");
|
"new.timestamp); END");
|
||||||
_tf_ssb_db_exec(db,
|
_tf_ssb_db_exec(db,
|
||||||
"CREATE TRIGGER IF NOT EXISTS messages_ad_stats AFTER DELETE ON messages BEGIN UPDATE messages_stats SET max_sequence = (SELECT MAX(messages.sequence) FROM messages WHERE "
|
"CREATE TRIGGER IF NOT EXISTS messages_ad_stats AFTER DELETE ON messages BEGIN UPDATE messages_stats SET max_sequence = (SELECT MAX(messages.sequence) FROM messages WHERE "
|
||||||
"messages.author = old.author), max_timestamp = (SELECT MAX(messages.timestamp) FROM messages WHERE messages.author = old.author); END");
|
"messages.author = old.author), max_timestamp = (SELECT MAX(messages.timestamp) FROM messages WHERE messages.author = old.author); END");
|
||||||
@ -228,6 +255,7 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
|
|
||||||
if (!_tf_ssb_db_has_rows(db, "PRAGMA table_list('messages_refs')"))
|
if (!_tf_ssb_db_has_rows(db, "PRAGMA table_list('messages_refs')"))
|
||||||
{
|
{
|
||||||
|
_tf_ssb_db_exec(db, "BEGIN TRANSACTION");
|
||||||
_tf_ssb_db_exec(db,
|
_tf_ssb_db_exec(db,
|
||||||
"CREATE TABLE IF NOT EXISTS messages_refs ("
|
"CREATE TABLE IF NOT EXISTS messages_refs ("
|
||||||
" message TEXT, "
|
" message TEXT, "
|
||||||
@ -242,6 +270,7 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
"j.value LIKE '%%%.sha256' OR "
|
"j.value LIKE '%%%.sha256' OR "
|
||||||
"j.value LIKE '@%.ed25519' "
|
"j.value LIKE '@%.ed25519' "
|
||||||
"ON CONFLICT DO NOTHING");
|
"ON CONFLICT DO NOTHING");
|
||||||
|
_tf_ssb_db_exec(db, "COMMIT TRANSACTION");
|
||||||
tf_printf("Done.\n");
|
tf_printf("Done.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,24 +288,94 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
|
|
||||||
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_refs_message_idx ON messages_refs (message)");
|
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_refs_message_idx ON messages_refs (message)");
|
||||||
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_refs_ref_idx ON messages_refs (ref)");
|
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_refs_ref_idx ON messages_refs (ref)");
|
||||||
|
|
||||||
|
if (!_tf_ssb_db_has_rows(db, "PRAGMA table_list('blobs_refs')"))
|
||||||
|
{
|
||||||
|
_tf_ssb_db_exec(db, "BEGIN TRANSACTION");
|
||||||
|
_tf_ssb_db_exec(db,
|
||||||
|
"CREATE TABLE IF NOT EXISTS blobs_refs ("
|
||||||
|
" blob TEXT, "
|
||||||
|
" ref TEXT, "
|
||||||
|
" UNIQUE(blob, ref)"
|
||||||
|
")");
|
||||||
|
tf_printf("Populating blobs_refs...\n");
|
||||||
|
_tf_ssb_db_exec(db,
|
||||||
|
"INSERT INTO blobs_refs(blob, ref) "
|
||||||
|
"SELECT blobs.id, j.value FROM blobs, json_tree(blobs.content) as j WHERE "
|
||||||
|
"json_valid(blobs.content) AND j.value LIKE '&%.sha256' "
|
||||||
|
"ON CONFLICT DO NOTHING");
|
||||||
|
_tf_ssb_db_exec(db, "COMMIT TRANSACTION");
|
||||||
|
tf_printf("Done.\n");
|
||||||
|
}
|
||||||
|
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS blobs_ai_refs");
|
||||||
|
_tf_ssb_db_exec(db,
|
||||||
|
"CREATE TRIGGER IF NOT EXISTS blobs_ai_refs AFTER INSERT ON blobs BEGIN "
|
||||||
|
"INSERT INTO blobs_refs(blob, ref) "
|
||||||
|
"SELECT DISTINCT new.id, j.value FROM json_tree(new.content) as j WHERE "
|
||||||
|
"json_valid(new.content) AND j.value LIKE '&%.sha256' "
|
||||||
|
"ON CONFLICT DO NOTHING; END");
|
||||||
|
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS blobs_ad_refs");
|
||||||
|
_tf_ssb_db_exec(db, "CREATE TRIGGER IF NOT EXISTS blobs_ad_refs AFTER DELETE ON blobs BEGIN DELETE FROM blobs_refs WHERE blobs_refs.blob = old.id; END");
|
||||||
|
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS blobs_refs_blob_idx ON blobs_refs (blob)");
|
||||||
|
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS blobs_refs_ref_idx ON blobs_refs (ref)");
|
||||||
|
|
||||||
_tf_ssb_db_exec(db, "DROP VIEW IF EXISTS blob_wants_view");
|
_tf_ssb_db_exec(db, "DROP VIEW IF EXISTS blob_wants_view");
|
||||||
_tf_ssb_db_exec(db,
|
_tf_ssb_db_exec(db,
|
||||||
"CREATE VIEW IF NOT EXISTS blob_wants_view (id, timestamp) AS "
|
"CREATE VIEW IF NOT EXISTS blob_wants_view (source, id, timestamp) AS "
|
||||||
" WITH wanted AS ( "
|
" WITH RECURSIVE "
|
||||||
" SELECT messages_refs.ref AS id, messages.timestamp AS timestamp "
|
" wanted1 AS ( "
|
||||||
|
" SELECT messages_refs.message AS source, messages_refs.ref AS id, messages.timestamp AS timestamp "
|
||||||
" FROM messages_refs "
|
" FROM messages_refs "
|
||||||
" JOIN messages ON messages.id = messages_refs.message "
|
" JOIN messages ON messages.id = messages_refs.message "
|
||||||
" UNION "
|
" UNION "
|
||||||
" SELECT messages_refs.ref AS id, unixepoch() * 1000 AS timestamp "
|
" SELECT messages_refs.message AS source, messages_refs.ref AS id, unixepoch() * 1000 AS timestamp "
|
||||||
" FROM messages_refs "
|
" FROM messages_refs "
|
||||||
" JOIN messages ON messages.id = messages_refs.message "
|
" JOIN messages ON messages.id = messages_refs.message "
|
||||||
" WHERE messages.content ->> 'type' = 'about' "
|
" WHERE messages.content ->> 'type' = 'about' "
|
||||||
|
" ), "
|
||||||
|
" wanted(source, id, timestamp) AS ( "
|
||||||
|
" SELECT wanted1.source AS source, wanted1.id AS id, wanted1.timestamp AS timestamp FROM wanted1 "
|
||||||
|
" UNION "
|
||||||
|
" SELECT wanted.source AS source, br.ref AS id, wanted.timestamp AS timestamp FROM wanted JOIN blobs_refs br ON br.blob = wanted.id "
|
||||||
" ) "
|
" ) "
|
||||||
" SELECT wanted.id, wanted.timestamp FROM wanted "
|
" SELECT wanted.source, wanted.id, wanted.timestamp FROM wanted "
|
||||||
" LEFT OUTER JOIN blobs ON wanted.id = blobs.id "
|
" LEFT OUTER JOIN blobs ON wanted.id = blobs.id "
|
||||||
" WHERE blobs.id IS NULL "
|
" WHERE blobs.id IS NULL "
|
||||||
" AND LENGTH(wanted.id) = 52 "
|
" AND LENGTH(wanted.id) = 52 "
|
||||||
" AND wanted.id LIKE '&%.sha256'");
|
" AND wanted.id LIKE '&%.sha256'");
|
||||||
|
if (!_tf_ssb_db_has_rows(db, "PRAGMA table_list('blob_wants_cache')"))
|
||||||
|
{
|
||||||
|
tf_printf("Populating blob_wants_cache...\n");
|
||||||
|
_tf_ssb_db_exec(db, "BEGIN TRANSACTION");
|
||||||
|
_tf_ssb_db_exec(db, "CREATE TABLE IF NOT EXISTS blob_wants_cache (source TEXT, id TEXT, timestamp REAL, UNIQUE(source, id))");
|
||||||
|
_tf_ssb_db_exec(
|
||||||
|
db, "INSERT INTO blob_wants_cache SELECT * FROM blob_wants_view WHERE true ON CONFLICT(source, id) DO UPDATE SET timestamp = MAX(timestamp, excluded.timestamp)");
|
||||||
|
_tf_ssb_db_exec(db, "COMMIT TRANSACTION");
|
||||||
|
tf_printf("Done.\n");
|
||||||
|
}
|
||||||
|
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS blob_wants_cache_id_idx ON blob_wants_cache (id)");
|
||||||
|
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS blob_wants_cache_timestamp_id_idx ON blob_wants_cache (timestamp, id)");
|
||||||
|
|
||||||
|
_tf_ssb_db_exec(db,
|
||||||
|
"CREATE TRIGGER IF NOT EXISTS messages_ai_blob_wants_cache AFTER INSERT ON messages_refs BEGIN "
|
||||||
|
"INSERT INTO blob_wants_cache (source, id, timestamp) "
|
||||||
|
"SELECT messages.id, new.ref, messages.timestamp FROM messages WHERE messages.id = new.message AND "
|
||||||
|
"LENGTH(new.ref) = 52 AND new.ref LIKE '&%.sha256' "
|
||||||
|
"ON CONFLICT (source, id) DO NOTHING; END");
|
||||||
|
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS blobs_refs_ai_blob_wants_cache");
|
||||||
|
_tf_ssb_db_exec(db,
|
||||||
|
"CREATE TRIGGER IF NOT EXISTS blobs_refs_ai_blob_wants_cache AFTER INSERT ON blobs_refs BEGIN "
|
||||||
|
"INSERT INTO blob_wants_cache (source, id, timestamp) "
|
||||||
|
"SELECT messages.id, new.ref, messages.timestamp FROM messages "
|
||||||
|
"JOIN blob_wants_cache bwc ON bwc.source = messages.id AND bwc.id = new.blob "
|
||||||
|
"ON CONFLICT (source, id) DO NOTHING; END");
|
||||||
|
_tf_ssb_db_exec(db,
|
||||||
|
"CREATE TRIGGER IF NOT EXISTS messages_ad_blob_wants_cache AFTER DELETE ON messages BEGIN "
|
||||||
|
"DELETE FROM blob_wants_cache WHERE blob_wants_cache.source = old.id; END");
|
||||||
|
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS blobs_ai_blob_wants_cache");
|
||||||
|
_tf_ssb_db_exec(db,
|
||||||
|
"CREATE TRIGGER IF NOT EXISTS blobs_ai_blob_wants_cache AFTER INSERT ON blobs BEGIN "
|
||||||
|
"DELETE FROM blob_wants_cache WHERE blob_wants_cache.id = new.id; END");
|
||||||
|
|
||||||
bool need_add_flags = true;
|
bool need_add_flags = true;
|
||||||
bool need_convert_timestamp_to_real = false;
|
bool need_convert_timestamp_to_real = false;
|
||||||
@ -344,10 +443,9 @@ static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author,
|
|||||||
return exists;
|
return exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t _tf_ssb_db_store_message_raw(tf_ssb_t* ssb, const char* id, const char* previous, const char* author, int64_t sequence, double timestamp, const char* content,
|
static int64_t _tf_ssb_db_store_message_raw(sqlite3* db, const char* id, const char* previous, const char* author, int64_t sequence, double timestamp, const char* content,
|
||||||
size_t content_len, const char* signature, int flags)
|
size_t content_len, const char* signature, int flags)
|
||||||
{
|
{
|
||||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
|
||||||
int64_t last_row_id = -1;
|
int64_t last_row_id = -1;
|
||||||
bool id_mismatch = false;
|
bool id_mismatch = false;
|
||||||
|
|
||||||
@ -396,7 +494,6 @@ static int64_t _tf_ssb_db_store_message_raw(tf_ssb_t* ssb, const char* id, const
|
|||||||
*/
|
*/
|
||||||
tf_printf("%p: Previous message doesn't exist for author=%s sequence=%" PRId64 " previous=%s.\n", db, author, sequence, previous);
|
tf_printf("%p: Previous message doesn't exist for author=%s sequence=%" PRId64 " previous=%s.\n", db, author, sequence, previous);
|
||||||
}
|
}
|
||||||
tf_ssb_release_db_writer(ssb, db);
|
|
||||||
return last_row_id;
|
return last_row_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,30 +566,47 @@ typedef struct _message_store_t
|
|||||||
static void _tf_ssb_db_store_message_work(tf_ssb_t* ssb, void* user_data)
|
static void _tf_ssb_db_store_message_work(tf_ssb_t* ssb, void* user_data)
|
||||||
{
|
{
|
||||||
message_store_t* store = user_data;
|
message_store_t* store = user_data;
|
||||||
int64_t last_row_id = _tf_ssb_db_store_message_raw(
|
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||||
ssb, store->id, *store->previous ? store->previous : NULL, store->author, store->sequence, store->timestamp, store->content, store->length, store->signature, store->flags);
|
bool in_transaction = _tf_ssb_db_try_exec(db, "BEGIN TRANSACTION") == SQLITE_OK;
|
||||||
if (last_row_id != -1)
|
|
||||||
|
while (store)
|
||||||
{
|
{
|
||||||
store->out_stored = true;
|
int64_t last_row_id = _tf_ssb_db_store_message_raw(db, store->id, *store->previous ? store->previous : NULL, store->author, store->sequence, store->timestamp,
|
||||||
store->out_blob_wants = _tf_ssb_db_get_message_blob_wants(ssb, last_row_id);
|
store->content, store->length, store->signature, store->flags);
|
||||||
|
if (last_row_id != -1)
|
||||||
|
{
|
||||||
|
store->out_stored = true;
|
||||||
|
store->out_blob_wants = _tf_ssb_db_get_message_blob_wants(ssb, last_row_id);
|
||||||
|
}
|
||||||
|
store = store->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (in_transaction)
|
||||||
|
{
|
||||||
|
if (_tf_ssb_db_try_exec(db, "COMMIT TRANSACTION") != SQLITE_OK)
|
||||||
|
{
|
||||||
|
store = user_data;
|
||||||
|
while (store)
|
||||||
|
{
|
||||||
|
store->out_stored = false;
|
||||||
|
store = store->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tf_ssb_release_db_writer(ssb, db);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _wake_up_queue(tf_ssb_t* ssb, tf_ssb_store_queue_t* queue)
|
static void _wake_up_queue(tf_ssb_t* ssb, tf_ssb_store_queue_t* queue)
|
||||||
{
|
{
|
||||||
if (!queue->running)
|
if (!queue->running)
|
||||||
{
|
{
|
||||||
message_store_t* next = queue->head;
|
message_store_t* stores = queue->head;
|
||||||
if (next)
|
queue->head = NULL;
|
||||||
|
queue->tail = NULL;
|
||||||
|
if (stores)
|
||||||
{
|
{
|
||||||
queue->head = next->next;
|
|
||||||
if (queue->tail == next)
|
|
||||||
{
|
|
||||||
queue->tail = NULL;
|
|
||||||
}
|
|
||||||
next->next = NULL;
|
|
||||||
queue->running = true;
|
queue->running = true;
|
||||||
tf_ssb_run_work(ssb, _tf_ssb_db_store_message_work, _tf_ssb_db_store_message_after_work, next);
|
tf_ssb_run_work(ssb, _tf_ssb_db_store_message_work, _tf_ssb_db_store_message_after_work, stores);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -501,43 +615,63 @@ static void _tf_ssb_db_store_message_after_work(tf_ssb_t* ssb, int status, void*
|
|||||||
{
|
{
|
||||||
message_store_t* store = user_data;
|
message_store_t* store = user_data;
|
||||||
tf_trace_t* trace = tf_ssb_get_trace(ssb);
|
tf_trace_t* trace = tf_ssb_get_trace(ssb);
|
||||||
if (store->out_stored)
|
|
||||||
|
message_store_t* last_stored = NULL;
|
||||||
|
|
||||||
|
while (store)
|
||||||
|
{
|
||||||
|
if (store->out_stored)
|
||||||
|
{
|
||||||
|
last_stored = store;
|
||||||
|
}
|
||||||
|
if (store->out_blob_wants)
|
||||||
|
{
|
||||||
|
tf_trace_begin(trace, "notify_blob_wants_added");
|
||||||
|
for (char* p = store->out_blob_wants; *p; p = p + strlen(p))
|
||||||
|
{
|
||||||
|
tf_ssb_notify_blob_want_added(ssb, p);
|
||||||
|
}
|
||||||
|
tf_free(store->out_blob_wants);
|
||||||
|
tf_trace_end(trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (store->callback)
|
||||||
|
{
|
||||||
|
store->callback(store->id, store->out_stored, store->user_data);
|
||||||
|
}
|
||||||
|
store = store->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_stored)
|
||||||
{
|
{
|
||||||
tf_trace_begin(trace, "notify_message_added");
|
tf_trace_begin(trace, "notify_message_added");
|
||||||
JSContext* context = tf_ssb_get_context(ssb);
|
JSContext* context = tf_ssb_get_context(ssb);
|
||||||
JSValue formatted =
|
JSValue formatted = tf_ssb_format_message(context, last_stored->previous, last_stored->author, last_stored->sequence, last_stored->timestamp, "sha256",
|
||||||
tf_ssb_format_message(context, store->previous, store->author, store->sequence, store->timestamp, "sha256", store->content, store->signature, store->flags);
|
last_stored->content, last_stored->signature, last_stored->flags);
|
||||||
JSValue message = JS_NewObject(context);
|
JSValue message = JS_NewObject(context);
|
||||||
JS_SetPropertyStr(context, message, "key", JS_NewString(context, store->id));
|
JS_SetPropertyStr(context, message, "key", JS_NewString(context, last_stored->id));
|
||||||
JS_SetPropertyStr(context, message, "value", formatted);
|
JS_SetPropertyStr(context, message, "value", formatted);
|
||||||
char timestamp_string[256];
|
char timestamp_string[256];
|
||||||
snprintf(timestamp_string, sizeof(timestamp_string), "%f", store->timestamp);
|
snprintf(timestamp_string, sizeof(timestamp_string), "%f", last_stored->timestamp);
|
||||||
JS_SetPropertyStr(context, message, "timestamp", JS_NewString(context, timestamp_string));
|
JS_SetPropertyStr(context, message, "timestamp", JS_NewString(context, timestamp_string));
|
||||||
tf_ssb_notify_message_added(ssb, store->id, message);
|
tf_ssb_notify_message_added(ssb, last_stored->author, last_stored->sequence, last_stored->id, message);
|
||||||
JS_FreeValue(context, message);
|
JS_FreeValue(context, message);
|
||||||
tf_trace_end(trace);
|
tf_trace_end(trace);
|
||||||
}
|
}
|
||||||
if (store->out_blob_wants)
|
|
||||||
{
|
|
||||||
tf_trace_begin(trace, "notify_blob_wants_added");
|
|
||||||
for (char* p = store->out_blob_wants; *p; p = p + strlen(p))
|
|
||||||
{
|
|
||||||
tf_ssb_notify_blob_want_added(ssb, p);
|
|
||||||
}
|
|
||||||
tf_free(store->out_blob_wants);
|
|
||||||
tf_trace_end(trace);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSContext* context = tf_ssb_get_context(ssb);
|
JSContext* context = tf_ssb_get_context(ssb);
|
||||||
if (store->callback)
|
store = user_data;
|
||||||
|
while (store)
|
||||||
{
|
{
|
||||||
store->callback(store->id, store->out_stored, store->user_data);
|
JS_FreeCString(context, store->content);
|
||||||
|
message_store_t* last = store;
|
||||||
|
store = store->next;
|
||||||
|
tf_free(last);
|
||||||
}
|
}
|
||||||
JS_FreeCString(context, store->content);
|
|
||||||
tf_ssb_store_queue_t* queue = tf_ssb_get_store_queue(ssb);
|
tf_ssb_store_queue_t* queue = tf_ssb_get_store_queue(ssb);
|
||||||
queue->running = false;
|
queue->running = false;
|
||||||
_wake_up_queue(ssb, queue);
|
_wake_up_queue(ssb, queue);
|
||||||
tf_free(store);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_ssb_db_store_message(
|
void tf_ssb_db_store_message(
|
||||||
@ -880,27 +1014,52 @@ bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, i
|
|||||||
bool found = false;
|
bool found = false;
|
||||||
sqlite3_stmt* statement;
|
sqlite3_stmt* statement;
|
||||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||||
const char* query = "SELECT id, sequence FROM messages WHERE author = ?1 AND sequence = (SELECT MAX(sequence) FROM messages WHERE author = ?1)";
|
|
||||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
if (out_message_id)
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
const char* query = "SELECT id, sequence FROM messages WHERE author = ?1 ORDER BY sequence DESC LIMIT 1";
|
||||||
|
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
if (out_sequence)
|
if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
||||||
{
|
{
|
||||||
*out_sequence = sqlite3_column_int64(statement, 1);
|
if (out_sequence)
|
||||||
|
{
|
||||||
|
*out_sequence = sqlite3_column_int64(statement, 1);
|
||||||
|
}
|
||||||
|
if (out_message_id)
|
||||||
|
{
|
||||||
|
strncpy(out_message_id, (const char*)sqlite3_column_text(statement, 0), out_message_id_size - 1);
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
}
|
}
|
||||||
if (out_message_id)
|
sqlite3_finalize(statement);
|
||||||
{
|
}
|
||||||
strncpy(out_message_id, (const char*)sqlite3_column_text(statement, 0), out_message_id_size - 1);
|
else
|
||||||
}
|
{
|
||||||
found = true;
|
tf_printf("%s: prepare failed: %s\n", __FUNCTION__, sqlite3_errmsg(db));
|
||||||
}
|
}
|
||||||
sqlite3_finalize(statement);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tf_printf("%s: prepare failed: %s\n", __FUNCTION__, sqlite3_errmsg(db));
|
const char* query = "SELECT max_sequence FROM messages_stats WHERE author = ?1";
|
||||||
|
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
if (out_sequence)
|
||||||
|
{
|
||||||
|
*out_sequence = sqlite3_column_int64(statement, 0);
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tf_printf("%s: prepare failed: %s\n", __FUNCTION__, sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tf_ssb_release_db_reader(ssb, db);
|
tf_ssb_release_db_reader(ssb, db);
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
@ -1004,8 +1163,8 @@ int tf_ssb_sqlite_authorizer(void* user_data, int action_code, const char* arg0,
|
|||||||
result = SQLITE_OK;
|
result = SQLITE_OK;
|
||||||
break;
|
break;
|
||||||
case SQLITE_READ:
|
case SQLITE_READ:
|
||||||
result = (strcmp(arg0, "blob_wants_view") == 0 || strcmp(arg0, "json_each") == 0 || strcmp(arg0, "json_tree") == 0 || strcmp(arg0, "messages") == 0 ||
|
result = (strcmp(arg0, "blob_wants_cache") == 0 || strcmp(arg0, "blob_wants_view") == 0 || strcmp(arg0, "json_each") == 0 || strcmp(arg0, "json_tree") == 0 ||
|
||||||
strcmp(arg0, "messages_stats") == 0 || strcmp(arg0, "messages_fts") == 0 || strcmp(arg0, "messages_fts_idx") == 0 ||
|
strcmp(arg0, "messages") == 0 || strcmp(arg0, "messages_stats") == 0 || strcmp(arg0, "messages_fts") == 0 || strcmp(arg0, "messages_fts_idx") == 0 ||
|
||||||
strcmp(arg0, "messages_fts_config") == 0 || strcmp(arg0, "messages_refs") == 0 || strcmp(arg0, "messages_refs_message_idx") == 0 ||
|
strcmp(arg0, "messages_fts_config") == 0 || strcmp(arg0, "messages_refs") == 0 || strcmp(arg0, "messages_refs_message_idx") == 0 ||
|
||||||
strcmp(arg0, "messages_refs_ref_idx") == 0 || strcmp(arg0, "sqlite_master") == 0 || false)
|
strcmp(arg0, "messages_refs_ref_idx") == 0 || strcmp(arg0, "sqlite_master") == 0 || false)
|
||||||
? SQLITE_OK
|
? SQLITE_OK
|
||||||
@ -1367,9 +1526,9 @@ static void _populate_follows_and_blocks(tf_ssb_t* ssb, following_t* entry, foll
|
|||||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||||
sqlite3_stmt* statement = NULL;
|
sqlite3_stmt* statement = NULL;
|
||||||
if (sqlite3_prepare(db,
|
if (sqlite3_prepare(db,
|
||||||
"SELECT json_extract(content, '$.contact') AS contact, json_extract(content, '$.following'), json_extract(content, '$.blocking') "
|
"SELECT content ->> '$.contact' AS contact, content ->> '$.following', content ->> '$.blocking' "
|
||||||
"FROM messages "
|
"FROM messages "
|
||||||
"WHERE contact IS NOT NULL AND author = ? AND json_extract(content, '$.type') = 'contact' "
|
"WHERE contact IS NOT NULL AND author = ? AND content ->> '$.type' = 'contact' "
|
||||||
"ORDER BY sequence",
|
"ORDER BY sequence",
|
||||||
-1, &statement, NULL) == SQLITE_OK)
|
-1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
@ -2205,7 +2364,7 @@ const char* tf_ssb_db_get_profile_name(sqlite3* db, const char* id)
|
|||||||
if (sqlite3_prepare(db,
|
if (sqlite3_prepare(db,
|
||||||
"SELECT name FROM (SELECT messages.author, RANK() OVER (PARTITION BY messages.author ORDER BY messages.sequence DESC) AS author_rank, "
|
"SELECT name FROM (SELECT messages.author, RANK() OVER (PARTITION BY messages.author ORDER BY messages.sequence DESC) AS author_rank, "
|
||||||
"messages.content ->> 'name' AS name FROM messages WHERE messages.author = ? "
|
"messages.content ->> 'name' AS name FROM messages WHERE messages.author = ? "
|
||||||
"AND json_extract(messages.content, '$.type') = 'about' AND content ->> 'about' = messages.author AND name IS NOT NULL) "
|
"AND 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) == SQLITE_OK)
|
-1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
@ -2331,3 +2490,120 @@ bool tf_ssb_db_has_invite(sqlite3* db, const char* id)
|
|||||||
}
|
}
|
||||||
return has;
|
return has;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_db_get_identity_info_visit(const char* identity, void* user_data)
|
||||||
|
{
|
||||||
|
tf_ssb_identity_info_t* info = user_data;
|
||||||
|
info->identity = tf_resize_vec(info->identity, (info->count + 1) * sizeof(char*));
|
||||||
|
info->name = tf_resize_vec(info->name, (info->count + 1) * sizeof(char*));
|
||||||
|
char buffer[k_id_base64_len];
|
||||||
|
snprintf(buffer, sizeof(buffer), "@%s", identity);
|
||||||
|
info->identity[info->count] = tf_strdup(buffer);
|
||||||
|
info->name[info->count] = NULL;
|
||||||
|
info->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
tf_ssb_identity_info_t* tf_ssb_db_get_identity_info(tf_ssb_t* ssb, const char* user, const char* package_owner, const char* package_name)
|
||||||
|
{
|
||||||
|
tf_ssb_identity_info_t* info = tf_malloc(sizeof(tf_ssb_identity_info_t));
|
||||||
|
*info = (tf_ssb_identity_info_t) { 0 };
|
||||||
|
|
||||||
|
char id[k_id_base64_len] = "";
|
||||||
|
if (tf_ssb_db_user_has_permission(ssb, NULL, user, "administration"))
|
||||||
|
{
|
||||||
|
if (tf_ssb_whoami(ssb, id, sizeof(id)))
|
||||||
|
{
|
||||||
|
_tf_ssb_db_get_identity_info_visit(*id == '@' ? id + 1 : id, info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tf_ssb_db_identity_visit(ssb, user, _tf_ssb_db_get_identity_info_visit, info);
|
||||||
|
|
||||||
|
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||||
|
sqlite3_stmt* statement = NULL;
|
||||||
|
int result = sqlite3_prepare(db,
|
||||||
|
"SELECT author, name FROM ( "
|
||||||
|
" SELECT "
|
||||||
|
" messages.author, "
|
||||||
|
" RANK() OVER (PARTITION BY messages.author ORDER BY messages.sequence DESC) AS author_rank, "
|
||||||
|
" messages.content ->> 'name' AS name "
|
||||||
|
" FROM messages "
|
||||||
|
" JOIN identities ON messages.author = ('@' || identities.public_key) "
|
||||||
|
" WHERE "
|
||||||
|
" (identities.user = ? OR identities.public_key = ?) AND "
|
||||||
|
" messages.content ->> '$.type' = 'about' AND "
|
||||||
|
" content ->> 'about' = messages.author AND name IS NOT NULL) "
|
||||||
|
"WHERE author_rank = 1 ",
|
||||||
|
-1, &statement, NULL);
|
||||||
|
if (result == SQLITE_OK)
|
||||||
|
{
|
||||||
|
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, *id == '@' ? id + 1 : id, -1, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
int r = SQLITE_OK;
|
||||||
|
while ((r = sqlite3_step(statement)) == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
const char* identity = (const char*)sqlite3_column_text(statement, 0);
|
||||||
|
const char* name = (const char*)sqlite3_column_text(statement, 1);
|
||||||
|
for (int i = 0; i < info->count; i++)
|
||||||
|
{
|
||||||
|
if (!info->name[i] && strcmp(info->identity[i], identity) == 0)
|
||||||
|
{
|
||||||
|
info->name[i] = tf_strdup(name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tf_printf("prepare failed: %s.\n", sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
|
||||||
|
tf_ssb_db_identity_get_active(db, user, package_owner, package_name, info->active_identity, sizeof(info->active_identity));
|
||||||
|
if (!*info->active_identity && info->count)
|
||||||
|
{
|
||||||
|
snprintf(info->active_identity, sizeof(info->active_identity), "%s", info->identity[0]);
|
||||||
|
}
|
||||||
|
tf_ssb_release_db_reader(ssb, db);
|
||||||
|
|
||||||
|
size_t size = sizeof(tf_ssb_identity_info_t) + sizeof(char*) * info->count * 2;
|
||||||
|
for (int i = 0; i < info->count; i++)
|
||||||
|
{
|
||||||
|
size += strlen(info->identity[i]) + 1;
|
||||||
|
size += info->name[i] ? strlen(info->name[i]) + 1 : 0;
|
||||||
|
}
|
||||||
|
tf_ssb_identity_info_t* copy = tf_malloc(size);
|
||||||
|
*copy = *info;
|
||||||
|
|
||||||
|
copy->identity = (const char**)(copy + 1);
|
||||||
|
copy->name = (const char**)(copy + 1) + copy->count;
|
||||||
|
|
||||||
|
char* p = (char*)((const char**)(copy + 1) + copy->count * 2);
|
||||||
|
|
||||||
|
for (int i = 0; i < info->count; i++)
|
||||||
|
{
|
||||||
|
size_t length = strlen(info->identity[i]);
|
||||||
|
memcpy(p, info->identity[i], length + 1);
|
||||||
|
copy->identity[i] = p;
|
||||||
|
p += length + 1;
|
||||||
|
tf_free((void*)info->identity[i]);
|
||||||
|
|
||||||
|
if (info->name[i])
|
||||||
|
{
|
||||||
|
length = strlen(info->name[i]);
|
||||||
|
memcpy(p, info->name[i], length + 1);
|
||||||
|
copy->name[i] = p;
|
||||||
|
p += length + 1;
|
||||||
|
tf_free((void*)info->name[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
copy->name[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tf_free(info->name);
|
||||||
|
tf_free(info->identity);
|
||||||
|
tf_free(info);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
25
src/ssb.db.h
25
src/ssb.db.h
@ -564,4 +564,29 @@ int tf_ssb_sqlite_authorizer(void* user_data, int action_code, const char* arg0,
|
|||||||
*/
|
*/
|
||||||
bool tf_ssb_db_has_invite(sqlite3* db, const char* id);
|
bool tf_ssb_db_has_invite(sqlite3* db, const char* id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Identity information
|
||||||
|
*/
|
||||||
|
typedef struct _tf_ssb_identity_info_t
|
||||||
|
{
|
||||||
|
/** An array of identities. */
|
||||||
|
const char** identity;
|
||||||
|
/** A array of identities, one for each identity. */
|
||||||
|
const char** name;
|
||||||
|
/** The number of elements in both the identity and name arrays. */
|
||||||
|
int count;
|
||||||
|
/** The active identity. */
|
||||||
|
char active_identity[k_id_base64_len];
|
||||||
|
} tf_ssb_identity_info_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Get available identities, names, and the active identity for a user.
|
||||||
|
** @param ssb The SSB instance.
|
||||||
|
** @param user The user name.
|
||||||
|
** @param package_owner The owner of the package for which identity info is being fetched.
|
||||||
|
** @param package_name The name of the package for which identity info is being fetched.
|
||||||
|
** @return A struct of identities, names, and the active identity. Free with tf_free().
|
||||||
|
*/
|
||||||
|
tf_ssb_identity_info_t* tf_ssb_db_get_identity_info(tf_ssb_t* ssb, const char* user, const char* package_owner, const char* package_name);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
170
src/ssb.ebt.c
170
src/ssb.ebt.c
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "uv.h"
|
#include "uv.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
typedef struct _ebt_entry_t
|
typedef struct _ebt_entry_t
|
||||||
@ -29,6 +30,9 @@ typedef struct _tf_ssb_ebt_t
|
|||||||
int entries_count;
|
int entries_count;
|
||||||
|
|
||||||
int send_clock_pending;
|
int send_clock_pending;
|
||||||
|
|
||||||
|
int max_in;
|
||||||
|
int max_out;
|
||||||
} tf_ssb_ebt_t;
|
} tf_ssb_ebt_t;
|
||||||
|
|
||||||
tf_ssb_ebt_t* tf_ssb_ebt_create(tf_ssb_connection_t* connection)
|
tf_ssb_ebt_t* tf_ssb_ebt_create(tf_ssb_connection_t* connection)
|
||||||
@ -55,8 +59,33 @@ static int _ebt_entry_compare(const void* a, const void* b)
|
|||||||
return strcmp(id, entry->id);
|
return strcmp(id, entry->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _ebt_count_messages(tf_ssb_ebt_t* ebt, int* in, int* out)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < ebt->entries_count; i++)
|
||||||
|
{
|
||||||
|
ebt_entry_t* entry = &ebt->entries[i];
|
||||||
|
if (entry->in >= 0 && entry->out >= 0)
|
||||||
|
{
|
||||||
|
if (entry->out_receive && entry->in > entry->out)
|
||||||
|
{
|
||||||
|
*in += entry->in - entry->out;
|
||||||
|
}
|
||||||
|
else if (entry->in_receive && entry->out > entry->in)
|
||||||
|
{
|
||||||
|
*out += entry->out - entry->in;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static ebt_entry_t* _ebt_get_entry(tf_ssb_ebt_t* ebt, const char* id)
|
static ebt_entry_t* _ebt_get_entry(tf_ssb_ebt_t* ebt, const char* id)
|
||||||
{
|
{
|
||||||
|
uint8_t bin[k_id_bin_len];
|
||||||
|
if (!tf_ssb_id_str_to_bin(bin, id))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int index = tf_util_insert_index(id, ebt->entries, ebt->entries_count, sizeof(ebt_entry_t), _ebt_entry_compare);
|
int index = tf_util_insert_index(id, ebt->entries, ebt->entries_count, sizeof(ebt_entry_t), _ebt_entry_compare);
|
||||||
if (index < ebt->entries_count && strcmp(id, ebt->entries[index].id) == 0)
|
if (index < ebt->entries_count && strcmp(id, ebt->entries[index].id) == 0)
|
||||||
{
|
{
|
||||||
@ -104,27 +133,37 @@ void tf_ssb_ebt_receive_clock(tf_ssb_ebt_t* ebt, JSContext* context, JSValue clo
|
|||||||
JS_ToInt64(context, &sequence, in_clock);
|
JS_ToInt64(context, &sequence, in_clock);
|
||||||
|
|
||||||
ebt_entry_t* entry = _ebt_get_entry(ebt, author);
|
ebt_entry_t* entry = _ebt_get_entry(ebt, author);
|
||||||
if (sequence < 0)
|
if (entry)
|
||||||
{
|
{
|
||||||
entry->in = -1;
|
if (sequence < 0)
|
||||||
entry->in_replicate = false;
|
{
|
||||||
entry->in_receive = false;
|
entry->in = tf_max(entry->in, -1);
|
||||||
}
|
entry->in_replicate = false;
|
||||||
else
|
entry->in_receive = false;
|
||||||
{
|
}
|
||||||
entry->in = sequence >> 1;
|
else
|
||||||
entry->in_replicate = true;
|
{
|
||||||
entry->in_receive = (sequence & 1) == 0;
|
entry->in = tf_max(entry->in, sequence >> 1);
|
||||||
}
|
entry->in_replicate = true;
|
||||||
if (!entry->in_receive)
|
entry->in_receive = (sequence & 1) == 0;
|
||||||
{
|
}
|
||||||
tf_ssb_connection_remove_new_message_request(ebt->connection, author);
|
if (!entry->in_receive)
|
||||||
|
{
|
||||||
|
tf_ssb_connection_remove_new_message_request(ebt->connection, author);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
JS_FreeCString(context, author);
|
JS_FreeCString(context, author);
|
||||||
JS_FreeValue(context, key);
|
JS_FreeValue(context, key);
|
||||||
}
|
}
|
||||||
JS_FreeValue(context, in_clock);
|
JS_FreeValue(context, in_clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int in = 0;
|
||||||
|
int out = 0;
|
||||||
|
_ebt_count_messages(ebt, &in, &out);
|
||||||
|
ebt->max_in = tf_max(in, ebt->max_in);
|
||||||
|
ebt->max_out = tf_max(out, ebt->max_out);
|
||||||
|
|
||||||
uv_mutex_unlock(&ebt->mutex);
|
uv_mutex_unlock(&ebt->mutex);
|
||||||
for (uint32_t i = 0; i < plen; ++i)
|
for (uint32_t i = 0; i < plen; ++i)
|
||||||
{
|
{
|
||||||
@ -154,29 +193,33 @@ static void _ebt_add_to_clock(ebt_get_clock_t* work, const char* id, int64_t val
|
|||||||
{
|
{
|
||||||
int count = work->clock ? work->clock->count : 0;
|
int count = work->clock ? work->clock->count : 0;
|
||||||
ebt_entry_t* entry = _ebt_get_entry(work->ebt, id);
|
ebt_entry_t* entry = _ebt_get_entry(work->ebt, id);
|
||||||
if ((replicate && !entry->out_replicate) || (receive && !entry->out_receive) || ((replicate || receive || entry->out_replicate || entry->out_receive) && entry->out != value))
|
if (entry)
|
||||||
{
|
{
|
||||||
entry->out = value;
|
if ((replicate && !entry->out_replicate) || (receive && !entry->out_receive) ||
|
||||||
|
((replicate || receive || entry->out_replicate || entry->out_receive) && entry->out != value))
|
||||||
|
{
|
||||||
|
int index = tf_util_insert_index(id, count ? work->clock->entries : NULL, count, sizeof(tf_ssb_ebt_clock_entry_t), _ebt_compare_entry);
|
||||||
|
int64_t out_value = (entry->out_replicate || replicate) ? ((tf_max(entry->out, value) << 1) | ((entry->out_receive || receive) ? 0 : 1)) : -1;
|
||||||
|
if (index < count && strcmp(id, work->clock->entries[index].id) == 0)
|
||||||
|
{
|
||||||
|
work->clock->entries[index].value = out_value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
work->clock = tf_resize_vec(work->clock, sizeof(tf_ssb_ebt_clock_t) + (count + 1) * sizeof(tf_ssb_ebt_clock_entry_t));
|
||||||
|
if (index < count)
|
||||||
|
{
|
||||||
|
memmove(work->clock->entries + index + 1, work->clock->entries + index, (count - index) * sizeof(tf_ssb_ebt_clock_entry_t));
|
||||||
|
}
|
||||||
|
work->clock->entries[index] = (tf_ssb_ebt_clock_entry_t) { .value = out_value };
|
||||||
|
snprintf(work->clock->entries[index].id, sizeof(work->clock->entries[index].id), "%s", id);
|
||||||
|
work->clock->count = count + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->out = tf_max(entry->out, value);
|
||||||
entry->out_replicate = entry->out_replicate || replicate;
|
entry->out_replicate = entry->out_replicate || replicate;
|
||||||
entry->out_receive = entry->out_receive || receive;
|
entry->out_receive = entry->out_receive || receive;
|
||||||
|
|
||||||
int index = tf_util_insert_index(id, count ? work->clock->entries : NULL, count, sizeof(tf_ssb_ebt_clock_entry_t), _ebt_compare_entry);
|
|
||||||
int64_t out_value = entry->out_replicate ? ((value << 1) | (entry->out_receive ? 0 : 1)) : -1;
|
|
||||||
if (index < count && strcmp(id, work->clock->entries[index].id) == 0)
|
|
||||||
{
|
|
||||||
work->clock->entries[index].value = out_value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
work->clock = tf_resize_vec(work->clock, sizeof(tf_ssb_ebt_clock_t) + (count + 1) * sizeof(tf_ssb_ebt_clock_entry_t));
|
|
||||||
if (index < count)
|
|
||||||
{
|
|
||||||
memmove(work->clock->entries + index + 1, work->clock->entries + index, (count - index) * sizeof(tf_ssb_ebt_clock_entry_t));
|
|
||||||
}
|
|
||||||
work->clock->entries[index] = (tf_ssb_ebt_clock_entry_t) { .value = out_value };
|
|
||||||
snprintf(work->clock->entries[index].id, sizeof(work->clock->entries[index].id), "%s", id);
|
|
||||||
work->clock->count = count + 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,6 +300,14 @@ static void _tf_ssb_ebt_get_send_clock_work(tf_ssb_connection_t* connection, voi
|
|||||||
uv_mutex_unlock(&work->ebt->mutex);
|
uv_mutex_unlock(&work->ebt->mutex);
|
||||||
tf_free(requested);
|
tf_free(requested);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uv_mutex_lock(&work->ebt->mutex);
|
||||||
|
int in = 0;
|
||||||
|
int out = 0;
|
||||||
|
_ebt_count_messages(work->ebt, &in, &out);
|
||||||
|
work->ebt->max_in = tf_max(in, work->ebt->max_in);
|
||||||
|
work->ebt->max_out = tf_max(out, work->ebt->max_out);
|
||||||
|
uv_mutex_unlock(&work->ebt->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _tf_ssb_ebt_get_send_clock_after_work(tf_ssb_connection_t* connection, int status, void* user_data)
|
static void _tf_ssb_ebt_get_send_clock_after_work(tf_ssb_connection_t* connection, int status, void* user_data)
|
||||||
@ -303,10 +354,24 @@ void tf_ssb_ebt_set_messages_sent(tf_ssb_ebt_t* ebt, const char* id, int64_t seq
|
|||||||
{
|
{
|
||||||
uv_mutex_lock(&ebt->mutex);
|
uv_mutex_lock(&ebt->mutex);
|
||||||
ebt_entry_t* entry = _ebt_get_entry(ebt, id);
|
ebt_entry_t* entry = _ebt_get_entry(ebt, id);
|
||||||
entry->in = tf_max(entry->in, sequence);
|
if (entry)
|
||||||
if (entry->in == entry->out && (tf_ssb_connection_get_flags(ebt->connection) & k_tf_ssb_connect_flag_one_shot) == 0)
|
|
||||||
{
|
{
|
||||||
tf_ssb_connection_add_new_message_request(ebt->connection, id, tf_ssb_connection_get_ebt_request_number(ebt->connection), false);
|
entry->in = tf_max(entry->in, sequence);
|
||||||
|
if (entry->in == entry->out && (tf_ssb_connection_get_flags(ebt->connection) & k_tf_ssb_connect_flag_one_shot) == 0)
|
||||||
|
{
|
||||||
|
tf_ssb_connection_add_new_message_request(ebt->connection, id, tf_ssb_connection_get_ebt_request_number(ebt->connection), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uv_mutex_unlock(&ebt->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tf_ssb_ebt_set_messages_received(tf_ssb_ebt_t* ebt, const char* id, int64_t sequence)
|
||||||
|
{
|
||||||
|
uv_mutex_lock(&ebt->mutex);
|
||||||
|
ebt_entry_t* entry = _ebt_get_entry(ebt, id);
|
||||||
|
if (entry)
|
||||||
|
{
|
||||||
|
entry->out = tf_max(entry->out, sequence);
|
||||||
}
|
}
|
||||||
uv_mutex_unlock(&ebt->mutex);
|
uv_mutex_unlock(&ebt->mutex);
|
||||||
}
|
}
|
||||||
@ -320,3 +385,34 @@ void tf_ssb_ebt_set_send_clock_pending(tf_ssb_ebt_t* ebt, int pending)
|
|||||||
{
|
{
|
||||||
ebt->send_clock_pending = pending;
|
ebt->send_clock_pending = pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tf_ssb_ebt_debug_clock(tf_ssb_ebt_t* ebt, JSContext* context, JSValue debug)
|
||||||
|
{
|
||||||
|
uv_mutex_lock(&ebt->mutex);
|
||||||
|
for (int i = 0; i < ebt->entries_count; i++)
|
||||||
|
{
|
||||||
|
ebt_entry_t* entry = &ebt->entries[i];
|
||||||
|
JSValue clock = JS_NewObject(context);
|
||||||
|
JSValue out = JS_NewObject(context);
|
||||||
|
JSValue in = JS_NewObject(context);
|
||||||
|
JS_SetPropertyStr(context, out, "value", JS_NewInt64(context, entry->out));
|
||||||
|
JS_SetPropertyStr(context, out, "replicate", JS_NewBool(context, entry->out_replicate));
|
||||||
|
JS_SetPropertyStr(context, out, "receive", JS_NewBool(context, entry->out_receive));
|
||||||
|
JS_SetPropertyStr(context, clock, "out", out);
|
||||||
|
JS_SetPropertyStr(context, in, "value", JS_NewInt64(context, entry->in));
|
||||||
|
JS_SetPropertyStr(context, in, "replicate", JS_NewBool(context, entry->in_replicate));
|
||||||
|
JS_SetPropertyStr(context, in, "receive", JS_NewBool(context, entry->in_receive));
|
||||||
|
JS_SetPropertyStr(context, clock, "in", in);
|
||||||
|
JS_SetPropertyStr(context, debug, entry->id, clock);
|
||||||
|
}
|
||||||
|
uv_mutex_unlock(&ebt->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tf_ssb_ebt_get_progress(tf_ssb_ebt_t* ebt, int* in_pending, int* in_total, int* out_pending, int* out_total)
|
||||||
|
{
|
||||||
|
uv_mutex_lock(&ebt->mutex);
|
||||||
|
_ebt_count_messages(ebt, in_pending, out_pending);
|
||||||
|
*in_total = tf_max(*in_pending, ebt->max_in);
|
||||||
|
*out_total = tf_max(*out_pending, ebt->max_out);
|
||||||
|
uv_mutex_unlock(&ebt->mutex);
|
||||||
|
}
|
||||||
|
@ -78,6 +78,14 @@ tf_ssb_ebt_clock_t* tf_ssb_ebt_get_messages_to_send(tf_ssb_ebt_t* ebt);
|
|||||||
*/
|
*/
|
||||||
void tf_ssb_ebt_set_messages_sent(tf_ssb_ebt_t* ebt, const char* id, int64_t sequence);
|
void tf_ssb_ebt_set_messages_sent(tf_ssb_ebt_t* ebt, const char* id, int64_t sequence);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Update the clock state indicating the messages that have been received for an account.
|
||||||
|
** @param ebt The EBT instance.
|
||||||
|
** @param id The identity to update.
|
||||||
|
** @param sequence The maximum sequence number received.
|
||||||
|
*/
|
||||||
|
void tf_ssb_ebt_set_messages_received(tf_ssb_ebt_t* ebt, const char* id, int64_t sequence);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Destroy an EBT instance.
|
** Destroy an EBT instance.
|
||||||
** @param ebt The EBT instance.
|
** @param ebt The EBT instance.
|
||||||
@ -97,3 +105,22 @@ int tf_ssb_ebt_get_send_clock_pending(tf_ssb_ebt_t* ebt);
|
|||||||
** @param pending A value representing the pending status.
|
** @param pending A value representing the pending status.
|
||||||
*/
|
*/
|
||||||
void tf_ssb_ebt_set_send_clock_pending(tf_ssb_ebt_t* ebt, int pending);
|
void tf_ssb_ebt_set_send_clock_pending(tf_ssb_ebt_t* ebt, int pending);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Get a JSON representation of the clock state for
|
||||||
|
** debugging.
|
||||||
|
** @param ebt The EBT instance.
|
||||||
|
** @param context The JS context.
|
||||||
|
** @param debug A JS object populated with the information.
|
||||||
|
*/
|
||||||
|
void tf_ssb_ebt_debug_clock(tf_ssb_ebt_t* ebt, JSContext* context, JSValue debug);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Get a representation of sync progress.
|
||||||
|
** @param ebt The EBT instance.
|
||||||
|
** @param in_pending Populated with the number of messages remaining to be received.
|
||||||
|
** @param in_total Populated with the total number of messages to receive this session.
|
||||||
|
** @param out_pending Populated with the number of messages remaining to send.
|
||||||
|
** @param out_total Populated with the total number of messages to send this session.
|
||||||
|
*/
|
||||||
|
void tf_ssb_ebt_get_progress(tf_ssb_ebt_t* ebt, int* in_pending, int* in_total, int* out_pending, int* out_total);
|
||||||
|
@ -636,10 +636,12 @@ void tf_ssb_remove_broadcasts_changed_callback(tf_ssb_t* ssb, tf_ssb_broadcasts_
|
|||||||
/**
|
/**
|
||||||
** A callback called when a message is added to the database.
|
** A callback called when a message is added to the database.
|
||||||
** @param ssb The SSB instance.
|
** @param ssb The SSB instance.
|
||||||
|
** @param author The author identity.
|
||||||
|
** @param sequence The message sequence number.
|
||||||
** @param id The message identifier.
|
** @param id The message identifier.
|
||||||
** @param user_data The user data.
|
** @param user_data The user data.
|
||||||
*/
|
*/
|
||||||
typedef void(tf_ssb_message_added_callback_t)(tf_ssb_t* ssb, const char* id, void* user_data);
|
typedef void(tf_ssb_message_added_callback_t)(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, void* user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Register a callback called when a message is added to the database.
|
** Register a callback called when a message is added to the database.
|
||||||
@ -661,10 +663,12 @@ void tf_ssb_remove_message_added_callback(tf_ssb_t* ssb, tf_ssb_message_added_ca
|
|||||||
/**
|
/**
|
||||||
** Call all callbacks registered for when a message is added to the database.
|
** Call all callbacks registered for when a message is added to the database.
|
||||||
** @param ssb The SSB instance.
|
** @param ssb The SSB instance.
|
||||||
|
** @param author The message author's identity.
|
||||||
|
** @param sequence The message sequence number.
|
||||||
** @param id The message identity added.
|
** @param id The message identity added.
|
||||||
** @param message_with_keys The message added in the format required if keys are requested.
|
** @param message_with_keys The message added in the format required if keys are requested.
|
||||||
*/
|
*/
|
||||||
void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* id, JSValue message_with_keys);
|
void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, JSValue message_with_keys);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Record that a new blob was stored.
|
** Record that a new blob was stored.
|
||||||
|
113
src/ssb.js.c
113
src/ssb.js.c
@ -3,6 +3,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
#include "ssb.db.h"
|
#include "ssb.db.h"
|
||||||
|
#include "ssb.ebt.h"
|
||||||
#include "ssb.h"
|
#include "ssb.h"
|
||||||
#include "util.js.h"
|
#include "util.js.h"
|
||||||
|
|
||||||
@ -613,87 +614,14 @@ typedef struct _identity_info_work_t
|
|||||||
const char* name;
|
const char* name;
|
||||||
const char* package_owner;
|
const char* package_owner;
|
||||||
const char* package_name;
|
const char* package_name;
|
||||||
int count;
|
tf_ssb_identity_info_t* info;
|
||||||
char** identities;
|
|
||||||
char** names;
|
|
||||||
int result;
|
|
||||||
char active_identity[k_id_base64_len];
|
|
||||||
JSValue promise[2];
|
JSValue promise[2];
|
||||||
} identity_info_work_t;
|
} identity_info_work_t;
|
||||||
|
|
||||||
static void _tf_ssb_getIdentityInfo_visit(const char* identity, void* data)
|
|
||||||
{
|
|
||||||
identity_info_work_t* request = data;
|
|
||||||
request->identities = tf_resize_vec(request->identities, (request->count + 1) * sizeof(char*));
|
|
||||||
request->names = tf_resize_vec(request->names, (request->count + 1) * sizeof(char*));
|
|
||||||
char buffer[k_id_base64_len];
|
|
||||||
snprintf(buffer, sizeof(buffer), "@%s", identity);
|
|
||||||
request->identities[request->count] = tf_strdup(buffer);
|
|
||||||
request->names[request->count] = NULL;
|
|
||||||
request->count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
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] = "";
|
request->info = tf_ssb_db_get_identity_info(ssb, request->name, request->package_owner, request->package_name);
|
||||||
if (tf_ssb_db_user_has_permission(ssb, NULL, 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);
|
|
||||||
|
|
||||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
|
||||||
sqlite3_stmt* statement = NULL;
|
|
||||||
request->result = sqlite3_prepare(db,
|
|
||||||
"SELECT author, name FROM ( "
|
|
||||||
" SELECT "
|
|
||||||
" messages.author, "
|
|
||||||
" RANK() OVER (PARTITION BY messages.author ORDER BY messages.sequence DESC) AS author_rank, "
|
|
||||||
" messages.content ->> 'name' AS name "
|
|
||||||
" FROM messages "
|
|
||||||
" JOIN identities ON messages.author = ('@' || identities.public_key) "
|
|
||||||
" 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 ",
|
|
||||||
-1, &statement, NULL);
|
|
||||||
if (request->result == 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;
|
|
||||||
while ((r = sqlite3_step(statement)) == SQLITE_ROW)
|
|
||||||
{
|
|
||||||
const char* identity = (const char*)sqlite3_column_text(statement, 0);
|
|
||||||
const char* name = (const char*)sqlite3_column_text(statement, 1);
|
|
||||||
for (int i = 0; i < request->count; i++)
|
|
||||||
{
|
|
||||||
if (!request->names[i] && strcmp(request->identities[i], identity) == 0)
|
|
||||||
{
|
|
||||||
request->names[i] = tf_strdup(name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sqlite3_finalize(statement);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tf_printf("prepare failed: %s.\n", sqlite3_errmsg(db));
|
|
||||||
}
|
|
||||||
|
|
||||||
tf_ssb_db_identity_get_active(db, request->name, request->package_owner, request->package_name, request->active_identity, sizeof(request->active_identity));
|
|
||||||
if (!*request->active_identity && request->count)
|
|
||||||
{
|
|
||||||
snprintf(request->active_identity, sizeof(request->active_identity), "%s", request->identities[0]);
|
|
||||||
}
|
|
||||||
tf_ssb_release_db_reader(ssb, db);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _tf_ssb_getIdentityInfo_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
static void _tf_ssb_getIdentityInfo_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
@ -703,20 +631,20 @@ static void _tf_ssb_getIdentityInfo_after_work(tf_ssb_t* ssb, int status, void*
|
|||||||
JSValue result = JS_NewObject(context);
|
JSValue result = JS_NewObject(context);
|
||||||
|
|
||||||
JSValue identities = JS_NewArray(context);
|
JSValue identities = JS_NewArray(context);
|
||||||
for (int i = 0; i < request->count; i++)
|
for (int i = 0; i < request->info->count; i++)
|
||||||
{
|
{
|
||||||
JS_SetPropertyUint32(context, identities, i, JS_NewString(context, request->identities[i]));
|
JS_SetPropertyUint32(context, identities, i, JS_NewString(context, request->info->identity[i]));
|
||||||
}
|
}
|
||||||
JS_SetPropertyStr(context, result, "identities", identities);
|
JS_SetPropertyStr(context, result, "identities", identities);
|
||||||
|
|
||||||
JSValue names = JS_NewObject(context);
|
JSValue names = JS_NewObject(context);
|
||||||
for (int i = 0; i < request->count; i++)
|
for (int i = 0; i < request->info->count; i++)
|
||||||
{
|
{
|
||||||
JS_SetPropertyStr(context, names, request->identities[i], JS_NewString(context, request->names[i] ? request->names[i] : request->identities[i]));
|
JS_SetPropertyStr(context, names, request->info->identity[i], JS_NewString(context, request->info->name[i] ? request->info->name[i] : request->info->identity[i]));
|
||||||
}
|
}
|
||||||
JS_SetPropertyStr(context, result, "names", names);
|
JS_SetPropertyStr(context, result, "names", names);
|
||||||
|
|
||||||
JS_SetPropertyStr(context, result, "identity", JS_NewString(context, request->active_identity));
|
JS_SetPropertyStr(context, result, "identity", JS_NewString(context, request->info->active_identity));
|
||||||
|
|
||||||
JSValue error = JS_Call(context, request->promise[0], JS_UNDEFINED, 1, &result);
|
JSValue error = JS_Call(context, request->promise[0], JS_UNDEFINED, 1, &result);
|
||||||
tf_util_report_error(context, error);
|
tf_util_report_error(context, error);
|
||||||
@ -725,16 +653,10 @@ static void _tf_ssb_getIdentityInfo_after_work(tf_ssb_t* ssb, int status, void*
|
|||||||
JS_FreeValue(context, request->promise[0]);
|
JS_FreeValue(context, request->promise[0]);
|
||||||
JS_FreeValue(context, request->promise[1]);
|
JS_FreeValue(context, request->promise[1]);
|
||||||
|
|
||||||
for (int i = 0; i < request->count; i++)
|
|
||||||
{
|
|
||||||
tf_free(request->identities[i]);
|
|
||||||
tf_free(request->names[i]);
|
|
||||||
}
|
|
||||||
tf_free(request->identities);
|
|
||||||
tf_free(request->names);
|
|
||||||
tf_free((void*)request->name);
|
tf_free((void*)request->name);
|
||||||
tf_free((void*)request->package_owner);
|
tf_free((void*)request->package_owner);
|
||||||
tf_free((void*)request->package_name);
|
tf_free((void*)request->package_name);
|
||||||
|
tf_free(request->info);
|
||||||
tf_free(request);
|
tf_free(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1021,6 +943,21 @@ static JSValue _tf_ssb_connections(JSContext* context, JSValueConst this_val, in
|
|||||||
{
|
{
|
||||||
JS_SetPropertyStr(context, object, "destroy_reason", JS_NewString(context, destroy_reason));
|
JS_SetPropertyStr(context, object, "destroy_reason", JS_NewString(context, destroy_reason));
|
||||||
}
|
}
|
||||||
|
int in = 0;
|
||||||
|
int out = 0;
|
||||||
|
int max_in = 0;
|
||||||
|
int max_out = 0;
|
||||||
|
tf_ssb_ebt_get_progress(tf_ssb_connection_get_ebt(connection), &in, &max_in, &out, &max_out);
|
||||||
|
JSValue progress = JS_NewObject(context);
|
||||||
|
JSValue in_progress = JS_NewObject(context);
|
||||||
|
JS_SetPropertyStr(context, in_progress, "current", JS_NewInt32(context, in));
|
||||||
|
JS_SetPropertyStr(context, in_progress, "total", JS_NewInt32(context, max_in));
|
||||||
|
JS_SetPropertyStr(context, progress, "in", in_progress);
|
||||||
|
JSValue out_progress = JS_NewObject(context);
|
||||||
|
JS_SetPropertyStr(context, out_progress, "current", JS_NewInt32(context, out));
|
||||||
|
JS_SetPropertyStr(context, out_progress, "total", JS_NewInt32(context, max_out));
|
||||||
|
JS_SetPropertyStr(context, progress, "out", out_progress);
|
||||||
|
JS_SetPropertyStr(context, object, "progress", progress);
|
||||||
JS_SetPropertyUint32(context, result, i, object);
|
JS_SetPropertyUint32(context, result, i, object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1657,7 +1594,7 @@ static void _tf_ssb_cleanup_value(tf_ssb_t* ssb, void* user_data)
|
|||||||
JS_FreeValue(tf_ssb_get_context(ssb), callback);
|
JS_FreeValue(tf_ssb_get_context(ssb), callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _tf_ssb_on_message_added_callback(tf_ssb_t* ssb, const char* id, void* user_data)
|
static void _tf_ssb_on_message_added_callback(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, void* user_data)
|
||||||
{
|
{
|
||||||
JSContext* context = tf_ssb_get_context(ssb);
|
JSContext* context = tf_ssb_get_context(ssb);
|
||||||
JSValue callback = JS_MKPTR(JS_TAG_OBJECT, user_data);
|
JSValue callback = JS_MKPTR(JS_TAG_OBJECT, user_data);
|
||||||
|
@ -232,7 +232,7 @@ static void _tf_ssb_request_blob_wants_work(tf_ssb_connection_t* connection, voi
|
|||||||
|
|
||||||
db = tf_ssb_acquire_db_reader(ssb);
|
db = tf_ssb_acquire_db_reader(ssb);
|
||||||
sqlite3_stmt* statement;
|
sqlite3_stmt* statement;
|
||||||
if (sqlite3_prepare(db, "SELECT id FROM blob_wants_view WHERE id > ? AND timestamp > ? ORDER BY id LIMIT ?", -1, &statement, NULL) == SQLITE_OK)
|
if (sqlite3_prepare(db, "SELECT id FROM blob_wants_cache WHERE id > ? AND timestamp > ? ORDER BY id LIMIT ?", -1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_text(statement, 1, blob_wants->last_id, -1, NULL) == SQLITE_OK && sqlite3_bind_int64(statement, 2, timestamp) == SQLITE_OK &&
|
if (sqlite3_bind_text(statement, 1, blob_wants->last_id, -1, NULL) == SQLITE_OK && sqlite3_bind_int64(statement, 2, timestamp) == SQLITE_OK &&
|
||||||
sqlite3_bind_int(statement, 3, tf_countof(work->out_id)) == SQLITE_OK)
|
sqlite3_bind_int(statement, 3, tf_countof(work->out_id)) == SQLITE_OK)
|
||||||
@ -593,6 +593,7 @@ static void _tf_ssb_rpc_connection_blobs_get_callback(
|
|||||||
bool stored = true;
|
bool stored = true;
|
||||||
tf_ssb_connection_rpc_send(connection, k_ssb_rpc_flag_json | k_ssb_rpc_flag_stream | k_ssb_rpc_flag_end_error, -request_number, NULL,
|
tf_ssb_connection_rpc_send(connection, k_ssb_rpc_flag_json | k_ssb_rpc_flag_stream | k_ssb_rpc_flag_end_error, -request_number, NULL,
|
||||||
(const uint8_t*)(stored ? "true" : "false"), strlen(stored ? "true" : "false"), NULL, NULL, NULL);
|
(const uint8_t*)(stored ? "true" : "false"), strlen(stored ? "true" : "false"), NULL, NULL, NULL);
|
||||||
|
tf_ssb_connection_remove_request(connection, -request_number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -670,6 +671,22 @@ static void _tf_ssb_rpc_connection_blobs_create_wants_after_work(tf_ssb_connecti
|
|||||||
tf_free(work);
|
tf_free(work);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct _blob_get_t
|
||||||
|
{
|
||||||
|
char id[k_blob_id_len];
|
||||||
|
size_t size;
|
||||||
|
} blob_get_t;
|
||||||
|
|
||||||
|
static void _tf_ssb_rpc_connection_blobs_get_idle(tf_ssb_connection_t* connection, bool skip, void* user_data)
|
||||||
|
{
|
||||||
|
blob_get_t* get = user_data;
|
||||||
|
if (!skip)
|
||||||
|
{
|
||||||
|
_tf_ssb_rpc_connection_blobs_get(connection, get->id, get->size);
|
||||||
|
}
|
||||||
|
tf_free(get);
|
||||||
|
}
|
||||||
|
|
||||||
static void _tf_ssb_rpc_connection_blobs_createWants_callback(
|
static void _tf_ssb_rpc_connection_blobs_createWants_callback(
|
||||||
tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
|
tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
|
||||||
{
|
{
|
||||||
@ -724,7 +741,10 @@ static void _tf_ssb_rpc_connection_blobs_createWants_callback(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_tf_ssb_rpc_connection_blobs_get(connection, blob_id, size);
|
blob_get_t* get = tf_malloc(sizeof(blob_get_t));
|
||||||
|
*get = (blob_get_t) { .size = size };
|
||||||
|
snprintf(get->id, sizeof(get->id), "%s", blob_id);
|
||||||
|
tf_ssb_connection_schedule_idle(connection, blob_id, _tf_ssb_rpc_connection_blobs_get_idle, get);
|
||||||
}
|
}
|
||||||
JS_FreeCString(context, blob_id);
|
JS_FreeCString(context, blob_id);
|
||||||
JS_FreeValue(context, key);
|
JS_FreeValue(context, key);
|
||||||
@ -964,9 +984,9 @@ static void _tf_ssb_connection_send_history_stream_after_work(tf_ssb_connection_
|
|||||||
|
|
||||||
static void _tf_ssb_connection_send_history_stream_callback(tf_ssb_connection_t* connection, bool skip, void* user_data)
|
static void _tf_ssb_connection_send_history_stream_callback(tf_ssb_connection_t* connection, bool skip, void* user_data)
|
||||||
{
|
{
|
||||||
tf_ssb_connection_adjust_write_count(connection, 1);
|
|
||||||
if (!skip && tf_ssb_connection_is_connected(connection) && !tf_ssb_is_shutting_down(tf_ssb_connection_get_ssb(connection)))
|
if (!skip && tf_ssb_connection_is_connected(connection) && !tf_ssb_is_shutting_down(tf_ssb_connection_get_ssb(connection)))
|
||||||
{
|
{
|
||||||
|
tf_ssb_connection_adjust_write_count(connection, 1);
|
||||||
tf_ssb_connection_run_work(connection, _tf_ssb_connection_send_history_stream_work, _tf_ssb_connection_send_history_stream_after_work, user_data);
|
tf_ssb_connection_run_work(connection, _tf_ssb_connection_send_history_stream_work, _tf_ssb_connection_send_history_stream_after_work, user_data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -989,9 +1009,7 @@ static void _tf_ssb_connection_send_history_stream(
|
|||||||
.end_request = end_request,
|
.end_request = end_request,
|
||||||
};
|
};
|
||||||
snprintf(async->author, sizeof(async->author), "%s", author);
|
snprintf(async->author, sizeof(async->author), "%s", author);
|
||||||
char key[128];
|
tf_ssb_connection_schedule_idle(connection, author, _tf_ssb_connection_send_history_stream_callback, async);
|
||||||
snprintf(key, sizeof(key), "%s:%" PRId64, author, sequence);
|
|
||||||
tf_ssb_connection_schedule_idle(connection, key, _tf_ssb_connection_send_history_stream_callback, async);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1039,6 +1057,12 @@ static void _tf_ssb_rpc_createHistoryStream(
|
|||||||
|
|
||||||
static void _tf_ssb_rpc_ebt_replicate_send_messages(tf_ssb_connection_t* connection)
|
static void _tf_ssb_rpc_ebt_replicate_send_messages(tf_ssb_connection_t* connection)
|
||||||
{
|
{
|
||||||
|
int32_t request_number = tf_ssb_connection_get_ebt_request_number(connection);
|
||||||
|
if (!request_number)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
tf_ssb_ebt_t* ebt = tf_ssb_connection_get_ebt(connection);
|
tf_ssb_ebt_t* ebt = tf_ssb_connection_get_ebt(connection);
|
||||||
tf_ssb_ebt_clock_t* clock = tf_ssb_ebt_get_messages_to_send(ebt);
|
tf_ssb_ebt_clock_t* clock = tf_ssb_ebt_get_messages_to_send(ebt);
|
||||||
if (clock)
|
if (clock)
|
||||||
@ -1046,7 +1070,6 @@ static void _tf_ssb_rpc_ebt_replicate_send_messages(tf_ssb_connection_t* connect
|
|||||||
for (int i = 0; i < clock->count; i++)
|
for (int i = 0; i < clock->count; i++)
|
||||||
{
|
{
|
||||||
tf_ssb_ebt_clock_entry_t* entry = &clock->entries[i];
|
tf_ssb_ebt_clock_entry_t* entry = &clock->entries[i];
|
||||||
int32_t request_number = tf_ssb_connection_get_ebt_request_number(connection);
|
|
||||||
bool live = (tf_ssb_connection_get_flags(connection) & k_tf_ssb_connect_flag_one_shot) == 0;
|
bool live = (tf_ssb_connection_get_flags(connection) & k_tf_ssb_connect_flag_one_shot) == 0;
|
||||||
_tf_ssb_connection_send_history_stream(connection, request_number, entry->id, entry->value, false, live, false);
|
_tf_ssb_connection_send_history_stream(connection, request_number, entry->id, entry->value, false, live, false);
|
||||||
}
|
}
|
||||||
@ -1118,8 +1141,9 @@ static void _tf_ssb_rpc_ebt_schedule_send_clock(tf_ssb_connection_t* connection)
|
|||||||
{
|
{
|
||||||
tf_ssb_ebt_t* ebt = tf_ssb_connection_get_ebt(connection);
|
tf_ssb_ebt_t* ebt = tf_ssb_connection_get_ebt(connection);
|
||||||
int pending = tf_ssb_ebt_get_send_clock_pending(ebt) + 1;
|
int pending = tf_ssb_ebt_get_send_clock_pending(ebt) + 1;
|
||||||
|
int32_t request_number = tf_ssb_connection_get_ebt_request_number(connection);
|
||||||
tf_ssb_ebt_set_send_clock_pending(ebt, pending);
|
tf_ssb_ebt_set_send_clock_pending(ebt, pending);
|
||||||
if (pending == 1)
|
if (pending == 1 && request_number)
|
||||||
{
|
{
|
||||||
resend_clock_t* resend = tf_malloc(sizeof(resend_clock_t));
|
resend_clock_t* resend = tf_malloc(sizeof(resend_clock_t));
|
||||||
*resend = (resend_clock_t) {
|
*resend = (resend_clock_t) {
|
||||||
@ -1138,6 +1162,7 @@ static void _tf_ssb_rpc_ebt_replicate(tf_ssb_connection_t* connection, uint8_t f
|
|||||||
if (_is_error(context, args))
|
if (_is_error(context, args))
|
||||||
{
|
{
|
||||||
/* TODO: Send createHistoryStream. */
|
/* TODO: Send createHistoryStream. */
|
||||||
|
tf_ssb_connection_set_ebt_request_number(connection, 0);
|
||||||
tf_ssb_connection_remove_request(connection, -request_number);
|
tf_ssb_connection_remove_request(connection, -request_number);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1874,7 +1899,7 @@ static void _tf_ssb_rpc_invite_use(tf_ssb_connection_t* connection, uint8_t flag
|
|||||||
tf_ssb_connection_run_work(connection, _tf_ssb_rpc_invite_use_work, _tf_ssb_rpc_invite_use_after_work, work);
|
tf_ssb_connection_run_work(connection, _tf_ssb_rpc_invite_use_work, _tf_ssb_rpc_invite_use_after_work, work);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _tf_ssb_rpc_message_added_callback(tf_ssb_t* ssb, const char* id, void* user_data)
|
static void _tf_ssb_rpc_message_added_callback(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, void* user_data)
|
||||||
{
|
{
|
||||||
tf_ssb_connection_t* connections[256];
|
tf_ssb_connection_t* connections[256];
|
||||||
int count = tf_ssb_get_connections(ssb, connections, tf_countof(connections));
|
int count = tf_ssb_get_connections(ssb, connections, tf_countof(connections));
|
||||||
@ -1883,6 +1908,7 @@ static void _tf_ssb_rpc_message_added_callback(tf_ssb_t* ssb, const char* id, vo
|
|||||||
tf_ssb_connection_t* connection = connections[i];
|
tf_ssb_connection_t* connection = connections[i];
|
||||||
if (tf_ssb_connection_is_connected(connection) && !tf_ssb_is_shutting_down(tf_ssb_connection_get_ssb(connection)) && !tf_ssb_connection_is_closing(connection))
|
if (tf_ssb_connection_is_connected(connection) && !tf_ssb_is_shutting_down(tf_ssb_connection_get_ssb(connection)) && !tf_ssb_connection_is_closing(connection))
|
||||||
{
|
{
|
||||||
|
tf_ssb_ebt_set_messages_received(tf_ssb_connection_get_ebt(connections[i]), author, sequence);
|
||||||
_tf_ssb_rpc_ebt_schedule_send_clock(connections[i]);
|
_tf_ssb_rpc_ebt_schedule_send_clock(connections[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ static int _ssb_test_count_messages(tf_ssb_t* ssb)
|
|||||||
return count.count;
|
return count.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _message_added(tf_ssb_t* ssb, const char* id, void* user_data)
|
static void _message_added(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, void* user_data)
|
||||||
{
|
{
|
||||||
++*(int*)user_data;
|
++*(int*)user_data;
|
||||||
}
|
}
|
||||||
@ -720,24 +720,21 @@ void tf_ssb_test_bench(const tf_test_options_t* options)
|
|||||||
tf_ssb_server_open(ssb0, 12347);
|
tf_ssb_server_open(ssb0, 12347);
|
||||||
tf_ssb_connect(ssb1, "127.0.0.1", 12347, id0bin, 0, NULL, NULL);
|
tf_ssb_connect(ssb1, "127.0.0.1", 12347, id0bin, 0, NULL, NULL);
|
||||||
|
|
||||||
tf_printf("Waiting for messages.\n");
|
|
||||||
clock_gettime(CLOCK_REALTIME, &start_time);
|
|
||||||
int count = 0;
|
|
||||||
tf_ssb_add_message_added_callback(ssb1, _message_added, NULL, &count);
|
|
||||||
while (count < k_messages)
|
|
||||||
{
|
|
||||||
uv_run(&loop, UV_RUN_ONCE);
|
|
||||||
}
|
|
||||||
tf_ssb_remove_message_added_callback(ssb1, _message_added, &count);
|
|
||||||
clock_gettime(CLOCK_REALTIME, &end_time);
|
|
||||||
|
|
||||||
tf_ssb_set_main_thread(ssb0, false);
|
tf_ssb_set_main_thread(ssb0, false);
|
||||||
tf_ssb_set_main_thread(ssb1, false);
|
tf_ssb_set_main_thread(ssb1, false);
|
||||||
count = _ssb_test_count_messages(ssb1);
|
|
||||||
if (count < k_messages)
|
tf_printf("Waiting for messages.\n");
|
||||||
|
clock_gettime(CLOCK_REALTIME, &start_time);
|
||||||
|
while (_ssb_test_count_messages(ssb1) < k_messages)
|
||||||
{
|
{
|
||||||
abort();
|
tf_ssb_set_main_thread(ssb0, true);
|
||||||
|
tf_ssb_set_main_thread(ssb1, true);
|
||||||
|
uv_run(&loop, UV_RUN_ONCE);
|
||||||
|
tf_ssb_set_main_thread(ssb0, false);
|
||||||
|
tf_ssb_set_main_thread(ssb1, false);
|
||||||
}
|
}
|
||||||
|
clock_gettime(CLOCK_REALTIME, &end_time);
|
||||||
|
|
||||||
tf_printf("Done.\n");
|
tf_printf("Done.\n");
|
||||||
tf_printf("replicate = %f seconds\n", (end_time.tv_sec - start_time.tv_sec) + (end_time.tv_nsec - start_time.tv_nsec) / 1e9);
|
tf_printf("replicate = %f seconds\n", (end_time.tv_sec - start_time.tv_sec) + (end_time.tv_nsec - start_time.tv_nsec) / 1e9);
|
||||||
|
|
||||||
|
77
src/task.c
77
src/task.c
@ -1,5 +1,6 @@
|
|||||||
#include "task.h"
|
#include "task.h"
|
||||||
|
|
||||||
|
#include "api.js.h"
|
||||||
#include "bcrypt.js.h"
|
#include "bcrypt.js.h"
|
||||||
#include "database.js.h"
|
#include "database.js.h"
|
||||||
#include "file.js.h"
|
#include "file.js.h"
|
||||||
@ -163,10 +164,14 @@ typedef struct _tf_task_t
|
|||||||
|
|
||||||
promise_stack_t* _promise_stacks;
|
promise_stack_t* _promise_stacks;
|
||||||
int _promise_stack_count;
|
int _promise_stack_count;
|
||||||
|
bool _promise_stack_debug;
|
||||||
|
|
||||||
timeout_t* timeouts;
|
timeout_t* timeouts;
|
||||||
|
|
||||||
hitch_t hitches[32];
|
hitch_t hitches[32];
|
||||||
|
|
||||||
|
uint64_t last_gc_ns;
|
||||||
|
int64_t last_gc_duration_ns;
|
||||||
} tf_task_t;
|
} tf_task_t;
|
||||||
|
|
||||||
typedef struct _export_record_t
|
typedef struct _export_record_t
|
||||||
@ -1251,10 +1256,13 @@ static void _add_promise_stack(tf_task_t* task, uint32_t hash, const char* stack
|
|||||||
|
|
||||||
static void _remove_promise_stack(tf_task_t* task, uint32_t hash)
|
static void _remove_promise_stack(tf_task_t* task, uint32_t hash)
|
||||||
{
|
{
|
||||||
promise_stack_t* found = bsearch(&hash, task->_promise_stacks, task->_promise_stack_count, sizeof(promise_stack_t), _promise_stack_compare);
|
if (task->_promise_stack_debug)
|
||||||
if (found)
|
|
||||||
{
|
{
|
||||||
found->count--;
|
promise_stack_t* found = bsearch(&hash, task->_promise_stacks, task->_promise_stack_count, sizeof(promise_stack_t), _promise_stack_compare);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
found->count--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1270,33 +1278,26 @@ static void _tf_task_free_promise(tf_task_t* task, promiseid_t id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t fnv32a(const void* buffer, int length, uint32_t start)
|
|
||||||
{
|
|
||||||
uint32_t result = 0x811c9dc5;
|
|
||||||
for (int i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
result ^= ((const uint8_t*)buffer)[i];
|
|
||||||
result += (result << 1) + (result << 4) + (result << 7) + (result << 8) + (result << 24);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
|
JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
|
||||||
{
|
{
|
||||||
JSValue error = JS_ThrowInternalError(task->_context, "promise callstack");
|
uint32_t stack_hash = 0;
|
||||||
JSValue exception = JS_GetException(task->_context);
|
if (task->_promise_stack_debug)
|
||||||
JSValue stack_value = JS_GetPropertyStr(task->_context, exception, "stack");
|
{
|
||||||
size_t length = 0;
|
JSValue error = JS_ThrowInternalError(task->_context, "promise callstack");
|
||||||
const char* stack = JS_ToCStringLen(task->_context, &length, stack_value);
|
JSValue exception = JS_GetException(task->_context);
|
||||||
uint32_t stack_hash = fnv32a((const void*)stack, (int)length, 0);
|
JSValue stack_value = JS_GetPropertyStr(task->_context, exception, "stack");
|
||||||
void* buffer[32];
|
size_t length = 0;
|
||||||
int count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer));
|
const char* stack = JS_ToCStringLen(task->_context, &length, stack_value);
|
||||||
stack_hash = fnv32a((const void*)buffer, sizeof(void*) * count, stack_hash);
|
stack_hash = tf_util_fnv32a((const void*)stack, (int)length, 0);
|
||||||
_add_promise_stack(task, stack_hash, stack, buffer, count);
|
void* buffer[32];
|
||||||
JS_FreeCString(task->_context, stack);
|
int count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer));
|
||||||
JS_FreeValue(task->_context, stack_value);
|
stack_hash = tf_util_fnv32a((const void*)buffer, sizeof(void*) * count, stack_hash);
|
||||||
JS_FreeValue(task->_context, exception);
|
_add_promise_stack(task, stack_hash, stack, buffer, count);
|
||||||
JS_FreeValue(task->_context, error);
|
JS_FreeCString(task->_context, stack);
|
||||||
|
JS_FreeValue(task->_context, stack_value);
|
||||||
|
JS_FreeValue(task->_context, exception);
|
||||||
|
JS_FreeValue(task->_context, error);
|
||||||
|
}
|
||||||
|
|
||||||
promiseid_t promiseId;
|
promiseid_t promiseId;
|
||||||
do
|
do
|
||||||
@ -1458,12 +1459,19 @@ static void _tf_task_promise_rejection_tracker(JSContext* context, JSValueConst
|
|||||||
static void _tf_task_gc_timer(uv_timer_t* timer)
|
static void _tf_task_gc_timer(uv_timer_t* timer)
|
||||||
{
|
{
|
||||||
tf_task_t* task = timer->data;
|
tf_task_t* task = timer->data;
|
||||||
tf_trace_begin(task->_trace, "JS_RunGC");
|
uint64_t start_ns = uv_hrtime();
|
||||||
JS_RunGC(task->_runtime);
|
if (task->last_gc_duration_ns < (int64_t)(start_ns - task->last_gc_ns))
|
||||||
tf_trace_end(task->_trace);
|
{
|
||||||
|
tf_trace_begin(task->_trace, "JS_RunGC");
|
||||||
|
JS_RunGC(task->_runtime);
|
||||||
|
tf_trace_end(task->_trace);
|
||||||
#ifdef M_TRIM_THRESHOLD
|
#ifdef M_TRIM_THRESHOLD
|
||||||
malloc_trim(0);
|
malloc_trim(0);
|
||||||
#endif
|
#endif
|
||||||
|
uint64_t end_ns = uv_hrtime();
|
||||||
|
task->last_gc_duration_ns = end_ns - start_ns;
|
||||||
|
task->last_gc_ns = end_ns;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _tf_task_trace_timer(uv_timer_t* timer)
|
static void _tf_task_trace_timer(uv_timer_t* timer)
|
||||||
@ -1591,6 +1599,10 @@ tf_task_t* tf_task_create()
|
|||||||
*task = (tf_task_t) { 0 };
|
*task = (tf_task_t) { 0 };
|
||||||
++_count;
|
++_count;
|
||||||
|
|
||||||
|
char buffer[8] = { 0 };
|
||||||
|
size_t buffer_size = sizeof(buffer);
|
||||||
|
task->_promise_stack_debug = uv_os_getenv("TF_PROMISE_DEBUG", buffer, &buffer_size) == 0 && strcmp(buffer, "1") == 0;
|
||||||
|
|
||||||
JSMallocFunctions funcs = { 0 };
|
JSMallocFunctions funcs = { 0 };
|
||||||
tf_get_js_malloc_functions(&funcs);
|
tf_get_js_malloc_functions(&funcs);
|
||||||
task->_runtime = JS_NewRuntime2(&funcs, NULL);
|
task->_runtime = JS_NewRuntime2(&funcs, NULL);
|
||||||
@ -1697,6 +1709,7 @@ void tf_task_activate(tf_task_t* task)
|
|||||||
task->_ssb = tf_ssb_create(&task->_loop, task->_context, task->_db_path, task->_network_key);
|
task->_ssb = tf_ssb_create(&task->_loop, task->_context, task->_db_path, task->_network_key);
|
||||||
tf_ssb_set_trace(task->_ssb, task->_trace);
|
tf_ssb_set_trace(task->_ssb, task->_trace);
|
||||||
tf_ssb_register(context, task->_ssb);
|
tf_ssb_register(context, task->_ssb);
|
||||||
|
tf_api_register(context);
|
||||||
tf_ssb_set_hitch_callback(task->_ssb, _tf_task_record_hitch, task);
|
tf_ssb_set_hitch_callback(task->_ssb, _tf_task_record_hitch, task);
|
||||||
|
|
||||||
if (task->_args)
|
if (task->_args)
|
||||||
|
@ -781,7 +781,7 @@ static void _test_http(const tf_test_options_t* options)
|
|||||||
tf_http_t* http = tf_http_create(&loop);
|
tf_http_t* http = tf_http_create(&loop);
|
||||||
tf_http_add_handler(http, "/hello", _test_http_handler, NULL, NULL);
|
tf_http_add_handler(http, "/hello", _test_http_handler, NULL, NULL);
|
||||||
tf_http_add_handler(http, "/post", _test_http_handler_post, NULL, NULL);
|
tf_http_add_handler(http, "/post", _test_http_handler_post, NULL, NULL);
|
||||||
tf_http_listen(http, 23456, NULL, NULL, NULL);
|
tf_http_listen(http, 23456, true, NULL, NULL, NULL);
|
||||||
|
|
||||||
test_http_t test = { .loop = &loop };
|
test_http_t test = { .loop = &loop };
|
||||||
uv_async_init(&loop, &test.async, _test_http_async);
|
uv_async_init(&loop, &test.async, _test_http_async);
|
||||||
|
39
src/trace.c
39
src/trace.c
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
#include "util.js.h"
|
#include "util.js.h"
|
||||||
|
|
||||||
@ -149,25 +150,37 @@ static int _tf_trace_escape_name(char* out, size_t out_size, const char* name)
|
|||||||
{
|
{
|
||||||
case '"':
|
case '"':
|
||||||
case '\\':
|
case '\\':
|
||||||
out[p++] = '\\';
|
if ((size_t)p + 2 < out_size)
|
||||||
if ((size_t)p + 1 < out_size)
|
|
||||||
{
|
{
|
||||||
|
out[p++] = '\\';
|
||||||
out[p++] = *c;
|
out[p++] = *c;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out[p++] = '$';
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case '\t':
|
case '\t':
|
||||||
out[p++] = '\\';
|
if ((size_t)p + 2 < out_size)
|
||||||
if ((size_t)p + 1 < out_size)
|
|
||||||
{
|
{
|
||||||
|
out[p++] = '\\';
|
||||||
out[p++] = 't';
|
out[p++] = 't';
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out[p++] = '$';
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case '\n':
|
case '\n':
|
||||||
out[p++] = '\\';
|
if ((size_t)p + 2 < out_size)
|
||||||
if ((size_t)p + 1 < out_size)
|
|
||||||
{
|
{
|
||||||
|
out[p++] = '\\';
|
||||||
out[p++] = 'n';
|
out[p++] = 'n';
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out[p++] = '$';
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
out[p++] = *c;
|
out[p++] = *c;
|
||||||
@ -320,21 +333,23 @@ char* tf_trace_export(tf_trace_t* trace)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const int k_extra_size = 1024;
|
static const int k_extra_size = 1024;
|
||||||
char* buffer = tf_malloc(k_buffer_size + k_extra_size);
|
static const size_t k_out_buffer_size = k_buffer_size + k_extra_size;
|
||||||
|
char* buffer = tf_malloc(k_out_buffer_size);
|
||||||
uv_mutex_lock(&trace->mutex);
|
uv_mutex_lock(&trace->mutex);
|
||||||
const char* newline = strchr(trace->buffer + trace->write_offset, '\n');
|
const char* newline = strchr(trace->buffer + trace->write_offset, '\n');
|
||||||
int begin = newline ? newline - trace->buffer : 0;
|
int begin = newline ? newline - trace->buffer : 0;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
size += snprintf(buffer, k_buffer_size, "{\"displayTimeUnit\": \"ns\",\n\"traceEvents\": [\n");
|
size += snprintf(buffer, k_out_buffer_size, "{\"displayTimeUnit\": \"ns\",\n\"traceEvents\": [\n");
|
||||||
if (*trace->process_name)
|
if (*trace->process_name)
|
||||||
{
|
{
|
||||||
size += snprintf(buffer + size, k_buffer_size - size, "{\"ph\":\"M\",\"pid\":%d,\"name\":\"process_name\",\"args\":{\"name\":\"%s\"}},\n", getpid(), trace->process_name);
|
size +=
|
||||||
|
snprintf(buffer + size, k_out_buffer_size - size, "{\"ph\":\"M\",\"pid\":%d,\"name\":\"process_name\",\"args\":{\"name\":\"%s\"}},\n", getpid(), trace->process_name);
|
||||||
}
|
}
|
||||||
uv_rwlock_rdlock(&trace->threads_lock);
|
uv_rwlock_rdlock(&trace->threads_lock);
|
||||||
for (int i = 0; i < trace->threads_count; i++)
|
for (int i = 0; i < trace->threads_count; i++)
|
||||||
{
|
{
|
||||||
tf_trace_thread_t* thread = trace->threads[i];
|
tf_trace_thread_t* thread = trace->threads[i];
|
||||||
size += snprintf(buffer + size, k_buffer_size - size, "{\"ph\":\"M\",\"pid\":%d,\"tid\":%d,\"name\":\"thread_name\",\"args\":{\"name\":\"%s\"}},\n", getpid(),
|
size += snprintf(buffer + size, k_out_buffer_size - size, "{\"ph\":\"M\",\"pid\":%d,\"tid\":%d,\"name\":\"thread_name\",\"args\":{\"name\":\"%s\"}},\n", getpid(),
|
||||||
thread->index, thread->name);
|
thread->index, thread->name);
|
||||||
}
|
}
|
||||||
uv_rwlock_rdunlock(&trace->threads_lock);
|
uv_rwlock_rdunlock(&trace->threads_lock);
|
||||||
@ -352,8 +367,8 @@ char* tf_trace_export(tf_trace_t* trace)
|
|||||||
buffer[size - 2] = '\n';
|
buffer[size - 2] = '\n';
|
||||||
size--;
|
size--;
|
||||||
}
|
}
|
||||||
size += snprintf(buffer + size, k_buffer_size - size, "]}\n");
|
size += snprintf(buffer + size, k_out_buffer_size - size, "]}\n");
|
||||||
assert(size < (size_t)k_buffer_size + k_extra_size);
|
assert(size < k_out_buffer_size);
|
||||||
buffer[size] = '\0';
|
buffer[size] = '\0';
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
@ -345,6 +345,10 @@ static const setting_t k_settings[] = {
|
|||||||
.type = "integer",
|
.type = "integer",
|
||||||
.description = "Port on which to listen for SSB secure handshake connections.",
|
.description = "Port on which to listen for SSB secure handshake connections.",
|
||||||
.default_value = { .kind = k_kind_int, .int_value = 8008 } },
|
.default_value = { .kind = k_kind_int, .int_value = 8008 } },
|
||||||
|
{ .name = "http_local_only",
|
||||||
|
.type = "boolean",
|
||||||
|
.description = "Whether to bind http(s) to the loopback address. Otherwise any.",
|
||||||
|
.default_value = { .kind = k_kind_bool, .bool_value = TF_IS_MOBILE ? true : false } },
|
||||||
{ .name = "http_port", .type = "integer", .description = "Port on which to listen for HTTP connections.", .default_value = { .kind = k_kind_int, .int_value = 12345 } },
|
{ .name = "http_port", .type = "integer", .description = "Port on which to listen for HTTP connections.", .default_value = { .kind = k_kind_int, .int_value = 12345 } },
|
||||||
{ .name = "https_port", .type = "integer", .description = "Port on which to listen for secure HTTP connections.", .default_value = { .kind = k_kind_int, .int_value = 0 } },
|
{ .name = "https_port", .type = "integer", .description = "Port on which to listen for secure HTTP connections.", .default_value = { .kind = k_kind_int, .int_value = 0 } },
|
||||||
{ .name = "out_http_port_file", .type = "hidden", .description = "File to which to write bound HTTP port.", .default_value = { .kind = k_kind_string, .string_value = NULL } },
|
{ .name = "out_http_port_file", .type = "hidden", .description = "File to which to write bound HTTP port.", .default_value = { .kind = k_kind_string, .string_value = NULL } },
|
||||||
@ -683,3 +687,14 @@ bool tf_util_is_mobile()
|
|||||||
{
|
{
|
||||||
return TF_IS_MOBILE != 0;
|
return TF_IS_MOBILE != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t tf_util_fnv32a(const void* buffer, int length, uint32_t start)
|
||||||
|
{
|
||||||
|
uint32_t result = 0x811c9dc5;
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
result ^= ((const uint8_t*)buffer)[i];
|
||||||
|
result += (result << 1) + (result << 4) + (result << 7) + (result << 8) + (result << 24);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
@ -224,4 +224,13 @@ void tf_util_document_settings(const char* line_prefix);
|
|||||||
*/
|
*/
|
||||||
bool tf_util_is_mobile();
|
bool tf_util_is_mobile();
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Compute a 32-bit hash of a buffer.
|
||||||
|
** @param buffer The data.
|
||||||
|
** @param length The size of the buffer in bytes.
|
||||||
|
** @param start The hash seed.
|
||||||
|
** @return The computed hash.
|
||||||
|
*/
|
||||||
|
uint32_t tf_util_fnv32a(const void* buffer, int length, uint32_t start);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
#define VERSION_NUMBER "0.0.28"
|
#define VERSION_NUMBER "0.0.29"
|
||||||
#define VERSION_NAME "This program kills fascists."
|
#define VERSION_NAME "This program kills fascists."
|
||||||
|
Reference in New Issue
Block a user