Files
backend/web/app.js
Andriy Oblivantsev 18328706bd
CI / test (push) Successful in 5s
Server keys in etc/, bind in docker compose
- bin/gen-server-keys.sh: generate Ed25519 keypair to etc/server-service.{pub,key,env}
- main.go: read keys from file (ADMIN_PUBLIC_KEY_FILE) when env empty
- docker-compose: env_file etc/server-service.env, mount etc/
- bin/up.sh: auto-run gen-server-keys if etc/server-service.env missing
- ErrRegistrationNotConfigured for clearer 503 when keys not set
- etc/README.md, etc/.gitignore
- bin/gen-admin-key.sh for one-off key gen
- .env.example

Made-with: Cursor
2026-03-01 13:02:40 +00:00

100 lines
2.8 KiB
JavaScript

import { createApiClient } from "./api.js";
const { createApp, ref, reactive, onMounted } = Vue;
createApp({
setup() {
const apiBase = ref(localStorage.getItem("geo_api_base") || "https://momswap.produktor.duckdns.org");
const state = reactive({
publicKey: "",
privateKey: "",
accessToken: "",
collections: [],
newCollectionName: "",
status: "Ready",
});
let client = createApiClient(apiBase.value);
const rebuildClient = () => {
client = createApiClient(apiBase.value);
localStorage.setItem("geo_api_base", apiBase.value);
state.status = `API base set to ${apiBase.value}`;
};
const ensureKeys = async () => {
try {
const keys = await client.ensureKeysInStorage();
state.publicKey = keys.publicKey;
state.privateKey = keys.privateKey;
state.status = "Keys loaded from localStorage.";
} catch (err) {
state.status = err.message;
}
};
const register = async () => {
try {
await client.registerBySigningServiceKey(state.publicKey, state.privateKey);
state.status = "Registered. Use Login to authenticate.";
} catch (err) {
state.status = err.message;
}
};
const login = async () => {
try {
try {
await client.registerBySigningServiceKey(state.publicKey, state.privateKey);
} catch (err) {
if (!err.message.includes("already registered") && !err.message.includes("not configured") && !err.message.includes("ADMIN_PUBLIC_KEY")) {
throw err;
}
// Proceed to login: already registered or registration disabled (invitation flow)
}
state.accessToken = await client.loginWithSignature(state.publicKey, state.privateKey);
client.setAccessToken(state.accessToken);
state.status = "Authenticated.";
} catch (err) {
state.status = err.message;
}
};
const listCollections = async () => {
try {
client.setAccessToken(state.accessToken);
const data = await client.listCollections();
state.collections = data.collections || [];
} catch (err) {
state.status = err.message;
}
};
const createCollection = async () => {
try {
client.setAccessToken(state.accessToken);
await client.createCollection(state.newCollectionName);
state.newCollectionName = "";
await listCollections();
} catch (err) {
state.status = err.message;
}
};
onMounted(async () => {
await ensureKeys();
});
return {
apiBase,
state,
rebuildClient,
ensureKeys,
register,
login,
listCollections,
createCollection,
};
},
}).use(Vuetify.createVuetify()).mount("#app");