+ create user, login, logout

This commit is contained in:
2026-05-06 23:17:50 +02:00
parent 424799692a
commit 5f36ff0b7d
9 changed files with 430 additions and 29 deletions

View File

@@ -1,18 +1,22 @@
import { getUserId } from "./identity";
import {
User,
addOption,
toggleVote,
setDeadline,
clearDeadline,
getDeadline,
} from "./state";
import { v4 as uuidv4 } from 'uuid';
import { initSync } from "./sync";
import { initUserSync } from "./userSync";
import { StatusBar } from "./components/StatusBar";
import { PollTitle } from "./components/PollTitle";
import { AddOption } from "./components/AddOption";
import { PollList } from "./components/PollList";
import { ShareSection } from "./components/ShareSection";
import { DeadlineTimer } from "./components/DeadlineTimer";
import { generateUserKeyPair, exportPrivateKey, savePrivateKeyToFile, exportPublicKey, stringToCryptoKey } from "./crypto";
const ROOM_PARAM = "room";
@@ -38,8 +42,9 @@ function ensureRoomId(): string {
export function initApp(container: HTMLElement): () => void {
const roomId = ensureRoomId();
const userId = getUserId();
const sync = initSync(roomId);
const userSync = initUserSync();
let user : User | undefined = undefined;
const shareUrl = window.location.href;
@@ -47,12 +52,12 @@ export function initApp(container: HTMLElement): () => void {
const actions = {
addOption: (label: string) => {
if (isVotingClosed()) return;
return addOption(sync.options, label, userId);
if (!user || isVotingClosed()) return;
return addOption(sync.options, label, user.userid);
},
toggleVote: (optionId: string) => {
if (isVotingClosed()) return;
toggleVote(sync.votes, userId, optionId);
if (!user || isVotingClosed()) return;
toggleVote(sync.votes, user.userid, optionId);
},
startDeadline: (durationMs: number) => {
setDeadline(sync.deadlineMap, durationMs);
@@ -60,6 +65,73 @@ export function initApp(container: HTMLElement): () => void {
clearDeadline: () => {
clearDeadline(sync.deadlineMap);
},
onLoginLogout: async (event: Event) => {
if(user){
user = undefined
return false;
} else {
const target = event.target as HTMLInputElement;
const file = target.files?.[0];
if (file) {
try {
const content = await file.text();
console.log("File loaded: ");
if (file.name && content) {
try {
const uuid = file.name.replace(".pem", "");
// Standardize the string for the importer
const pkBase64 = content.replace(/-----BEGIN PRIVATE KEY-----|-----END PRIVATE KEY-----/g, "").replace(/\s+/g, "");
const key = await stringToCryptoKey(pkBase64, "private");
user = {
userid: uuid,
private_key: key,
public_key: undefined, // Note: You might need to import a pub key too!
};
console.log("Login successful for:", uuid);
return true;
} catch (err) {
console.error("Crypto Import Error:", err);
alert("The file content is not a valid Private Key.");
}
}
} catch (e) {
console.error("Failed to read file", e);
}
}
return false;
}
},
onCreateUser: async (event: Event) => {
try {
const keypair = await generateUserKeyPair();
console.log('keypair:', keypair);
const uuid = uuidv4();
user = {
userid: uuid,
private_key: keypair.privateKey,
public_key: keypair.publicKey,
};
const prvKeyString = await exportPrivateKey(keypair.privateKey);
savePrivateKeyToFile(prvKeyString,uuid+".pem")
const pubKeyString = await exportPublicKey(keypair.publicKey);
userSync.users.set(user.userid,pubKeyString)
return true;
} catch (err) {
user = undefined
console.error("Failed to create new User!", err);
}
return false;
}
};
function isVotingClosed() {
@@ -85,7 +157,7 @@ export function initApp(container: HTMLElement): () => void {
<span>Polly</span>
`;
const statusBar = StatusBar(sync.provider);
const statusBar = StatusBar(sync.provider,userSync.provider,actions.onLoginLogout,actions.onCreateUser);
header.append(wordmark, statusBar);
// Main card
@@ -98,7 +170,7 @@ export function initApp(container: HTMLElement): () => void {
if (result && !result.ok) return result.error;
return null;
});
const pollList = PollList(sync.options, sync.votes, userId, isVotingClosed, actions.toggleVote);
const pollList = PollList(sync.options, sync.votes, user, isVotingClosed, actions.toggleVote);
const deadlineTimer = DeadlineTimer(
sync.deadlineMap,
actions.startDeadline,