Compare commits
28 Commits
3c17810747
...
latest_rel
Author | SHA1 | Date | |
---|---|---|---|
94b7703ca9 | |||
a391dd1316 | |||
b6ba5211b7 | |||
8e8e130045 | |||
1f40bc1a0f | |||
5437212222 | |||
a8ab845cd2 | |||
8cee6dc98b | |||
70c2b73414 | |||
98013c4422 | |||
e9e22b762d | |||
620db19936 | |||
94a79dd62c | |||
b56c3efde0 | |||
066827f8f1 | |||
c3b65d9cd8 | |||
a15b916b06 | |||
31d0a5c233 | |||
140179e80a | |||
53cba2d7e4 | |||
e54312d3b8 | |||
cadc27b7b5 | |||
388b829ec1 | |||
67861f0f33 | |||
a1f1eb34d5 | |||
2a6789063e | |||
cbf1273a55 | |||
8143a23ced |
14
GNUmakefile
14
GNUmakefile
@ -18,7 +18,7 @@ MAKEFLAGS += --no-builtin-rules
|
||||
|
||||
VERSION_CODE := 38
|
||||
VERSION_CODE_IOS := 14
|
||||
VERSION_NUMBER := 0.0.32-wip
|
||||
VERSION_NUMBER := 0.0.32
|
||||
VERSION_NAME := This program kills fascists.
|
||||
|
||||
IPHONEOS_VERSION_MIN=14.0
|
||||
@ -1483,6 +1483,18 @@ help: ## Display this help message.
|
||||
.PHONY: help
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
docs: debug
|
||||
docs: ## Build HTML docs.
|
||||
@echo '# CLI Usage\n' > docs/usage.md
|
||||
@echo "## tildefriends -h" >> docs/usage.md
|
||||
@echo '\n```' >> docs/usage.md
|
||||
@out/debug/tildefriends -h >> docs/usage.md
|
||||
@echo '```' >> docs/usage.md
|
||||
@for command in $$(out/debug/tildefriends -h | grep -Po '[A-Za-z_]*(?= - )'); do
|
||||
@ echo "\n## tildefriends $$command -h" >> docs/usage.md
|
||||
@ echo '\n```' >> docs/usage.md
|
||||
@ out/debug/tildefriends $$command -h >> docs/usage.md
|
||||
@ echo '```' >> docs/usage.md
|
||||
@done
|
||||
@doxygen
|
||||
.PHONY: docs
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "💡",
|
||||
"previous": "&ckE7T/dt9f1xV8jNSgXVcXYkAzMhU9689MRQbhOi9Wo=.sha256"
|
||||
"previous": "&eN6DNPpQUNhGvxneLuLPgsOXR6qyFZ7u+MAz0b4fa7k=.sha256"
|
||||
}
|
||||
|
@ -5,7 +5,10 @@ async function main() {
|
||||
}
|
||||
|
||||
tfrpc.register(async function complete() {
|
||||
if ((await core.globalSettingsGet('index')) == '/~core/intro/') {
|
||||
if (
|
||||
core.user?.credentials?.permissions?.administration &&
|
||||
(await core.globalSettingsGet('index')) == '/~core/intro/'
|
||||
) {
|
||||
return await core.globalSettingsSet('index', '/~core/ssb/');
|
||||
}
|
||||
});
|
||||
|
@ -82,7 +82,7 @@
|
||||
<div class="w3-container w3-large w3-left-align">
|
||||
<p>
|
||||
Secure Scuttlebutt is a social network whose technical operation
|
||||
attempts to mirrors human social interaction.
|
||||
attempts to mirror human social interaction.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🦀",
|
||||
"previous": "&Bpf8wmdsVYGPSBduytapHM7XHz2iCGfaysVHfEKYK/s=.sha256"
|
||||
"previous": "&Rn4Eg5ev5qhrYRnwxPB0DiEwO7VdGMDGp7tL/W7bRZo=.sha256"
|
||||
}
|
||||
|
@ -106,6 +106,15 @@ tfrpc.register(async function sync() {
|
||||
tfrpc.register(async function url() {
|
||||
return core.url;
|
||||
});
|
||||
tfrpc.register(async function globalSettingsGet(key) {
|
||||
return core.globalSettingsGet(key);
|
||||
});
|
||||
tfrpc.register(async function globalSettingsSet(key, value) {
|
||||
return core.globalSettingsSet(key, value);
|
||||
});
|
||||
tfrpc.register(function isAdministrator() {
|
||||
return core.user?.credentials?.permissions?.administration;
|
||||
});
|
||||
|
||||
core.register('onBroadcastsChanged', async function () {
|
||||
await tfrpc.rpc.set('broadcasts', await ssb.getBroadcasts());
|
||||
|
@ -350,55 +350,84 @@ class TfElement extends LitElement {
|
||||
return [cache.latest, cache.messages];
|
||||
}
|
||||
|
||||
async query_timed(sql, args) {
|
||||
let start = new Date();
|
||||
let result = await tfrpc.rpc.query(sql, args);
|
||||
let end = new Date();
|
||||
console.log((end - start) / 1000, sql);
|
||||
return result;
|
||||
}
|
||||
|
||||
async load_channels_latest(following) {
|
||||
let start_time = new Date();
|
||||
let latest_private = this.get_latest_private(following);
|
||||
let channels = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
GROUP by channel
|
||||
UNION
|
||||
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN messages_refs ON messages.id = messages_refs.message
|
||||
JOIN json_each(?1) AS channels ON messages_refs.ref = '#' || channels.value
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
GROUP by channel
|
||||
UNION
|
||||
SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
UNION
|
||||
SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3)
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE messages.author != ?4
|
||||
UNION
|
||||
SELECT '👍' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'vote' AND
|
||||
messages.author != ?4
|
||||
`,
|
||||
[
|
||||
JSON.stringify(this.channels),
|
||||
JSON.stringify(following),
|
||||
'"' + this.whoami.replace('"', '""') + '"',
|
||||
this.whoami,
|
||||
]
|
||||
);
|
||||
const k_args = [
|
||||
JSON.stringify(this.channels),
|
||||
JSON.stringify(following),
|
||||
'"' + this.whoami.replace('"', '""') + '"',
|
||||
this.whoami,
|
||||
];
|
||||
let channels = (
|
||||
await Promise.all([
|
||||
this.query_timed(
|
||||
`
|
||||
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
GROUP by channel
|
||||
`,
|
||||
k_args
|
||||
),
|
||||
this.query_timed(
|
||||
`
|
||||
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN messages_refs ON messages.id = messages_refs.message
|
||||
JOIN json_each(?1) AS channels ON messages_refs.ref = '#' || channels.value
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
GROUP by channel
|
||||
`,
|
||||
k_args
|
||||
),
|
||||
this.query_timed(
|
||||
`
|
||||
SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
`,
|
||||
k_args
|
||||
),
|
||||
this.query_timed(
|
||||
`
|
||||
SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3)
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE messages.author != ?4
|
||||
`,
|
||||
k_args
|
||||
),
|
||||
this.query_timed(
|
||||
`
|
||||
SELECT '👍' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'vote' AND
|
||||
messages.author != ?4
|
||||
`,
|
||||
k_args
|
||||
),
|
||||
])
|
||||
).flat();
|
||||
let latest = {};
|
||||
for (let row of channels) {
|
||||
if (!latest[row.channel]) {
|
||||
|
@ -632,8 +632,10 @@ class TfMessageElement extends LitElement {
|
||||
}
|
||||
|
||||
allow_unread() {
|
||||
return;
|
||||
!this.channel.startsWith('@') && !this.channel.startsWith('%');
|
||||
return (
|
||||
this.channel == '@' ||
|
||||
(!this.channel.startsWith('@') && !this.channel.startsWith('%'))
|
||||
);
|
||||
}
|
||||
|
||||
render_unread_icon() {
|
||||
|
@ -14,6 +14,7 @@ class TfNewsElement extends LitElement {
|
||||
channel: {type: String},
|
||||
channel_unread: {type: Number},
|
||||
recent_reactions: {type: Array},
|
||||
hash: {type: String},
|
||||
};
|
||||
}
|
||||
|
||||
@ -193,15 +194,21 @@ class TfNewsElement extends LitElement {
|
||||
return result;
|
||||
}
|
||||
|
||||
unread_allowed() {
|
||||
return !this.hash?.startsWith('#%') && !this.hash?.startsWith('#@');
|
||||
}
|
||||
|
||||
load_and_render(messages) {
|
||||
let messages_by_id = this.process_messages(messages);
|
||||
let final_messages = this.group_following(
|
||||
this.finalize_messages(messages_by_id)
|
||||
);
|
||||
let unread_rowid = -1;
|
||||
for (let message of final_messages) {
|
||||
if (message.rowid >= this.channel_unread) {
|
||||
unread_rowid = message.rowid;
|
||||
if (this.unread_allowed()) {
|
||||
for (let message of final_messages) {
|
||||
if (message.rowid >= this.channel_unread) {
|
||||
unread_rowid = message.rowid;
|
||||
}
|
||||
}
|
||||
}
|
||||
return html`
|
||||
|
@ -275,6 +275,13 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
];
|
||||
}
|
||||
|
||||
unread_allowed() {
|
||||
return (
|
||||
this.hash == '#@' ||
|
||||
(!this.hash.startsWith('#%') && !this.hash.startsWith('#@'))
|
||||
);
|
||||
}
|
||||
|
||||
async load_more() {
|
||||
this.loading++;
|
||||
this.loading_canceled = false;
|
||||
@ -424,9 +431,16 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
if (!this.hash.startsWith('#%')) {
|
||||
more = html`
|
||||
<p>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.mark_all_read}>
|
||||
Mark All Read
|
||||
</button>
|
||||
${this.unread_allowed()
|
||||
? html`
|
||||
<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${this.mark_all_read}
|
||||
>
|
||||
Mark All Read
|
||||
</button>
|
||||
`
|
||||
: undefined}
|
||||
<button
|
||||
?disabled=${this.loading}
|
||||
class="w3-button w3-theme-d1"
|
||||
@ -458,7 +472,7 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
`;
|
||||
}
|
||||
return cache(html`
|
||||
${!this.hash.startsWith('#%')
|
||||
${this.unread_allowed()
|
||||
? html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${this.mark_all_read}
|
||||
@ -474,6 +488,7 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
.following=${this.following}
|
||||
.drafts=${this.drafts}
|
||||
.expanded=${this.expanded}
|
||||
hash=${this.hash}
|
||||
channel=${this.channel()}
|
||||
channel_unread=${this.channels_unread?.[this.channel()]}
|
||||
.recent_reactions=${this.recent_reactions}
|
||||
|
@ -25,6 +25,7 @@ class TfTabNewsElement extends LitElement {
|
||||
connections: {type: Array},
|
||||
private_messages: {type: Array},
|
||||
recent_reactions: {type: Array},
|
||||
peer_exchange: {type: Boolean},
|
||||
};
|
||||
}
|
||||
|
||||
@ -48,6 +49,7 @@ class TfTabNewsElement extends LitElement {
|
||||
tfrpc.rpc.localStorageGet('drafts').then(function (d) {
|
||||
self.drafts = JSON.parse(d || '{}');
|
||||
});
|
||||
this.check_peer_exchange();
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
@ -60,6 +62,14 @@ class TfTabNewsElement extends LitElement {
|
||||
document.body.removeEventListener('keypress', this.on_keypress.bind(this));
|
||||
}
|
||||
|
||||
async check_peer_exchange() {
|
||||
if (await tfrpc.rpc.isAdministrator()) {
|
||||
this.peer_exchange = await tfrpc.rpc.globalSettingsGet('peer_exchange');
|
||||
} else {
|
||||
this.peer_exchange = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
load_latest() {
|
||||
let news = this.shadowRoot?.getElementById('news');
|
||||
if (news) {
|
||||
@ -164,6 +174,15 @@ class TfTabNewsElement extends LitElement {
|
||||
.map((x) => x[0]);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
tfrpc.rpc.sync();
|
||||
}
|
||||
|
||||
async enable_peer_exchange() {
|
||||
await tfrpc.rpc.globalSettingsSet('peer_exchange', true);
|
||||
await this.check_peer_exchange();
|
||||
}
|
||||
|
||||
render_sidebar() {
|
||||
return html`
|
||||
<div
|
||||
@ -240,6 +259,26 @@ class TfTabNewsElement extends LitElement {
|
||||
<a class="w3-bar-item w3-theme-d2 w3-button" href="#connections">
|
||||
<h4 style="margin: 0">Connections</h4>
|
||||
</a>
|
||||
${this.connections?.filter((x) => x.id)?.length == 0
|
||||
? html`
|
||||
<button
|
||||
class=${'w3-bar-item w3-button' +
|
||||
(this.connections?.some((x) => x.flags.one_shot)
|
||||
? ' w3-spin'
|
||||
: '')}
|
||||
@click=${this.refresh}
|
||||
>
|
||||
↻ Sync now
|
||||
</button>
|
||||
<button
|
||||
class=${'w3-bar-item w3-button' +
|
||||
(this.peer_exchange !== false ? ' w3-hide' : '')}
|
||||
@click=${this.enable_peer_exchange}
|
||||
>
|
||||
Enable peer exchange
|
||||
</button>
|
||||
`
|
||||
: undefined}
|
||||
${this.connections
|
||||
.filter((x) => x.id)
|
||||
.map(
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "👋",
|
||||
"previous": "&lqnpIZjz89faC8vMHQiW3mDI5UsaByC3/x8diBr0EtI=.sha256"
|
||||
"previous": "&fY3YUKPuH/wqOgKPVNJu1vWEHCXf5fToL2qiVXMRmxc=.sha256"
|
||||
}
|
||||
|
1521
apps/welcome/hermietildefriends.svg
Normal file
1521
apps/welcome/hermietildefriends.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 86 KiB |
@ -41,7 +41,7 @@
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-black w3-padding-large"
|
||||
href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
||||
href="https://dev.tildefriends.net/cory/tildefriends/releases/latest"
|
||||
><i class="fa fa-download"></i> Download</a
|
||||
>
|
||||
<a
|
||||
@ -210,11 +210,11 @@
|
||||
<!-- SSB Section -->
|
||||
<div class="w3-light-grey">
|
||||
<div class="w3-row-padding w3-padding-64">
|
||||
<div class="w3-col l4 m6 s4">
|
||||
<div class="w3-col l4 m6 s4 w3-center">
|
||||
<a href="https://scuttlebutt.nz/"
|
||||
><img
|
||||
class="w3-image w3-round-large"
|
||||
src="ssb.png"
|
||||
class="w3-image"
|
||||
src="hermietildefriends.svg"
|
||||
alt="Secure Scuttlebutt"
|
||||
/></a>
|
||||
</div>
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 50 KiB |
2
deps/codemirror/cm6.js
vendored
2
deps/codemirror/cm6.js
vendored
File diff suppressed because one or more lines are too long
215
deps/codemirror_src/package-lock.json
generated
vendored
215
deps/codemirror_src/package-lock.json
generated
vendored
@ -83,9 +83,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-json": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz",
|
||||
"integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==",
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz",
|
||||
"integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@lezer/json": "^1.0.0"
|
||||
@ -133,9 +133,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/theme-one-dark": {
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz",
|
||||
"integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==",
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz",
|
||||
"integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
@ -144,9 +144,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/view": {
|
||||
"version": "6.37.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.37.1.tgz",
|
||||
"integrity": "sha512-Qy4CAUwngy/VQkEz0XzMKVRcckQuqLYWKqVpDDDghBe5FSXSqfVrJn49nw3ePZHxRUz4nRmb05Lgi+9csWo4eg==",
|
||||
"version": "6.37.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.37.2.tgz",
|
||||
"integrity": "sha512-XD3LdgQpxQs5jhOOZ2HRVT+Rj59O4Suc7g2ULvZ+Yi8eCkickrkZ5JFuoDhs2ST1mNI5zSsNYgR3NGa4OUrbnw==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.5.0",
|
||||
"crelt": "^1.0.6",
|
||||
@ -324,9 +324,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
|
||||
"integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==",
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz",
|
||||
"integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==",
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
@ -345,9 +345,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.42.0.tgz",
|
||||
"integrity": "sha512-gldmAyS9hpj+H6LpRNlcjQWbuKUtb94lodB9uCz71Jm+7BxK1VIOo7y62tZZwxhA7j1ylv/yQz080L5WkS+LoQ==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.0.tgz",
|
||||
"integrity": "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -357,9 +357,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.42.0.tgz",
|
||||
"integrity": "sha512-bpRipfTgmGFdCZDFLRvIkSNO1/3RGS74aWkJJTFJBH7h3MRV4UijkaEUeOMbi9wxtxYmtAbVcnMtHTPBhLEkaw==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.0.tgz",
|
||||
"integrity": "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -369,9 +369,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.42.0.tgz",
|
||||
"integrity": "sha512-JxHtA081izPBVCHLKnl6GEA0w3920mlJPLh89NojpU2GsBSB6ypu4erFg/Wx1qbpUbepn0jY4dVWMGZM8gplgA==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz",
|
||||
"integrity": "sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -381,9 +381,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.42.0.tgz",
|
||||
"integrity": "sha512-rv5UZaWVIJTDMyQ3dCEK+m0SAn6G7H3PRc2AZmExvbDvtaDc+qXkei0knQWcI3+c9tEs7iL/4I4pTQoPbNL2SA==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.0.tgz",
|
||||
"integrity": "sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -393,9 +393,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.42.0.tgz",
|
||||
"integrity": "sha512-fJcN4uSGPWdpVmvLuMtALUFwCHgb2XiQjuECkHT3lWLZhSQ3MBQ9pq+WoWeJq2PrNxr9rPM1Qx+IjyGj8/c6zQ==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.0.tgz",
|
||||
"integrity": "sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -405,9 +405,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.42.0.tgz",
|
||||
"integrity": "sha512-CziHfyzpp8hJpCVE/ZdTizw58gr+m7Y2Xq5VOuCSrZR++th2xWAz4Nqk52MoIIrV3JHtVBhbBsJcAxs6NammOQ==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.0.tgz",
|
||||
"integrity": "sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -417,9 +417,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.42.0.tgz",
|
||||
"integrity": "sha512-UsQD5fyLWm2Fe5CDM7VPYAo+UC7+2Px4Y+N3AcPh/LdZu23YcuGPegQly++XEVaC8XUTFVPscl5y5Cl1twEI4A==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.0.tgz",
|
||||
"integrity": "sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -429,9 +429,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.42.0.tgz",
|
||||
"integrity": "sha512-/i8NIrlgc/+4n1lnoWl1zgH7Uo0XK5xK3EDqVTf38KvyYgCU/Rm04+o1VvvzJZnVS5/cWSd07owkzcVasgfIkQ==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.0.tgz",
|
||||
"integrity": "sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -441,9 +441,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.42.0.tgz",
|
||||
"integrity": "sha512-eoujJFOvoIBjZEi9hJnXAbWg+Vo1Ov8n/0IKZZcPZ7JhBzxh2A+2NFyeMZIRkY9iwBvSjloKgcvnjTbGKHE44Q==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.0.tgz",
|
||||
"integrity": "sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -453,9 +453,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.42.0.tgz",
|
||||
"integrity": "sha512-/3NrcOWFSR7RQUQIuZQChLND36aTU9IYE4j+TB40VU78S+RA0IiqHR30oSh6P1S9f9/wVOenHQnacs/Byb824g==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.0.tgz",
|
||||
"integrity": "sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -465,9 +465,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.42.0.tgz",
|
||||
"integrity": "sha512-O8AplvIeavK5ABmZlKBq9/STdZlnQo7Sle0LLhVA7QT+CiGpNVe197/t8Aph9bhJqbDVGCHpY2i7QyfEDDStDg==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.0.tgz",
|
||||
"integrity": "sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@ -477,9 +477,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.42.0.tgz",
|
||||
"integrity": "sha512-6Qb66tbKVN7VyQrekhEzbHRxXXFFD8QKiFAwX5v9Xt6FiJ3BnCVBuyBxa2fkFGqxOCSGGYNejxd8ht+q5SnmtA==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.0.tgz",
|
||||
"integrity": "sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@ -489,9 +489,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.42.0.tgz",
|
||||
"integrity": "sha512-KQETDSEBamQFvg/d8jajtRwLNBlGc3aKpaGiP/LvEbnmVUKlFta1vqJqTrvPtsYsfbE/DLg5CC9zyXRX3fnBiA==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.0.tgz",
|
||||
"integrity": "sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@ -501,9 +501,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.42.0.tgz",
|
||||
"integrity": "sha512-qMvnyjcU37sCo/tuC+JqeDKSuukGAd+pVlRl/oyDbkvPJ3awk6G6ua7tyum02O3lI+fio+eM5wsVd66X0jQtxw==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.0.tgz",
|
||||
"integrity": "sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@ -513,9 +513,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.42.0.tgz",
|
||||
"integrity": "sha512-I2Y1ZUgTgU2RLddUHXTIgyrdOwljjkmcZ/VilvaEumtS3Fkuhbw4p4hgHc39Ypwvo2o7sBFNl2MquNvGCa55Iw==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.0.tgz",
|
||||
"integrity": "sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@ -525,9 +525,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.42.0.tgz",
|
||||
"integrity": "sha512-Gfm6cV6mj3hCUY8TqWa63DB8Mx3NADoFwiJrMpoZ1uESbK8FQV3LXkhfry+8bOniq9pqY1OdsjFWNsSbfjPugw==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz",
|
||||
"integrity": "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -537,9 +537,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.42.0.tgz",
|
||||
"integrity": "sha512-g86PF8YZ9GRqkdi0VoGlcDUb4rYtQKyTD1IVtxxN4Hpe7YqLBShA7oHMKU6oKTCi3uxwW4VkIGnOaH/El8de3w==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.0.tgz",
|
||||
"integrity": "sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -549,9 +549,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.42.0.tgz",
|
||||
"integrity": "sha512-+axkdyDGSp6hjyzQ5m1pgcvQScfHnMCcsXkx8pTgy/6qBmWVhtRVlgxjWwDp67wEXXUr0x+vD6tp5W4x6V7u1A==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.0.tgz",
|
||||
"integrity": "sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -561,9 +561,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.42.0.tgz",
|
||||
"integrity": "sha512-F+5J9pelstXKwRSDq92J0TEBXn2nfUrQGg+HK1+Tk7VOL09e0gBqUHugZv7SW4MGrYj41oNCUe3IKCDGVlis2g==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.0.tgz",
|
||||
"integrity": "sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@ -573,9 +573,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.42.0.tgz",
|
||||
"integrity": "sha512-LpHiJRwkaVz/LqjHjK8LCi8osq7elmpwujwbXKNW88bM8eeGxavJIKKjkjpMHAh/2xfnrt1ZSnhTv41WYUHYmA==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz",
|
||||
"integrity": "sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -595,9 +595,9 @@
|
||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.14.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
||||
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
@ -613,9 +613,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/codemirror": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
|
||||
"integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz",
|
||||
"integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/commands": "^6.0.0",
|
||||
@ -746,11 +746,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.42.0.tgz",
|
||||
"integrity": "sha512-LW+Vse3BJPyGJGAJt1j8pWDKPd73QM8cRXYK1IxOBgL2AGLu7Xd2YOW0M2sLUBCkF5MshXXtMApyEAEzMVMsnw==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.0.tgz",
|
||||
"integrity": "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==",
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.7"
|
||||
"@types/estree": "1.0.8"
|
||||
},
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
@ -760,34 +760,29 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.42.0",
|
||||
"@rollup/rollup-android-arm64": "4.42.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.42.0",
|
||||
"@rollup/rollup-darwin-x64": "4.42.0",
|
||||
"@rollup/rollup-freebsd-arm64": "4.42.0",
|
||||
"@rollup/rollup-freebsd-x64": "4.42.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.42.0",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.42.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.42.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.42.0",
|
||||
"@rollup/rollup-linux-loongarch64-gnu": "4.42.0",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.42.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.42.0",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.42.0",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.42.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.42.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.42.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.42.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.42.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.42.0",
|
||||
"@rollup/rollup-android-arm-eabi": "4.44.0",
|
||||
"@rollup/rollup-android-arm64": "4.44.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.44.0",
|
||||
"@rollup/rollup-darwin-x64": "4.44.0",
|
||||
"@rollup/rollup-freebsd-arm64": "4.44.0",
|
||||
"@rollup/rollup-freebsd-x64": "4.44.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.44.0",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.44.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.44.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.44.0",
|
||||
"@rollup/rollup-linux-loongarch64-gnu": "4.44.0",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.44.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.44.0",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.44.0",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.44.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.44.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.44.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.44.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.44.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.44.0",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup/node_modules/@types/estree": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
|
||||
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
@ -859,9 +854,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.41.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.41.0.tgz",
|
||||
"integrity": "sha512-H406eLPXpZbAX14+B8psIuvIr8+3c+2hkuYzpMkoE0ij+NdsVATbA78vb8neA/eqrj7rywa2pIkdmWRsXW6wmw==",
|
||||
"version": "5.43.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
|
||||
"integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
|
288
docs/usage.md
Normal file
288
docs/usage.md
Normal file
@ -0,0 +1,288 @@
|
||||
# CLI Usage
|
||||
|
||||
## tildefriends -h
|
||||
|
||||
```
|
||||
Usage: out/debug/tildefriends command [command-options]
|
||||
commands:
|
||||
run - Run tildefriends (default).
|
||||
sandbox - Run a sandboxed tildefriends sandbox process (used internally).
|
||||
import - Import apps from file to the database.
|
||||
export - Export apps from the database to file.
|
||||
publish - Append a message to a feed.
|
||||
private - Append a private post message to a feed.
|
||||
create_invite - Create an invite.
|
||||
get_sequence - Get the last sequence number for a feed.
|
||||
get_identity - Get the server account identity.
|
||||
get_profile - Get profile information for the given identity.
|
||||
get_contacts - Get information about followed, blocked, and friend identities.
|
||||
has_blob - Check whether a blob is in the blob store.
|
||||
get_blob - Read a file from the blob store.
|
||||
store_blob - Write a file to the blob store.
|
||||
verify - Verify a feed.
|
||||
test - Test SSB.
|
||||
```
|
||||
|
||||
## tildefriends run -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends run [options]
|
||||
|
||||
Run tildefriends (default).
|
||||
|
||||
options:
|
||||
-s, --script script Script to run (default: core/core.js).
|
||||
-d, --db-path path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-k, --ssb-network-key key SSB network key to use.
|
||||
-n, --count count Number of instances to run.
|
||||
-a, --args args Arguments of the format key=value,foo=bar,verbose=true (note: these are persisted to the database).
|
||||
code_of_conduct (default: ""): Code of conduct presented at sign-in.
|
||||
ssb_port (default: 8008): Port on which to listen for SSB secure handshake connections.
|
||||
http_local_only (default: false): Whether to bind http(s) to the loopback address. Otherwise any.
|
||||
http_port (default: 12345): Port on which to listen for HTTP connections.
|
||||
https_port (default: 0): Port on which to listen for secure HTTP connections.
|
||||
out_http_port_file (default: ""): File to which to write bound HTTP port.
|
||||
blob_fetch_age_seconds (default: -1): Only blobs mentioned more recently than this age will be automatically fetched.
|
||||
blob_expire_age_seconds (default: -1): Blobs older than this will be automatically deleted.
|
||||
fetch_hosts (default: ""): Comma-separated list of host names to which HTTP fetch requests are allowed. None if empty.
|
||||
http_redirect (default: ""): If connecting by HTTP and HTTPS is configured, Location header prefix (ie, "http://example.com")
|
||||
index (default: "/~core/intro/"): Default path.
|
||||
index_map (default: ""): Mappings from hostname to redirect path, one per line, as in: "www.tildefriends.net=/~core/index/"
|
||||
peer_exchange (default: false): Enable discovery of, sharing of, and connecting to internet peer strangers, including announcing this instance.
|
||||
replicator (default: true): Enable message and blob replication.
|
||||
room (default: true): Enable peers to tunnel through this instance as a room.
|
||||
room_name (default: "tilde friends tunnel"): Name of the room.
|
||||
seeds_host (default: "seeds.tildefriends.net"): Hostname for seed connections.
|
||||
account_registration (default: true): Allow registration of new accounts.
|
||||
replication_hops (default: 2): Number of hops to replicate (1 = direct follows, 2 = follows of follows, etc.).
|
||||
delete_stale_feeds (default: false): Periodically delete feeds that aren't visible from local accounts or related follows.
|
||||
talk_to_strangers (default: true): Whether connections are accepted from accounts that aren't in the replication range or otherwise already known.
|
||||
autologin (default: false): Whether mobile autologin is supported.
|
||||
broadcast (default: true): Send network discovery broadcasts.
|
||||
discovery (default: true): Receive network discovery broadcasts.
|
||||
-o, --one-proc Run everything in one process (unsafely!).
|
||||
-z, --zip path Zip archive from which to load files.
|
||||
-v, --verbose Log raw messages.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends sandbox -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends sandbox [options]
|
||||
|
||||
Run a sandboxed tildefriends sandbox process (used internally).
|
||||
|
||||
options:
|
||||
-h, --help Show this usage information.
|
||||
-f, --fd File descriptor with which to communicate with parent process.
|
||||
```
|
||||
|
||||
## tildefriends import -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends import [options] [paths...]
|
||||
|
||||
Import apps from file to the database.
|
||||
|
||||
options:
|
||||
-u, --user user User into whose account apps will be imported (default: "import").
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends export -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends export [options] [paths...]
|
||||
|
||||
Export apps from the database to file.
|
||||
|
||||
options:
|
||||
-u, --user user User from whose account apps will be exported (default: "core").
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-h, --help Show this usage information.
|
||||
|
||||
paths Paths of apps to export (example: /~core/ssb /~user/app).
|
||||
```
|
||||
|
||||
## tildefriends publish -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends publish [options]
|
||||
|
||||
Append a message to a feed.
|
||||
|
||||
options:
|
||||
-u, --user user User owning identity with which to publish.
|
||||
-i, --id identity Identity with which to publish message.
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-c, --content json JSON content of message to publish.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends private -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends private [options]
|
||||
|
||||
Append a private post message to a feed.
|
||||
|
||||
options:
|
||||
-u, --user user User owning identity with which to publish (optional).
|
||||
-i, --id identity Identity with which to publish message.
|
||||
-r, --recipients recipients Recipient identities.
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-t, --text text Private post text.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends create_invite -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends create_invite [options]
|
||||
|
||||
Create an invite.
|
||||
|
||||
options:
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-i, --identity identity Account from which to get latest sequence number.
|
||||
-a, --address address Address to which the recipient will connect.
|
||||
-p, --port port Port to which the recipient will connect.
|
||||
-u, --use_count count Number of times this invite may be used (default: 1).
|
||||
-e, --expires seconds How long this invite is valid in seconds (-1 for indefinitely, default: 1 hour).
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends get_sequence -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends get_sequence [options]
|
||||
|
||||
Get the last sequence number for a feed.
|
||||
|
||||
options:
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-i, --identity identity Account from which to get latest sequence number.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends get_identity -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends get_identity [options]
|
||||
|
||||
Get the server account identity.
|
||||
|
||||
options:
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends get_profile -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends get_profile [options]
|
||||
|
||||
Get profile information for the given identity.
|
||||
|
||||
options:
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-i, --identity identity Account for which to get profile information.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends get_contacts -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends get_contacts [options]
|
||||
|
||||
Get information about followed, blocked, and friend identities.
|
||||
|
||||
options:
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-i, --identity identity Account from which to get contact information.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends has_blob -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends has_blob [options]
|
||||
|
||||
Check whether a blob is in the blob store.
|
||||
|
||||
options:
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-b, --blob_id blob_id ID of blob to query.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends get_blob -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends get_blob [options]
|
||||
|
||||
Read a file from the blob store.
|
||||
|
||||
options:
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-b, --blob blob_id Blob identifier to retrieve.
|
||||
-o, --output file_path Location to write the retrieved blob.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends store_blob -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends store_blob [options]
|
||||
|
||||
Write a file to the blob store.
|
||||
|
||||
options:
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-f, --file file_path Path to file to add to the blob store.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends verify -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends verify [options]
|
||||
|
||||
Verify a feed.
|
||||
|
||||
options:
|
||||
-i, --identity identity Identity to verify.
|
||||
-s, --sequence sequence Sequence number to debug.
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends test -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends test [options]
|
||||
|
||||
Test SSB.
|
||||
|
||||
options:
|
||||
-t, --tests tests Comma-separated list of tests to run. (default: all)
|
||||
-h, --help Show this usage information.
|
||||
```
|
14
metadata/en-US/changelogs/38.txt
Normal file
14
metadata/en-US/changelogs/38.txt
Normal file
@ -0,0 +1,14 @@
|
||||
* Improve load times.
|
||||
* Fix the messages_refs table, and make it usable for hashtags.
|
||||
* Fix a circumstance where we would fail to call promise callbacks.
|
||||
* Fix the private messages tab.
|
||||
* Fix the room app.
|
||||
* Expose followed accounts in a user's profile.
|
||||
* Show connection status in the sidebar.
|
||||
* Simplify placeholder messages.
|
||||
* Only show "Mark as Read" when relevant.
|
||||
* Treat profile images more like post images.
|
||||
* Limit the WAL file size.
|
||||
* Updates:
|
||||
* CodeMirror
|
||||
* sqlite 3.50.1
|
6
package-lock.json
generated
6
package-lock.json
generated
@ -11,9 +11,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
|
||||
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.1.tgz",
|
||||
"integrity": "sha512-5xGWRa90Sp2+x1dQtNpIpeOQpTDBs9cZDmA/qs2vDNN2i18PdapqY7CmBeyLlMuGqXJRIOPaCaVZTLNQRWUH/A==",
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
|
@ -2,7 +2,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.unprompted.tildefriends"
|
||||
android:versionCode="38"
|
||||
android:versionName="0.0.32-wip">
|
||||
android:versionName="0.0.32">
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<application
|
||||
|
@ -11,7 +11,8 @@
|
||||
** @{
|
||||
*/
|
||||
|
||||
#include "quickjs.h"
|
||||
/** A JS context. */
|
||||
typedef struct JSContext JSContext;
|
||||
|
||||
/**
|
||||
** An HTTP server instance.
|
||||
|
82
src/main.c
82
src/main.c
@ -169,8 +169,8 @@ typedef struct _command_t
|
||||
const command_t k_commands[] = {
|
||||
{ "run", _tf_command_run, "Run tildefriends (default)." },
|
||||
{ "sandbox", _tf_command_sandbox, "Run a sandboxed tildefriends sandbox process (used internally)." },
|
||||
{ "import", _tf_command_import, "Import apps to SSB." },
|
||||
{ "export", _tf_command_export, "Export apps from SSB." },
|
||||
{ "import", _tf_command_import, "Import apps from file to the database." },
|
||||
{ "export", _tf_command_export, "Export apps from the database to file." },
|
||||
{ "publish", _tf_command_publish, "Append a message to a feed." },
|
||||
{ "private", _tf_command_private, "Append a private post message to a feed." },
|
||||
{ "create_invite", _tf_command_create_invite, "Create an invite." },
|
||||
@ -185,6 +185,18 @@ const command_t k_commands[] = {
|
||||
{ "test", _tf_command_test, "Test SSB." },
|
||||
};
|
||||
|
||||
static const char* _description(const char* name)
|
||||
{
|
||||
for (int i = 0; i < tf_countof(k_commands); i++)
|
||||
{
|
||||
if (strcmp(name, k_commands[i].name) == 0)
|
||||
{
|
||||
return k_commands[i].description;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _tf_command_test(const char* file, int argc, char* argv[])
|
||||
{
|
||||
#if !defined(__ANDROID__)
|
||||
@ -233,8 +245,9 @@ static int _tf_command_test(const char* file, int argc, char* argv[])
|
||||
|
||||
if (show_usage)
|
||||
{
|
||||
tf_printf("\n%s test [options]\n\n", file);
|
||||
tf_printf("options\n");
|
||||
tf_printf("\nUsage: %s test [options]\n\n", file);
|
||||
tf_printf("%s\n\n", _description("test"));
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -t, --tests tests Comma-separated list of tests to run. (default: all)\n");
|
||||
tf_printf(" -h, --help Show this usage information.\n");
|
||||
tf_free((void*)default_db_path);
|
||||
@ -288,7 +301,8 @@ static int _tf_command_import(const char* file, int argc, char* argv[])
|
||||
|
||||
if (show_usage)
|
||||
{
|
||||
tf_printf("\n%s import [options] [paths...]\n\n", file);
|
||||
tf_printf("\nUsage: %s import [options] [paths...]\n\n", file);
|
||||
tf_printf("%s\n\n", _description("import"));
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -u, --user user User into whose account apps will be imported (default: \"import\").\n");
|
||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||
@ -357,7 +371,8 @@ static int _tf_command_export(const char* file, int argc, char* argv[])
|
||||
|
||||
if (show_usage)
|
||||
{
|
||||
tf_printf("\n%s export [options] [paths...]\n\n", file);
|
||||
tf_printf("\nUsage: %s export [options] [paths...]\n\n", file);
|
||||
tf_printf("%s\n\n", _description("export"));
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -u, --user user User from whose account apps will be exported (default: \"core\").\n");
|
||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||
@ -473,7 +488,8 @@ static int _tf_command_publish(const char* file, int argc, char* argv[])
|
||||
|
||||
if (show_usage || !identity || !content)
|
||||
{
|
||||
tf_printf("\n%s publish [options]\n\n", file);
|
||||
tf_printf("\nUsage: %s publish [options]\n\n", file);
|
||||
tf_printf("%s\n\n", _description("publish"));
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -u, --user user User owning identity with which to publish.\n");
|
||||
tf_printf(" -i, --id identity Identity with which to publish message.\n");
|
||||
@ -501,7 +517,7 @@ static int _tf_command_publish(const char* file, int argc, char* argv[])
|
||||
if (tf_ssb_db_identity_get_private_key(ssb, user, identity, private_key, sizeof(private_key)))
|
||||
{
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
int64_t sequence = 0;
|
||||
int32_t sequence = 0;
|
||||
char previous[k_id_base64_len] = { 0 };
|
||||
tf_ssb_db_get_latest_message_by_author(ssb, identity, &sequence, previous, sizeof(previous));
|
||||
JSValue content_value = JS_ParseJSON(context, content, strlen(content), NULL);
|
||||
@ -592,7 +608,8 @@ static int _tf_command_private(const char* file, int argc, char* argv[])
|
||||
|
||||
if (show_usage || !identity || !recipients || !text)
|
||||
{
|
||||
tf_printf("\n%s private [options]\n\n", file);
|
||||
tf_printf("\nUsage: %s private [options]\n\n", file);
|
||||
tf_printf("%s\n\n", _description("private"));
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -u, --user user User owning identity with which to publish (optional).\n");
|
||||
tf_printf(" -i, --id identity Identity with which to publish message.\n");
|
||||
@ -653,7 +670,7 @@ static int _tf_command_private(const char* file, int argc, char* argv[])
|
||||
char* encrypted = tf_ssb_private_message_encrypt(private_key, recipient_list, recipient_count, message_str, strlen(message_str));
|
||||
if (encrypted)
|
||||
{
|
||||
int64_t sequence = 0;
|
||||
int32_t sequence = 0;
|
||||
char previous[k_id_base64_len] = { 0 };
|
||||
tf_ssb_db_get_latest_message_by_author(ssb, identity, &sequence, previous, sizeof(previous));
|
||||
|
||||
@ -723,7 +740,8 @@ static int _tf_command_store_blob(const char* file, int argc, char* argv[])
|
||||
|
||||
if (show_usage || !file_path)
|
||||
{
|
||||
tf_printf("\n%s store_blob [options]\n\n", file);
|
||||
tf_printf("\nUsage: %s store_blob [options]\n\n", file);
|
||||
tf_printf("%s\n\n", _description("store_blob"));
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||
tf_printf(" -f, --file file_path Path to file to add to the blob store.\n");
|
||||
@ -825,7 +843,8 @@ static int _tf_command_get_blob(const char* file, int argc, char* argv[])
|
||||
|
||||
if (show_usage || !blob_id)
|
||||
{
|
||||
tf_printf("\n%s store_blob [options]\n\n", file);
|
||||
tf_printf("\nUsage: %s get_blob [options]\n\n", file);
|
||||
tf_printf("%s\n\n", _description("get_blob"));
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||
tf_printf(" -b, --blob blob_id Blob identifier to retrieve.\n");
|
||||
@ -927,7 +946,8 @@ static int _tf_command_has_blob(const char* file, int argc, char* argv[])
|
||||
|
||||
if (show_usage || !blob_id)
|
||||
{
|
||||
tf_printf("\n%s has_blob [options]\n\n", file);
|
||||
tf_printf("\nUsage: %s has_blob [options]\n\n", file);
|
||||
tf_printf("%s\n\n", _description("has_blob"));
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||
tf_printf(" -b, --blob_id blob_id ID of blob to query.\n");
|
||||
@ -1005,7 +1025,8 @@ static int _tf_command_create_invite(const char* file, int argc, char* argv[])
|
||||
|
||||
if (show_usage || !identity || !use_count || !expires || !host || !port)
|
||||
{
|
||||
tf_printf("\n%s get_sequence [options]\n\n", file);
|
||||
tf_printf("\nUsage: %s create_invite [options]\n\n", file);
|
||||
tf_printf("%s\n\n", _description("create_invite"));
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||
tf_printf(" -i, --identity identity Account from which to get latest sequence number.\n");
|
||||
@ -1073,7 +1094,8 @@ static int _tf_command_get_sequence(const char* file, int argc, char* argv[])
|
||||
|
||||
if (show_usage || !identity)
|
||||
{
|
||||
tf_printf("\n%s get_sequence [options]\n\n", file);
|
||||
tf_printf("\nUsage: %s get_sequence [options]\n\n", file);
|
||||
tf_printf("%s\n\n", _description("get_sequence"));
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||
tf_printf(" -i, --identity identity Account from which to get latest sequence number.\n");
|
||||
@ -1084,9 +1106,9 @@ static int _tf_command_get_sequence(const char* file, int argc, char* argv[])
|
||||
|
||||
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL);
|
||||
tf_ssb_set_quiet(ssb, true);
|
||||
int64_t sequence = -1;
|
||||
int32_t sequence = -1;
|
||||
int result = tf_ssb_db_get_latest_message_by_author(ssb, identity, &sequence, NULL, 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
tf_printf("%" PRId64 "\n", sequence);
|
||||
tf_printf("%d\n", sequence);
|
||||
tf_ssb_destroy(ssb);
|
||||
tf_free((void*)default_db_path);
|
||||
return result;
|
||||
@ -1126,7 +1148,8 @@ static int _tf_command_get_identity(const char* file, int argc, char* argv[])
|
||||
|
||||
if (show_usage)
|
||||
{
|
||||
tf_printf("\n%s get_identity [options]\n\n", file);
|
||||
tf_printf("\nUsage: %s get_identity [options]\n\n", file);
|
||||
tf_printf("%s\n\n", _description("get_identity"));
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||
tf_printf(" -h, --help Show this usage information.\n");
|
||||
@ -1183,7 +1206,8 @@ static int _tf_command_get_profile(const char* file, int argc, char* argv[])
|
||||
|
||||
if (show_usage || !identity)
|
||||
{
|
||||
tf_printf("\n%s get_profile [options]\n\n", file);
|
||||
tf_printf("\nUsage: %s get_profile [options]\n\n", file);
|
||||
tf_printf("%s\n\n", _description("get_profile"));
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||
tf_printf(" -i, --identity identity Account for which to get profile information.\n");
|
||||
@ -1242,7 +1266,8 @@ static int _tf_command_get_contacts(const char* file, int argc, char* argv[])
|
||||
|
||||
if (show_usage || !identity)
|
||||
{
|
||||
tf_printf("\n%s get_contacts [options]\n\n", file);
|
||||
tf_printf("\nUsage: %s get_contacts [options]\n\n", file);
|
||||
tf_printf("%s\n\n", _description("get_contacts"));
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||
tf_printf(" -i, --identity identity Account from which to get contact information.\n");
|
||||
@ -1304,7 +1329,7 @@ static int _tf_command_verify(const char* file, int argc, char* argv[])
|
||||
const char* identity = NULL;
|
||||
const char* default_db_path = _get_db_path();
|
||||
const char* db_path = default_db_path;
|
||||
int64_t sequence = 0;
|
||||
int32_t sequence = 0;
|
||||
bool show_usage = false;
|
||||
|
||||
while (!show_usage)
|
||||
@ -1333,7 +1358,7 @@ static int _tf_command_verify(const char* file, int argc, char* argv[])
|
||||
identity = optarg;
|
||||
break;
|
||||
case 's':
|
||||
sequence = atoll(optarg);
|
||||
sequence = atoi(optarg);
|
||||
break;
|
||||
case 'd':
|
||||
db_path = optarg;
|
||||
@ -1343,7 +1368,8 @@ static int _tf_command_verify(const char* file, int argc, char* argv[])
|
||||
|
||||
if (show_usage)
|
||||
{
|
||||
tf_printf("\n%s import [options] [paths...]\n\n", file);
|
||||
tf_printf("\nUsage: %s verify [options]\n\n", file);
|
||||
tf_printf("%s\n\n", _description("verify"));
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -i, --identity identity Identity to verify.\n");
|
||||
tf_printf(" -s, --sequence sequence Sequence number to debug.\n");
|
||||
@ -1671,8 +1697,11 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
|
||||
|
||||
if (show_usage)
|
||||
{
|
||||
tf_printf("\n%s run [options]\n\n", file);
|
||||
tf_printf("options\n");
|
||||
tf_printf("\nUsage: %s run [options]\n\n", file);
|
||||
#if !defined(__ANDROID__)
|
||||
tf_printf("%s\n\n", _description("run"));
|
||||
#endif
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -s, --script script Script to run (default: core/core.js).\n");
|
||||
tf_printf(" -d, --db-path path SQLite database path (default: %s).\n", default_db_path);
|
||||
tf_printf(" -k, --ssb-network-key key SSB network key to use.\n");
|
||||
@ -1757,6 +1786,9 @@ static int _tf_command_sandbox(const char* file, int argc, char* argv[])
|
||||
if (show_usage)
|
||||
{
|
||||
tf_printf("\nUsage: %s sandbox [options]\n\n", file);
|
||||
#if !defined(__ANDROID__)
|
||||
tf_printf("%s\n\n", _description("sandbox"));
|
||||
#endif
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -h, --help Show this usage information.\n");
|
||||
tf_printf(" -f, --fd File descriptor with which to communicate with parent process.\n");
|
||||
|
65
src/ssb.c
65
src/ssb.c
@ -165,6 +165,12 @@ typedef struct _tf_ssb_timer_t
|
||||
void* user_data;
|
||||
} tf_ssb_timer_t;
|
||||
|
||||
typedef struct _tf_ssb_broadcast_result_t
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
int result;
|
||||
} tf_ssb_broadcast_result_t;
|
||||
|
||||
typedef struct _tf_ssb_t
|
||||
{
|
||||
bool own_context;
|
||||
@ -192,6 +198,9 @@ typedef struct _tf_ssb_t
|
||||
uv_timer_t request_activity_timer;
|
||||
uv_tcp_t server;
|
||||
|
||||
tf_ssb_broadcast_result_t* broadcast_results;
|
||||
int broadcast_results_count;
|
||||
|
||||
uint8_t network_key[32];
|
||||
|
||||
uint8_t pub[crypto_sign_PUBLICKEYBYTES];
|
||||
@ -1839,7 +1848,7 @@ static void _tf_ssb_connection_rpc_recv_push(tf_ssb_connection_t* connection, co
|
||||
|
||||
while (connection->rpc_recv_size >= 9)
|
||||
{
|
||||
uint8_t flags = *connection->rpc_recv_buffer;
|
||||
uint8_t flags = (*connection->rpc_recv_buffer) & 0xf;
|
||||
uint32_t body_len;
|
||||
int32_t request_number;
|
||||
memcpy(&body_len, connection->rpc_recv_buffer + 1, sizeof(body_len));
|
||||
@ -1917,10 +1926,10 @@ static bool _tf_ssb_connection_box_stream_recv(tf_ssb_connection_t* connection)
|
||||
return true;
|
||||
}
|
||||
|
||||
JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* private_key, JSValue message, const char* previous_id, int64_t previous_sequence)
|
||||
JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* private_key, JSValue message, const char* previous_id, int32_t previous_sequence)
|
||||
{
|
||||
char actual_previous_id[crypto_hash_sha256_BYTES * 2];
|
||||
int64_t actual_previous_sequence = 0;
|
||||
int32_t actual_previous_sequence = 0;
|
||||
bool have_previous = false;
|
||||
if (previous_id)
|
||||
{
|
||||
@ -1938,7 +1947,7 @@ JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* pr
|
||||
|
||||
JS_SetPropertyStr(context, root, "previous", have_previous ? JS_NewString(context, actual_previous_id) : JS_NULL);
|
||||
JS_SetPropertyStr(context, root, "author", JS_NewString(context, author));
|
||||
JS_SetPropertyStr(context, root, "sequence", JS_NewInt64(context, actual_previous_sequence + 1));
|
||||
JS_SetPropertyStr(context, root, "sequence", JS_NewInt32(context, actual_previous_sequence + 1));
|
||||
|
||||
int64_t now = (int64_t)time(NULL);
|
||||
JS_SetPropertyStr(context, root, "timestamp", JS_NewInt64(context, now * 1000LL));
|
||||
@ -2841,6 +2850,12 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
|
||||
tf_free(ssb->room_name);
|
||||
ssb->room_name = NULL;
|
||||
}
|
||||
if (ssb->broadcast_results_count)
|
||||
{
|
||||
tf_free(ssb->broadcast_results);
|
||||
ssb->broadcast_results = NULL;
|
||||
ssb->broadcast_results_count = 0;
|
||||
}
|
||||
|
||||
ssb->shutting_down_deferred = true;
|
||||
if (ssb->connection_ref_count == 0 && ssb->db_ref_count == 0)
|
||||
@ -3257,6 +3272,39 @@ static void _tf_ssb_on_connection(uv_stream_t* stream, int status)
|
||||
_tf_ssb_connection_read_start(connection);
|
||||
}
|
||||
|
||||
static void _tf_ssb_update_broadcast_result(tf_ssb_t* ssb, struct sockaddr* address, const char* address_str, int result)
|
||||
{
|
||||
for (int i = 0; i < ssb->broadcast_results_count; i++)
|
||||
{
|
||||
if (ssb->broadcast_results[i].addr.ss_family == address->sa_family && address->sa_family == AF_INET &&
|
||||
memcmp(&ssb->broadcast_results[i].addr, address, sizeof(struct sockaddr_in)) == 0)
|
||||
{
|
||||
if (result != ssb->broadcast_results[i].result)
|
||||
{
|
||||
if (result)
|
||||
{
|
||||
char broadcast_str[256] = { 0 };
|
||||
uv_ip4_name((struct sockaddr_in*)address, broadcast_str, sizeof(broadcast_str));
|
||||
tf_printf("Unable to send broadcast for %s via %s (%d): %s.\n", address_str, broadcast_str, result, uv_strerror(result));
|
||||
}
|
||||
ssb->broadcast_results[i].result = result;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (address->sa_family == AF_INET)
|
||||
{
|
||||
struct sockaddr_storage storage = { 0 };
|
||||
memcpy(&storage, address, sizeof(struct sockaddr_in));
|
||||
ssb->broadcast_results = tf_resize_vec(ssb->broadcast_results, sizeof(tf_ssb_broadcast_result_t) * (ssb->broadcast_results_count + 1));
|
||||
ssb->broadcast_results[ssb->broadcast_results_count++] = (tf_ssb_broadcast_result_t) {
|
||||
.result = result,
|
||||
.addr = storage,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static void _tf_ssb_send_broadcast(tf_ssb_t* ssb, struct sockaddr_in* address, struct sockaddr_in* netmask)
|
||||
{
|
||||
struct sockaddr server_addr;
|
||||
@ -3290,12 +3338,7 @@ static void _tf_ssb_send_broadcast(tf_ssb_t* ssb, struct sockaddr_in* address, s
|
||||
broadcast_addr.sin_port = htons(8008);
|
||||
broadcast_addr.sin_addr.s_addr = (address->sin_addr.s_addr & netmask->sin_addr.s_addr) | (INADDR_BROADCAST & ~netmask->sin_addr.s_addr);
|
||||
r = uv_udp_try_send(&ssb->broadcast_sender, &buf, 1, (struct sockaddr*)&broadcast_addr);
|
||||
if (r < 0)
|
||||
{
|
||||
char broadcast_str[256] = { 0 };
|
||||
uv_ip4_name(&broadcast_addr, broadcast_str, sizeof(broadcast_str));
|
||||
tf_printf("failed to send broadcast for %s via %s (%d): %s\n", address_str, broadcast_str, r, uv_strerror(r));
|
||||
}
|
||||
_tf_ssb_update_broadcast_result(ssb, (struct sockaddr*)&broadcast_addr, address_str, r);
|
||||
}
|
||||
|
||||
typedef struct _seeds_t
|
||||
@ -3923,7 +3966,7 @@ void tf_ssb_notify_blob_stored(tf_ssb_t* ssb, const char* id)
|
||||
ssb->blobs_stored++;
|
||||
}
|
||||
|
||||
void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, JSValue message_keys)
|
||||
void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int32_t sequence, const char* id, JSValue message_keys)
|
||||
{
|
||||
tf_ssb_message_added_callback_node_t* next = NULL;
|
||||
ssb->messages_stored++;
|
||||
|
70
src/ssb.db.c
70
src/ssb.db.c
@ -232,6 +232,7 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
||||
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_size_by_author_index ON messages (author, length(content))");
|
||||
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_timestamp_index ON messages (timestamp)");
|
||||
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_type_timestamp_index ON messages (content ->> 'type', timestamp)");
|
||||
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_type_root_index ON messages (author, content ->> 'type', content ->> 'root')");
|
||||
_tf_ssb_db_exec(db, "DROP INDEX IF EXISTS messages_author_id_index");
|
||||
_tf_ssb_db_exec(db, "DROP INDEX IF EXISTS messages_by_author_index");
|
||||
_tf_ssb_db_exec(db, "DROP INDEX IF EXISTS messages_id_index");
|
||||
@ -495,7 +496,7 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
}
|
||||
|
||||
static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author, int64_t sequence, const char* previous, bool* out_id_mismatch)
|
||||
static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author, int32_t sequence, const char* previous, bool* out_id_mismatch)
|
||||
{
|
||||
bool exists = false;
|
||||
if (sequence == 1)
|
||||
@ -507,7 +508,7 @@ static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author,
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare_v2(db, "SELECT COUNT(*), id != ?3 AS is_mismatch FROM messages WHERE author = ?1 AND sequence = ?2", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK && sqlite3_bind_int64(statement, 2, sequence - 1) == SQLITE_OK &&
|
||||
if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK && sqlite3_bind_int(statement, 2, sequence - 1) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 3, previous, -1, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
exists = sqlite3_column_int(statement, 0) != 0;
|
||||
@ -519,7 +520,7 @@ static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author,
|
||||
return exists;
|
||||
}
|
||||
|
||||
static int64_t _tf_ssb_db_store_message_raw(sqlite3* db, const char* id, const char* previous, const char* author, int64_t sequence, double timestamp, const char* content,
|
||||
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,
|
||||
size_t content_len, const char* signature, int flags)
|
||||
{
|
||||
int64_t last_row_id = -1;
|
||||
@ -534,7 +535,7 @@ static int64_t _tf_ssb_db_store_message_raw(sqlite3* db, const char* id, const c
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
|
||||
(previous ? sqlite3_bind_text(statement, 2, previous, -1, NULL) : sqlite3_bind_null(statement, 2)) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 3, author, -1, NULL) == SQLITE_OK && sqlite3_bind_int64(statement, 4, sequence) == SQLITE_OK &&
|
||||
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)
|
||||
@ -568,7 +569,7 @@ static int64_t _tf_ssb_db_store_message_raw(sqlite3* db, const char* id, const c
|
||||
** message when trying to receive what we don't have, and
|
||||
** that's not helping anybody.
|
||||
*/
|
||||
tf_printf("%p: Previous message doesn't exist for author=%s sequence=%" PRId64 " previous=%s.\n", db, author, sequence, previous);
|
||||
tf_printf("%p: Previous message doesn't exist for author=%s sequence=%d previous=%s.\n", db, author, sequence, previous);
|
||||
}
|
||||
return last_row_id;
|
||||
}
|
||||
@ -625,7 +626,7 @@ typedef struct _message_store_t
|
||||
int flags;
|
||||
char previous[k_id_base64_len];
|
||||
char author[k_id_base64_len];
|
||||
int64_t sequence;
|
||||
int32_t sequence;
|
||||
double timestamp;
|
||||
const char* content;
|
||||
size_t length;
|
||||
@ -761,9 +762,9 @@ void tf_ssb_db_store_message(
|
||||
const char* author = JS_ToCString(context, authorval);
|
||||
JS_FreeValue(context, authorval);
|
||||
|
||||
int64_t sequence = -1;
|
||||
int32_t sequence = -1;
|
||||
JSValue sequenceval = JS_GetPropertyStr(context, val, "sequence");
|
||||
JS_ToInt64(context, &sequence, sequenceval);
|
||||
JS_ToInt32(context, &sequence, sequenceval);
|
||||
JS_FreeValue(context, sequenceval);
|
||||
|
||||
double timestamp = -1.0;
|
||||
@ -1055,7 +1056,7 @@ bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char*
|
||||
return result;
|
||||
}
|
||||
|
||||
bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, char* out_previous,
|
||||
bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int32_t sequence, char* out_message_id, size_t out_message_id_size, char* out_previous,
|
||||
size_t out_previous_size, double* out_timestamp, char** out_content, char* out_hash, size_t out_hash_size, char* out_signature, size_t out_signature_size, int* out_flags)
|
||||
{
|
||||
bool found = false;
|
||||
@ -1064,7 +1065,7 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
if (sqlite3_prepare_v2(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK && sqlite3_bind_int64(statement, 2, sequence) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
||||
if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK && sqlite3_bind_int(statement, 2, sequence) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
if (out_message_id)
|
||||
{
|
||||
@ -1116,7 +1117,7 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
|
||||
return found;
|
||||
}
|
||||
|
||||
bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, int64_t* out_sequence, char* out_message_id, size_t out_message_id_size)
|
||||
bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, int32_t* out_sequence, char* out_message_id, size_t out_message_id_size)
|
||||
{
|
||||
bool found = false;
|
||||
sqlite3_stmt* statement;
|
||||
@ -1131,7 +1132,7 @@ bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, i
|
||||
{
|
||||
if (out_sequence)
|
||||
{
|
||||
*out_sequence = sqlite3_column_int64(statement, 1);
|
||||
*out_sequence = sqlite3_column_int(statement, 1);
|
||||
}
|
||||
if (out_message_id)
|
||||
{
|
||||
@ -1155,7 +1156,7 @@ bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, i
|
||||
{
|
||||
if (out_sequence)
|
||||
{
|
||||
*out_sequence = sqlite3_column_int64(statement, 0);
|
||||
*out_sequence = sqlite3_column_int(statement, 0);
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
@ -1333,19 +1334,19 @@ JSValue tf_ssb_db_visit_query(tf_ssb_t* ssb, const char* query, const JSValue bi
|
||||
}
|
||||
|
||||
JSValue tf_ssb_format_message(
|
||||
JSContext* context, const char* previous, const char* author, int64_t sequence, double timestamp, const char* hash, const char* content, const char* signature, int flags)
|
||||
JSContext* context, const char* previous, const char* author, int32_t sequence, double timestamp, const char* hash, const char* content, const char* signature, int flags)
|
||||
{
|
||||
JSValue value = JS_NewObject(context);
|
||||
JS_SetPropertyStr(context, value, "previous", (previous && *previous) ? JS_NewString(context, previous) : JS_NULL);
|
||||
if (flags & k_tf_ssb_message_flag_sequence_before_author)
|
||||
{
|
||||
JS_SetPropertyStr(context, value, "sequence", JS_NewInt64(context, sequence));
|
||||
JS_SetPropertyStr(context, value, "sequence", JS_NewInt32(context, sequence));
|
||||
JS_SetPropertyStr(context, value, "author", JS_NewString(context, author));
|
||||
}
|
||||
else
|
||||
{
|
||||
JS_SetPropertyStr(context, value, "author", JS_NewString(context, author));
|
||||
JS_SetPropertyStr(context, value, "sequence", JS_NewInt64(context, sequence));
|
||||
JS_SetPropertyStr(context, value, "sequence", JS_NewInt32(context, sequence));
|
||||
}
|
||||
JS_SetPropertyStr(context, value, "timestamp", JS_NewFloat64(context, timestamp));
|
||||
JS_SetPropertyStr(context, value, "hash", JS_NewString(context, hash));
|
||||
@ -1552,14 +1553,14 @@ typedef struct _following_t following_t;
|
||||
typedef struct _following_t
|
||||
{
|
||||
char id[k_id_base64_len];
|
||||
bool populated;
|
||||
int depth;
|
||||
following_t** following;
|
||||
following_t** blocking;
|
||||
int following_count;
|
||||
int blocking_count;
|
||||
int depth;
|
||||
int ref_count;
|
||||
int block_ref_count;
|
||||
bool populated;
|
||||
} following_t;
|
||||
|
||||
static int _following_compare(const void* a, const void* b)
|
||||
@ -1745,7 +1746,7 @@ static sqlite3_stmt* _make_following_statement(sqlite3* db)
|
||||
"SELECT content ->> '$.contact' AS contact, content ->> '$.following', content ->> '$.blocking' "
|
||||
"FROM messages "
|
||||
"WHERE author = ? AND content ->> '$.type' = 'contact' AND contact IS NOT NULL "
|
||||
"ORDER BY content ->> '$.contact', sequence",
|
||||
"ORDER BY sequence",
|
||||
-1, &statement, NULL) != SQLITE_OK)
|
||||
{
|
||||
tf_printf("prepare failed: %s", sqlite3_errmsg(db));
|
||||
@ -1778,22 +1779,23 @@ tf_ssb_following_t* tf_ssb_db_following_deep(tf_ssb_t* ssb, const char** ids, in
|
||||
}
|
||||
|
||||
tf_ssb_following_t* result = tf_malloc(sizeof(tf_ssb_following_t) * (actual_following_count + 1));
|
||||
memset(result, 0, sizeof(tf_ssb_following_t) * (actual_following_count + 1));
|
||||
|
||||
int write_index = 0;
|
||||
for (int i = 0; i < following_count; i++)
|
||||
{
|
||||
if (following[i]->ref_count > 0 || include_blocks)
|
||||
{
|
||||
result[write_index] = (tf_ssb_following_t) {
|
||||
.following_count = following[i]->following_count,
|
||||
.blocking_count = following[i]->blocking_count,
|
||||
.followed_by_count = following[i]->ref_count,
|
||||
.blocked_by_count = following[i]->block_ref_count,
|
||||
.depth = following[i]->depth,
|
||||
};
|
||||
tf_string_set(result[write_index].id, sizeof(result[write_index].id), following[i]->id);
|
||||
result[write_index].following_count = following[i]->following_count;
|
||||
result[write_index].blocking_count = following[i]->blocking_count;
|
||||
result[write_index].followed_by_count = following[i]->ref_count;
|
||||
result[write_index].blocked_by_count = following[i]->block_ref_count;
|
||||
result[write_index].depth = following[i]->depth;
|
||||
write_index++;
|
||||
}
|
||||
}
|
||||
result[write_index] = (tf_ssb_following_t) { 0 };
|
||||
|
||||
for (int i = 0; i < following_count; i++)
|
||||
{
|
||||
@ -1900,7 +1902,7 @@ JSValue tf_ssb_db_get_message_by_id(tf_ssb_t* ssb, const char* id, bool is_keys)
|
||||
{
|
||||
JSValue message = JS_UNDEFINED;
|
||||
JSValue formatted = tf_ssb_format_message(context, (const char*)sqlite3_column_text(statement, 0), (const char*)sqlite3_column_text(statement, 1),
|
||||
sqlite3_column_int64(statement, 3), sqlite3_column_double(statement, 4), (const char*)sqlite3_column_text(statement, 5),
|
||||
sqlite3_column_int(statement, 3), sqlite3_column_double(statement, 4), (const char*)sqlite3_column_text(statement, 5),
|
||||
(const char*)sqlite3_column_text(statement, 6), (const char*)sqlite3_column_text(statement, 7), sqlite3_column_int(statement, 8));
|
||||
if (is_keys)
|
||||
{
|
||||
@ -2288,14 +2290,14 @@ static void _tf_ssb_db_set_flags(tf_ssb_t* ssb, const char* message_id, int flag
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
}
|
||||
|
||||
bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id, int64_t debug_sequence, bool fix)
|
||||
bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id, int32_t debug_sequence, bool fix)
|
||||
{
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
bool verified = true;
|
||||
int64_t sequence = -1;
|
||||
int32_t sequence = -1;
|
||||
if (tf_ssb_db_get_latest_message_by_author(ssb, id, &sequence, NULL, 0))
|
||||
{
|
||||
for (int64_t i = 1; i <= sequence; i++)
|
||||
for (int32_t i = 1; i <= sequence; i++)
|
||||
{
|
||||
char message_id[k_id_base64_len];
|
||||
char previous[256];
|
||||
@ -2314,12 +2316,12 @@ bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id, int64_t debug_sequence, boo
|
||||
if (!tf_ssb_verify_and_strip_signature(context, message, i == debug_sequence ? k_tf_ssb_verify_flag_debug : 0, calculated_id, sizeof(calculated_id),
|
||||
extracted_signature, sizeof(extracted_signature), &calculated_flags))
|
||||
{
|
||||
tf_printf("author=%s sequence=%" PRId64 " verify failed.\n", id, i);
|
||||
tf_printf("author=%s sequence=%d verify failed.\n", id, i);
|
||||
verified = false;
|
||||
}
|
||||
if (calculated_flags != flags)
|
||||
{
|
||||
tf_printf("author=%s sequence=%" PRId64 " flag mismatch %d => %d.\n", id, i, flags, calculated_flags);
|
||||
tf_printf("author=%s sequence=%d flag mismatch %d => %d.\n", id, i, flags, calculated_flags);
|
||||
if (fix)
|
||||
{
|
||||
_tf_ssb_db_set_flags(ssb, message_id, calculated_flags);
|
||||
@ -2331,7 +2333,7 @@ bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id, int64_t debug_sequence, boo
|
||||
}
|
||||
if (strcmp(message_id, calculated_id))
|
||||
{
|
||||
tf_printf("author=%s sequence=%" PRId64 " id mismatch %s => %s.\n", id, i, message_id, calculated_id);
|
||||
tf_printf("author=%s sequence=%d id mismatch %s => %s.\n", id, i, message_id, calculated_id);
|
||||
verified = false;
|
||||
}
|
||||
JS_FreeValue(context, message);
|
||||
@ -2344,7 +2346,7 @@ bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id, int64_t debug_sequence, boo
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_printf("Unable to find message with sequence=%" PRId64 " for author=%s.", i, id);
|
||||
tf_printf("Unable to find message with sequence=%d for author=%s.", i, id);
|
||||
verified = false;
|
||||
break;
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ JSValue tf_ssb_db_get_message_by_id(tf_ssb_t* ssb, const char* id, bool is_keys)
|
||||
** @param[out] out_flags Populated with flags describing the format of the message.
|
||||
** @return True if the message was found and retrieved.
|
||||
*/
|
||||
bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, char* out_previous,
|
||||
bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int32_t sequence, char* out_message_id, size_t out_message_id_size, char* out_previous,
|
||||
size_t out_previous_size, double* out_timestamp, char** out_content, char* out_hash, size_t out_hash_size, char* out_signature, size_t out_signature_size, int* out_flags);
|
||||
|
||||
/**
|
||||
@ -163,7 +163,7 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
|
||||
** @param out_message_id_size The size of the out_message_id buffer.
|
||||
** @return True if the message was found and information was retrieved.
|
||||
*/
|
||||
bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, int64_t* out_sequence, char* out_message_id, size_t out_message_id_size);
|
||||
bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, int32_t* out_sequence, char* out_message_id, size_t out_message_id_size);
|
||||
|
||||
/**
|
||||
** Call a function for each result row of an SQL query.
|
||||
@ -281,7 +281,7 @@ bool tf_ssb_db_identity_get_private_key(tf_ssb_t* ssb, const char* user, const c
|
||||
** @param flags tf_ssb_message_flags_t describing the message.
|
||||
*/
|
||||
JSValue tf_ssb_format_message(
|
||||
JSContext* context, const char* previous, const char* author, int64_t sequence, double timestamp, const char* hash, const char* content, const char* signature, int flags);
|
||||
JSContext* context, const char* previous, const char* author, int32_t sequence, double timestamp, const char* hash, const char* content, const char* signature, int flags);
|
||||
|
||||
/** Information about a single followed account. */
|
||||
typedef struct _tf_ssb_following_t
|
||||
@ -460,7 +460,7 @@ void tf_ssb_db_resolve_index_async(tf_ssb_t* ssb, const char* host, void (*callb
|
||||
** @param fix Fix invalid messages when possible.
|
||||
** @return true If the feed verified successfully.
|
||||
*/
|
||||
bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id, int64_t debug_sequence, bool fix);
|
||||
bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id, int32_t debug_sequence, bool fix);
|
||||
|
||||
/**
|
||||
** Check if a user has a specific permission.
|
||||
|
@ -128,8 +128,8 @@ void tf_ssb_ebt_receive_clock(tf_ssb_ebt_t* ebt, JSContext* context, JSValue clo
|
||||
if (!JS_IsUndefined(in_clock))
|
||||
{
|
||||
const char* author = JS_AtomToCString(context, ptab[i].atom);
|
||||
int64_t sequence = -1;
|
||||
JS_ToInt64(context, &sequence, in_clock);
|
||||
int32_t sequence = -1;
|
||||
JS_ToInt32(context, &sequence, in_clock);
|
||||
|
||||
ebt_entry_t* entry = _ebt_get_entry(ebt, author);
|
||||
if (entry)
|
||||
@ -235,12 +235,12 @@ static void _tf_ssb_ebt_get_send_clock_work(tf_ssb_connection_t* connection, voi
|
||||
const char** visible = tf_ssb_db_get_all_visible_identities(ssb, depth);
|
||||
if (visible)
|
||||
{
|
||||
int64_t* sequences = NULL;
|
||||
int32_t* sequences = NULL;
|
||||
for (int i = 0; visible[i]; i++)
|
||||
{
|
||||
int64_t sequence = 0;
|
||||
int32_t sequence = 0;
|
||||
tf_ssb_db_get_latest_message_by_author(ssb, visible[i], &sequence, NULL, 0);
|
||||
sequences = tf_resize_vec(sequences, (i + 1) * sizeof(int64_t));
|
||||
sequences = tf_resize_vec(sequences, (i + 1) * sizeof(int32_t));
|
||||
sequences[i] = sequence;
|
||||
}
|
||||
|
||||
@ -259,7 +259,7 @@ static void _tf_ssb_ebt_get_send_clock_work(tf_ssb_connection_t* connection, voi
|
||||
char id[k_id_base64_len] = "";
|
||||
if (tf_ssb_connection_get_id(connection, id, sizeof(id)))
|
||||
{
|
||||
int64_t sequence = 0;
|
||||
int32_t sequence = 0;
|
||||
tf_ssb_db_get_latest_message_by_author(ssb, id, &sequence, NULL, 0);
|
||||
uv_mutex_lock(&work->ebt->mutex);
|
||||
_ebt_add_to_clock(work, id, sequence, true, true);
|
||||
@ -348,7 +348,7 @@ tf_ssb_ebt_clock_t* tf_ssb_ebt_get_messages_to_send(tf_ssb_ebt_t* ebt)
|
||||
return clock;
|
||||
}
|
||||
|
||||
void tf_ssb_ebt_set_messages_sent(tf_ssb_ebt_t* ebt, const char* id, int64_t sequence)
|
||||
void tf_ssb_ebt_set_messages_sent(tf_ssb_ebt_t* ebt, const char* id, int32_t sequence)
|
||||
{
|
||||
uv_mutex_lock(&ebt->mutex);
|
||||
ebt_entry_t* entry = _ebt_get_entry(ebt, id);
|
||||
@ -363,7 +363,7 @@ void tf_ssb_ebt_set_messages_sent(tf_ssb_ebt_t* ebt, const char* id, int64_t seq
|
||||
uv_mutex_unlock(&ebt->mutex);
|
||||
}
|
||||
|
||||
void tf_ssb_ebt_set_messages_received(tf_ssb_ebt_t* ebt, const char* id, int64_t sequence)
|
||||
void tf_ssb_ebt_set_messages_received(tf_ssb_ebt_t* ebt, const char* id, int32_t sequence)
|
||||
{
|
||||
uv_mutex_lock(&ebt->mutex);
|
||||
ebt_entry_t* entry = _ebt_get_entry(ebt, id);
|
||||
|
@ -19,7 +19,7 @@ typedef struct _tf_ssb_ebt_clock_entry_t
|
||||
/** The identity. */
|
||||
char id[k_id_base64_len];
|
||||
/** The sequence number. */
|
||||
int64_t value;
|
||||
int32_t value;
|
||||
} tf_ssb_ebt_clock_entry_t;
|
||||
|
||||
/**
|
||||
@ -76,7 +76,7 @@ tf_ssb_ebt_clock_t* tf_ssb_ebt_get_messages_to_send(tf_ssb_ebt_t* ebt);
|
||||
** @param id The identity to update.
|
||||
** @param sequence The maximum sequence number sent.
|
||||
*/
|
||||
void tf_ssb_ebt_set_messages_sent(tf_ssb_ebt_t* ebt, const char* id, int64_t sequence);
|
||||
void tf_ssb_ebt_set_messages_sent(tf_ssb_ebt_t* ebt, const char* id, int32_t sequence);
|
||||
|
||||
/**
|
||||
** Update the clock state indicating the messages that have been received for an account.
|
||||
@ -84,7 +84,7 @@ void tf_ssb_ebt_set_messages_sent(tf_ssb_ebt_t* ebt, const char* id, int64_t seq
|
||||
** @param id The identity to update.
|
||||
** @param sequence The maximum sequence number received.
|
||||
*/
|
||||
void tf_ssb_ebt_set_messages_received(tf_ssb_ebt_t* ebt, const char* id, int64_t sequence);
|
||||
void tf_ssb_ebt_set_messages_received(tf_ssb_ebt_t* ebt, const char* id, int32_t sequence);
|
||||
|
||||
/**
|
||||
** Destroy an EBT instance.
|
||||
|
@ -323,7 +323,7 @@ void tf_ssb_run(tf_ssb_t* ssb);
|
||||
** @param previous_sequence The sequence number of the previous message in the feed. Optional.
|
||||
** @return The signed message.
|
||||
*/
|
||||
JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* private_key, JSValue message, const char* previous_id, int64_t previous_sequence);
|
||||
JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* private_key, JSValue message, const char* previous_id, int32_t previous_sequence);
|
||||
|
||||
/**
|
||||
** Get the server's identity.
|
||||
@ -651,7 +651,7 @@ void tf_ssb_remove_broadcasts_changed_callback(tf_ssb_t* ssb, tf_ssb_broadcasts_
|
||||
** @param id The message identifier.
|
||||
** @param user_data The user data.
|
||||
*/
|
||||
typedef void(tf_ssb_message_added_callback_t)(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, void* user_data);
|
||||
typedef void(tf_ssb_message_added_callback_t)(tf_ssb_t* ssb, const char* author, int32_t sequence, const char* id, void* user_data);
|
||||
|
||||
/**
|
||||
** Register a callback called when a message is added to the database.
|
||||
@ -678,7 +678,7 @@ void tf_ssb_remove_message_added_callback(tf_ssb_t* ssb, tf_ssb_message_added_ca
|
||||
** @param id The message identity added.
|
||||
** @param message_with_keys The message added in the format required if keys are requested.
|
||||
*/
|
||||
void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, JSValue message_with_keys);
|
||||
void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int32_t sequence, const char* id, JSValue message_with_keys);
|
||||
|
||||
/**
|
||||
** Record that a new blob was stored.
|
||||
|
@ -696,7 +696,7 @@ typedef struct _append_message_t
|
||||
uint8_t private_key[crypto_sign_SECRETKEYBYTES];
|
||||
bool got_private_key;
|
||||
char previous_id[512];
|
||||
int64_t previous_sequence;
|
||||
int32_t previous_sequence;
|
||||
JSContext* context;
|
||||
JSValue promise[2];
|
||||
JSValue message;
|
||||
@ -1604,7 +1604,7 @@ static void _tf_ssb_cleanup_value(tf_ssb_t* ssb, void* user_data)
|
||||
JS_FreeValue(tf_ssb_get_context(ssb), callback);
|
||||
}
|
||||
|
||||
static void _tf_ssb_on_message_added_callback(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, void* user_data)
|
||||
static void _tf_ssb_on_message_added_callback(tf_ssb_t* ssb, const char* author, int32_t sequence, const char* id, void* user_data)
|
||||
{
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
JSValue callback = JS_MKPTR(JS_TAG_OBJECT, user_data);
|
||||
|
168
src/ssb.rpc.c
168
src/ssb.rpc.c
@ -16,7 +16,7 @@
|
||||
#include <time.h>
|
||||
|
||||
static void _tf_ssb_connection_send_history_stream(
|
||||
tf_ssb_connection_t* connection, int32_t request_number, const char* author, int64_t sequence, bool keys, bool live, bool end_request);
|
||||
tf_ssb_connection_t* connection, int32_t request_number, const char* author, int32_t sequence, bool keys, bool live, bool end_request);
|
||||
static void _tf_ssb_rpc_send_peers_exchange(tf_ssb_connection_t* connection);
|
||||
static void _tf_ssb_rpc_start_delete_blobs(tf_ssb_t* ssb, int delay_ms);
|
||||
static void _tf_ssb_rpc_start_delete_feeds(tf_ssb_t* ssb, int delay_ms);
|
||||
@ -863,13 +863,13 @@ typedef struct _tf_ssb_connection_send_history_stream_t
|
||||
{
|
||||
int32_t request_number;
|
||||
char author[k_id_base64_len];
|
||||
int64_t sequence;
|
||||
int32_t sequence;
|
||||
bool keys;
|
||||
bool live;
|
||||
bool end_request;
|
||||
|
||||
bool out_finished;
|
||||
int64_t out_max_sequence_seen;
|
||||
int32_t out_max_sequence_seen;
|
||||
char** out_messages;
|
||||
int out_messages_count;
|
||||
} tf_ssb_connection_send_history_stream_t;
|
||||
@ -890,8 +890,8 @@ static void _tf_ssb_connection_send_history_stream_work(tf_ssb_connection_t* con
|
||||
"sequence < ?3 ORDER BY sequence",
|
||||
-1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, request->author, -1, NULL) == SQLITE_OK && sqlite3_bind_int64(statement, 2, request->sequence) == SQLITE_OK &&
|
||||
sqlite3_bind_int64(statement, 3, request->sequence + k_max) == SQLITE_OK)
|
||||
if (sqlite3_bind_text(statement, 1, request->author, -1, NULL) == SQLITE_OK && sqlite3_bind_int(statement, 2, request->sequence) == SQLITE_OK &&
|
||||
sqlite3_bind_int(statement, 3, request->sequence + k_max) == SQLITE_OK)
|
||||
{
|
||||
JSMallocFunctions funcs = { 0 };
|
||||
tf_get_js_malloc_functions(&funcs);
|
||||
@ -902,7 +902,7 @@ static void _tf_ssb_connection_send_history_stream_work(tf_ssb_connection_t* con
|
||||
while ((r = sqlite3_step(statement)) == SQLITE_ROW)
|
||||
{
|
||||
JSValue message = JS_UNDEFINED;
|
||||
request->out_max_sequence_seen = sqlite3_column_int64(statement, 3);
|
||||
request->out_max_sequence_seen = sqlite3_column_int(statement, 3);
|
||||
|
||||
JSValue formatted = tf_ssb_format_message(context, (const char*)sqlite3_column_text(statement, 0), (const char*)sqlite3_column_text(statement, 1),
|
||||
sqlite3_column_int64(statement, 3), sqlite3_column_double(statement, 4), (const char*)sqlite3_column_text(statement, 5),
|
||||
@ -997,7 +997,7 @@ static void _tf_ssb_connection_send_history_stream_callback(tf_ssb_connection_t*
|
||||
}
|
||||
|
||||
static void _tf_ssb_connection_send_history_stream(
|
||||
tf_ssb_connection_t* connection, int32_t request_number, const char* author, int64_t sequence, bool keys, bool live, bool end_request)
|
||||
tf_ssb_connection_t* connection, int32_t request_number, const char* author, int32_t sequence, bool keys, bool live, bool end_request)
|
||||
{
|
||||
if (tf_ssb_connection_is_connected(connection) && !tf_ssb_is_shutting_down(tf_ssb_connection_get_ssb(connection)) && !tf_ssb_connection_is_closing(connection))
|
||||
{
|
||||
@ -1036,8 +1036,8 @@ static void _tf_ssb_rpc_createHistoryStream(
|
||||
JSValue live = JS_GetPropertyStr(context, arg, "live");
|
||||
bool is_keys = JS_IsUndefined(keys) || JS_ToBool(context, keys) > 0;
|
||||
bool is_live = JS_ToBool(context, live) > 0 && (tf_ssb_connection_get_flags(connection) & k_tf_ssb_connect_flag_one_shot) == 0;
|
||||
int64_t sequence = 0;
|
||||
JS_ToInt64(context, &sequence, seq);
|
||||
int32_t sequence = 0;
|
||||
JS_ToInt32(context, &sequence, seq);
|
||||
const char* author = JS_ToCString(context, id);
|
||||
|
||||
_tf_ssb_connection_send_history_stream(connection, -request_number, author, sequence, is_keys, is_live, true);
|
||||
@ -1258,7 +1258,7 @@ typedef struct _invite_use_t
|
||||
;
|
||||
uint8_t private_key[512];
|
||||
char previous_id[64];
|
||||
int64_t previous_sequence;
|
||||
int32_t previous_sequence;
|
||||
|
||||
char host[256];
|
||||
int port;
|
||||
@ -1482,51 +1482,86 @@ static void _tf_ssb_rpc_delete_blobs_work(tf_ssb_t* ssb, void* user_data)
|
||||
return;
|
||||
}
|
||||
int64_t start_ns = uv_hrtime();
|
||||
db = tf_ssb_acquire_db_writer(ssb);
|
||||
sqlite3_stmt* statement;
|
||||
int64_t now = (int64_t)time(NULL) * 1000ULL;
|
||||
int64_t timestamp = now - age * 1000ULL;
|
||||
const int k_limit = 128;
|
||||
int deleted = 0;
|
||||
if (sqlite3_prepare_v2(db, "DELETE FROM blob_wants_cache WHERE source IS NULL and timestamp < ?1", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_int64(statement, 1, timestamp) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_step(statement) != SQLITE_DONE)
|
||||
{
|
||||
tf_printf("Deleting stale blob wants cache entries: %s.\n", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
sqlite3_stmt* statement;
|
||||
|
||||
char** ids = NULL;
|
||||
int ids_count = 0;
|
||||
|
||||
db = tf_ssb_acquire_db_reader(ssb);
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"DELETE FROM blobs WHERE blobs.id IN ("
|
||||
" SELECT blobs.id FROM blobs "
|
||||
" JOIN messages_refs ON blobs.id = messages_refs.ref "
|
||||
" JOIN messages ON messages.id = messages_refs.message "
|
||||
" WHERE blobs.created < ?1 / 1000 "
|
||||
" GROUP BY blobs.id HAVING MAX(messages.timestamp) < ?1 LIMIT ?2)",
|
||||
"SELECT blobs.id FROM blobs "
|
||||
"JOIN messages_refs ON blobs.id = messages_refs.ref "
|
||||
"JOIN messages ON messages.id = messages_refs.message "
|
||||
"WHERE blobs.created < ?1 / 1000 "
|
||||
"GROUP BY blobs.id HAVING MAX(messages.timestamp) < ?1 LIMIT ?2",
|
||||
-1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
const int k_limit = 128;
|
||||
if (sqlite3_bind_int64(statement, 1, timestamp) == SQLITE_OK && sqlite3_bind_int(statement, 2, k_limit) == SQLITE_OK)
|
||||
{
|
||||
int r = sqlite3_step(statement);
|
||||
int r = SQLITE_OK;
|
||||
while ((r = sqlite3_step(statement)) == SQLITE_ROW)
|
||||
{
|
||||
ids = tf_realloc(ids, sizeof(char*) * (ids_count + 1));
|
||||
ids[ids_count++] = tf_strdup((const char*)sqlite3_column_text(statement, 0));
|
||||
}
|
||||
if (r != SQLITE_DONE)
|
||||
{
|
||||
tf_printf("_tf_ssb_rpc_delete_blobs_work: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_printf("_tf_ssb_rpc_delete_blobs_work: %d rows\n", sqlite3_changes(db));
|
||||
}
|
||||
deleted = sqlite3_changes(db);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
|
||||
int deleted = 0;
|
||||
if (ids_count)
|
||||
{
|
||||
db = tf_ssb_acquire_db_writer(ssb);
|
||||
if (sqlite3_prepare_v2(db, "DELETE FROM blob_wants_cache WHERE source IS NULL and timestamp < ?1", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_int64(statement, 1, timestamp) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_step(statement) != SQLITE_DONE)
|
||||
{
|
||||
tf_printf("Deleting stale blob wants cache entries: %s.\n", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
if (sqlite3_prepare_v2(db, "DELETE FROM blobs WHERE blobs.id = ?", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
for (int i = 0; i < ids_count; i++)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, ids[i], -1, NULL) == SQLITE_OK)
|
||||
{
|
||||
int r = sqlite3_step(statement);
|
||||
if (r != SQLITE_DONE)
|
||||
{
|
||||
tf_printf("_tf_ssb_rpc_delete_blobs_work: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
else
|
||||
{
|
||||
deleted += sqlite3_changes(db);
|
||||
}
|
||||
}
|
||||
sqlite3_reset(statement);
|
||||
tf_free(ids[i]);
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
tf_free(ids);
|
||||
}
|
||||
delete->duration_ms = (uv_hrtime() - start_ns) / 1000000LL;
|
||||
tf_printf("Deleted %d blobs in %d ms.\n", deleted, (int)delete->duration_ms);
|
||||
}
|
||||
@ -1584,28 +1619,57 @@ static void _tf_ssb_rpc_delete_feeds_work(tf_ssb_t* ssb, void* user_data)
|
||||
JS_FreeValue(context, json);
|
||||
JS_FreeValue(context, array);
|
||||
|
||||
db = tf_ssb_acquire_db_writer(ssb);
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"DELETE FROM messages WHERE id IN ("
|
||||
" SELECT id FROM messages WHERE author NOT IN (SELECT value FROM json_each(?)) ORDER BY rowid DESC LIMIT 1024"
|
||||
")",
|
||||
-1, &statement, NULL) == SQLITE_OK)
|
||||
|
||||
char** ids = NULL;
|
||||
int ids_count = 0;
|
||||
|
||||
db = tf_ssb_acquire_db_reader(ssb);
|
||||
if (sqlite3_prepare_v2(db, "SELECT id FROM messages WHERE author NOT IN (SELECT value FROM json_each(?)) ORDER BY rowid DESC LIMIT 1024", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, arg, -1, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_step(statement) != SQLITE_DONE)
|
||||
int r = SQLITE_OK;
|
||||
while ((r = sqlite3_step(statement)) == SQLITE_ROW)
|
||||
{
|
||||
ids = tf_realloc(ids, sizeof(char*) * (ids_count + 1));
|
||||
ids[ids_count++] = tf_strdup((const char*)sqlite3_column_text(statement, 0));
|
||||
}
|
||||
if (r != SQLITE_DONE)
|
||||
{
|
||||
tf_printf("deleting messages: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
else
|
||||
{
|
||||
delete->deleted += sqlite3_changes(db);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
|
||||
if (ids_count)
|
||||
{
|
||||
db = tf_ssb_acquire_db_writer(ssb);
|
||||
if (sqlite3_prepare_v2(db, "DELETE FROM messages WHERE id = ?", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
for (int i = 0; i < ids_count; i++)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, ids[i], -1, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_step(statement) != SQLITE_DONE)
|
||||
{
|
||||
tf_printf("deleting messages: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
else
|
||||
{
|
||||
delete->deleted++;
|
||||
}
|
||||
}
|
||||
sqlite3_reset(statement);
|
||||
tf_free(ids[i]);
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
tf_free(ids);
|
||||
}
|
||||
|
||||
JS_FreeCString(context, arg);
|
||||
|
||||
@ -1780,7 +1844,7 @@ typedef struct _invite_t
|
||||
int32_t request_number;
|
||||
bool accepted;
|
||||
char previous_id[256];
|
||||
int64_t previous_sequence;
|
||||
int32_t previous_sequence;
|
||||
char* message;
|
||||
} invite_t;
|
||||
|
||||
@ -1882,7 +1946,7 @@ static void _tf_ssb_rpc_invite_use(tf_ssb_connection_t* connection, uint8_t flag
|
||||
tf_ssb_connection_run_work(connection, _tf_ssb_rpc_invite_use_work, _tf_ssb_rpc_invite_use_after_work, work);
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_message_added_callback(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, void* user_data)
|
||||
static void _tf_ssb_rpc_message_added_callback(tf_ssb_t* ssb, const char* author, int32_t sequence, const char* id, void* user_data)
|
||||
{
|
||||
tf_ssb_connection_t* connections[256];
|
||||
int count = tf_ssb_get_connections(ssb, connections, tf_countof(connections));
|
||||
|
@ -114,7 +114,7 @@ static int _ssb_test_count_messages(tf_ssb_t* ssb)
|
||||
return count.count;
|
||||
}
|
||||
|
||||
static void _message_added(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, void* user_data)
|
||||
static void _message_added(tf_ssb_t* ssb, const char* author, int32_t sequence, const char* id, void* user_data)
|
||||
{
|
||||
++*(int*)user_data;
|
||||
}
|
||||
@ -1046,11 +1046,11 @@ void tf_ssb_test_publish(const tf_test_options_t* options)
|
||||
static void _test_print_identity(const char* identity, void* user_data)
|
||||
{
|
||||
tf_ssb_t* ssb = user_data;
|
||||
int64_t sequence = -1;
|
||||
int32_t sequence = -1;
|
||||
char id[k_id_base64_len] = { 0 };
|
||||
snprintf(id, sizeof(id), "@%s", identity);
|
||||
tf_ssb_db_get_latest_message_by_author(ssb, id, &sequence, NULL, 0);
|
||||
tf_printf("IDENTITY %s: %d\n", id, (int)sequence);
|
||||
tf_printf("IDENTITY %s: %d\n", id, sequence);
|
||||
}
|
||||
|
||||
void tf_ssb_test_replicate(const tf_test_options_t* options)
|
||||
@ -1436,9 +1436,9 @@ void tf_ssb_test_triggers(const tf_test_options_t* options)
|
||||
clock_gettime(CLOCK_REALTIME, &end_time);
|
||||
tf_printf("insert = %f seconds\n", (end_time.tv_sec - start_time.tv_sec) + (end_time.tv_nsec - start_time.tv_nsec) / 1e9);
|
||||
|
||||
int64_t max_sequence = 0;
|
||||
int32_t max_sequence = 0;
|
||||
tf_ssb_db_get_latest_message_by_author(ssb0, id0, &max_sequence, NULL, 0);
|
||||
tf_printf("max_sequence=%" PRId64 "\n", max_sequence);
|
||||
tf_printf("max_sequence=%d\n", max_sequence);
|
||||
assert(max_sequence == 5);
|
||||
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb0);
|
||||
@ -1447,7 +1447,7 @@ void tf_ssb_test_triggers(const tf_test_options_t* options)
|
||||
|
||||
max_sequence = 0;
|
||||
tf_ssb_db_get_latest_message_by_author(ssb0, id0, &max_sequence, NULL, 0);
|
||||
tf_printf("max_sequence=%" PRId64 "\n", max_sequence);
|
||||
tf_printf("max_sequence=%d\n", max_sequence);
|
||||
assert(max_sequence == 4);
|
||||
|
||||
tf_ssb_acquire_db_writer(ssb0);
|
||||
@ -1456,7 +1456,7 @@ void tf_ssb_test_triggers(const tf_test_options_t* options)
|
||||
|
||||
max_sequence = 0;
|
||||
tf_ssb_db_get_latest_message_by_author(ssb0, id0, &max_sequence, NULL, 0);
|
||||
tf_printf("max_sequence=%" PRId64 "\n", max_sequence);
|
||||
tf_printf("max_sequence=%d\n", max_sequence);
|
||||
assert(max_sequence == 0);
|
||||
|
||||
uv_run(&loop, UV_RUN_DEFAULT);
|
||||
@ -1621,7 +1621,7 @@ void tf_ssb_test_following_perf(const tf_test_options_t* options)
|
||||
|
||||
uint64_t start = uv_hrtime();
|
||||
int count = 0;
|
||||
for (int i = 0; i < 100; i++)
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
const char** ids = tf_ssb_db_get_all_visible_identities(ssb, 2);
|
||||
while (ids[count])
|
||||
|
@ -84,7 +84,7 @@ typedef struct _promise_stack_t
|
||||
uint32_t hash;
|
||||
int count;
|
||||
const char* stack;
|
||||
void* cstack[32];
|
||||
void* cstack[31];
|
||||
int cstack_count;
|
||||
} promise_stack_t;
|
||||
|
||||
@ -550,6 +550,7 @@ static JSValue _task_invokeExport_internal(tf_taskstub_t* from, tf_task_t* to, e
|
||||
}
|
||||
|
||||
result = JS_Call(to->_context, function, this_val, length - 1, argument_array);
|
||||
tf_task_check_jobs(to);
|
||||
tf_trace_end(to->_trace);
|
||||
|
||||
JS_FreeValue(to->_context, this_val);
|
||||
@ -1220,6 +1221,7 @@ static void _add_promise_stack(tf_task_t* task, uint32_t hash, const char* stack
|
||||
{
|
||||
memmove(task->_promise_stacks + index + 1, task->_promise_stacks + index, sizeof(promise_stack_t) * (task->_promise_stack_count - index));
|
||||
}
|
||||
count = tf_min(count, tf_countof(task->_promise_stacks[index].cstack));
|
||||
task->_promise_stacks[index] = (promise_stack_t) { .hash = hash, .stack = tf_strdup(stack), .count = 1, .cstack_count = count };
|
||||
memcpy(task->_promise_stacks[index].cstack, buffer, sizeof(void*) * count);
|
||||
task->_promise_stack_count++;
|
||||
@ -1261,7 +1263,7 @@ JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
|
||||
size_t length = 0;
|
||||
const char* stack = JS_ToCStringLen(task->_context, &length, stack_value);
|
||||
stack_hash = tf_util_fnv32a((const void*)stack, (int)length, 0);
|
||||
void* buffer[32];
|
||||
void* buffer[31];
|
||||
int count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer));
|
||||
stack_hash = tf_util_fnv32a((const void*)buffer, sizeof(void*) * count, stack_hash);
|
||||
_add_promise_stack(task, stack_hash, stack, buffer, count);
|
||||
|
@ -500,8 +500,7 @@ void tf_util_document_settings(const char* line_prefix)
|
||||
JSValue tf_util_new_uint8_array(JSContext* context, const uint8_t* data, size_t size)
|
||||
{
|
||||
JSValue array_buffer = JS_NewArrayBufferCopy(context, data, size);
|
||||
JSValue args[] =
|
||||
{
|
||||
JSValue args[] = {
|
||||
array_buffer,
|
||||
JS_NewInt64(context, 0),
|
||||
JS_NewInt64(context, size),
|
||||
|
@ -1,2 +1,2 @@
|
||||
#define VERSION_NUMBER "0.0.32-wip"
|
||||
#define VERSION_NUMBER "0.0.32"
|
||||
#define VERSION_NAME "This program kills fascists."
|
||||
|
Reference in New Issue
Block a user