chore: upgrade pubky

Upgrade pubky to current main.
Bump package version to 0.4.0.
This commit is contained in:
coreyphillips
2024-09-24 20:19:28 -04:00
parent dd412fff26
commit 08db61d35a
28 changed files with 281 additions and 2035 deletions

View File

@@ -1237,7 +1237,7 @@ PODS:
- ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- react-native-pubky (0.3.0): - react-native-pubky (0.4.0):
- DoubleConversion - DoubleConversion
- glog - glog
- hermes-engine - hermes-engine
@@ -1757,7 +1757,7 @@ SPEC CHECKSUMS:
React-logger: 4072f39df335ca443932e0ccece41fbeb5ca8404 React-logger: 4072f39df335ca443932e0ccece41fbeb5ca8404
React-Mapbuffer: 714f2fae68edcabfc332b754e9fbaa8cfc68fdd4 React-Mapbuffer: 714f2fae68edcabfc332b754e9fbaa8cfc68fdd4
React-microtasksnativemodule: 4943ad8f99be8ccf5a63329fa7d269816609df9e React-microtasksnativemodule: 4943ad8f99be8ccf5a63329fa7d269816609df9e
react-native-pubky: 9fd2633ee974bafa9b77e0cd59e2619a0d9d708d react-native-pubky: d9834542073d368f48e8f0bcce3d7d92317159fa
React-nativeconfig: 4a9543185905fe41014c06776bf126083795aed9 React-nativeconfig: 4a9543185905fe41014c06776bf126083795aed9
React-NativeModulesApple: 0506da59fc40d2e1e6e12a233db5e81c46face27 React-NativeModulesApple: 0506da59fc40d2e1e6e12a233db5e81c46face27
React-perflogger: 3bbb82f18e9ac29a1a6931568e99d6305ef4403b React-perflogger: 3bbb82f18e9ac29a1a6931568e99d6305ef4403b

View File

@@ -4,6 +4,22 @@
<dict> <dict>
<key>AvailableLibraries</key> <key>AvailableLibraries</key>
<array> <array>
<dict>
<key>BinaryPath</key>
<string>libpubkymobile.a</string>
<key>HeadersPath</key>
<string>Headers</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>libpubkymobile.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
<dict> <dict>
<key>BinaryPath</key> <key>BinaryPath</key>
<string>libpubkymobile.a</string> <string>libpubkymobile.a</string>
@@ -22,22 +38,6 @@
<key>SupportedPlatformVariant</key> <key>SupportedPlatformVariant</key>
<string>simulator</string> <string>simulator</string>
</dict> </dict>
<dict>
<key>BinaryPath</key>
<string>libpubkymobile.a</string>
<key>HeadersPath</key>
<string>Headers</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>libpubkymobile.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
</array> </array>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>XFWK</string> <string>XFWK</string>

View File

@@ -3,9 +3,12 @@ pre-commit:
commands: commands:
lint: lint:
glob: "*.{js,ts,jsx,tsx}" glob: "*.{js,ts,jsx,tsx}"
exclude:
- "rust/**/*"
run: npx eslint {staged_files} run: npx eslint {staged_files}
types: types:
glob: "*.{js,ts, jsx, tsx}" glob: "*.{js,ts,jsx,tsx}"
exclude: "rust/**/*"
run: npx tsc run: npx tsc
commit-msg: commit-msg:
parallel: true parallel: true

View File

