Compare commits
18 Commits
2d38b3bd61
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 8abcdd1e7d | |||
| 97aeff60cc | |||
| 86d6a5c049 | |||
| f6f815eec1 | |||
| 73a1c1d978 | |||
| 5445072d36 | |||
| d9a2519e9b | |||
| 687a85dbd8 | |||
| 9e25aa1c54 | |||
| e309f519f2 | |||
| 5ccd9f16c3 | |||
| 7d596ebd3b | |||
| 938f728eb9 | |||
| 6e8a0031a8 | |||
| 085f62aadf | |||
| 71d556143b | |||
| 081bff9a26 | |||
| f2f4455c82 |
21
GNUmakefile
21
GNUmakefile
@@ -1213,7 +1213,26 @@ out/tildefriends-x86_64.AppImage: out/release/tildefriends out/data.zip
|
|||||||
@cd out; ./appimagetool --appimage-extract; cd ..
|
@cd out; ./appimagetool --appimage-extract; cd ..
|
||||||
@cd out; unset SOURCE_DATE_EPOCH; PATH=$$PATH:squashfs-root/usr/bin ARCH=x86_64 squashfs-root/usr/bin/appimagetool -u 'zsync|https://dev.tildefriends.net/releases/tildefriends-x86_64.AppImage.zsync' tildefriends.AppDir tildefriends-x86_64.AppImage; cd ..
|
@cd out; unset SOURCE_DATE_EPOCH; PATH=$$PATH:squashfs-root/usr/bin ARCH=x86_64 squashfs-root/usr/bin/appimagetool -u 'zsync|https://dev.tildefriends.net/releases/tildefriends-x86_64.AppImage.zsync' tildefriends.AppDir tildefriends-x86_64.AppImage; cd ..
|
||||||
|
|
||||||
appimage: out/tildefriends-x86_64.AppImage ## Build an AppImage.
|
out/tildefriends-aarch64.AppImage: out/armrelease/tildefriends out/data.zip
|
||||||
|
@echo "[appimage] $$@"
|
||||||
|
@rm -rf out/tildefriends_aarch64.AppDir
|
||||||
|
@mkdir -p out/tildefriends_aarch64.AppDir/usr/bin
|
||||||
|
@mkdir -p out/tildefriends_aarch64.AppDir/usr/share/applications
|
||||||
|
@mkdir -p out/tildefriends_aarch64.AppDir/usr/share/icons/hicolor/scalable/apps
|
||||||
|
@mkdir -p out/tildefriends_aarch64.AppDir/usr/share/tildefriends
|
||||||
|
@echo $(APPIMAGETOOL_MD5) > out/appimagetool.md5
|
||||||
|
@test -x out/appimagetool || curl -q -L -o out/appimagetool $(APPIMAGETOOL_URL) && md5sum -c out/appimagetool.md5 && chmod +x out/appimagetool
|
||||||
|
@echo "[Desktop Entry]\nName=tildefriends\nExec=/usr/bin/tildefriends\nIcon=/usr/share/icons/hicolor/scalable/apps/tildefriends\nType=Application\nCategories=Network" > out/tildefriends_aarch64.AppDir/tildefriends.desktop
|
||||||
|
@cp src/ios/tildefriends.svg out/tildefriends_aarch64.AppDir/usr/share/icons/hicolor/scalable/apps/
|
||||||
|
@cp src/ios/tildefriends.svg out/tildefriends_aarch64.AppDir/
|
||||||
|
@cp out/armrelease/tildefriends out/tildefriends_aarch64.AppDir/usr/bin/
|
||||||
|
@cp out/data.zip out/tildefriends_aarch64.AppDir/usr/share/tildefriends/data.zip
|
||||||
|
@echo "#!/bin/sh\n\$${APPDIR}/usr/bin/tildefriends run -z \$$APPDIR/usr/share/tildefriends/data.zip" > out/tildefriends_aarch64.AppDir/AppRun
|
||||||
|
@chmod +x out/tildefriends_aarch64.AppDir/AppRun
|
||||||
|
@cd out; ./appimagetool --appimage-extract; cd ..
|
||||||
|
@cd out; unset SOURCE_DATE_EPOCH; PATH=$$PATH:squashfs-root/usr/bin ARCH=arm_aarch64 squashfs-root/usr/bin/appimagetool -u 'zsync|https://dev.tildefriends.net/releases/tildefriends-aarch64.AppImage.zsync' tildefriends_aarch64.AppDir tildefriends-aarch64.AppImage; cd ..
|
||||||
|
|
||||||
|
appimage: out/tildefriends-x86_64.AppImage out/tildefriends-aarch64.AppImage ## Build AppImages.
|
||||||
.PHONY: appimage
|
.PHONY: appimage
|
||||||
|
|
||||||
flatpak: out/ ## Build a flatpak.
|
flatpak: out/ ## Build a flatpak.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "📚",
|
"emoji": "📖",
|
||||||
"previous": "&EO5ifwzemEeSJsN6SJ2VTyE+sqnwU2gikIngQimwnDo=.sha256"
|
"previous": "&u7ri5Gi1AK6SbWRmc3S8vN40QrWL90/DKDiDTeDDiPQ=.sha256"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,12 +46,25 @@ async function main() {
|
|||||||
fields.key,
|
fields.key,
|
||||||
RANK() OVER (PARTITION BY messages.author, messages.content ->> '$.about', fields.key ORDER BY messages.sequence DESC) AS rank,
|
RANK() OVER (PARTITION BY messages.author, messages.content ->> '$.about', fields.key ORDER BY messages.sequence DESC) AS rank,
|
||||||
fields.value
|
fields.value
|
||||||
FROM messages, json_each(messages.content) AS fields, json_each(?) AS book, json_each(?) AS following
|
FROM messages, json_each(messages.content) AS fields, json_each(?1) AS book, json_each(?2) AS following
|
||||||
ON messages.author = following.value
|
ON messages.author = following.value
|
||||||
WHERE
|
WHERE
|
||||||
messages.content ->> 'type' = 'about'
|
messages.content ->> 'type' = 'about'
|
||||||
AND messages.content ->> '$.about' = book.value
|
AND messages.content ->> '$.about' = book.value
|
||||||
AND NOT fields.key IN ('about', 'type')
|
AND NOT fields.key IN ('about', 'type')
|
||||||
|
UNION
|
||||||
|
SELECT
|
||||||
|
messages.author,
|
||||||
|
messages.content ->> '$.updates' AS about,
|
||||||
|
fields.key,
|
||||||
|
RANK() OVER (PARTITION BY messages.author, messages.content ->> '$.updates', fields.key ORDER BY messages.sequence DESC) AS rank,
|
||||||
|
fields.value
|
||||||
|
FROM messages, json_each(messages.content) AS fields, json_each(?1) AS book, json_each(?2) AS following
|
||||||
|
ON messages.author = following.value
|
||||||
|
WHERE
|
||||||
|
messages.content ->> 'type' = 'bookclubUpdate'
|
||||||
|
AND messages.content ->> '$.updates' = book.value
|
||||||
|
AND NOT fields.key IN ('about', 'updates', 'type')
|
||||||
) WHERE rank = 1
|
) WHERE rank = 1
|
||||||
GROUP BY author, about
|
GROUP BY author, about
|
||||||
`,
|
`,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "🦀",
|
"emoji": "🦀",
|
||||||
"previous": "&UUj21pyEAxsCGoNK0hk9Ue72kfpwfFKTzZDLP8fJ08g=.sha256"
|
"previous": "&aZw1V6n7AbOjTcVobViT2BpsIo7x1PFY950HHJpdBfE=.sha256"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ class TfElement extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetch_about(following, users) {
|
async fetch_about(following, users, transient) {
|
||||||
this.loading_about++;
|
this.loading_about++;
|
||||||
let ids = Object.keys(following).sort();
|
let ids = Object.keys(following).sort();
|
||||||
const k_cache_version = 3;
|
const k_cache_version = 3;
|
||||||
@@ -320,7 +320,7 @@ class TfElement extends LitElement {
|
|||||||
this.loading_about--;
|
this.loading_about--;
|
||||||
|
|
||||||
let new_cache = JSON.stringify(cache);
|
let new_cache = JSON.stringify(cache);
|
||||||
if (new_cache != original_cache) {
|
if (!transient && new_cache != original_cache) {
|
||||||
let start_time = new Date();
|
let start_time = new Date();
|
||||||
tfrpc.rpc.databaseSet('about', new_cache).then(function () {
|
tfrpc.rpc.databaseSet('about', new_cache).then(function () {
|
||||||
console.log('saving about took', (new Date() - start_time) / 1000);
|
console.log('saving about took', (new Date() - start_time) / 1000);
|
||||||
@@ -802,6 +802,18 @@ class TfElement extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async request_user(event) {
|
||||||
|
let users = {};
|
||||||
|
users[event.detail.id] = {};
|
||||||
|
users = await this.fetch_user_info(users);
|
||||||
|
if (this.users[event.detail.id]?.seq !== users[event.detail.id]?.seq) {
|
||||||
|
let self = this;
|
||||||
|
this.fetch_about(users, users, true).then(function (result) {
|
||||||
|
self.users = Object.assign({}, self.users, users);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
@@ -854,16 +866,20 @@ class TfElement extends LitElement {
|
|||||||
this.is_administrator
|
this.is_administrator
|
||||||
? html`
|
? html`
|
||||||
<button
|
<button
|
||||||
class=${'w3-bar-item w3-button w3-circle w3-ripple w3-right' +
|
class="w3-bar-item w3-button w3-circle w3-right"
|
||||||
(this.connections?.some((x) => x.flags.one_shot)
|
|
||||||
? ' w3-spin'
|
|
||||||
: '')}
|
|
||||||
@click=${this.refresh}
|
@click=${this.refresh}
|
||||||
>
|
>
|
||||||
↻
|
<span
|
||||||
|
style="display: inline-block"
|
||||||
|
class=${this.connections?.some((x) => x.flags.one_shot)
|
||||||
|
? ' w3-spin'
|
||||||
|
: ''}
|
||||||
|
>
|
||||||
|
↻
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="w3-bar-item w3-button w3-ripple w3-right"
|
class="w3-bar-item w3-button w3-right"
|
||||||
@click=${this.toggle_stay_connected}
|
@click=${this.toggle_stay_connected}
|
||||||
>
|
>
|
||||||
${this.stay_connected ? '🔗' : '⛓️💥'}
|
${this.stay_connected ? '🔗' : '⛓️💥'}
|
||||||
@@ -914,6 +930,7 @@ class TfElement extends LitElement {
|
|||||||
<div
|
<div
|
||||||
style="width: 100vw; min-height: 100vh; height: 100vh; display: flex; flex-direction: column"
|
style="width: 100vw; min-height: 100vh; height: 100vh; display: flex; flex-direction: column"
|
||||||
class="w3-theme-dark"
|
class="w3-theme-dark"
|
||||||
|
@tf-request-user=${this.request_user}
|
||||||
>
|
>
|
||||||
${progress}
|
${progress}
|
||||||
<div style="flex: 0 0">${tabs}</div>
|
<div style="flex: 0 0">${tabs}</div>
|
||||||
|
|||||||
@@ -25,6 +25,15 @@ class TfUserElement extends LitElement {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
let user = this.users[this.id];
|
let user = this.users[this.id];
|
||||||
|
if (!this.users[this.id]) {
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent('tf-request-user', {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
detail: {id: this.id},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
let shape =
|
let shape =
|
||||||
user?.follow_depth === undefined || user.follow_depth >= 2
|
user?.follow_depth === undefined || user.follow_depth >= 2
|
||||||
? 'w3-circle'
|
? 'w3-circle'
|
||||||
|
|||||||
66
core/core.js
66
core/core.js
@@ -375,73 +375,7 @@ exports.getProcessBlob = async function getProcessBlob(blobId, key, options) {
|
|||||||
Object.keys(ssb).map((key) => [key, ssb[key].bind(ssb)])
|
Object.keys(ssb).map((key) => [key, ssb[key].bind(ssb)])
|
||||||
);
|
);
|
||||||
imports.ssb.createIdentity = () => process.createIdentity();
|
imports.ssb.createIdentity = () => process.createIdentity();
|
||||||
imports.ssb.addIdentity = function (id) {
|
|
||||||
if (
|
|
||||||
process.credentials &&
|
|
||||||
process.credentials.session &&
|
|
||||||
process.credentials.session.name
|
|
||||||
) {
|
|
||||||
return Promise.resolve(
|
|
||||||
imports.core.permissionTest('ssb_id_add')
|
|
||||||
).then(function () {
|
|
||||||
return ssb.addIdentity(process.credentials.session.name, id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
imports.ssb.deleteIdentity = function (id) {
|
|
||||||
if (
|
|
||||||
process.credentials &&
|
|
||||||
process.credentials.session &&
|
|
||||||
process.credentials.session.name
|
|
||||||
) {
|
|
||||||
return Promise.resolve(
|
|
||||||
imports.core.permissionTest('ssb_id_delete')
|
|
||||||
).then(function () {
|
|
||||||
return ssb.deleteIdentity(process.credentials.session.name, id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
imports.ssb.setActiveIdentity = (id) => process.setActiveIdentity(id);
|
imports.ssb.setActiveIdentity = (id) => process.setActiveIdentity(id);
|
||||||
imports.ssb.getPrivateKey = function (id) {
|
|
||||||
if (
|
|
||||||
process.credentials &&
|
|
||||||
process.credentials.session &&
|
|
||||||
process.credentials.session.name
|
|
||||||
) {
|
|
||||||
return Promise.resolve(
|
|
||||||
imports.core.permissionTest('ssb_id_export')
|
|
||||||
).then(function () {
|
|
||||||
return ssb.getPrivateKey(process.credentials.session.name, id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
imports.ssb.appendMessageWithIdentity = function (id, message) {
|
|
||||||
if (
|
|
||||||
process.credentials &&
|
|
||||||
process.credentials.session &&
|
|
||||||
process.credentials.session.name
|
|
||||||
) {
|
|
||||||
let action;
|
|
||||||
try {
|
|
||||||
if (message?.type === 'vote' && message?.vote?.expression) {
|
|
||||||
action = `React with ${message?.vote?.expression}.`;
|
|
||||||
} else if (typeof message === 'string') {
|
|
||||||
action = `Post a private message.`;
|
|
||||||
} else {
|
|
||||||
action = `Publish ${'aeiou'.indexOf(message?.type?.toLowerCase()?.substring(0, 1)) != -1 ? 'an' : 'a'} "${message?.type}" message.`;
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
return Promise.resolve(
|
|
||||||
imports.core.permissionTest('ssb_append', action)
|
|
||||||
).then(function () {
|
|
||||||
return ssb.appendMessageWithIdentity(
|
|
||||||
process.credentials.session.name,
|
|
||||||
id,
|
|
||||||
message
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (
|
if (
|
||||||
process.credentials &&
|
process.credentials &&
|
||||||
process.credentials.session &&
|
process.credentials.session &&
|
||||||
|
|||||||
2
deps/codemirror/cm6.js
vendored
2
deps/codemirror/cm6.js
vendored
File diff suppressed because one or more lines are too long
222
deps/codemirror_src/package-lock.json
generated
vendored
222
deps/codemirror_src/package-lock.json
generated
vendored
@@ -129,14 +129,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/language": {
|
"node_modules/@codemirror/language": {
|
||||||
"version": "6.11.3",
|
"version": "6.12.1",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.3.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.1.tgz",
|
||||||
"integrity": "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==",
|
"integrity": "sha512-Fa6xkSiuGKc8XC8Cn96T+TQHYj4ZZ7RdFmXA3i9xe/3hLHfwPZdM+dqfX0Cp0zQklBKhVD8Yzc8LS45rkqcwpQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
"@codemirror/view": "^6.23.0",
|
"@codemirror/view": "^6.23.0",
|
||||||
"@lezer/common": "^1.1.0",
|
"@lezer/common": "^1.5.0",
|
||||||
"@lezer/highlight": "^1.0.0",
|
"@lezer/highlight": "^1.0.0",
|
||||||
"@lezer/lr": "^1.0.0",
|
"@lezer/lr": "^1.0.0",
|
||||||
"style-mod": "^4.0.0"
|
"style-mod": "^4.0.0"
|
||||||
@@ -165,9 +165,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/state": {
|
"node_modules/@codemirror/state": {
|
||||||
"version": "6.5.2",
|
"version": "6.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.3.tgz",
|
||||||
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
|
"integrity": "sha512-MerMzJzlXogk2fxWFU1nKp36bY5orBG59HnPiz0G9nLRebWa0zXuv2siH6PLIHBvv5TH8CkQRqjBs0MlxCZu+A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@marijn/find-cluster-break": "^1.0.0"
|
"@marijn/find-cluster-break": "^1.0.0"
|
||||||
@@ -186,9 +186,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/view": {
|
"node_modules/@codemirror/view": {
|
||||||
"version": "6.39.4",
|
"version": "6.39.7",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.4.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.7.tgz",
|
||||||
"integrity": "sha512-xMF6OfEAUVY5Waega4juo1QGACfNkNF+aJLqpd8oUJz96ms2zbfQ9Gh35/tI3y8akEV31FruKfj7hBnIU/nkqA==",
|
"integrity": "sha512-3Vif9hnNHJnl2YgOtkR/wzGzhYcQ8gy3LGdUhkLUU8xSBbgsTxrE8he/CMTpeINm5TgxLe2FmzvF6IYQL/BSAg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.5.0",
|
"@codemirror/state": "^6.5.0",
|
||||||
@@ -248,9 +248,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@lezer/common": {
|
"node_modules/@lezer/common": {
|
||||||
"version": "1.4.0",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.0.tgz",
|
||||||
"integrity": "sha512-DVeMRoGrgn/k45oQNu189BoW4SZwgZFzJ1+1TV5j2NJ/KFC83oa/enRqZSGshyeMk5cPWMhsKs9nx+8o0unwGg==",
|
"integrity": "sha512-PNGcolp9hr4PJdXR4ix7XtixDrClScvtSCYW3rQG106oVMOOI+jFb+0+J3mbeL/53g1Zd6s0kJzaw6Ri68GmAA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@lezer/css": {
|
"node_modules/@lezer/css": {
|
||||||
@@ -274,9 +274,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@lezer/html": {
|
"node_modules/@lezer/html": {
|
||||||
"version": "1.3.12",
|
"version": "1.3.13",
|
||||||
"resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.12.tgz",
|
"resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.13.tgz",
|
||||||
"integrity": "sha512-RJ7eRWdaJe3bsiiLLHjCFT1JMk8m1YP9kaUbvu2rMLEoOnke9mcTVDyfOslsln0LtujdWespjJ39w6zo+RsQYw==",
|
"integrity": "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/common": "^1.2.0",
|
"@lezer/common": "^1.2.0",
|
||||||
@@ -316,12 +316,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@lezer/markdown": {
|
"node_modules/@lezer/markdown": {
|
||||||
"version": "1.6.1",
|
"version": "1.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.6.2.tgz",
|
||||||
"integrity": "sha512-72ah+Sml7lD8Wn7lnz9vwYmZBo9aQT+I2gjK/0epI+gjdwUbWw3MJ/ZBGEqG1UfrIauRqH37/c5mVHXeCTGXtA==",
|
"integrity": "sha512-iNSdKrIK0FfOjVPVpV0fu7OykdncYpEzf4vkG9szFf60ql/ObZShoVbM9u1tgkogDOmubms1CyoNS2/unOXWNw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/common": "^1.0.0",
|
"@lezer/common": "^1.5.0",
|
||||||
"@lezer/highlight": "^1.0.0"
|
"@lezer/highlight": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -412,9 +412,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz",
|
||||||
"integrity": "sha512-iDGS/h7D8t7tvZ1t6+WPK04KD0MwzLZrG0se1hzBjSi5fyxlsiggoJHwh18PCFNn7tG43OWb6pdZ6Y+rMlmyNQ==",
|
"integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -425,9 +425,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm64": {
|
"node_modules/@rollup/rollup-android-arm64": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz",
|
||||||
"integrity": "sha512-wrSAViWvZHBMMlWk6EJhvg8/rjxzyEhEdgfMMjREHEq11EtJ6IP6yfcCH57YAEca2Oe3FNCE9DSTgU70EIGmVw==",
|
"integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -438,9 +438,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz",
|
||||||
"integrity": "sha512-S87zZPBmRO6u1YXQLwpveZm4JfPpAa6oHBX7/ghSiGH3rz/KDgAu1rKdGutV+WUI6tKDMbaBJomhnT30Y2t4VQ==",
|
"integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -451,9 +451,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-x64": {
|
"node_modules/@rollup/rollup-darwin-x64": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz",
|
||||||
"integrity": "sha512-YTbnsAaHo6VrAczISxgpTva8EkfQus0VPEVJCEaboHtZRIb6h6j0BNxRBOwnDciFTZLDPW5r+ZBmhL/+YpTZgA==",
|
"integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -464,9 +464,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz",
|
||||||
"integrity": "sha512-1T8eY2J8rKJWzaznV7zedfdhD1BqVs1iqILhmHDq/bqCUZsrMt+j8VCTHhP0vdfbHK3e1IQ7VYx3jlKqwlf+vw==",
|
"integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -477,9 +477,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz",
|
||||||
"integrity": "sha512-sHTiuXyBJApxRn+VFMaw1U+Qsz4kcNlxQ742snICYPrY+DDL8/ZbaC4DVIB7vgZmp3jiDaKA0WpBdP0aqPJoBQ==",
|
"integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -490,9 +490,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz",
|
||||||
"integrity": "sha512-dV3T9MyAf0w8zPVLVBptVlzaXxka6xg1f16VAQmjg+4KMSTWDvhimI/Y6mp8oHwNrmnmVl9XxJ/w/mO4uIQONA==",
|
"integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -503,9 +503,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz",
|
||||||
"integrity": "sha512-wIGYC1x/hyjP+KAu9+ewDI+fi5XSNiUi9Bvg6KGAh2TsNMA3tSEs+Sh6jJ/r4BV/bx/CyWu2ue9kDnIdRyafcQ==",
|
"integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -516,9 +516,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz",
|
||||||
"integrity": "sha512-Y+qVA0D9d0y2FRNiG9oM3Hut/DgODZbU9I8pLLPwAsU0tUKZ49cyV1tzmB/qRbSzGvY8lpgGkJuMyuhH7Ma+Vg==",
|
"integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -529,9 +529,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz",
|
||||||
"integrity": "sha512-juaC4bEgJsyFVfqhtGLz8mbopaWD+WeSOYr5E16y+1of6KQjc0BpwZLuxkClqY1i8sco+MdyoXPNiCkQou09+g==",
|
"integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -542,9 +542,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz",
|
||||||
"integrity": "sha512-rIEC0hZ17A42iXtHX+EPJVL/CakHo+tT7W0pbzdAGuWOt2jxDFh7A/lRhsNHBcqL4T36+UiAgwO8pbmn3dE8wA==",
|
"integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
@@ -555,9 +555,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz",
|
||||||
"integrity": "sha512-T7l409NhUE552RcAOcmJHj3xyZ2h7vMWzcwQI0hvn5tqHh3oSoclf9WgTl+0QqffWFG8MEVZZP1/OBglKZx52Q==",
|
"integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -568,9 +568,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz",
|
||||||
"integrity": "sha512-7OK5/GhxbnrMcxIFoYfhV/TkknarkYC1hqUw1wU2xUN3TVRLNT5FmBv4KkheSG2xZ6IEbRAhTooTV2+R5Tk0lQ==",
|
"integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@@ -581,9 +581,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz",
|
||||||
"integrity": "sha512-GwuDBE/PsXaTa76lO5eLJTyr2k8QkPipAyOrs4V/KJufHCZBJ495VCGJol35grx9xryk4V+2zd3Ri+3v7NPh+w==",
|
"integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@@ -594,9 +594,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz",
|
||||||
"integrity": "sha512-IAE1Ziyr1qNfnmiQLHBURAD+eh/zH1pIeJjeShleII7Vj8kyEm2PF77o+lf3WTHDpNJcu4IXJxNO0Zluro8bOw==",
|
"integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -607,9 +607,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz",
|
||||||
"integrity": "sha512-Pg6E+oP7GvZ4XwgRJBuSXZjcqpIW3yCBhK4BcsANvb47qMvAbCjR6E+1a/U2WXz1JJxp9/4Dno3/iSJLcm5auw==",
|
"integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -620,9 +620,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz",
|
||||||
"integrity": "sha512-txGtluxDKTxaMDzUduGP0wdfng24y1rygUMnmlUJ88fzCCULCLn7oE5kb2+tRB+MWq1QDZT6ObT5RrR8HFRKqg==",
|
"integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -633,9 +633,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-openharmony-arm64": {
|
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz",
|
||||||
"integrity": "sha512-3DFiLPnTxiOQV993fMc+KO8zXHTcIjgaInrqlG8zDp1TlhYl6WgrOHuJkJQ6M8zHEcntSJsUp1XFZSY8C1DYbg==",
|
"integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -646,9 +646,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz",
|
||||||
"integrity": "sha512-nggc/wPpNTgjGg75hu+Q/3i32R00Lq1B6N1DO7MCU340MRKL3WZJMjA9U4K4gzy3dkZPXm9E1Nc81FItBVGRlA==",
|
"integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -659,9 +659,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz",
|
||||||
"integrity": "sha512-U/54pTbdQpPLBdEzCT6NBCFAfSZMvmjr0twhnD9f4EIvlm9wy3jjQ38yQj1AGznrNO65EWQMgm/QUjuIVrYF9w==",
|
"integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -672,9 +672,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz",
|
||||||
"integrity": "sha512-2NqKgZSuLH9SXBBV2dWNRCZmocgSOx8OJSdpRaEcRlIfX8YrKxUT6z0F1NpvDVhOsl190UFTRh2F2WDWWCYp3A==",
|
"integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -685,9 +685,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz",
|
||||||
"integrity": "sha512-JRpZUhCfhZ4keB5v0fe02gQJy05GqboPOaxvjugW04RLSYYoB/9t2lx2u/tMs/Na/1NXfY8QYjgRljRpN+MjTQ==",
|
"integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -877,9 +877,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.53.5",
|
"version": "4.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.5.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz",
|
||||||
"integrity": "sha512-iTNAbFSlRpcHeeWu73ywU/8KuU/LZmNCSxp6fjQkJBD3ivUb8tpDrXhIxEzA05HlYMEwmtaUnb3RP+YNv162OQ==",
|
"integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "1.0.8"
|
"@types/estree": "1.0.8"
|
||||||
@@ -892,28 +892,28 @@
|
|||||||
"npm": ">=8.0.0"
|
"npm": ">=8.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rollup/rollup-android-arm-eabi": "4.53.5",
|
"@rollup/rollup-android-arm-eabi": "4.54.0",
|
||||||
"@rollup/rollup-android-arm64": "4.53.5",
|
"@rollup/rollup-android-arm64": "4.54.0",
|
||||||
"@rollup/rollup-darwin-arm64": "4.53.5",
|
"@rollup/rollup-darwin-arm64": "4.54.0",
|
||||||
"@rollup/rollup-darwin-x64": "4.53.5",
|
"@rollup/rollup-darwin-x64": "4.54.0",
|
||||||
"@rollup/rollup-freebsd-arm64": "4.53.5",
|
"@rollup/rollup-freebsd-arm64": "4.54.0",
|
||||||
"@rollup/rollup-freebsd-x64": "4.53.5",
|
"@rollup/rollup-freebsd-x64": "4.54.0",
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": "4.53.5",
|
"@rollup/rollup-linux-arm-gnueabihf": "4.54.0",
|
||||||
"@rollup/rollup-linux-arm-musleabihf": "4.53.5",
|
"@rollup/rollup-linux-arm-musleabihf": "4.54.0",
|
||||||
"@rollup/rollup-linux-arm64-gnu": "4.53.5",
|
"@rollup/rollup-linux-arm64-gnu": "4.54.0",
|
||||||
"@rollup/rollup-linux-arm64-musl": "4.53.5",
|
"@rollup/rollup-linux-arm64-musl": "4.54.0",
|
||||||
"@rollup/rollup-linux-loong64-gnu": "4.53.5",
|
"@rollup/rollup-linux-loong64-gnu": "4.54.0",
|
||||||
"@rollup/rollup-linux-ppc64-gnu": "4.53.5",
|
"@rollup/rollup-linux-ppc64-gnu": "4.54.0",
|
||||||
"@rollup/rollup-linux-riscv64-gnu": "4.53.5",
|
"@rollup/rollup-linux-riscv64-gnu": "4.54.0",
|
||||||
"@rollup/rollup-linux-riscv64-musl": "4.53.5",
|
"@rollup/rollup-linux-riscv64-musl": "4.54.0",
|
||||||
"@rollup/rollup-linux-s390x-gnu": "4.53.5",
|
"@rollup/rollup-linux-s390x-gnu": "4.54.0",
|
||||||
"@rollup/rollup-linux-x64-gnu": "4.53.5",
|
"@rollup/rollup-linux-x64-gnu": "4.54.0",
|
||||||
"@rollup/rollup-linux-x64-musl": "4.53.5",
|
"@rollup/rollup-linux-x64-musl": "4.54.0",
|
||||||
"@rollup/rollup-openharmony-arm64": "4.53.5",
|
"@rollup/rollup-openharmony-arm64": "4.54.0",
|
||||||
"@rollup/rollup-win32-arm64-msvc": "4.53.5",
|
"@rollup/rollup-win32-arm64-msvc": "4.54.0",
|
||||||
"@rollup/rollup-win32-ia32-msvc": "4.53.5",
|
"@rollup/rollup-win32-ia32-msvc": "4.54.0",
|
||||||
"@rollup/rollup-win32-x64-gnu": "4.53.5",
|
"@rollup/rollup-win32-x64-gnu": "4.54.0",
|
||||||
"@rollup/rollup-win32-x64-msvc": "4.53.5",
|
"@rollup/rollup-win32-x64-msvc": "4.54.0",
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
430
src/api.js.c
430
src/api.js.c
@@ -24,6 +24,8 @@
|
|||||||
#include <alloca.h>
|
#include <alloca.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static JSClassID s_permission_test_class_id;
|
||||||
|
|
||||||
typedef struct _app_path_pair_t
|
typedef struct _app_path_pair_t
|
||||||
{
|
{
|
||||||
const char* app;
|
const char* app;
|
||||||
@@ -884,38 +886,59 @@ typedef void(permission_test_callback_t)(JSContext* context, bool granted, JSVal
|
|||||||
|
|
||||||
typedef struct _permission_test_t
|
typedef struct _permission_test_t
|
||||||
{
|
{
|
||||||
|
JSContext* context;
|
||||||
|
bool completed;
|
||||||
permission_test_callback_t* callback;
|
permission_test_callback_t* callback;
|
||||||
void* user_data;
|
void* user_data;
|
||||||
} permission_test_t;
|
} permission_test_t;
|
||||||
|
|
||||||
static JSValue _tf_ssb_permission_test_resolve(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
static JSValue _tf_ssb_permission_test_resolve(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||||
{
|
{
|
||||||
JSClassID class_id = 0;
|
permission_test_t* work = JS_GetOpaque(data[0], s_permission_test_class_id);
|
||||||
permission_test_t* work = JS_GetAnyOpaque(data[0], &class_id);
|
work->completed = true;
|
||||||
JS_FreeValue(context, data[0]);
|
|
||||||
work->callback(context, true, argv[0], work->user_data);
|
work->callback(context, true, argv[0], work->user_data);
|
||||||
tf_free(work);
|
|
||||||
return JS_UNDEFINED;
|
return JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSValue _tf_ssb_permission_test_reject(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
static JSValue _tf_ssb_permission_test_reject(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||||
{
|
{
|
||||||
JSClassID class_id = 0;
|
permission_test_t* work = JS_GetOpaque(data[0], s_permission_test_class_id);
|
||||||
permission_test_t* work = JS_GetAnyOpaque(data[0], &class_id);
|
work->completed = true;
|
||||||
JS_FreeValue(context, data[0]);
|
|
||||||
work->callback(context, false, argv[0], work->user_data);
|
work->callback(context, false, argv[0], work->user_data);
|
||||||
tf_free(work);
|
|
||||||
return JS_UNDEFINED;
|
return JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_permission_test_finalizer(JSRuntime* runtime, JSValue value)
|
||||||
|
{
|
||||||
|
permission_test_t* work = JS_GetOpaque(value, s_permission_test_class_id);
|
||||||
|
if (!work->completed)
|
||||||
|
{
|
||||||
|
JSValue arg = JS_ThrowInternalError(work->context, "Permission test incomplete.");
|
||||||
|
work->callback(work->context, false, arg, work->user_data);
|
||||||
|
JS_FreeValue(work->context, arg);
|
||||||
|
}
|
||||||
|
tf_free(work);
|
||||||
|
}
|
||||||
|
|
||||||
static void _tf_ssb_permission_test(JSContext* context, JSValue process, const char* permission, const char* description, permission_test_callback_t* callback, void* user_data)
|
static void _tf_ssb_permission_test(JSContext* context, JSValue process, const char* permission, const char* description, permission_test_callback_t* callback, void* user_data)
|
||||||
{
|
{
|
||||||
|
if (!s_permission_test_class_id)
|
||||||
|
{
|
||||||
|
JSClassDef def = {
|
||||||
|
.class_name = "permission_test",
|
||||||
|
.finalizer = _tf_ssb_permission_test_finalizer,
|
||||||
|
};
|
||||||
|
JS_NewClassID(&s_permission_test_class_id);
|
||||||
|
JS_NewClass(JS_GetRuntime(context), s_permission_test_class_id, &def);
|
||||||
|
}
|
||||||
|
|
||||||
permission_test_t* payload = tf_malloc(sizeof(permission_test_t));
|
permission_test_t* payload = tf_malloc(sizeof(permission_test_t));
|
||||||
*payload = (permission_test_t) {
|
*payload = (permission_test_t) {
|
||||||
|
.context = context,
|
||||||
.callback = callback,
|
.callback = callback,
|
||||||
.user_data = user_data,
|
.user_data = user_data,
|
||||||
};
|
};
|
||||||
JSValue opaque = JS_NewObject(context);
|
JSValue opaque = JS_NewObjectClass(context, s_permission_test_class_id);
|
||||||
JS_SetOpaque(opaque, payload);
|
JS_SetOpaque(opaque, payload);
|
||||||
JSValue imports = JS_GetPropertyStr(context, process, "imports");
|
JSValue imports = JS_GetPropertyStr(context, process, "imports");
|
||||||
JSValue core = JS_GetPropertyStr(context, imports, "core");
|
JSValue core = JS_GetPropertyStr(context, imports, "core");
|
||||||
@@ -934,10 +957,11 @@ static void _tf_ssb_permission_test(JSContext* context, JSValue process, const c
|
|||||||
JS_FreeValue(context, result);
|
JS_FreeValue(context, result);
|
||||||
result = JS_Call(context, catch, promise, 1, &reject);
|
result = JS_Call(context, catch, promise, 1, &reject);
|
||||||
tf_util_report_error(context, result);
|
tf_util_report_error(context, result);
|
||||||
|
JS_FreeValue(context, result);
|
||||||
|
JS_FreeValue(context, opaque);
|
||||||
JS_FreeValue(context, promise);
|
JS_FreeValue(context, promise);
|
||||||
JS_FreeValue(context, resolve);
|
JS_FreeValue(context, resolve);
|
||||||
JS_FreeValue(context, reject);
|
JS_FreeValue(context, reject);
|
||||||
JS_FreeValue(context, result);
|
|
||||||
JS_FreeValue(context, then);
|
JS_FreeValue(context, then);
|
||||||
JS_FreeValue(context, catch);
|
JS_FreeValue(context, catch);
|
||||||
for (int i = 0; i < tf_countof(args); i++)
|
for (int i = 0; i < tf_countof(args); i++)
|
||||||
@@ -1577,6 +1601,388 @@ static JSValue _tf_ssb_private_message_decrypt(JSContext* context, JSValueConst
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct _append_message_t
|
||||||
|
{
|
||||||
|
char id[k_id_base64_len];
|
||||||
|
uint8_t private_key[crypto_sign_SECRETKEYBYTES];
|
||||||
|
bool got_private_key;
|
||||||
|
char previous_id[512];
|
||||||
|
int32_t previous_sequence;
|
||||||
|
JSContext* context;
|
||||||
|
JSValue promise[2];
|
||||||
|
JSValue message;
|
||||||
|
char user[];
|
||||||
|
} append_message_t;
|
||||||
|
|
||||||
|
static void _tf_ssb_appendMessage_finish(append_message_t* async, bool success, JSValue result)
|
||||||
|
{
|
||||||
|
JSValue error = JS_Call(async->context, success ? async->promise[0] : async->promise[1], JS_UNDEFINED, 1, &result);
|
||||||
|
tf_util_report_error(async->context, error);
|
||||||
|
JS_FreeValue(async->context, error);
|
||||||
|
JS_FreeValue(async->context, async->message);
|
||||||
|
JS_FreeValue(async->context, async->promise[0]);
|
||||||
|
JS_FreeValue(async->context, async->promise[1]);
|
||||||
|
tf_free(async);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_appendMessageWithIdentity_callback(const char* id, bool verified, bool is_new, void* user_data)
|
||||||
|
{
|
||||||
|
append_message_t* async = user_data;
|
||||||
|
JSValue result = JS_UNDEFINED;
|
||||||
|
if (verified)
|
||||||
|
{
|
||||||
|
result = is_new ? JS_TRUE : JS_FALSE;
|
||||||
|
}
|
||||||
|
_tf_ssb_appendMessage_finish(async, verified, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_append_message_with_identity_get_key_work(tf_ssb_t* ssb, void* user_data)
|
||||||
|
{
|
||||||
|
append_message_t* work = user_data;
|
||||||
|
work->got_private_key = tf_ssb_db_identity_get_private_key(ssb, work->user, work->id, work->private_key, sizeof(work->private_key));
|
||||||
|
if (!work->got_private_key && tf_ssb_db_user_has_permission(ssb, NULL, work->user, "administration"))
|
||||||
|
{
|
||||||
|
work->got_private_key = tf_ssb_db_identity_get_private_key(ssb, ":admin", work->id, work->private_key, sizeof(work->private_key));
|
||||||
|
}
|
||||||
|
tf_ssb_db_get_latest_message_by_author(ssb, work->id, &work->previous_sequence, work->previous_id, sizeof(work->previous_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_append_message_with_identity_get_key_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
|
{
|
||||||
|
append_message_t* work = user_data;
|
||||||
|
if (work->got_private_key)
|
||||||
|
{
|
||||||
|
JSValue signed_message = tf_ssb_sign_message(ssb, work->id, work->private_key, work->message, work->previous_id, work->previous_sequence);
|
||||||
|
tf_ssb_verify_strip_and_store_message(ssb, signed_message, _tf_ssb_appendMessageWithIdentity_callback, work);
|
||||||
|
JS_FreeValue(work->context, signed_message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_tf_ssb_appendMessage_finish(work, false, JS_ThrowInternalError(work->context, "Unable to get private key for user %s with identity %s.", work->user, work->id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_append_message_permission_callback(JSContext* context, bool granted, JSValue value, void* user_data)
|
||||||
|
{
|
||||||
|
tf_task_t* task = tf_task_get(context);
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
append_message_t* work = user_data;
|
||||||
|
if (granted)
|
||||||
|
{
|
||||||
|
tf_ssb_run_work(ssb, _tf_ssb_append_message_with_identity_get_key_work, _tf_ssb_append_message_with_identity_get_key_after_work, work);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_tf_ssb_appendMessage_finish(work, false, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_ssb_appendMessageWithIdentity(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||||
|
{
|
||||||
|
JSValue process = data[0];
|
||||||
|
|
||||||
|
char description[1024] = "Publish a new message.";
|
||||||
|
const char* type = tf_util_get_property_as_string(context, argv[1], "type");
|
||||||
|
if (type)
|
||||||
|
{
|
||||||
|
if (strcmp(type, "vote") == 0)
|
||||||
|
{
|
||||||
|
JSValue vote = JS_GetPropertyStr(context, argv[1], "vote");
|
||||||
|
const char* expression = tf_util_get_property_as_string(context, vote, "expression");
|
||||||
|
snprintf(description, sizeof(description), "React with %s.", expression);
|
||||||
|
JS_FreeCString(context, expression);
|
||||||
|
JS_FreeValue(context, vote);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(description, sizeof(description), "Publish a new %s message.", type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (JS_IsString(argv[1]))
|
||||||
|
{
|
||||||
|
tf_string_set(description, sizeof(description), "Publish a new private message.");
|
||||||
|
}
|
||||||
|
JS_FreeCString(context, type);
|
||||||
|
|
||||||
|
const char* user = _tf_ssb_get_process_credentials_session_name(context, process);
|
||||||
|
if (!user)
|
||||||
|
{
|
||||||
|
return JS_ThrowInternalError(context, "Invalid user.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t user_length = strlen(user);
|
||||||
|
const char* id = JS_ToCString(context, argv[0]);
|
||||||
|
|
||||||
|
append_message_t* work = tf_malloc(sizeof(append_message_t) + user_length + 1);
|
||||||
|
*work = (append_message_t) { .context = context, .message = JS_DupValue(context, argv[1]) };
|
||||||
|
memcpy(work->user, user, user_length + 1);
|
||||||
|
tf_string_set(work->id, sizeof(work->id), id);
|
||||||
|
|
||||||
|
JS_FreeCString(context, user);
|
||||||
|
JS_FreeCString(context, id);
|
||||||
|
|
||||||
|
JSValue result = JS_NewPromiseCapability(context, work->promise);
|
||||||
|
_tf_ssb_permission_test(context, process, "ssb_append", description, _tf_ssb_append_message_permission_callback, work);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _add_identity_t
|
||||||
|
{
|
||||||
|
uint8_t key[crypto_sign_SECRETKEYBYTES / 2];
|
||||||
|
bool added;
|
||||||
|
JSValue result;
|
||||||
|
JSValue promise[2];
|
||||||
|
char user[];
|
||||||
|
} add_identity_t;
|
||||||
|
|
||||||
|
static void _tf_ssb_add_identity_work(tf_ssb_t* ssb, void* user_data)
|
||||||
|
{
|
||||||
|
add_identity_t* work = user_data;
|
||||||
|
uint8_t public_key[crypto_sign_PUBLICKEYBYTES];
|
||||||
|
unsigned char seed[crypto_sign_SEEDBYTES];
|
||||||
|
uint8_t secret_key[crypto_sign_SECRETKEYBYTES] = { 0 };
|
||||||
|
memcpy(secret_key, work->key, sizeof(secret_key) / 2);
|
||||||
|
if (crypto_sign_ed25519_sk_to_seed(seed, secret_key) == 0 && crypto_sign_seed_keypair(public_key, secret_key, seed) == 0)
|
||||||
|
{
|
||||||
|
char public_key_b64[512];
|
||||||
|
tf_base64_encode(public_key, sizeof(public_key), public_key_b64, sizeof(public_key_b64));
|
||||||
|
snprintf(public_key_b64 + strlen(public_key_b64), sizeof(public_key_b64) - strlen(public_key_b64), ".ed25519");
|
||||||
|
|
||||||
|
uint8_t combined[crypto_sign_SECRETKEYBYTES];
|
||||||
|
memcpy(combined, work->key, sizeof(work->key));
|
||||||
|
memcpy(combined + sizeof(work->key), public_key, sizeof(public_key));
|
||||||
|
char combined_b64[512];
|
||||||
|
tf_base64_encode(combined, sizeof(combined), combined_b64, sizeof(combined_b64));
|
||||||
|
snprintf(combined_b64 + strlen(combined_b64), sizeof(combined_b64) - strlen(combined_b64), ".ed25519");
|
||||||
|
|
||||||
|
work->added = tf_ssb_db_identity_add(ssb, work->user, public_key_b64, combined_b64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_add_identity_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
|
{
|
||||||
|
add_identity_t* work = user_data;
|
||||||
|
JSContext* context = tf_ssb_get_context(ssb);
|
||||||
|
JSValue result = JS_IsUndefined(work->result) ? (work->added ? JS_TRUE : JS_UNDEFINED) : work->result;
|
||||||
|
JSValue error = JS_Call(context, work->promise[work->added ? 0 : 1], JS_UNDEFINED, 1, &result);
|
||||||
|
JS_FreeValue(context, result);
|
||||||
|
tf_util_report_error(context, error);
|
||||||
|
JS_FreeValue(context, error);
|
||||||
|
JS_FreeValue(context, work->result);
|
||||||
|
JS_FreeValue(context, work->promise[0]);
|
||||||
|
JS_FreeValue(context, work->promise[1]);
|
||||||
|
tf_free(work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_add_identity_permission_callback(JSContext* context, bool granted, JSValue value, void* user_data)
|
||||||
|
{
|
||||||
|
add_identity_t* work = user_data;
|
||||||
|
tf_task_t* task = tf_task_get(context);
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
if (granted)
|
||||||
|
{
|
||||||
|
tf_ssb_run_work(ssb, _tf_ssb_add_identity_work, _tf_ssb_add_identity_after_work, work);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
work->result = JS_DupValue(context, value);
|
||||||
|
_tf_ssb_add_identity_after_work(ssb, -1, work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_ssb_addIdentity(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||||
|
{
|
||||||
|
JSValue process = data[0];
|
||||||
|
JSValue result = JS_UNDEFINED;
|
||||||
|
const char* user = _tf_ssb_get_process_credentials_session_name(context, process);
|
||||||
|
size_t user_length = user ? strlen(user) : 0;
|
||||||
|
|
||||||
|
JSValue buffer = JS_UNDEFINED;
|
||||||
|
size_t length = 0;
|
||||||
|
uint8_t* array = tf_util_try_get_array_buffer(context, &length, argv[0]);
|
||||||
|
if (!array)
|
||||||
|
{
|
||||||
|
size_t offset;
|
||||||
|
size_t element_size;
|
||||||
|
buffer = tf_util_try_get_typed_array_buffer(context, argv[0], &offset, &length, &element_size);
|
||||||
|
if (!JS_IsException(buffer))
|
||||||
|
{
|
||||||
|
array = tf_util_try_get_array_buffer(context, &length, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array)
|
||||||
|
{
|
||||||
|
if (length == crypto_sign_SECRETKEYBYTES / 2)
|
||||||
|
{
|
||||||
|
add_identity_t* work = tf_malloc(sizeof(add_identity_t) + user_length + 1);
|
||||||
|
*work = (add_identity_t) { .result = JS_UNDEFINED };
|
||||||
|
memcpy(work->key, array, sizeof(work->key));
|
||||||
|
if (user)
|
||||||
|
{
|
||||||
|
memcpy(work->user, user, user_length + 1);
|
||||||
|
}
|
||||||
|
result = JS_NewPromiseCapability(context, work->promise);
|
||||||
|
_tf_ssb_permission_test(context, process, "ssb_id_add", "Add an identity.", _tf_ssb_add_identity_permission_callback, work);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = JS_ThrowInternalError(context, "Unexpected private key size: %d vs. %d\n", (int)length, crypto_sign_SECRETKEYBYTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = JS_ThrowInternalError(context, "Expected array argument.");
|
||||||
|
}
|
||||||
|
JS_FreeValue(context, buffer);
|
||||||
|
JS_FreeCString(context, user);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _delete_identity_t
|
||||||
|
{
|
||||||
|
char id[k_id_base64_len];
|
||||||
|
bool deleted;
|
||||||
|
JSValue result;
|
||||||
|
JSValue promise[2];
|
||||||
|
char user[];
|
||||||
|
} delete_identity_t;
|
||||||
|
|
||||||
|
static void _tf_ssb_delete_identity_work(tf_ssb_t* ssb, void* user_data)
|
||||||
|
{
|
||||||
|
delete_identity_t* work = user_data;
|
||||||
|
work->deleted = tf_ssb_db_identity_delete(ssb, work->user, work->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_delete_identity_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
|
{
|
||||||
|
delete_identity_t* work = user_data;
|
||||||
|
JSContext* context = tf_ssb_get_context(ssb);
|
||||||
|
JSValue result = work->deleted ? JS_TRUE : JS_FALSE;
|
||||||
|
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
|
||||||
|
JS_FreeValue(context, result);
|
||||||
|
tf_util_report_error(context, error);
|
||||||
|
JS_FreeValue(context, error);
|
||||||
|
JS_FreeValue(context, work->promise[0]);
|
||||||
|
JS_FreeValue(context, work->promise[1]);
|
||||||
|
tf_free(work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_delete_identity_permission_callback(JSContext* context, bool granted, JSValue value, void* user_data)
|
||||||
|
{
|
||||||
|
delete_identity_t* work = user_data;
|
||||||
|
tf_task_t* task = tf_task_get(context);
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
if (granted)
|
||||||
|
{
|
||||||
|
tf_ssb_run_work(ssb, _tf_ssb_delete_identity_work, _tf_ssb_delete_identity_after_work, work);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
work->result = JS_DupValue(context, value);
|
||||||
|
_tf_ssb_delete_identity_after_work(ssb, -1, work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_ssb_deleteIdentity(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||||
|
{
|
||||||
|
JSValue result = JS_UNDEFINED;
|
||||||
|
JSValue process = data[0];
|
||||||
|
const char* user = _tf_ssb_get_process_credentials_session_name(context, process);
|
||||||
|
const char* id = JS_ToCString(context, argv[0]);
|
||||||
|
if (id && user)
|
||||||
|
{
|
||||||
|
size_t user_length = strlen(user);
|
||||||
|
delete_identity_t* work = tf_malloc(sizeof(delete_identity_t) + user_length + 1);
|
||||||
|
*work = (delete_identity_t) { .result = JS_UNDEFINED };
|
||||||
|
tf_string_set(work->id, sizeof(work->id), *id == '@' ? id + 1 : id);
|
||||||
|
memcpy(work->user, user, user_length + 1);
|
||||||
|
result = JS_NewPromiseCapability(context, work->promise);
|
||||||
|
_tf_ssb_permission_test(context, process, "ssb_id_delete", "Delete an identity.", _tf_ssb_delete_identity_permission_callback, work);
|
||||||
|
}
|
||||||
|
JS_FreeCString(context, id);
|
||||||
|
JS_FreeCString(context, user);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _get_private_key_t
|
||||||
|
{
|
||||||
|
JSContext* context;
|
||||||
|
JSValue result;
|
||||||
|
JSValue promise[2];
|
||||||
|
char id[k_id_base64_len];
|
||||||
|
uint8_t private_key[crypto_sign_SECRETKEYBYTES];
|
||||||
|
bool got_private_key;
|
||||||
|
char user[];
|
||||||
|
} get_private_key_t;
|
||||||
|
|
||||||
|
static void _tf_ssb_get_private_key_work(tf_ssb_t* ssb, void* user_data)
|
||||||
|
{
|
||||||
|
get_private_key_t* work = user_data;
|
||||||
|
work->got_private_key = tf_ssb_db_identity_get_private_key(ssb, work->user, work->id, work->private_key, sizeof(work->private_key));
|
||||||
|
if (!work->got_private_key && tf_ssb_db_user_has_permission(ssb, NULL, work->user, "administration"))
|
||||||
|
{
|
||||||
|
work->got_private_key = tf_ssb_db_identity_get_private_key(ssb, ":admin", work->id, work->private_key, sizeof(work->private_key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_get_private_key_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
|
{
|
||||||
|
get_private_key_t* work = user_data;
|
||||||
|
JSValue result = JS_UNDEFINED;
|
||||||
|
JSContext* context = work->context;
|
||||||
|
if (work->got_private_key && JS_IsUndefined(work->result))
|
||||||
|
{
|
||||||
|
result = tf_util_new_uint8_array(context, work->private_key, sizeof(work->private_key) / 2);
|
||||||
|
}
|
||||||
|
JSValue error = JS_Call(context, work->promise[work->got_private_key ? 0 : 1], JS_UNDEFINED, 1, &result);
|
||||||
|
JS_FreeValue(context, result);
|
||||||
|
tf_util_report_error(context, error);
|
||||||
|
JS_FreeValue(context, error);
|
||||||
|
JS_FreeValue(context, work->promise[0]);
|
||||||
|
JS_FreeValue(context, work->promise[1]);
|
||||||
|
JS_FreeValue(context, work->result);
|
||||||
|
tf_free(work);
|
||||||
|
}
|
||||||
|
static void _tf_ssb_get_private_key_permission_callback(JSContext* context, bool granted, JSValue value, void* user_data)
|
||||||
|
{
|
||||||
|
get_private_key_t* work = user_data;
|
||||||
|
tf_task_t* task = tf_task_get(context);
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
if (granted)
|
||||||
|
{
|
||||||
|
tf_ssb_run_work(ssb, _tf_ssb_get_private_key_work, _tf_ssb_get_private_key_after_work, work);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
work->result = JS_DupValue(context, value);
|
||||||
|
_tf_ssb_get_private_key_after_work(ssb, -1, work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_ssb_getPrivateKey(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||||
|
{
|
||||||
|
JSValue process = data[0];
|
||||||
|
const char* user = _tf_ssb_get_process_credentials_session_name(context, process);
|
||||||
|
size_t user_length = user ? strlen(user) : 0;
|
||||||
|
const char* id = JS_ToCString(context, argv[0]);
|
||||||
|
get_private_key_t* work = tf_malloc(sizeof(get_private_key_t) + user_length + 1);
|
||||||
|
*work = (get_private_key_t) { .context = context, .result = JS_UNDEFINED };
|
||||||
|
if (user)
|
||||||
|
{
|
||||||
|
memcpy(work->user, user, user_length + 1);
|
||||||
|
}
|
||||||
|
tf_string_set(work->id, sizeof(work->id), id);
|
||||||
|
JSValue result = JS_NewPromiseCapability(context, work->promise);
|
||||||
|
_tf_ssb_permission_test(context, process, "ssb_id_export", "Export a private key.", _tf_ssb_get_private_key_permission_callback, work);
|
||||||
|
|
||||||
|
JS_FreeCString(context, user);
|
||||||
|
JS_FreeCString(context, id);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static JSValue _tf_api_register_imports(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
static JSValue _tf_api_register_imports(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
{
|
{
|
||||||
JSValue imports = argv[0];
|
JSValue imports = argv[0];
|
||||||
@@ -1606,6 +2012,10 @@ static JSValue _tf_api_register_imports(JSContext* context, JSValueConst this_va
|
|||||||
JS_SetPropertyStr(context, ssb, "getOwnerIdentities", JS_NewCFunctionData(context, _tf_ssb_getOwnerIdentities, 0, 0, 1, &process));
|
JS_SetPropertyStr(context, ssb, "getOwnerIdentities", JS_NewCFunctionData(context, _tf_ssb_getOwnerIdentities, 0, 0, 1, &process));
|
||||||
JS_SetPropertyStr(context, ssb, "privateMessageEncrypt", JS_NewCFunctionData(context, _tf_ssb_private_message_encrypt, 3, 0, 1, &process));
|
JS_SetPropertyStr(context, ssb, "privateMessageEncrypt", JS_NewCFunctionData(context, _tf_ssb_private_message_encrypt, 3, 0, 1, &process));
|
||||||
JS_SetPropertyStr(context, ssb, "privateMessageDecrypt", JS_NewCFunctionData(context, _tf_ssb_private_message_decrypt, 2, 0, 1, &process));
|
JS_SetPropertyStr(context, ssb, "privateMessageDecrypt", JS_NewCFunctionData(context, _tf_ssb_private_message_decrypt, 2, 0, 1, &process));
|
||||||
|
JS_SetPropertyStr(context, ssb, "appendMessageWithIdentity", JS_NewCFunctionData(context, _tf_ssb_appendMessageWithIdentity, 2, 0, 1, &process));
|
||||||
|
JS_SetPropertyStr(context, ssb, "addIdentity", JS_NewCFunctionData(context, _tf_ssb_addIdentity, 1, 0, 1, &process));
|
||||||
|
JS_SetPropertyStr(context, ssb, "deleteIdentity", JS_NewCFunctionData(context, _tf_ssb_deleteIdentity, 1, 0, 1, &process));
|
||||||
|
JS_SetPropertyStr(context, ssb, "getPrivateKey", JS_NewCFunctionData(context, _tf_ssb_getPrivateKey, 1, 0, 1, &process));
|
||||||
JS_FreeValue(context, ssb);
|
JS_FreeValue(context, ssb);
|
||||||
|
|
||||||
JSValue credentials = JS_GetPropertyStr(context, process, "credentials");
|
JSValue credentials = JS_GetPropertyStr(context, process, "credentials");
|
||||||
|
|||||||
@@ -671,7 +671,7 @@ static void _httpd_auth_query_after_work(tf_ssb_t* ssb, int status, void* user_d
|
|||||||
JSValue out_permissions = JS_NewObject(context);
|
JSValue out_permissions = JS_NewObject(context);
|
||||||
JS_SetPropertyStr(context, work->credentials, "permissions", out_permissions);
|
JS_SetPropertyStr(context, work->credentials, "permissions", out_permissions);
|
||||||
JSValue permissions = !JS_IsUndefined(settings_value) ? JS_GetPropertyStr(context, settings_value, "permissions") : JS_UNDEFINED;
|
JSValue permissions = !JS_IsUndefined(settings_value) ? JS_GetPropertyStr(context, settings_value, "permissions") : JS_UNDEFINED;
|
||||||
JSValue user_permissions = !JS_IsUndefined(permissions) ? JS_GetPropertyStr(context, permissions, name_string) : JS_UNDEFINED;
|
JSValue user_permissions = name_string && !JS_IsUndefined(permissions) ? JS_GetPropertyStr(context, permissions, name_string) : JS_UNDEFINED;
|
||||||
int length = !JS_IsUndefined(user_permissions) ? tf_util_get_length(context, user_permissions) : 0;
|
int length = !JS_IsUndefined(user_permissions) ? tf_util_get_length(context, user_permissions) : 0;
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
@@ -743,7 +743,6 @@ static void _httpd_auth_query_after_work(tf_ssb_t* ssb, int status, void* user_d
|
|||||||
tf_free((void*)cookie);
|
tf_free((void*)cookie);
|
||||||
JS_FreeCString(context, name_string);
|
JS_FreeCString(context, name_string);
|
||||||
|
|
||||||
// tf_http_request_unref(request);
|
|
||||||
request->on_message = _httpd_app_on_message;
|
request->on_message = _httpd_app_on_message;
|
||||||
request->on_close = _httpd_app_on_close;
|
request->on_close = _httpd_app_on_close;
|
||||||
request->context = context;
|
request->context = context;
|
||||||
@@ -769,18 +768,19 @@ void tf_httpd_endpoint_app_socket(tf_http_request_t* request)
|
|||||||
JSValue jwt = tf_httpd_authenticate_jwt(ssb, context, session);
|
JSValue jwt = tf_httpd_authenticate_jwt(ssb, context, session);
|
||||||
tf_free((void*)session);
|
tf_free((void*)session);
|
||||||
|
|
||||||
|
JSValue credentials = JS_NewObject(context);
|
||||||
if (!JS_IsUndefined(jwt))
|
if (!JS_IsUndefined(jwt))
|
||||||
{
|
{
|
||||||
JSValue credentials = JS_NewObject(context);
|
|
||||||
JS_SetPropertyStr(context, credentials, "session", jwt);
|
JS_SetPropertyStr(context, credentials, "session", jwt);
|
||||||
tf_http_request_ref(request);
|
|
||||||
app_t* work = tf_malloc(sizeof(app_t));
|
|
||||||
*work = (app_t) {
|
|
||||||
.request = request,
|
|
||||||
.credentials = credentials,
|
|
||||||
.timer = { .data = work },
|
|
||||||
};
|
|
||||||
uv_timer_init(tf_ssb_get_loop(ssb), &work->timer);
|
|
||||||
tf_ssb_run_work(ssb, _httpd_auth_query_work, _httpd_auth_query_after_work, work);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tf_http_request_ref(request);
|
||||||
|
app_t* work = tf_malloc(sizeof(app_t));
|
||||||
|
*work = (app_t) {
|
||||||
|
.request = request,
|
||||||
|
.credentials = credentials,
|
||||||
|
.timer = { .data = work },
|
||||||
|
};
|
||||||
|
uv_timer_init(tf_ssb_get_loop(ssb), &work->timer);
|
||||||
|
tf_ssb_run_work(ssb, _httpd_auth_query_work, _httpd_auth_query_after_work, work);
|
||||||
}
|
}
|
||||||
|
|||||||
87
src/ssb.db.c
87
src/ssb.db.c
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
|
#include "ssb.ebt.h"
|
||||||
#include "ssb.h"
|
#include "ssb.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "util.js.h"
|
#include "util.js.h"
|
||||||
@@ -440,11 +441,12 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
_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_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 INDEX IF NOT EXISTS blob_wants_cache_timestamp_id_idx ON blob_wants_cache (timestamp, id)");
|
||||||
|
|
||||||
|
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS messages_ai_blob_wants_cache");
|
||||||
_tf_ssb_db_exec(db,
|
_tf_ssb_db_exec(db,
|
||||||
"CREATE TRIGGER IF NOT EXISTS messages_ai_blob_wants_cache AFTER INSERT ON messages_refs BEGIN "
|
"CREATE TRIGGER IF NOT EXISTS messages_ai_blob_wants_cache AFTER INSERT ON messages_refs BEGIN "
|
||||||
"INSERT INTO blob_wants_cache (source, id, timestamp) "
|
"INSERT INTO blob_wants_cache (source, id, timestamp) "
|
||||||
"SELECT messages.id, new.ref, messages.timestamp FROM messages "
|
"SELECT messages.id, new.ref, messages.timestamp FROM messages "
|
||||||
"JOIN blobs ON new.ref = blobs.id "
|
"LEFT OUTER JOIN blobs ON new.ref = blobs.id "
|
||||||
"WHERE messages.id = new.message AND "
|
"WHERE messages.id = new.message AND "
|
||||||
"LENGTH(new.ref) = 52 AND new.ref LIKE '&%.sha256' AND "
|
"LENGTH(new.ref) = 52 AND new.ref LIKE '&%.sha256' AND "
|
||||||
"blobs.content IS NULL "
|
"blobs.content IS NULL "
|
||||||
@@ -455,7 +457,7 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
"INSERT INTO blob_wants_cache (source, id, timestamp) "
|
"INSERT INTO blob_wants_cache (source, id, timestamp) "
|
||||||
"SELECT messages.id, new.ref, messages.timestamp FROM messages "
|
"SELECT messages.id, new.ref, messages.timestamp FROM messages "
|
||||||
"JOIN blob_wants_cache bwc ON bwc.source = messages.id AND bwc.id = new.blob "
|
"JOIN blob_wants_cache bwc ON bwc.source = messages.id AND bwc.id = new.blob "
|
||||||
"JOIN blobs ON bwc.id = blobs.id "
|
"LEFT OUTER JOIN blobs ON bwc.id = blobs.id "
|
||||||
"WHERE blobs.content IS NULL "
|
"WHERE blobs.content IS NULL "
|
||||||
"ON CONFLICT (source, id) DO NOTHING; END");
|
"ON CONFLICT (source, id) DO NOTHING; END");
|
||||||
_tf_ssb_db_exec(db,
|
_tf_ssb_db_exec(db,
|
||||||
@@ -534,46 +536,36 @@ 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(sqlite3* db, const char* id, const char* previous, const char* author, int32_t sequence, double timestamp, const char* content,
|
static int64_t _tf_ssb_db_store_message_raw(sqlite3* db, sqlite3_stmt* statement, const char* id, const char* previous, const char* author, int32_t sequence, double timestamp,
|
||||||
size_t content_len, const char* signature, int flags)
|
const char* content, size_t content_len, const char* signature, int flags)
|
||||||
{
|
{
|
||||||
int64_t last_row_id = -1;
|
int64_t last_row_id = -1;
|
||||||
bool id_mismatch = false;
|
bool id_mismatch = false;
|
||||||
|
|
||||||
if (_tf_ssb_db_previous_message_exists(db, author, sequence, previous, &id_mismatch))
|
if (_tf_ssb_db_previous_message_exists(db, author, sequence, previous, &id_mismatch))
|
||||||
{
|
{
|
||||||
const char* query = "INSERT INTO messages (id, previous, author, sequence, timestamp, content, hash, signature, flags) VALUES (?, ?, ?, ?, ?, jsonb(?), "
|
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
|
||||||
"?, ?, ?) ON CONFLICT DO NOTHING";
|
(previous ? sqlite3_bind_text(statement, 2, previous, -1, NULL) : sqlite3_bind_null(statement, 2)) == SQLITE_OK &&
|
||||||
sqlite3_stmt* statement;
|
sqlite3_bind_text(statement, 3, author, -1, NULL) == SQLITE_OK && sqlite3_bind_int(statement, 4, sequence) == SQLITE_OK &&
|
||||||
if (sqlite3_prepare_v2(db, query, -1, &statement, NULL) == SQLITE_OK)
|
sqlite3_bind_double(statement, 5, timestamp) == SQLITE_OK && sqlite3_bind_text(statement, 6, content, content_len, NULL) == SQLITE_OK &&
|
||||||
|
sqlite3_bind_text(statement, 7, "sha256", 6, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 8, signature, -1, NULL) == SQLITE_OK &&
|
||||||
|
sqlite3_bind_int(statement, 9, flags) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
|
int r = sqlite3_step(statement);
|
||||||
(previous ? sqlite3_bind_text(statement, 2, previous, -1, NULL) : sqlite3_bind_null(statement, 2)) == SQLITE_OK &&
|
if (r != SQLITE_DONE)
|
||||||
sqlite3_bind_text(statement, 3, author, -1, NULL) == SQLITE_OK && sqlite3_bind_int(statement, 4, sequence) == SQLITE_OK &&
|
|
||||||
sqlite3_bind_double(statement, 5, timestamp) == SQLITE_OK && sqlite3_bind_text(statement, 6, content, content_len, NULL) == SQLITE_OK &&
|
|
||||||
sqlite3_bind_text(statement, 7, "sha256", 6, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 8, signature, -1, NULL) == SQLITE_OK &&
|
|
||||||
sqlite3_bind_int(statement, 9, flags) == SQLITE_OK)
|
|
||||||
{
|
{
|
||||||
int r = sqlite3_step(statement);
|
tf_printf("_tf_ssb_db_store_message_raw: %s\n", sqlite3_errmsg(db));
|
||||||
if (r != SQLITE_DONE)
|
|
||||||
{
|
|
||||||
tf_printf("_tf_ssb_db_store_message_raw: %s\n", sqlite3_errmsg(db));
|
|
||||||
}
|
|
||||||
if (r == SQLITE_DONE && sqlite3_changes(db) != 0)
|
|
||||||
{
|
|
||||||
last_row_id = sqlite3_last_insert_rowid(db);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
if (r == SQLITE_DONE && sqlite3_changes(db) != 0)
|
||||||
{
|
{
|
||||||
tf_printf("bind failed\n");
|
last_row_id = sqlite3_last_insert_rowid(db);
|
||||||
}
|
}
|
||||||
sqlite3_finalize(statement);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tf_printf("%s: prepare failed: %s\n", __FUNCTION__, sqlite3_errmsg(db));
|
tf_printf("bind failed: %s\n", sqlite3_errmsg(db));
|
||||||
}
|
}
|
||||||
|
sqlite3_reset(statement);
|
||||||
}
|
}
|
||||||
else if (id_mismatch)
|
else if (id_mismatch)
|
||||||
{
|
{
|
||||||
@@ -665,16 +657,27 @@ static void _tf_ssb_db_store_message_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||||
bool in_transaction = _tf_ssb_db_try_exec(db, "BEGIN TRANSACTION") == SQLITE_OK;
|
bool in_transaction = _tf_ssb_db_try_exec(db, "BEGIN TRANSACTION") == SQLITE_OK;
|
||||||
|
|
||||||
while (store)
|
const char* query = "INSERT INTO messages (id, previous, author, sequence, timestamp, content, hash, signature, flags) VALUES (?, ?, ?, ?, ?, jsonb(?), "
|
||||||
|
"?, ?, ?) ON CONFLICT DO NOTHING";
|
||||||
|
sqlite3_stmt* insert_statement = NULL;
|
||||||
|
if (sqlite3_prepare_v2(db, query, -1, &insert_statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
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,
|
while (store)
|
||||||
store->content, store->length, store->signature, store->flags);
|
|
||||||
if (last_row_id != -1)
|
|
||||||
{
|
{
|
||||||
store->out_stored = true;
|
int64_t last_row_id = _tf_ssb_db_store_message_raw(db, insert_statement, store->id, *store->previous ? store->previous : NULL, store->author, store->sequence,
|
||||||
store->out_blob_wants = _tf_ssb_db_get_message_blob_wants(db, last_row_id);
|
store->timestamp, 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(db, last_row_id);
|
||||||
|
}
|
||||||
|
store = store->next;
|
||||||
}
|
}
|
||||||
store = store->next;
|
sqlite3_finalize(insert_statement);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tf_printf("prepare failed: %s.\n", sqlite3_errmsg(db));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_transaction)
|
if (in_transaction)
|
||||||
@@ -760,6 +763,22 @@ static void _tf_ssb_db_store_message_after_work(tf_ssb_t* ssb, int status, void*
|
|||||||
store = store->next;
|
store = store->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tf_ssb_connection_t* connections[256];
|
||||||
|
int count = tf_ssb_get_connections(ssb, connections, tf_countof(connections));
|
||||||
|
store = user_data;
|
||||||
|
while (store)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; 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))
|
||||||
|
{
|
||||||
|
tf_ssb_ebt_set_messages_received(tf_ssb_connection_get_ebt(connections[i]), store->author, store->sequence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
store = store->next;
|
||||||
|
}
|
||||||
|
|
||||||
if (last_stored)
|
if (last_stored)
|
||||||
{
|
{
|
||||||
tf_trace_begin(trace, "notify_message_added");
|
tf_trace_begin(trace, "notify_message_added");
|
||||||
|
|||||||
296
src/ssb.js.c
296
src/ssb.js.c
@@ -18,8 +18,6 @@ static const int k_sql_async_timeout_ms = 60 * 1000;
|
|||||||
|
|
||||||
static JSClassID _tf_ssb_classId;
|
static JSClassID _tf_ssb_classId;
|
||||||
|
|
||||||
static JSValue _tf_ssb_appendMessageWithIdentity(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
|
|
||||||
|
|
||||||
typedef struct _create_identity_t
|
typedef struct _create_identity_t
|
||||||
{
|
{
|
||||||
char id[k_id_base64_len];
|
char id[k_id_base64_len];
|
||||||
@@ -98,209 +96,6 @@ static JSValue _tf_ssb_createIdentity(JSContext* context, JSValueConst this_val,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct _add_identity_t
|
|
||||||
{
|
|
||||||
uint8_t key[crypto_sign_SECRETKEYBYTES / 2];
|
|
||||||
bool added;
|
|
||||||
JSValue promise[2];
|
|
||||||
char user[];
|
|
||||||
} add_identity_t;
|
|
||||||
|
|
||||||
static void _tf_ssb_add_identity_work(tf_ssb_t* ssb, void* user_data)
|
|
||||||
{
|
|
||||||
add_identity_t* work = user_data;
|
|
||||||
uint8_t public_key[crypto_sign_PUBLICKEYBYTES];
|
|
||||||
unsigned char seed[crypto_sign_SEEDBYTES];
|
|
||||||
uint8_t secret_key[crypto_sign_SECRETKEYBYTES] = { 0 };
|
|
||||||
memcpy(secret_key, work->key, sizeof(secret_key) / 2);
|
|
||||||
if (crypto_sign_ed25519_sk_to_seed(seed, secret_key) == 0 && crypto_sign_seed_keypair(public_key, secret_key, seed) == 0)
|
|
||||||
{
|
|
||||||
char public_key_b64[512];
|
|
||||||
tf_base64_encode(public_key, sizeof(public_key), public_key_b64, sizeof(public_key_b64));
|
|
||||||
snprintf(public_key_b64 + strlen(public_key_b64), sizeof(public_key_b64) - strlen(public_key_b64), ".ed25519");
|
|
||||||
|
|
||||||
uint8_t combined[crypto_sign_SECRETKEYBYTES];
|
|
||||||
memcpy(combined, work->key, sizeof(work->key));
|
|
||||||
memcpy(combined + sizeof(work->key), public_key, sizeof(public_key));
|
|
||||||
char combined_b64[512];
|
|
||||||
tf_base64_encode(combined, sizeof(combined), combined_b64, sizeof(combined_b64));
|
|
||||||
snprintf(combined_b64 + strlen(combined_b64), sizeof(combined_b64) - strlen(combined_b64), ".ed25519");
|
|
||||||
|
|
||||||
work->added = tf_ssb_db_identity_add(ssb, work->user, public_key_b64, combined_b64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _tf_ssb_add_identity_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
|
||||||
{
|
|
||||||
add_identity_t* work = user_data;
|
|
||||||
JSContext* context = tf_ssb_get_context(ssb);
|
|
||||||
JSValue result = work->added ? JS_TRUE : JS_UNDEFINED;
|
|
||||||
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
|
|
||||||
JS_FreeValue(context, result);
|
|
||||||
tf_util_report_error(context, error);
|
|
||||||
JS_FreeValue(context, error);
|
|
||||||
JS_FreeValue(context, work->promise[0]);
|
|
||||||
JS_FreeValue(context, work->promise[1]);
|
|
||||||
tf_free(work);
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSValue _tf_ssb_addIdentity(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
|
||||||
{
|
|
||||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
|
||||||
JSValue result = JS_UNDEFINED;
|
|
||||||
if (ssb)
|
|
||||||
{
|
|
||||||
size_t user_length = 0;
|
|
||||||
const char* user = JS_ToCStringLen(context, &user_length, argv[0]);
|
|
||||||
|
|
||||||
JSValue buffer = JS_UNDEFINED;
|
|
||||||
size_t length = 0;
|
|
||||||
uint8_t* array = tf_util_try_get_array_buffer(context, &length, argv[1]);
|
|
||||||
if (!array)
|
|
||||||
{
|
|
||||||
size_t offset;
|
|
||||||
size_t element_size;
|
|
||||||
buffer = tf_util_try_get_typed_array_buffer(context, argv[1], &offset, &length, &element_size);
|
|
||||||
if (!JS_IsException(buffer))
|
|
||||||
{
|
|
||||||
array = tf_util_try_get_array_buffer(context, &length, buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array)
|
|
||||||
{
|
|
||||||
if (length == crypto_sign_SECRETKEYBYTES / 2)
|
|
||||||
{
|
|
||||||
add_identity_t* work = tf_malloc(sizeof(add_identity_t) + user_length + 1);
|
|
||||||
*work = (add_identity_t) { 0 };
|
|
||||||
memcpy(work->key, array, sizeof(work->key));
|
|
||||||
memcpy(work->user, user, user_length + 1);
|
|
||||||
result = JS_NewPromiseCapability(context, work->promise);
|
|
||||||
tf_ssb_run_work(ssb, _tf_ssb_add_identity_work, _tf_ssb_add_identity_after_work, work);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = JS_ThrowInternalError(context, "Unexpected private key size: %d vs. %d\n", (int)length, crypto_sign_SECRETKEYBYTES);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = JS_ThrowInternalError(context, "Expected array argument.");
|
|
||||||
}
|
|
||||||
JS_FreeValue(context, buffer);
|
|
||||||
JS_FreeCString(context, user);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _delete_identity_t
|
|
||||||
{
|
|
||||||
char id[k_id_base64_len];
|
|
||||||
bool deleted;
|
|
||||||
JSValue promise[2];
|
|
||||||
char user[];
|
|
||||||
} delete_identity_t;
|
|
||||||
|
|
||||||
static void _tf_ssb_delete_identity_work(tf_ssb_t* ssb, void* user_data)
|
|
||||||
{
|
|
||||||
delete_identity_t* work = user_data;
|
|
||||||
work->deleted = tf_ssb_db_identity_delete(ssb, work->user, work->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _tf_ssb_delete_identity_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
|
||||||
{
|
|
||||||
delete_identity_t* work = user_data;
|
|
||||||
JSContext* context = tf_ssb_get_context(ssb);
|
|
||||||
JSValue result = work->deleted ? JS_TRUE : JS_FALSE;
|
|
||||||
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
|
|
||||||
JS_FreeValue(context, result);
|
|
||||||
tf_util_report_error(context, error);
|
|
||||||
JS_FreeValue(context, error);
|
|
||||||
JS_FreeValue(context, work->promise[0]);
|
|
||||||
JS_FreeValue(context, work->promise[1]);
|
|
||||||
tf_free(work);
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSValue _tf_ssb_deleteIdentity(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
|
||||||
{
|
|
||||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
|
||||||
JSValue result = JS_UNDEFINED;
|
|
||||||
if (ssb)
|
|
||||||
{
|
|
||||||
size_t user_length = 0;
|
|
||||||
const char* user = JS_ToCStringLen(context, &user_length, argv[0]);
|
|
||||||
const char* id = JS_ToCString(context, argv[1]);
|
|
||||||
if (id && user)
|
|
||||||
{
|
|
||||||
delete_identity_t* work = tf_malloc(sizeof(delete_identity_t) + user_length + 1);
|
|
||||||
*work = (delete_identity_t) { 0 };
|
|
||||||
tf_string_set(work->id, sizeof(work->id), *id == '@' ? id + 1 : id);
|
|
||||||
memcpy(work->user, user, user_length + 1);
|
|
||||||
result = JS_NewPromiseCapability(context, work->promise);
|
|
||||||
tf_ssb_run_work(ssb, _tf_ssb_delete_identity_work, _tf_ssb_delete_identity_after_work, work);
|
|
||||||
}
|
|
||||||
JS_FreeCString(context, id);
|
|
||||||
JS_FreeCString(context, user);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _get_private_key_t
|
|
||||||
{
|
|
||||||
JSContext* context;
|
|
||||||
JSValue promise[2];
|
|
||||||
char id[k_id_base64_len];
|
|
||||||
uint8_t private_key[crypto_sign_SECRETKEYBYTES];
|
|
||||||
bool got_private_key;
|
|
||||||
char user[];
|
|
||||||
} get_private_key_t;
|
|
||||||
|
|
||||||
static void _tf_ssb_get_private_key_work(tf_ssb_t* ssb, void* user_data)
|
|
||||||
{
|
|
||||||
get_private_key_t* work = user_data;
|
|
||||||
work->got_private_key = tf_ssb_db_identity_get_private_key(ssb, work->user, work->id, work->private_key, sizeof(work->private_key));
|
|
||||||
if (!work->got_private_key && tf_ssb_db_user_has_permission(ssb, NULL, work->user, "administration"))
|
|
||||||
{
|
|
||||||
work->got_private_key = tf_ssb_db_identity_get_private_key(ssb, ":admin", work->id, work->private_key, sizeof(work->private_key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _tf_ssb_get_private_key_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
|
||||||
{
|
|
||||||
get_private_key_t* work = user_data;
|
|
||||||
JSValue result = JS_UNDEFINED;
|
|
||||||
JSContext* context = work->context;
|
|
||||||
if (work->got_private_key)
|
|
||||||
{
|
|
||||||
result = tf_util_new_uint8_array(context, work->private_key, sizeof(work->private_key) / 2);
|
|
||||||
}
|
|
||||||
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
|
|
||||||
JS_FreeValue(context, result);
|
|
||||||
tf_util_report_error(context, error);
|
|
||||||
JS_FreeValue(context, error);
|
|
||||||
JS_FreeValue(context, work->promise[0]);
|
|
||||||
JS_FreeValue(context, work->promise[1]);
|
|
||||||
tf_free(work);
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSValue _tf_ssb_getPrivateKey(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
|
||||||
{
|
|
||||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
|
||||||
size_t user_length = 0;
|
|
||||||
const char* user = JS_ToCStringLen(context, &user_length, argv[0]);
|
|
||||||
const char* id = JS_ToCString(context, argv[1]);
|
|
||||||
get_private_key_t* work = tf_malloc(sizeof(get_private_key_t) + user_length + 1);
|
|
||||||
*work = (get_private_key_t) { .context = context };
|
|
||||||
memcpy(work->user, user, user_length + 1);
|
|
||||||
tf_string_set(work->id, sizeof(work->id), id);
|
|
||||||
JSValue result = JS_NewPromiseCapability(context, work->promise);
|
|
||||||
tf_ssb_run_work(ssb, _tf_ssb_get_private_key_work, _tf_ssb_get_private_key_after_work, work);
|
|
||||||
|
|
||||||
JS_FreeCString(context, user);
|
|
||||||
JS_FreeCString(context, id);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSValue _tf_ssb_getServerIdentity(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
static JSValue _tf_ssb_getServerIdentity(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
{
|
{
|
||||||
JSValue result = JS_UNDEFINED;
|
JSValue result = JS_UNDEFINED;
|
||||||
@@ -390,92 +185,6 @@ static JSValue _tf_ssb_getIdentityInfo(JSContext* context, JSValueConst this_val
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct _append_message_t
|
|
||||||
{
|
|
||||||
char id[k_id_base64_len];
|
|
||||||
uint8_t private_key[crypto_sign_SECRETKEYBYTES];
|
|
||||||
bool got_private_key;
|
|
||||||
char previous_id[512];
|
|
||||||
int32_t previous_sequence;
|
|
||||||
JSContext* context;
|
|
||||||
JSValue promise[2];
|
|
||||||
JSValue message;
|
|
||||||
char user[];
|
|
||||||
} append_message_t;
|
|
||||||
|
|
||||||
static void _tf_ssb_appendMessage_finish(append_message_t* async, bool success, JSValue result)
|
|
||||||
{
|
|
||||||
JSValue error = JS_Call(async->context, success ? async->promise[0] : async->promise[1], JS_UNDEFINED, 1, &result);
|
|
||||||
tf_util_report_error(async->context, error);
|
|
||||||
JS_FreeValue(async->context, error);
|
|
||||||
JS_FreeValue(async->context, async->message);
|
|
||||||
JS_FreeValue(async->context, async->promise[0]);
|
|
||||||
JS_FreeValue(async->context, async->promise[1]);
|
|
||||||
tf_free(async);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _tf_ssb_appendMessageWithIdentity_callback(const char* id, bool verified, bool is_new, void* user_data)
|
|
||||||
{
|
|
||||||
append_message_t* async = user_data;
|
|
||||||
JSValue result = JS_UNDEFINED;
|
|
||||||
if (verified)
|
|
||||||
{
|
|
||||||
result = is_new ? JS_TRUE : JS_FALSE;
|
|
||||||
}
|
|
||||||
_tf_ssb_appendMessage_finish(async, verified, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _tf_ssb_append_message_with_identity_get_key_work(tf_ssb_t* ssb, void* user_data)
|
|
||||||
{
|
|
||||||
append_message_t* work = user_data;
|
|
||||||
work->got_private_key = tf_ssb_db_identity_get_private_key(ssb, work->user, work->id, work->private_key, sizeof(work->private_key));
|
|
||||||
if (!work->got_private_key && tf_ssb_db_user_has_permission(ssb, NULL, work->user, "administration"))
|
|
||||||
{
|
|
||||||
work->got_private_key = tf_ssb_db_identity_get_private_key(ssb, ":admin", work->id, work->private_key, sizeof(work->private_key));
|
|
||||||
}
|
|
||||||
tf_ssb_db_get_latest_message_by_author(ssb, work->id, &work->previous_sequence, work->previous_id, sizeof(work->previous_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _tf_ssb_append_message_with_identity_get_key_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
|
||||||
{
|
|
||||||
append_message_t* work = user_data;
|
|
||||||
if (work->got_private_key)
|
|
||||||
{
|
|
||||||
JSValue signed_message = tf_ssb_sign_message(ssb, work->id, work->private_key, work->message, work->previous_id, work->previous_sequence);
|
|
||||||
tf_ssb_verify_strip_and_store_message(ssb, signed_message, _tf_ssb_appendMessageWithIdentity_callback, work);
|
|
||||||
JS_FreeValue(work->context, signed_message);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_tf_ssb_appendMessage_finish(work, false, JS_ThrowInternalError(work->context, "Unable to get private key for user %s with identity %s.", work->user, work->id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSValue _tf_ssb_appendMessageWithIdentity(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
|
||||||
{
|
|
||||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
|
||||||
if (!ssb)
|
|
||||||
{
|
|
||||||
return JS_ThrowInternalError(context, "No SSB instance.");
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t user_length = 0;
|
|
||||||
const char* user = JS_ToCStringLen(context, &user_length, argv[0]);
|
|
||||||
const char* id = JS_ToCString(context, argv[1]);
|
|
||||||
|
|
||||||
append_message_t* work = tf_malloc(sizeof(append_message_t) + user_length + 1);
|
|
||||||
*work = (append_message_t) { .context = context, .message = JS_DupValue(context, argv[2]) };
|
|
||||||
memcpy(work->user, user, user_length + 1);
|
|
||||||
tf_string_set(work->id, sizeof(work->id), id);
|
|
||||||
|
|
||||||
JS_FreeCString(context, id);
|
|
||||||
JS_FreeCString(context, user);
|
|
||||||
|
|
||||||
JSValue result = JS_NewPromiseCapability(context, work->promise);
|
|
||||||
tf_ssb_run_work(ssb, _tf_ssb_append_message_with_identity_get_key_work, _tf_ssb_append_message_with_identity_get_key_after_work, work);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _blob_get_t
|
typedef struct _blob_get_t
|
||||||
{
|
{
|
||||||
JSContext* context;
|
JSContext* context;
|
||||||
@@ -1791,12 +1500,7 @@ void tf_ssb_register(JSContext* context, tf_ssb_t* ssb)
|
|||||||
|
|
||||||
/* Requires an identity. */
|
/* Requires an identity. */
|
||||||
JS_SetPropertyStr(context, object, "createIdentity", JS_NewCFunction(context, _tf_ssb_createIdentity, "createIdentity", 1));
|
JS_SetPropertyStr(context, object, "createIdentity", JS_NewCFunction(context, _tf_ssb_createIdentity, "createIdentity", 1));
|
||||||
JS_SetPropertyStr(context, object, "addIdentity", JS_NewCFunction(context, _tf_ssb_addIdentity, "addIdentity", 2));
|
|
||||||
JS_SetPropertyStr(context, object, "deleteIdentity", JS_NewCFunction(context, _tf_ssb_deleteIdentity, "deleteIdentity", 2));
|
|
||||||
JS_SetPropertyStr(context, object, "getPrivateKey", JS_NewCFunction(context, _tf_ssb_getPrivateKey, "getPrivateKey", 2));
|
|
||||||
JS_SetPropertyStr(context, object, "setUserPermission", JS_NewCFunction(context, _tf_ssb_set_user_permission, "setUserPermission", 5));
|
JS_SetPropertyStr(context, object, "setUserPermission", JS_NewCFunction(context, _tf_ssb_set_user_permission, "setUserPermission", 5));
|
||||||
/* Write. */
|
|
||||||
JS_SetPropertyStr(context, object, "appendMessageWithIdentity", JS_NewCFunction(context, _tf_ssb_appendMessageWithIdentity, "appendMessageWithIdentity", 3));
|
|
||||||
|
|
||||||
/* Does not require an identity. */
|
/* Does not require an identity. */
|
||||||
JS_SetPropertyStr(context, object, "getServerIdentity", JS_NewCFunction(context, _tf_ssb_getServerIdentity, "getServerIdentity", 0));
|
JS_SetPropertyStr(context, object, "getServerIdentity", JS_NewCFunction(context, _tf_ssb_getServerIdentity, "getServerIdentity", 0));
|
||||||
|
|||||||
@@ -190,15 +190,31 @@ static void _tf_ssb_rpc_blobs_has(tf_ssb_connection_t* connection, uint8_t flags
|
|||||||
JS_FreeValue(context, ids);
|
JS_FreeValue(context, ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool _tf_ssb_rpc_are_messages_pending_in(tf_ssb_connection_t* connection)
|
||||||
|
{
|
||||||
|
int in_pending = 0;
|
||||||
|
int in_total = 0;
|
||||||
|
int out_pending = 0;
|
||||||
|
int out_total = 0;
|
||||||
|
if (connection)
|
||||||
|
{
|
||||||
|
tf_ssb_ebt_get_progress(tf_ssb_connection_get_ebt(connection), &in_pending, &in_total, &out_pending, &out_total);
|
||||||
|
}
|
||||||
|
return in_pending != 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void _tf_ssb_rpc_blob_wants_added_callback(tf_ssb_t* ssb, const char* id, void* user_data)
|
static void _tf_ssb_rpc_blob_wants_added_callback(tf_ssb_t* ssb, const char* id, void* user_data)
|
||||||
{
|
{
|
||||||
tf_ssb_connection_t* connection = user_data;
|
tf_ssb_connection_t* connection = user_data;
|
||||||
tf_ssb_blob_wants_t* blob_wants = tf_ssb_connection_get_blob_wants_state(connection);
|
if (!_tf_ssb_rpc_are_messages_pending_in(connection))
|
||||||
JSContext* context = tf_ssb_get_context(ssb);
|
{
|
||||||
JSValue message = JS_NewObject(context);
|
tf_ssb_blob_wants_t* blob_wants = tf_ssb_connection_get_blob_wants_state(connection);
|
||||||
JS_SetPropertyStr(context, message, id, JS_NewInt64(context, -1));
|
JSContext* context = tf_ssb_get_context(ssb);
|
||||||
tf_ssb_connection_rpc_send_json(connection, k_ssb_rpc_flag_stream, -blob_wants->request_number, NULL, message, NULL, NULL, NULL);
|
JSValue message = JS_NewObject(context);
|
||||||
JS_FreeValue(context, message);
|
JS_SetPropertyStr(context, message, id, JS_NewInt64(context, -1));
|
||||||
|
tf_ssb_connection_rpc_send_json(connection, k_ssb_rpc_flag_stream, -blob_wants->request_number, NULL, message, NULL, NULL, NULL);
|
||||||
|
JS_FreeValue(context, message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct _blob_wants_work_t
|
typedef struct _blob_wants_work_t
|
||||||
@@ -235,7 +251,8 @@ static void _tf_ssb_request_blob_wants_work(tf_ssb_connection_t* connection, voi
|
|||||||
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)
|
||||||
{
|
{
|
||||||
while (sqlite3_step(statement) == SQLITE_ROW)
|
int r = SQLITE_OK;
|
||||||
|
while ((r = sqlite3_step(statement)) == SQLITE_ROW)
|
||||||
{
|
{
|
||||||
tf_string_set(work->out_id[work->out_id_count], sizeof(work->out_id[work->out_id_count]), (const char*)sqlite3_column_text(statement, 0));
|
tf_string_set(work->out_id[work->out_id_count], sizeof(work->out_id[work->out_id_count]), (const char*)sqlite3_column_text(statement, 0));
|
||||||
work->out_id_count++;
|
work->out_id_count++;
|
||||||
@@ -264,7 +281,10 @@ static void _tf_ssb_request_blob_wants_after_work(tf_ssb_connection_t* connectio
|
|||||||
JS_SetPropertyStr(context, message, work->out_id[i], JS_NewInt32(context, -1));
|
JS_SetPropertyStr(context, message, work->out_id[i], JS_NewInt32(context, -1));
|
||||||
send_failed = !tf_ssb_connection_rpc_send_json(connection, k_ssb_rpc_flag_stream, -blob_wants->request_number, NULL, message, NULL, NULL, NULL);
|
send_failed = !tf_ssb_connection_rpc_send_json(connection, k_ssb_rpc_flag_stream, -blob_wants->request_number, NULL, message, NULL, NULL, NULL);
|
||||||
JS_FreeValue(context, message);
|
JS_FreeValue(context, message);
|
||||||
blob_wants->wants_sent++;
|
if (!send_failed)
|
||||||
|
{
|
||||||
|
blob_wants->wants_sent++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (work->out_id_count)
|
if (work->out_id_count)
|
||||||
{
|
{
|
||||||
@@ -276,9 +296,12 @@ static void _tf_ssb_request_blob_wants_after_work(tf_ssb_connection_t* connectio
|
|||||||
|
|
||||||
static void _tf_ssb_rpc_request_more_blobs(tf_ssb_connection_t* connection)
|
static void _tf_ssb_rpc_request_more_blobs(tf_ssb_connection_t* connection)
|
||||||
{
|
{
|
||||||
blob_wants_work_t* work = tf_malloc(sizeof(blob_wants_work_t));
|
if (tf_ssb_connection_get_blob_wants_state(connection)->wants_sent == 0 && !_tf_ssb_rpc_are_messages_pending_in(connection))
|
||||||
memset(work, 0, sizeof(*work));
|
{
|
||||||
tf_ssb_connection_run_work(connection, _tf_ssb_request_blob_wants_work, _tf_ssb_request_blob_wants_after_work, work);
|
blob_wants_work_t* work = tf_malloc(sizeof(blob_wants_work_t));
|
||||||
|
memset(work, 0, sizeof(*work));
|
||||||
|
tf_ssb_connection_run_work(connection, _tf_ssb_request_blob_wants_work, _tf_ssb_request_blob_wants_after_work, work);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _tf_ssb_rpc_blobs_createWants(
|
static void _tf_ssb_rpc_blobs_createWants(
|
||||||
@@ -718,10 +741,12 @@ static void _tf_ssb_rpc_connection_blobs_createWants_callback(
|
|||||||
}
|
}
|
||||||
int64_t size = 0;
|
int64_t size = 0;
|
||||||
JS_ToInt64(context, &size, key_value);
|
JS_ToInt64(context, &size, key_value);
|
||||||
if (--blob_wants->wants_sent == 0)
|
/* We don't have the context here to disambiguate responses to incoming requests. */
|
||||||
|
if (blob_wants->wants_sent > 0)
|
||||||
{
|
{
|
||||||
_tf_ssb_rpc_request_more_blobs(connection);
|
--blob_wants->wants_sent;
|
||||||
}
|
}
|
||||||
|
_tf_ssb_rpc_request_more_blobs(connection);
|
||||||
if (size < 0)
|
if (size < 0)
|
||||||
{
|
{
|
||||||
blob_create_wants_work_t* work = tf_malloc(sizeof(blob_create_wants_work_t));
|
blob_create_wants_work_t* work = tf_malloc(sizeof(blob_create_wants_work_t));
|
||||||
@@ -1940,8 +1965,8 @@ static void _tf_ssb_rpc_message_added_callback(tf_ssb_t* ssb, const char* author
|
|||||||
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]);
|
||||||
|
_tf_ssb_rpc_request_more_blobs(connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ success = False
|
|||||||
try:
|
try:
|
||||||
options = webdriver.FirefoxOptions()
|
options = webdriver.FirefoxOptions()
|
||||||
service = Service(log_output = 'out/geckodriver.log')
|
service = Service(log_output = 'out/geckodriver.log')
|
||||||
#options.add_argument('--headless')
|
options.add_argument('--headless')
|
||||||
driver = webdriver.Firefox(options = options, service = service)
|
driver = webdriver.Firefox(options = options, service = service)
|
||||||
wait = WebDriverWait(driver, 10)
|
wait = WebDriverWait(driver, 10)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user