Demo app (web/):
- Collections: select, rename, remove (x button per row), delete
- Features: add point (lon/lat validation), remove, list in selected collection
- QR codes for pk (private) and pb (public) keys
- Restore public key from private key
- 409 Conflict handled for already-registered login
- Title: Momswap Geo Backend Use-Cases Test
Backend:
- PATCH /v1/collections/{id} for rename
- DELETE /v1/collections/{id}
- Clearer lon/lat validation errors (-180..180, -90..90)
Client:
- updateCollection, deleteCollection, derivePublicKey
Docs:
- docs/frontend-development.md (demo app, local dev)
- README links to all docs
Made-with: Cursor
This commit is contained in:
Vendored
+24
-1
@@ -470,6 +470,11 @@ async function generateKeyPair() {
|
||||
privateKey: bytesToBase64Url(privateKey)
|
||||
};
|
||||
}
|
||||
async function getPublicKeyFromPrivate(privateKeyBase64) {
|
||||
const privateKey = base64UrlToBytes(privateKeyBase64);
|
||||
const publicKey = await getPublicKeyAsync(privateKey);
|
||||
return bytesToBase64Url(publicKey);
|
||||
}
|
||||
async function signMessage(privateKeyBase64, message) {
|
||||
const privateKey = base64UrlToBytes(privateKeyBase64);
|
||||
const signature = await signAsync(textToBytes(message), privateKey);
|
||||
@@ -519,6 +524,9 @@ class GeoApiClient {
|
||||
getStoredKeys() {
|
||||
return loadKeys(this.storage, this.storageKey);
|
||||
}
|
||||
async derivePublicKey(privateKey) {
|
||||
return getPublicKeyFromPrivate(privateKey);
|
||||
}
|
||||
importKeys(keys) {
|
||||
saveKeys(this.storage, keys, this.storageKey);
|
||||
}
|
||||
@@ -538,7 +546,9 @@ class GeoApiClient {
|
||||
const res = await fetch(`${this.baseUrl}${path}`, { ...init, headers, body });
|
||||
if (!res.ok) {
|
||||
const maybeJson = await res.json().catch(() => ({}));
|
||||
const msg = maybeJson.error ?? `HTTP ${res.status}`;
|
||||
let msg = maybeJson.error ?? `HTTP ${res.status}`;
|
||||
if (maybeJson.hint)
|
||||
msg += `. ${maybeJson.hint}`;
|
||||
throw new Error(msg);
|
||||
}
|
||||
if (res.status === 204) {
|
||||
@@ -604,6 +614,15 @@ class GeoApiClient {
|
||||
async createCollection(name) {
|
||||
return this.request("/v1/collections", { method: "POST", body: { name } });
|
||||
}
|
||||
async updateCollection(collectionId, name) {
|
||||
return this.request(`/v1/collections/${collectionId}`, {
|
||||
method: "PATCH",
|
||||
body: { name }
|
||||
});
|
||||
}
|
||||
async deleteCollection(collectionId) {
|
||||
return this.request(`/v1/collections/${collectionId}`, { method: "DELETE" });
|
||||
}
|
||||
async listFeatures(collectionId) {
|
||||
return this.request(`/v1/collections/${collectionId}/features`, { method: "GET" });
|
||||
}
|
||||
@@ -616,11 +635,15 @@ class GeoApiClient {
|
||||
}
|
||||
});
|
||||
}
|
||||
async deleteFeature(featureId) {
|
||||
return this.request(`/v1/features/${featureId}`, { method: "DELETE" });
|
||||
}
|
||||
}
|
||||
export {
|
||||
signMessage,
|
||||
saveKeys,
|
||||
loadKeys,
|
||||
getPublicKeyFromPrivate,
|
||||
generateKeyPair,
|
||||
clearKeys,
|
||||
GeoApiClient,
|
||||
|
||||
Reference in New Issue
Block a user