@@ -1,6 +1,6 @@
{ {
"name": "@synonymdev/react-native-pubky", "name": "@synonymdev/react-native-pubky",
"version": "0.3.0", "version": "0.4.0",
"description": "React Native Implementation of Pubky", "description": "React Native Implementation of Pubky",
"source": "./src/index.tsx", "source": "./src/index.tsx",
"main": "./lib/commonjs/index.js", "main": "./lib/commonjs/index.js",

View File

@@ -13,14 +13,22 @@
</head> </head>
<body> <body>
<pubky-auth-widget <pubky-auth-widget
id="widget"
relay="https://demo.httprelay.io/link/" relay="https://demo.httprelay.io/link/"
caps="/pub/pubky.app/:rw,/pub/example.com/nested:rw"
> >
</pubky-auth-widget> </pubky-auth-widget>
<main> <main>
<h1>Third Party app!</h1> <h1>Third Party app!</h1>
<p>this is a demo for using Pubky Auth in an unhosted (no backend) app.</p> <p>this is a demo for using Pubky Auth in an unhosted (no backend) app.</p>
<form>
<label style="display:block">
<input type="checkbox" onChange="document.getElementById('widget').switchTestnet()">testnet (use local test network)</input>
</label>
<label style="display:block">
<input type="checkbox" onChange="let w = document.getElementById('widget'); w.caps.length > 0 ? w.setCapabilities(null) : w.setCapabilities('/pub/pubky.app/:rw,/pub/example.com/nested:rw')">Authz (Authorization, set if your pubky has an account on a Homeserver)</input>
</label>
</form>
</main> </main>
</body> </body>
</html> </html>

View File

@@ -8,7 +8,7 @@
"name": "pubky-auth-3rd-party", "name": "pubky-auth-3rd-party",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@synonymdev/pubky": "file:../../../pubky/pkg", "@synonymdev/pubky": "^0.1.16",
"lit": "^3.2.0", "lit": "^3.2.0",
"qrcode": "^1.5.4" "qrcode": "^1.5.4"
}, },
@@ -16,17 +16,6 @@
"vite": "^5.4.2" "vite": "^5.4.2"
} }
}, },
"../../../pubky/pkg": {
"name": "@synonymdev/pubky",
"version": "0.1.14",
"license": "MIT",
"devDependencies": {
"browser-resolve": "^2.0.0",
"esmify": "^2.1.1",
"tape": "^5.8.1",
"tape-run": "^11.0.0"
}
},
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5", "version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
@@ -617,8 +606,9 @@
] ]
}, },
"node_modules/@synonymdev/pubky": { "node_modules/@synonymdev/pubky": {
"resolved": "../../../pubky/pkg", "version": "0.1.16",
"link": true "resolved": "https://registry.npmjs.org/@synonymdev/pubky/-/pubky-0.1.16.tgz",
"integrity": "sha512-jtFahEUUDfrTE7vpZx6m/uB4wMEBoqpEtuUoWCf30HH8cmm0Hfrv8v0xmwaSwPfSdcZlIG8beE5XjbX+eLmLUA=="
}, },
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.5", "version": "1.0.5",

View File

@@ -10,7 +10,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@synonymdev/pubky": "file:../../../pubky/pkg", "@synonymdev/pubky": "^0.1.16",
"lit": "^3.2.0", "lit": "^3.2.0",
"qrcode": "^1.5.4" "qrcode": "^1.5.4"
}, },

View File

@@ -1 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.2" viewBox="0 0 1511 1511" width="1511" height="1511"><style>.a{fill:#fff}</style><path d="m269 0h973c148.6 0 269 120.4 269 269v973c0 148.6-120.4 269-269 269h-973c-148.6 0-269-120.4-269-269v-973c0-148.6 120.4-269 269-269z"/><path fill-rule="evenodd" class="a" d="m630.1 1064.3l14.9-59.6c50.5-27.7 90.8-71.7 113.7-124.8-47.3 51.2-115.2 83.3-190.5 83.3-51.9 0-100.1-15.1-140.4-41.2l-39.8 142.3c0 0-199.3 0-200 0l162.4-619.3h210.5l-0.1 0.1q3.7-0.1 7.4-0.1c77.6 0 147.2 34 194.7 88l22-88h201.9l-46.9 180.8 183.7-180.8h248.8l-322.8 332.6 223.9 286.7h-290.8l-116.6-154.6-40.3 154.6c0 0-195 0-195.7 0z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" version="1.2" viewBox="0 0 452 690">
<style>
path { fill: black; }
@media (prefers-color-scheme: dark) {
path { fill: white; }
}
</style>
<path fill-rule="evenodd" class="a" d="m0.1 84.7l80.5 17.1 15.8-74.5 73.8 44.2 54.7-71.5 55.2 71.5 70.3-44.2 19.4 74.5 81.6-17.1-74.5 121.5c-40.5-35.3-93.5-56.6-151.4-56.6-57.8 0-110.7 21.3-151.2 56.4zm398.4 293.8c0 40.6-14 78-37.4 107.4l67 203.8h-403.1l66.2-202.3c-24.1-29.7-38.6-67.6-38.6-108.9 0-95.5 77.4-172.8 173-172.8 95.5 0 172.9 77.3 172.9 172.8zm-212.9 82.4l-48.2 147.3h178.1l-48.6-148 2.9-1.6c28.2-15.6 47.3-45.6 47.3-80.1 0-50.5-41-91.4-91.5-91.4-50.6 0-91.6 40.9-91.6 91.4 0 35 19.7 65.4 48.6 80.8z"/>
</svg>

