Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions examples/js-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"test:pubky-helper": "node scripts/test-pubky-helper.mjs",
"test:creator-session-state": "node scripts/test-creator-session-state.mjs",
"test:pubky": "node scripts/test-pubky.mjs",
"test:release-version-contract": "node scripts/test-release-version-contract.mjs",
"start-server": "node scripts/start-demo-server.mjs",
"start-reader-server": "node scripts/start-reader-demo-server.mjs",
"init-paykit-compose": "node scripts/init-paykit-compose.mjs",
Expand All @@ -34,9 +35,9 @@
"validate:paykit-compose": "node scripts/validate-paykit-compose.mjs",
"validate:staging-compose": "node scripts/validate-staging-compose.mjs",
"check:paykit-setup-contract": "node scripts/check-paykit-setup-contract.mjs",
"smoke:paykit-compose": "npm run validate:paykit-compose && npm run check:paykit-setup-contract && npm run test:pubky && npm run test:paykit-reader-worker && node scripts/smoke-paykit-compose.mjs",
"smoke:paykit-compose": "npm run validate:paykit-compose && npm run check:paykit-setup-contract && npm run test:release-version-contract && npm run test:pubky && npm run test:paykit-reader-worker && node scripts/smoke-paykit-compose.mjs",
"smoke": "npm --prefix ../../locks-sdk/bindings/js run smoke:examples",
"check": "node --check scripts/test-reader-load-state.mjs && node --check reader-load-state.js && node --check scripts/test-pubky.mjs && node --check scripts/init-config.mjs && node --check scripts/init-staging-config.mjs && node --check scripts/lib/staging-config.mjs && node --check scripts/lib/demo-runtime.mjs && node --check scripts/test-staging-config.mjs && node --check scripts/validate-staging-compose.mjs && node --check scripts/test-staging-compose.mjs && node --check scripts/test-staging-creator-mode.mjs && node --check scripts/test-staging-reader-mode.mjs && node --check scripts/test-reader-persistence.mjs && node --check scripts/test-connection-state-indicator.mjs && node --check scripts/test-pubky-helper.mjs && node --check scripts/test-creator-session-state.mjs && node --check scripts/reset-paykit-staging-demo-local.mjs && node --check scripts/test-reset-paykit-staging-demo-local.mjs && node --check scripts/init-paykit-compose.mjs && node --check scripts/homegate-bridge.mjs && node --check scripts/generate-paykit-account-tpub.mjs && node --check scripts/reset-paykit-demo.mjs && node --check scripts/validate-paykit-compose.mjs && node --check scripts/check-paykit-setup-contract.mjs && node --check scripts/electrum-readiness.mjs && node --check scripts/smoke-paykit-compose.mjs && node --check scripts/test-paykit-reader-worker.mjs && node --check scripts/create-user.mjs && node --check scripts/publish-creator-profile.mjs && node --check scripts/authenticate.mjs && node --check scripts/authenticate-paykit.mjs && node --check scripts/register-paykit-reader.mjs && node --check scripts/prepare-paykit-reader.mjs && node --check scripts/receive-paykit-request.mjs && node --check scripts/lib/creator-session-state.mjs && node --check scripts/lib/creator-static-path.mjs && node --check scripts/lib/paykit-reader-helper.mjs && node --check scripts/lib/paykit-reader-status.mjs && node --check scripts/lib/paykit-reader-worker.mjs && node --check scripts/start-demo-server.mjs && node --check scripts/start-reader-demo-server.mjs && node --check demo-network.js && node --check reader-persistence.js && node --check reader-staging-paykit.js && node --check app.js && node --check app-iframe.js && node --check creator-identity.js && node --check creator-lock-policy.js && node --check payment-amount.js && node --check paykit-setup.js && node --check creator-complete-flow.js && node --check reader-app.js && node --check reader-flow.js"
"check": "node --check scripts/test-reader-load-state.mjs && node --check reader-load-state.js && node --check scripts/test-pubky.mjs && node --check scripts/test-release-version-contract.mjs && node --check scripts/lib/release-version-contract.mjs && node --check scripts/init-config.mjs && node --check scripts/init-staging-config.mjs && node --check scripts/lib/staging-config.mjs && node --check scripts/lib/demo-runtime.mjs && node --check scripts/test-staging-config.mjs && node --check scripts/validate-staging-compose.mjs && node --check scripts/test-staging-compose.mjs && node --check scripts/test-staging-creator-mode.mjs && node --check scripts/test-staging-reader-mode.mjs && node --check scripts/test-reader-persistence.mjs && node --check scripts/test-connection-state-indicator.mjs && node --check scripts/test-pubky-helper.mjs && node --check scripts/test-creator-session-state.mjs && node --check scripts/reset-paykit-staging-demo-local.mjs && node --check scripts/test-reset-paykit-staging-demo-local.mjs && node --check scripts/init-paykit-compose.mjs && node --check scripts/homegate-bridge.mjs && node --check scripts/generate-paykit-account-tpub.mjs && node --check scripts/reset-paykit-demo.mjs && node --check scripts/validate-paykit-compose.mjs && node --check scripts/check-paykit-setup-contract.mjs && node --check scripts/electrum-readiness.mjs && node --check scripts/smoke-paykit-compose.mjs && node --check scripts/test-paykit-reader-worker.mjs && node --check scripts/create-user.mjs && node --check scripts/publish-creator-profile.mjs && node --check scripts/authenticate.mjs && node --check scripts/authenticate-paykit.mjs && node --check scripts/register-paykit-reader.mjs && node --check scripts/prepare-paykit-reader.mjs && node --check scripts/receive-paykit-request.mjs && node --check scripts/lib/creator-session-state.mjs && node --check scripts/lib/creator-static-path.mjs && node --check scripts/lib/paykit-reader-helper.mjs && node --check scripts/lib/paykit-reader-status.mjs && node --check scripts/lib/paykit-reader-worker.mjs && node --check scripts/start-demo-server.mjs && node --check scripts/start-reader-demo-server.mjs && node --check demo-network.js && node --check reader-persistence.js && node --check reader-staging-paykit.js && node --check app.js && node --check app-iframe.js && node --check creator-identity.js && node --check creator-lock-policy.js && node --check payment-amount.js && node --check paykit-setup.js && node --check creator-complete-flow.js && node --check reader-app.js && node --check reader-flow.js"
},
"dependencies": {
"@synonymdev/pubky": "^0.16.2"
Expand Down
8 changes: 8 additions & 0 deletions examples/js-sdk/scripts/lib/release-version-contract.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function assertCargoLockPackageVersions(cargoLockSource, expectedVersions) {
for (const [packageName, expectedVersion] of expectedVersions) {
const packageEntry = `name = "${packageName}"\nversion = "${expectedVersion}"`;
if (!cargoLockSource.includes(packageEntry)) {
throw new Error(`${packageName} Cargo.lock version must be ${expectedVersion}`);
}
}
}
26 changes: 11 additions & 15 deletions examples/js-sdk/scripts/smoke-paykit-compose.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ import {
DEFAULT_PAYKIT_SERVER_CONTEXT,
PAYKIT_SERVER_REF,
} from './lib/paykit-server-source.mjs';
import { assertCargoLockPackageVersions } from './lib/release-version-contract.mjs';

const lockServerPubky = 'pubky7ir1ttte48bcp4zjychjyscicrwi1j34mtt91ptsafdbjmr8g9eo';
const creatorPubky = 'pubkytkrq8zmwb8a3m9k15csu3q17qmfgqnp9dskbrg9uq1rydpyxp7qy';
const locksReleaseVersion = '0.1.0';
const locksServerReleaseVersion = '0.1.1-rc1';
assert.equal(PAYKIT_SERVER_REF, '4cc8e7b836dba28446a8d821f593ee40bb4acbeb');
assert.equal(
DEFAULT_PAYKIT_SERVER_CONTEXT,
Expand Down Expand Up @@ -89,20 +91,14 @@ assert.match(composeSource, /^# Local development and demonstration only;/);
assert.match(composeSource, /^name: pubky-locks-paykit-demo$/m);
assert.match(cargoManifestSource, new RegExp(`^version = "${locksReleaseVersion}"$`, 'm'));
assert.equal(jsPackage.version, locksReleaseVersion);
for (const packageName of [
'locks-core',
'locks-e2e',
'locks-sdk',
'locks-sdk-wasm',
'locks-server',
'locks-service',
]) {
assert.match(
cargoLockSource,
new RegExp(`name = "${packageName}"\\nversion = "${locksReleaseVersion}"`),
`${packageName} Cargo.lock version must match the release candidate`,
);
}
assertCargoLockPackageVersions(cargoLockSource, new Map([
['locks-core', locksReleaseVersion],
['locks-e2e', locksReleaseVersion],
['locks-sdk', locksReleaseVersion],
['locks-sdk-wasm', locksReleaseVersion],
['locks-server', locksServerReleaseVersion],
['locks-service', locksReleaseVersion],
]));
assert.match(creatorAppSource, /hasExactKeys\(event\.data, \['type', 'state', 'code'\]\)/);
assert.match(creatorAppSource, /body: JSON\.stringify\(\{ level \}\)/);
assert.match(creatorAppSource, /\[result\.authorizationUrl, result\.command\]\.filter\(Boolean\)/);
Expand Down Expand Up @@ -590,7 +586,7 @@ assert.ok(
);
assert.equal(
packageJson.scripts['smoke:paykit-compose'],
'npm run validate:paykit-compose && npm run check:paykit-setup-contract && npm run test:pubky && npm run test:paykit-reader-worker && node scripts/smoke-paykit-compose.mjs',
'npm run validate:paykit-compose && npm run check:paykit-setup-contract && npm run test:release-version-contract && npm run test:pubky && npm run test:paykit-reader-worker && node scripts/smoke-paykit-compose.mjs',
);

console.log('Paykit Compose smoke check passed');
27 changes: 27 additions & 0 deletions examples/js-sdk/scripts/test-release-version-contract.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env node
import assert from 'node:assert/strict';

import { assertCargoLockPackageVersions } from './lib/release-version-contract.mjs';

const expectedVersions = new Map([
['locks-core', '0.1.0'],
['locks-server', '0.1.1-rc1'],
]);
const currentCargoLock = `[[package]]
name = "locks-core"
version = "0.1.0"

[[package]]
name = "locks-server"
version = "0.1.1-rc1"
`;

assert.doesNotThrow(() => assertCargoLockPackageVersions(currentCargoLock, expectedVersions));

const staleCargoLock = currentCargoLock.replace('version = "0.1.1-rc1"', 'version = "0.1.0"');
assert.throws(
() => assertCargoLockPackageVersions(staleCargoLock, expectedVersions),
/locks-server Cargo.lock version must be 0\.1\.1-rc1/,
);

console.log('Release version contract tests passed');
2 changes: 1 addition & 1 deletion locks-server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "locks-server"
version.workspace = true
version = "0.1.1-rc1"
Comment thread
Jasonvdb marked this conversation as resolved.
Resolved
edition.workspace = true
license.workspace = true
repository.workspace = true
Expand Down
88 changes: 57 additions & 31 deletions locks-server/src/config/loading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,34 @@ fn initialize_default_config(
service_home: PathBuf,
secret_path: PathBuf,
identity_provider: &impl LockServerIdentityProvider,
) -> Result<LockServerRuntimeConfig, ConfigError> {
initialize_default_config_with_database_url(
config_path,
service_home,
secret_path,
identity_provider,
|| {
std::env::var("PUBKY_LOCK_DATABASE_URL").map_err(|_| {
ConfigError::MissingDatabaseUrlEnv("PUBKY_LOCK_DATABASE_URL".to_owned())
})
},
)
}

fn initialize_default_config_with_database_url(
config_path: PathBuf,
service_home: PathBuf,
secret_path: PathBuf,
identity_provider: &impl LockServerIdentityProvider,
database_url: impl FnOnce() -> Result<String, ConfigError>,
) -> Result<LockServerRuntimeConfig, ConfigError> {
std::fs::create_dir_all(&service_home).map_err(|source| ConfigError::CreateServiceHome {
path: service_home.clone(),
source,
})?;

let public_key = identity_provider.generate_secret(&secret_path)?;
let database_url = database_url()?;
let config = LockServerRuntimeConfig {
bind_addr: "127.0.0.1:3000".parse().expect("static bind addr is valid"),
credentials: LockServerCredentialsConfig {
Expand All @@ -142,9 +163,7 @@ fn initialize_default_config(
max_ttl_seconds: 900,
},
database: DatabaseConfig {
url: std::env::var("PUBKY_LOCK_DATABASE_URL").map_err(|_| {
ConfigError::MissingDatabaseUrlEnv("PUBKY_LOCK_DATABASE_URL".to_owned())
})?,
url: database_url,
max_connections: 10,
run_migrations_on_startup: true,
},
Expand Down Expand Up @@ -355,26 +374,39 @@ mod tests {
use crate::config::{FilesystemLockServerIdentityProvider, GrantConnectAcquisitionConfig};
use tempfile::tempdir;

const TEST_DATABASE_URL: &str = "postgres://localhost/locks_test";

fn initialize_test_default_config(home: &Path) -> LockServerRuntimeConfig {
let service_home = home.join(DEFAULT_SERVICE_HOME);
initialize_default_config_with_database_url(
service_home.join(DEFAULT_CONFIG_FILE),
service_home.clone(),
service_home.join(DEFAULT_SECRET_FILE),
&FilesystemLockServerIdentityProvider,
|| Ok(TEST_DATABASE_URL.to_owned()),
)
.unwrap()
}

#[test]
fn generated_default_config_comments_every_setting_and_remains_parseable() {
let home = tempdir().unwrap();
unsafe {
std::env::set_var(
"PUBKY_LOCK_DATABASE_URL",
"postgres://locks:locks@localhost/locks_test",
);
}

let generated =
load_or_initialize_config(None, home.path(), &FilesystemLockServerIdentityProvider)
.unwrap();
let generated = initialize_test_default_config(home.path());
let config_path = home.path().join(".pubky-lock/config.toml");
let config_text = std::fs::read_to_string(&config_path).unwrap();

for line in config_text.lines().filter(|line| line.contains(" = ")) {
assert!(line.contains(" # "), "missing inline comment: {line}");
}

std::fs::write(
&config_path,
config_text.replace(
"url_env = \"PUBKY_LOCK_DATABASE_URL\"",
&format!("url = \"{TEST_DATABASE_URL}\""),
),
)
.unwrap();
let parsed = load_existing_config_from_path(&config_path).unwrap();

assert_eq!(parsed, generated);
Expand All @@ -394,15 +426,7 @@ mod tests {
#[test]
fn paykit_config_requires_signing_seed_secret_on_startup() {
let home = tempdir().unwrap();
unsafe {
std::env::set_var(
"PUBKY_LOCK_DATABASE_URL",
"postgres://locks:locks@localhost/locks_test",
);
}
let generated =
load_or_initialize_config(None, home.path(), &FilesystemLockServerIdentityProvider)
.unwrap();
let generated = initialize_test_default_config(home.path());
let secret_path = home.path().join(".pubky-lock/secret.sess");
std::fs::write(
&secret_path,
Expand All @@ -413,6 +437,16 @@ mod tests {
)
.unwrap();

let config_path = home.path().join(".pubky-lock/config.toml");
let config_text = std::fs::read_to_string(&config_path).unwrap();
std::fs::write(
&config_path,
config_text.replace(
"url_env = \"PUBKY_LOCK_DATABASE_URL\"",
&format!("url = \"{TEST_DATABASE_URL}\""),
),
)
.unwrap();
let error =
load_or_initialize_config(None, home.path(), &FilesystemLockServerIdentityProvider)
.unwrap_err();
Expand All @@ -423,15 +457,7 @@ mod tests {
#[test]
fn grant_connect_requires_signing_seed_secret_on_startup() {
let home = tempdir().unwrap();
unsafe {
std::env::set_var(
"PUBKY_LOCK_DATABASE_URL",
"postgres://locks:***@localhost/locks_test",
);
}
let mut generated =
load_or_initialize_config(None, home.path(), &FilesystemLockServerIdentityProvider)
.unwrap();
let mut generated = initialize_test_default_config(home.path());
generated.paykit = None;
generated.creator_authority_acquisition.method =
CreatorAuthorityAcquisitionMethod::GrantConnect;
Expand Down
46 changes: 25 additions & 21 deletions locks-server/src/pkdns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ async fn run_periodic<P, F, E>(
interval.tick().await;
loop {
tokio::select! {
biased;
changed = shutdown.changed() => {
if changed.is_err() || *shutdown.borrow() {
return;
Expand Down Expand Up @@ -223,29 +224,32 @@ mod tests {

#[tokio::test]
async fn failed_periodic_publication_is_retried_until_shutdown() {
let attempts = Arc::new(AtomicUsize::new(0));
let observed = Arc::clone(&attempts);
let (shutdown_tx, shutdown_rx) = tokio::sync::watch::channel(false);

run_periodic(
std::time::Duration::from_millis(1),
shutdown_rx,
move || {
let attempt = observed.fetch_add(1, Ordering::SeqCst);
let shutdown_tx = shutdown_tx.clone();
async move {
if attempt == 0 {
Err("relay unavailable")
} else {
shutdown_tx.send(true).unwrap();
Ok(())
for _ in 0..20 {
let attempts = Arc::new(AtomicUsize::new(0));
let observed = Arc::clone(&attempts);
let (shutdown_tx, shutdown_rx) = tokio::sync::watch::channel(false);

run_periodic(
std::time::Duration::from_millis(1),
shutdown_rx,
move || {
let attempt = observed.fetch_add(1, Ordering::SeqCst);
let shutdown_tx = shutdown_tx.clone();
async move {
if attempt == 0 {
Err("relay unavailable")
} else {
shutdown_tx.send(true).unwrap();
tokio::time::sleep(std::time::Duration::from_millis(2)).await;
Ok(())
}
}
}
},
)
.await;
},
)
.await;

assert_eq!(attempts.load(Ordering::SeqCst), 2);
assert_eq!(attempts.load(Ordering::SeqCst), 2);
}
}

#[test]
Expand Down
Loading