feat: Added QR login

fix: Fixed webview for windows builds
This commit is contained in:
Simeon Radivoev 2026-03-03 15:51:47 +02:00
parent 01b91aa48c
commit 4739b89933
Signed by: simeonradivoev
GPG key ID: 7611A451D2A5D37A
26 changed files with 545 additions and 83 deletions

View file

@ -1,32 +1,47 @@
import Elysia, { status } from "elysia";
import { config, db, jar } from "./app";
import Elysia, { sse, status } from "elysia";
import { config, jar, taskQueue } from "./app";
import z from "zod";
import { client } from "@clients/romm/client.gen";
import { loginApiLoginPost } from "@clients/romm";
import { loginApiLoginPost, logoutApiLogoutPost } from "@clients/romm";
import secrets from '../api/secrets';
import { LoginJob } from "./jobs/login-job";
export default new Elysia()
.post('/login', async ({ body: { host, username, password } }) =>
.post('/login/remote/start', async () =>
{
if (config.has('rommAddress') && config.has('rommUser'))
if (taskQueue.hasActiveOfType(LoginJob))
{
await logout();
const oldRommAddress = config.get('rommAddress');
if (oldRommAddress)
{
const cookies = await jar.getCookies(oldRommAddress);
cookies.map(c => jar.store.removeCookie(c.domain, c.path, c.key));
}
return status("Conflict", "Login Already Active");
}
config.set('rommAddress', host);
config.set('rommUser', username);
const job = new LoginJob();
taskQueue.enqueue("login", job);
return status("OK");
})
.get('/login/remote/status', async function* ()
{
const job = taskQueue.findJob("login");
if (job)
{
const loginJob = job.job as LoginJob;
yield sse({ data: { endsAt: loginJob.endsAt, url: loginJob.url } });
await taskQueue.waitForJob('login');
yield sse({ data: {} });
}
await secrets.set({ service: 'gameflow', name: 'romm', value: password });
await login();
return status(200);
}, { body: z.object({ host: z.url(), username: z.string(), password: z.string() }) })
yield sse({ data: {} });
})
.post('/login/remote/cancel', async () =>
{
const job = taskQueue.findJob("login");
if (job)
{
job.abort("cancel");
await taskQueue.waitForJob('login');
}
return {};
})
.post('/login', async ({ body }) => tryLoginAndSave(body), { body: z.object({ host: z.url(), username: z.string(), password: z.string() }) })
.get('/login', async () =>
{
const credentials = await secrets.get({ service: 'gameflow', name: 'romm' });
@ -54,6 +69,31 @@ async function updateClient ()
});
}
export async function tryLoginAndSave ({ host, username, password }: { host: string, username: string, password: string; })
{
if (config.has('rommAddress') && config.has('rommUser'))
{
await logout();
const oldRommAddress = config.get('rommAddress');
if (oldRommAddress)
{
const cookies = await jar.getCookies(oldRommAddress);
await Promise.all(cookies.map(c => jar.store.removeCookie(c.domain, c.path, c.key)));
}
}
const response = await login({ rommAddress: host, rommUser: username, rommPassword: password });
if (response?.code === 200)
{
config.set('rommAddress', host);
config.set('rommUser', username);
await secrets.set({ service: 'gameflow', name: 'romm', value: password });
}
return response;
}
export async function logout ()
{
if (!config.has('rommAddress'))
@ -66,11 +106,12 @@ export async function logout ()
console.log("Logging Out of ROMM");
try
{
await loginApiLoginPost({
await logoutApiLogoutPost({
baseUrl: rommAddress, headers: {
'cookie': await jar.getCookieString(rommAddress)
}
});
await jar.store.removeCookie(new URL(rommAddress).host, null, "romm_session");
} catch (error)
{
console.error("Failed to logout of ROMM ", error);
@ -78,20 +119,39 @@ export async function logout ()
}
}
export async function login ()
export async function login (data?: { rommAddress?: string, rommUser?: string, rommPassword?: string; })
{
if (!config.has('rommAddress') || !config.has('rommUser'))
const address = data?.rommAddress ?? config.get('rommAddress');
const user = data?.rommUser ?? config.get('rommUser');
const password = data?.rommPassword ?? await secrets.get({ service: 'gameflow', name: "romm" });
if (!address || !user)
{
return;
console.warn("Romm not setup");
return status(404);
}
const rommAddress = config.get('rommAddress');
const rommUser = config.get('rommUser');
if (rommAddress && rommUser)
{
console.log("Logging In to ROMM");
const password = await secrets.get({ service: 'gameflow', name: "romm" });
if (password === null)
{
return status(404, "No Found Password");
}
const loginResponse = await loginApiLoginPost({ baseUrl: rommAddress, auth: `${rommUser}:${password}` });
loginResponse.response.headers.getSetCookie().map(c => jar.setCookie(c, rommAddress));
await updateClient();
if (loginResponse.response.status === 200)
{
loginResponse.response.headers.getSetCookie().map(c => jar.setCookie(c, rommAddress));
await updateClient();
return status(200, loginResponse.response.statusText);
} else
{
console.error("Could not Login to Romm: ", loginResponse.response.statusText);
return status(loginResponse.response.status, loginResponse.response.statusText);
}
}
}
}