Before

Width:  |  Height:  |  Size: 655 B

After

Width:  |  Height:  |  Size: 724 B

View File

@@ -7,8 +7,11 @@ const DEFAULT_HTTP_RELAY = "https://demo.httprelay.io/link"
/** /**
*/ */
export class PubkyAuthWidget extends LitElement { export class PubkyAuthWidget extends LitElement {
static get properties() { static get properties() {
return { return {
// === Config ===
/** /**
* Relay endpoint for the widget to receive Pubky AuthTokens * Relay endpoint for the widget to receive Pubky AuthTokens
* *
@@ -23,6 +26,9 @@ export class PubkyAuthWidget extends LitElement {
* Capabilities requested or this application encoded as a string. * Capabilities requested or this application encoded as a string.
*/ */
caps: { type: String }, caps: { type: String },
// === State ===
/** /**
* Widget's state (open or closed) * Widget's state (open or closed)
*/ */
@@ -31,6 +37,10 @@ export class PubkyAuthWidget extends LitElement {
* Show "copied to clipboard" note * Show "copied to clipboard" note
*/ */
showCopied: { type: Boolean }, showCopied: { type: Boolean },
// === Internal ===
testnet: { type: Boolean },
pubky: { type: Object }
} }
} }
@@ -43,63 +53,67 @@ export class PubkyAuthWidget extends LitElement {
super() super()
this.testnet = false;
this.open = false; this.open = false;
// TODO: allow using mainnet
/** @type {import("@synonymdev/pubky").PubkyClient} */ /** @type {import("@synonymdev/pubky").PubkyClient} */
this.pubkyClient = window.pubky.PubkyClient.testnet(); this.pubkyClient = new window.pubky.PubkyClient();
this.caps = this.caps || ""
} }
connectedCallback() { connectedCallback() {
super.connectedCallback() super.connectedCallback()
this._generateURL()
}
switchTestnet() {
this.testnet = !this.testnet;
console.debug("Switching testnet");
if (this.testnet) {
this.pubkyClient = window.pubky.PubkyClient.testnet()
} else {
this.pubkyClient = new window.pubky.PubkyClient();
}
console.debug("Pkarr Relays: " + this.pubkyClient.getPkarrRelays())
this._generateURL()
}
setCapabilities(caps) {
this.caps = caps || ""
this._generateURL(this.caps);
console.debug("Updated capabilities");
}
_generateURL() {
let [url, promise] = this.pubkyClient.authRequest(this.relay || DEFAULT_HTTP_RELAY, this.caps); let [url, promise] = this.pubkyClient.authRequest(this.relay || DEFAULT_HTTP_RELAY, this.caps);
promise.then(session => { promise.then(pubky => {
console.log({ id: session.pubky().z32(), capabilities: session.capabilities() }) this.pubky = pubky.z32();
alert(`Successfully signed in to ${session.pubky().z32()} with capabilities: ${session.capabilities().join(",")}`)
}).catch(e => { }).catch(e => {
console.error(e) console.error(e)
}) })
// let keypair = pubky.Keypair.random();
// const Homeserver = pubky.PublicKey.from('8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo')
// this.pubkyClient.signup(keypair, Homeserver).then(() => {
// this.pubkyClient.sendAuthToken(keypair, url)
// })
this.authUrl = url this.authUrl = url
this._updateQr();
} }
render() { _updateQr() {
return html` if (this.canvas) {
<div this._setQr(this.canvas);
id="widget" }
class=${this.open ? "open" : ""}
>
<button class="header" @click=${this._switchOpen}>
<svg id="pubky-icon" version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1511 1511"><path fill-rule="evenodd" d="m636.3 1066.7 14.9-59.7c50.5-27.7 90.8-71.7 113.7-124.9-47.3 51.3-115.2 83.4-190.6 83.4-51.9 0-100.1-15.1-140.5-41.2L394 1066.7H193.9L356.4 447H567l-.1.1q3.7-.1 7.4-.1c77.7 0 147.3 34 194.8 88l22-88h202.1l-47 180.9L1130 447h249l-323 332.8 224 286.9H989L872.4 912l-40.3 154.7H636.3z" style="fill:#fff"/></svg>
<span class="text">
Pubky Auth
</span>
</button>
<div class="line"></div>
<div id="widget-content">
<p>Scan or copy Pubky auth URL</p>
<div class="card">
<canvas id="qr" ${ref(this._setQr)}></canvas>
</div>
<button class="card url" @click=${this._copyToClipboard}>
<div class="copied ${this.showCopied ? "show" : ""}">Copied to Clipboard</div>
<p>${this.authUrl}</p>
<svg width="14" height="16" viewBox="0 0 14 16" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="10" height="12" rx="2" fill="white"></rect><rect x="3" y="3" width="10" height="12" rx="2" fill="white" stroke="#3B3B3B"></rect></svg>
</button>
</div>
</div>
`
} }
_setQr(canvas) { _setQr(canvas) {
this.canvas = canvas
QRCode.toCanvas(canvas, this.authUrl, { QRCode.toCanvas(canvas, this.authUrl, {
margin: 2, margin: 2,
scale: 8, scale: 8,
@@ -113,6 +127,7 @@ export class PubkyAuthWidget extends LitElement {
_switchOpen() { _switchOpen() {
this.open = !this.open this.open = !this.open
setTimeout(() => { this.pubky = null }, 80)
} }
async _copyToClipboard() { async _copyToClipboard() {
@@ -134,13 +149,39 @@ export class PubkyAuthWidget extends LitElement {
class=${this.open ? "open" : ""} class=${this.open ? "open" : ""}
> >
<button class="header" @click=${this._switchOpen}> <button class="header" @click=${this._switchOpen}>
<svg id="pubky-icon" version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1511 1511"><path fill-rule="evenodd" d="m636.3 1066.7 14.9-59.7c50.5-27.7 90.8-71.7 113.7-124.9-47.3 51.3-115.2 83.4-190.6 83.4-51.9 0-100.1-15.1-140.5-41.2L394 1066.7H193.9L356.4 447H567l-.1.1q3.7-.1 7.4-.1c77.7 0 147.3 34 194.8 88l22-88h202.1l-47 180.9L1130 447h249l-323 332.8 224 286.9H989L872.4 912l-40.3 154.7H636.3z" style="fill:#fff"/></svg> <div class="header-content">
<svg id="pubky-icon" xmlns="http://www.w3.org/2000/svg" version="1.2" viewBox="0 0 452 690">
<style>
path { fill: black; }
@media (prefers-color-scheme: dark) {
path { fill: white; }
}
</style>
<path fill-rule="evenodd" class="a" d="m0.1 84.7l80.5 17.1 15.8-74.5 73.8 44.2 54.7-71.5 55.2 71.5 70.3-44.2 19.4 74.5 81.6-17.1-74.5 121.5c-40.5-35.3-93.5-56.6-151.4-56.6-57.8 0-110.7 21.3-151.2 56.4zm398.4 293.8c0 40.6-14 78-37.4 107.4l67 203.8h-403.1l66.2-202.3c-24.1-29.7-38.6-67.6-38.6-108.9 0-95.5 77.4-172.8 173-172.8 95.5 0 172.9 77.3 172.9 172.8zm-212.9 82.4l-48.2 147.3h178.1l-48.6-148 2.9-1.6c28.2-15.6 47.3-45.6 47.3-80.1 0-50.5-41-91.4-91.5-91.4-50.6 0-91.6 40.9-91.6 91.4 0 35 19.7 65.4 48.6 80.8z"/>
</svg>
<span class="text"> <span class="text">
Pubky Auth Pubky Auth
</span> </span>
</div>
</button> </button>
<div class="line"></div> <div class="line"></div>
<div id="widget-content"> <div id="widget-content">
${this.pubky
? this.caps.length > 0
? html`
<p>Successfully authorized: </p>
<p>${this.pubky}</p>
<p>With capabilities</p>
${this.caps.split(",").map(cap => html`
<p>${cap}</p>
`)
}
`
: html`
<p>Successfully authenticated to: </p>
<p>${this.pubky}</p>
`
: html`
<p>Scan or copy Pubky auth URL</p> <p>Scan or copy Pubky auth URL</p>
<div class="card"> <div class="card">
<canvas id="qr" ${ref(this._setQr)}></canvas> <canvas id="qr" ${ref(this._setQr)}></canvas>
@@ -150,11 +191,17 @@ export class PubkyAuthWidget extends LitElement {
<p>${this.authUrl}</p> <p>${this.authUrl}</p>
<svg width="14" height="16" viewBox="0 0 14 16" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="10" height="12" rx="2" fill="white"></rect><rect x="3" y="3" width="10" height="12" rx="2" fill="white" stroke="#3B3B3B"></rect></svg> <svg width="14" height="16" viewBox="0 0 14 16" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="10" height="12" rx="2" fill="white"></rect><rect x="3" y="3" width="10" height="12" rx="2" fill="white" stroke="#3B3B3B"></rect></svg>
</button> </button>
`
}
</div> </div>
</div> </div>
` `
} }
_renderWidgetContentBase() {
}
static get styles() { static get styles() {
return css` return css`
* { * {
@@ -219,16 +266,25 @@ export class PubkyAuthWidget extends LitElement {
} }
.header { .header {
width: 100%;
height: var(--header-height); height: var(--header-height);
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items:center;
}
.header-content {
display: flex;
justify-content: center;
align-items: baseline;
column-gap: .5rem;
} }
#widget #widget
.header .text { .header .text {
display: none; display: none;
font-weight: bold; font-weight: bold;
font-size: 1.5rem;
} }
#widget.open #widget.open
.header .text { .header .text {
@@ -242,14 +298,13 @@ export class PubkyAuthWidget extends LitElement {
} }
#pubky-icon { #pubky-icon {
height: 100%; height: 1.5rem;
width: 100%; width: 100%;
} }
#widget.open #widget.open
#pubky-icon { #pubky-icon {
width: var(--header-height); width: auto;
height: 74%;
} }
#widget-content{ #widget-content{

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
//! Monotonic unix timestamp in microseconds //! Strictly monotonic unix timestamp in microseconds
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Display; use std::fmt::Display;
@@ -31,7 +31,7 @@ impl TimestampFactory {
} }
pub fn now(&mut self) -> Timestamp { pub fn now(&mut self) -> Timestamp {
// Ensure monotonicity. // Ensure strict monotonicity.
self.last_time = (system_time() & TIME_MASK).max(self.last_time + CLOCK_MASK + 1); self.last_time = (system_time() & TIME_MASK).max(self.last_time + CLOCK_MASK + 1);
// Add clock_id to the end of the timestamp // Add clock_id to the end of the timestamp
@@ -48,13 +48,13 @@ impl Default for TimestampFactory {
static DEFAULT_FACTORY: Lazy<Mutex<TimestampFactory>> = static DEFAULT_FACTORY: Lazy<Mutex<TimestampFactory>> =
Lazy::new(|| Mutex::new(TimestampFactory::default())); Lazy::new(|| Mutex::new(TimestampFactory::default()));
/// Monotonic timestamp since [SystemTime::UNIX_EPOCH] in microseconds as u64. /// STrictly monotonic timestamp since [SystemTime::UNIX_EPOCH] in microseconds as u64.
/// ///
/// The purpose of this timestamp is to unique per "user", not globally, /// The purpose of this timestamp is to unique per "user", not globally,
/// it achieves this by: /// it achieves this by:
/// 1. Override the last byte with a random `clock_id`, reducing the probability /// 1. Override the last byte with a random `clock_id`, reducing the probability
/// of two matching timestamps across multiple machines/threads. /// of two matching timestamps across multiple machines/threads.
/// 2. Gurantee that the remaining 3 bytes are ever increasing (monotonic) within /// 2. Gurantee that the remaining 3 bytes are ever increasing (strictly monotonic) within
/// the same thread regardless of the wall clock value /// the same thread regardless of the wall clock value
/// ///
/// This timestamp is also serialized as BE bytes to remain sortable. /// This timestamp is also serialized as BE bytes to remain sortable.
@@ -215,7 +215,7 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn monotonic() { fn strictly_monotonic() {
const COUNT: usize = 100; const COUNT: usize = 100;
let mut set = HashSet::with_capacity(COUNT); let mut set = HashSet::with_capacity(COUNT);

View File

@@ -1,5 +1,5 @@
# Use testnet network (local DHT) for testing. # Use testnet network (local DHT) for testing.
testnet = true testnet = false
# Secret key (in hex) to generate the Homeserver's Keypair # Secret key (in hex) to generate the Homeserver's Keypair
secret_key = "0000000000000000000000000000000000000000000000000000000000000000" secret_key = "0000000000000000000000000000000000000000000000000000000000000000"
# Domain to be published in Pkarr records for this server to be accessible by. # Domain to be published in Pkarr records for this server to be accessible by.

View File

@@ -26,7 +26,7 @@ impl DB {
self.tables self.tables
.blobs .blobs
.get(&rtxn, entry.content_hash())? .get(&rtxn, entry.content_hash())?
.map(|blob| bytes::Bytes::from(blob.to_vec())) .map(|blob| bytes::Bytes::from(blob[8..].to_vec()))
} else { } else {
None None
}; };

View File

@@ -43,7 +43,26 @@ impl DB {
let hash = hasher.finalize(); let hash = hasher.finalize();
self.tables.blobs.put(&mut wtxn, hash.as_bytes(), &bytes)?; let key = hash.as_bytes();
let mut bytes_with_ref_count = Vec::with_capacity(bytes.len() + 8);
bytes_with_ref_count.extend_from_slice(&u64::to_be_bytes(0));
bytes_with_ref_count.extend_from_slice(&bytes);
// TODO: For now, we set the first 8 bytes to a reference counter
let exists = self
.tables
.blobs
.get(&wtxn, key)?
.unwrap_or(bytes_with_ref_count.as_slice());
let new_count = u64::from_be_bytes(exists[0..8].try_into().unwrap()) + 1;
bytes_with_ref_count[0..8].copy_from_slice(&u64::to_be_bytes(new_count));
self.tables
.blobs
.put(&mut wtxn, hash.as_bytes(), &bytes_with_ref_count)?;
let mut entry = Entry::new(); let mut entry = Entry::new();
@@ -82,8 +101,28 @@ impl DB {
let deleted = if let Some(bytes) = self.tables.entries.get(&wtxn, &key)? { let deleted = if let Some(bytes) = self.tables.entries.get(&wtxn, &key)? {
let entry = Entry::deserialize(bytes)?; let entry = Entry::deserialize(bytes)?;
// TODO: reference counting of blobs let mut bytes_with_ref_count = self
let deleted_blobs = self.tables.blobs.delete(&mut wtxn, entry.content_hash())?; .tables
.blobs
.get(&wtxn, entry.content_hash())?
.map_or(vec![], |s| s.to_vec());
let arr: [u8; 8] = bytes_with_ref_count[0..8].try_into().unwrap_or([0; 8]);
let reference_count = u64::from_be_bytes(arr);
let deleted_blobs = if reference_count > 1 {
// decrement reference count
bytes_with_ref_count[0..8].copy_from_slice(&(reference_count - 1).to_be_bytes());
self.tables
.blobs
.put(&mut wtxn, entry.content_hash(), &bytes_with_ref_count)?;
true
} else {
self.tables.blobs.delete(&mut wtxn, entry.content_hash())?
};
let deleted_entry = self.tables.entries.delete(&mut wtxn, &key)?; let deleted_entry = self.tables.entries.delete(&mut wtxn, &key)?;
@@ -102,7 +141,7 @@ impl DB {
// TODO: move to events.rs // TODO: move to events.rs
} }
deleted_entry & deleted_blobs deleted_entry && deleted_blobs
} else { } else {
false false
}; };

View File

@@ -98,7 +98,7 @@ let [pubkyauthUrl, sessionPromise] = client.authRequest(relay, capabilities);
showQr(pubkyauthUrl); showQr(pubkyauthUrl);
let session = await sessionPromise; let pubky = await sessionPromise;
``` ```
Sign in to a user's Homeserver, without access to their [Keypair](#keypair), nor even [PublicKey](#publickey), Sign in to a user's Homeserver, without access to their [Keypair](#keypair), nor even [PublicKey](#publickey),
@@ -109,7 +109,7 @@ instead request permissions (showing the user pubkyauthUrl), and await a Session
Returns: Returns:
- pubkyauthUrl: A url to show to the user to scan or paste into an Authenticator app holding the user [Keypair](#keypair) - pubkyauthUrl: A url to show to the user to scan or paste into an Authenticator app holding the user [Keypair](#keypair)
- sessionPromise: A promise that resolves into a [Session](#session) on success. - sessionPromise: A promise that resolves into a [PublicKey](#publickey) on success, which you can use in `client.session(pubky)` to resolve more information about the Session.
#### sendAuthToken #### sendAuthToken
```js ```js
@@ -139,7 +139,7 @@ let response = await client.put(url, body);
let response = await client.get(url) let response = await client.get(url)
``` ```
- url: A string representing the Pubky URL. - url: A string representing the Pubky URL.
- Returns: A response object containing the requested data. - Returns: A Uint8Array object containing the requested data, or `undefined` if `NOT_FOUND`.
### delete ### delete

View File

@@ -2,7 +2,7 @@
"name": "@synonymdev/pubky", "name": "@synonymdev/pubky",
"type": "module", "type": "module",
"description": "Pubky client", "description": "Pubky client",
"version": "0.1.15", "version": "0.1.16",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@@ -56,8 +56,10 @@ test("3rd party signin", async (t) => {
await client.sendAuthToken(keypair, pubkyauth_url) await client.sendAuthToken(keypair, pubkyauth_url)
} }
let session = await pubkyauthResponse; let authedPubky = await pubkyauthResponse;
t.is(session.pubky().z32(), pubky) t.is(authedPubky.z32(), pubky);
let session = await client.session(authedPubky);
t.deepEqual(session.capabilities(), capabilities.split(',')) t.deepEqual(session.capabilities(), capabilities.split(','))
}) })

View File

@@ -184,14 +184,14 @@ impl PubkyClient {
&self, &self,
relay: impl TryInto<Url>, relay: impl TryInto<Url>,
capabilities: &Capabilities, capabilities: &Capabilities,
) -> Result<(Url, tokio::sync::oneshot::Receiver<Option<Session>>)> { ) -> Result<(Url, tokio::sync::oneshot::Receiver<PublicKey>)> {
let mut relay: Url = relay let mut relay: Url = relay
.try_into() .try_into()
.map_err(|_| Error::Generic("Invalid relay Url".into()))?; .map_err(|_| Error::Generic("Invalid relay Url".into()))?;
let (pubkyauth_url, client_secret) = self.create_auth_request(&mut relay, capabilities)?; let (pubkyauth_url, client_secret) = self.create_auth_request(&mut relay, capabilities)?;
let (tx, rx) = oneshot::channel::<Option<Session>>(); let (tx, rx) = oneshot::channel::<PublicKey>();
let this = self.clone(); let this = self.clone();

View File

@@ -212,18 +212,17 @@ impl PubkyClient {
&self, &self,
relay: Url, relay: Url,
client_secret: &[u8; 32], client_secret: &[u8; 32],
) -> Result<Option<Session>> { ) -> Result<PublicKey> {
let response = self.http.request(Method::GET, relay).send().await?; let response = self.http.request(Method::GET, relay).send().await?;
let encrypted_token = response.bytes().await?; let encrypted_token = response.bytes().await?;
let token_bytes = decrypt(&encrypted_token, client_secret)?; let token_bytes = decrypt(&encrypted_token, client_secret)?;
let token = AuthToken::verify(&token_bytes)?; let token = AuthToken::verify(&token_bytes)?;
if token.capabilities().is_empty() { if !token.capabilities().is_empty() {
Ok(None) self.signin_with_authtoken(&token).await?;
} else {
let session = self.signin_with_authtoken(&token).await?;
Ok(Some(session))
} }
Ok(token.pubky().clone())
} }
} }

View File

@@ -141,7 +141,7 @@ impl PubkyClient {
return Err(Error::ResolveEndpoint(original_target.into())); return Err(Error::ResolveEndpoint(original_target.into()));
} }
if let Some(public_key) = endpoint_public_key { if endpoint_public_key.is_some() {
let url = Url::parse(&format!( let url = Url::parse(&format!(
"{}://{}", "{}://{}",
if origin.starts_with("localhost") { if origin.starts_with("localhost") {
@@ -152,7 +152,7 @@ impl PubkyClient {
origin origin
))?; ))?;
return Ok(Endpoint { public_key, url }); return Ok(Endpoint { url });
} }
Err(Error::ResolveEndpoint(original_target.into())) Err(Error::ResolveEndpoint(original_target.into()))
@@ -173,8 +173,6 @@ impl PubkyClient {
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct Endpoint { pub(crate) struct Endpoint {
// TODO: we don't use this at all?
pub public_key: PublicKey,
pub url: Url, pub url: Url,
} }
@@ -326,12 +324,11 @@ mod tests {
.await .await
.unwrap(); .unwrap();
let Endpoint { public_key, url } = client let Endpoint { url, .. } = client
.resolve_pubky_homeserver(&pubky.public_key()) .resolve_pubky_homeserver(&pubky.public_key())
.await .await
.unwrap(); .unwrap();
assert_eq!(public_key, server.public_key());
assert_eq!(url.host_str(), Some("localhost")); assert_eq!(url.host_str(), Some("localhost"));
assert_eq!(url.port(), Some(server.port())); assert_eq!(url.port(), Some(server.port()));
} }

View File

@@ -765,4 +765,60 @@ mod tests {
let get = client.get(url.as_str()).await.unwrap(); let get = client.get(url.as_str()).await.unwrap();
dbg!(get); dbg!(get);
} }
#[tokio::test]
async fn dont_delete_shared_blobs() {
let testnet = Testnet::new(10);
let homeserver = Homeserver::start_test(&testnet).await.unwrap();
let client = PubkyClient::test(&testnet);
let homeserver_pubky = homeserver.public_key();
let user_1 = Keypair::random();
let user_2 = Keypair::random();
client.signup(&user_1, &homeserver_pubky).await.unwrap();
client.signup(&user_2, &homeserver_pubky).await.unwrap();
let user_1_id = user_1.public_key();
let user_2_id = user_2.public_key();
let url_1 = format!("pubky://{user_1_id}/pub/pubky.app/file/file_1");
let url_2 = format!("pubky://{user_2_id}/pub/pubky.app/file/file_1");
let file = vec![1];
client.put(url_1.as_str(), &file).await.unwrap();
client.put(url_2.as_str(), &file).await.unwrap();
// Delete file 1
client.delete(url_1.as_str()).await.unwrap();
let blob = client.get(url_2.as_str()).await.unwrap().unwrap();
assert_eq!(blob, file);
let feed_url = format!("http://localhost:{}/events/", homeserver.port());
let response = client
.request(
Method::GET,
format!("{feed_url}").as_str().try_into().unwrap(),
)
.send()
.await
.unwrap();
let text = response.text().await.unwrap();
let lines = text.split('\n').collect::<Vec<_>>();
assert_eq!(
lines,
vec![
format!("PUT pubky://{user_1_id}/pub/pubky.app/file/file_1",),
format!("PUT pubky://{user_2_id}/pub/pubky.app/file/file_1",),
format!("DEL pubky://{user_1_id}/pub/pubky.app/file/file_1",),
lines.last().unwrap().to_string()
]
)
}
} }

View File

@@ -81,7 +81,7 @@ impl PubkyClient {
Ok(Session( Ok(Session(
self.inner_signup(keypair.as_inner(), homeserver.as_inner()) self.inner_signup(keypair.as_inner(), homeserver.as_inner())
.await .await
.map_err(|e| JsValue::from(e))?, .map_err(JsValue::from)?,
)) ))
} }
@@ -135,12 +135,7 @@ impl PubkyClient {
let future = async move { let future = async move {
this.subscribe_to_auth_response(relay, &client_secret) this.subscribe_to_auth_response(relay, &client_secret)
.await .await
.map(|opt| { .map(|pubky| JsValue::from(PublicKey(pubky)))
opt.map_or_else(
|| JsValue::NULL, // Convert `None` to `JsValue::NULL`
|session| JsValue::from(Session(session)),
)
})
.map_err(|err| JsValue::from_str(&format!("{:?}", err))) .map_err(|err| JsValue::from_str(&format!("{:?}", err)))
}; };