feat: add user authentication flow and configuration management
This commit is contained in:
parent
ccb755603e
commit
c97cfabfa4
@ -4,12 +4,14 @@ import { invoke } from '@tauri-apps/api/core';
|
|||||||
import { Crypto } from './crypto';
|
import { Crypto } from './crypto';
|
||||||
|
|
||||||
export class Config {
|
export class Config {
|
||||||
|
private fileName: string;
|
||||||
private config: Record<string, unknown> = {};
|
private config: Record<string, unknown> = {};
|
||||||
private crypto: Crypto | undefined;
|
private crypto: Crypto | undefined;
|
||||||
private configFilePath: string | undefined;
|
private configFilePath: string | undefined;
|
||||||
private encrypt: boolean;
|
private encrypt: boolean;
|
||||||
|
|
||||||
constructor(encrypt?: boolean) {
|
constructor(fileName: string, encrypt?: boolean) {
|
||||||
|
this.fileName = fileName;
|
||||||
this.encrypt = encrypt ?? false;
|
this.encrypt = encrypt ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,7 +22,7 @@ export class Config {
|
|||||||
|
|
||||||
const homeDir = await path.homeDir();
|
const homeDir = await path.homeDir();
|
||||||
const folderPath = await path.join(homeDir, '.ezpplauncher');
|
const folderPath = await path.join(homeDir, '.ezpplauncher');
|
||||||
this.configFilePath = await path.join(folderPath, 'user_settings');
|
this.configFilePath = await path.join(folderPath, this.fileName);
|
||||||
|
|
||||||
const createFolder = !(await exists(folderPath));
|
const createFolder = !(await exists(folderPath));
|
||||||
if (createFolder) await mkdir(folderPath);
|
if (createFolder) await mkdir(folderPath);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import { Config } from './config';
|
import { Config } from './config';
|
||||||
|
import type { EZPPUser } from './types';
|
||||||
|
|
||||||
export const userAuth = writable<Config>(new Config(true));
|
export const userAuth = writable<Config>(new Config("user_auth", true));
|
||||||
|
export const currentUser = writable<EZPPUser | undefined>(undefined);
|
||||||
export const username = writable<string>("");
|
|
||||||
export const password = writable<string>("")
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import { Config } from './config';
|
import { Config } from './config';
|
||||||
|
|
||||||
export const userSettings = writable<Config>(new Config(false));
|
export const userSettings = writable<Config>(new Config('user_settings', false));
|
||||||
|
|
||||||
export const customCursor = writable<boolean>(true);
|
export const customCursor = writable<boolean>(true);
|
||||||
export const cursorSmoothening = writable<boolean>(true);
|
export const cursorSmoothening = writable<boolean>(true);
|
||||||
|
@ -4,14 +4,12 @@
|
|||||||
import Badge from '@/components/ui/badge/badge.svelte';
|
import Badge from '@/components/ui/badge/badge.svelte';
|
||||||
import Button from '@/components/ui/button/button.svelte';
|
import Button from '@/components/ui/button/button.svelte';
|
||||||
import * as Select from '@/components/ui/select';
|
import * as Select from '@/components/ui/select';
|
||||||
import { beatmap_sets, online_friends, server_connection_fails, server_ping } from '@/global';
|
import { beatmap_sets, current_view, server_connection_fails, server_ping } from '@/global';
|
||||||
import { WebviewWindow } from '@tauri-apps/api/webviewWindow';
|
|
||||||
import {
|
import {
|
||||||
LoaderCircle,
|
LoaderCircle,
|
||||||
Logs,
|
Logs,
|
||||||
Music2,
|
Music2,
|
||||||
Play,
|
Play,
|
||||||
Users,
|
|
||||||
Wifi,
|
Wifi,
|
||||||
Gamepad2,
|
Gamepad2,
|
||||||
WifiOff,
|
WifiOff,
|
||||||
@ -22,7 +20,7 @@
|
|||||||
import * as AlertDialog from '@/components/ui/alert-dialog';
|
import * as AlertDialog from '@/components/ui/alert-dialog';
|
||||||
import Progress from '@/components/ui/progress/progress.svelte';
|
import Progress from '@/components/ui/progress/progress.svelte';
|
||||||
import { numberHumanReadable } from '@/utils';
|
import { numberHumanReadable } from '@/utils';
|
||||||
import { fade, fly, scale } from 'svelte/transition';
|
import { scale } from 'svelte/transition';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
import Label from '@/components/ui/label/label.svelte';
|
import Label from '@/components/ui/label/label.svelte';
|
||||||
import {
|
import {
|
||||||
@ -36,6 +34,8 @@
|
|||||||
import { open } from '@tauri-apps/plugin-dialog';
|
import { open } from '@tauri-apps/plugin-dialog';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
|
import Login from './Login.svelte';
|
||||||
|
import { currentUser } from '@/userAuthentication';
|
||||||
|
|
||||||
let selectedTab = $state('home');
|
let selectedTab = $state('home');
|
||||||
let launching = $state(false);
|
let launching = $state(false);
|
||||||
@ -85,14 +85,17 @@
|
|||||||
<div class="w-full h-full border-r border-theme-800/90 flex flex-col gap-6 py-3">
|
<div class="w-full h-full border-r border-theme-800/90 flex flex-col gap-6 py-3">
|
||||||
<div class="flex flex-col items-center gap-2 border-b pb-6">
|
<div class="flex flex-col items-center gap-2 border-b pb-6">
|
||||||
<Avatar.Root class="w-20 h-20 border-2 border-theme-800">
|
<Avatar.Root class="w-20 h-20 border-2 border-theme-800">
|
||||||
<Avatar.Image src="https://a.ez-pp.farm/1001" />
|
<Avatar.Image src="https://a.ez-pp.farm/{$currentUser?.id ?? 0}" />
|
||||||
<Avatar.Fallback class="bg-theme-900">
|
<Avatar.Fallback class="bg-theme-900">
|
||||||
<LoaderCircle class="animate-spin" size={32} />
|
<LoaderCircle class="animate-spin" size={32} />
|
||||||
</Avatar.Fallback>
|
</Avatar.Fallback>
|
||||||
</Avatar.Root>
|
</Avatar.Root>
|
||||||
<span class="font-semibold text-2xl text-theme-50">Quetzalcoatl</span>
|
<span class="font-semibold text-2xl text-theme-50">{$currentUser?.name ?? 'Guest'}</span>
|
||||||
<div class="flex flex-row gap-2">
|
<div class="flex flex-row gap-2">
|
||||||
<Badge variant="destructive">Owner</Badge>
|
<!-- <Badge variant="destructive">Owner</Badge> -->
|
||||||
|
{#if !$currentUser}
|
||||||
|
<Button variant="outline" size="sm" onclick={() => current_view.set(Login)}>Login</Button>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-6 h-full px-3">
|
<div class="flex flex-col gap-6 h-full px-3">
|
||||||
@ -402,7 +405,7 @@
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="flex flex-row items-center gap-3 font-semibold text-xl px-3 pt-3">
|
<div class="flex flex-row items-center gap-3 font-semibold text-xl px-3 pt-3">
|
||||||
<Settings2 /> EZPPLauncher Settings
|
<Settings2 /> osu! Settings
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="grid grid-cols-[0.7fr_auto] gap-y-5 items-center border-t border-theme-800 py-3 px-6"
|
class="grid grid-cols-[0.7fr_auto] gap-y-5 items-center border-t border-theme-800 py-3 px-6"
|
||||||
|
@ -7,6 +7,9 @@
|
|||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import SetupWizard from './SetupWizard.svelte';
|
import SetupWizard from './SetupWizard.svelte';
|
||||||
import Launch from './Launch.svelte';
|
import Launch from './Launch.svelte';
|
||||||
|
import { currentUser, userAuth } from '@/userAuthentication';
|
||||||
|
import { ezppfarm } from '@/api/ezpp';
|
||||||
|
import { toast } from 'svelte-sonner';
|
||||||
|
|
||||||
let ezppLogo: HTMLImageElement;
|
let ezppLogo: HTMLImageElement;
|
||||||
let spinnerCircle: SVGCircleElement;
|
let spinnerCircle: SVGCircleElement;
|
||||||
@ -48,6 +51,29 @@
|
|||||||
|
|
||||||
const prepare = async () => {
|
const prepare = async () => {
|
||||||
await calculateCursorSmoothness();
|
await calculateCursorSmoothness();
|
||||||
|
|
||||||
|
const username = $userAuth.value('username').get('');
|
||||||
|
const password = $userAuth.value('password').get('');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const loginResult = await ezppfarm.login(username, password);
|
||||||
|
if (loginResult && loginResult.user) {
|
||||||
|
toast.success('Login successful!', {
|
||||||
|
description: `Welcome back, ${loginResult.user.name}!`,
|
||||||
|
});
|
||||||
|
|
||||||
|
currentUser.set(loginResult.user);
|
||||||
|
} else {
|
||||||
|
toast.error('Login failed!', {
|
||||||
|
description: 'Please check your username and password.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
toast.error('Server error occurred during login.', {
|
||||||
|
description: 'There was an issue connecting to the server. Please try again later.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
animate(ezppLogo, {
|
animate(ezppLogo, {
|
||||||
opacity: [1, 0],
|
opacity: [1, 0],
|
||||||
scale: [1, 1.05],
|
scale: [1, 1.05],
|
||||||
|
@ -1,28 +1,115 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import Logo from '$assets/logo.png';
|
||||||
import { ezppfarm } from '@/api/ezpp';
|
import { ezppfarm } from '@/api/ezpp';
|
||||||
|
import Button from '@/components/ui/button/button.svelte';
|
||||||
|
import Input from '@/components/ui/input/input.svelte';
|
||||||
|
import Label from '@/components/ui/label/label.svelte';
|
||||||
|
import { current_view } from '@/global';
|
||||||
|
import { currentUser, userAuth } from '@/userAuthentication';
|
||||||
|
import { animate } from 'animejs';
|
||||||
|
import { LoaderCircle } from 'lucide-svelte';
|
||||||
|
import { toast } from 'svelte-sonner';
|
||||||
|
import Launch from './Launch.svelte';
|
||||||
|
|
||||||
let username = $state('');
|
let username = $state('');
|
||||||
let password = $state('');
|
let password = $state('');
|
||||||
|
|
||||||
let errorMessage = $state('');
|
|
||||||
let isLoading = $state(false);
|
let isLoading = $state(false);
|
||||||
|
|
||||||
|
let ezppLogo: HTMLImageElement | undefined = $state(undefined);
|
||||||
|
|
||||||
|
const logo_mouseenter = () => {
|
||||||
|
if (ezppLogo) {
|
||||||
|
animate(ezppLogo, {
|
||||||
|
duration: 700,
|
||||||
|
scale: 1.2,
|
||||||
|
ease: (t: number) => Math.pow(2, -5 * t) * Math.sin((t - 0.075) * 20.94) + 1 - 0.0005 * t,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const logo_mouseleave = () => {
|
||||||
|
if (ezppLogo) {
|
||||||
|
animate(ezppLogo, {
|
||||||
|
duration: 700,
|
||||||
|
scale: 1,
|
||||||
|
ease: (t: number) => (t - 1) ** 7 + 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const performLogin = async () => {
|
const performLogin = async () => {
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
errorMessage = '';
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const loginResult = await ezppfarm.login(username, password);
|
const loginResult = await ezppfarm.login(username, password);
|
||||||
if (loginResult) {
|
if (loginResult && loginResult.user) {
|
||||||
// Handle successful login, e.g., redirect to the main app or store the token
|
toast.success('Login successful!', {
|
||||||
console.log('Login successful:', loginResult);
|
description: `Welcome back, ${loginResult.user.name}!`,
|
||||||
|
});
|
||||||
|
|
||||||
|
$userAuth.value('username').set(username);
|
||||||
|
$userAuth.value('password').set(password);
|
||||||
|
await $userAuth.save();
|
||||||
|
|
||||||
|
currentUser.set(loginResult.user);
|
||||||
|
current_view.set(Launch);
|
||||||
} else {
|
} else {
|
||||||
errorMessage = 'Invalid username or password.';
|
toast.error('Login failed!', {
|
||||||
|
description: 'Please check your username and password.',
|
||||||
|
});
|
||||||
|
isLoading = false;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
errorMessage = 'Login failed. Please check your credentials.';
|
toast.error('Server error occurred during login.', {
|
||||||
} finally {
|
description: 'There was an issue connecting to the server. Please try again later.',
|
||||||
|
});
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div class="mt-[50px] h-[calc(100vh-50px)] w-full">
|
||||||
|
<div class="w-full h-full flex flex-col items-center justify-center">
|
||||||
|
<img
|
||||||
|
src={Logo}
|
||||||
|
alt="EZPPLauncher Logo"
|
||||||
|
class="w-52 h-52 mb-2"
|
||||||
|
bind:this={ezppLogo}
|
||||||
|
onmouseenter={logo_mouseenter}
|
||||||
|
onmouseleave={logo_mouseleave}
|
||||||
|
/>
|
||||||
|
<form onsubmit={performLogin} class="w-full max-w-sm">
|
||||||
|
<div class="mb-4">
|
||||||
|
<Label for="username" class="block text-sm font-medium">Username</Label>
|
||||||
|
<Input
|
||||||
|
class="mt-4 w-full bg-theme-900 border-theme-800"
|
||||||
|
type="text"
|
||||||
|
id="username"
|
||||||
|
bind:value={username}
|
||||||
|
disabled={isLoading}
|
||||||
|
autocomplete="off"
|
||||||
|
autocorrect="off"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<Label for="password" class="block text-sm font-medium">Password</Label>
|
||||||
|
<Input
|
||||||
|
class="mt-4 w-full bg-theme-900 border-theme-800"
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
bind:value={password}
|
||||||
|
disabled={isLoading}
|
||||||
|
autocomplete="off"
|
||||||
|
autocorrect="off"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button class="w-full" type="submit" disabled={isLoading}>
|
||||||
|
{#if isLoading}
|
||||||
|
<LoaderCircle class="animate-spin" />
|
||||||
|
{:else}
|
||||||
|
Login
|
||||||
|
{/if}
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
} from '@/userSettings';
|
} from '@/userSettings';
|
||||||
import { Buffer } from 'buffer';
|
import { Buffer } from 'buffer';
|
||||||
import { Toaster } from '@/components/ui/sonner';
|
import { Toaster } from '@/components/ui/sonner';
|
||||||
|
import { userAuth } from '@/userAuthentication';
|
||||||
let { children } = $props();
|
let { children } = $props();
|
||||||
|
|
||||||
function disableReload() {
|
function disableReload() {
|
||||||
@ -68,6 +69,7 @@
|
|||||||
disableReload();
|
disableReload();
|
||||||
setupValues();
|
setupValues();
|
||||||
const firstStartup = await $userSettings.init();
|
const firstStartup = await $userSettings.init();
|
||||||
|
$userAuth.init();
|
||||||
|
|
||||||
const config_custom_cursor = $userSettings.value('custom_cursor');
|
const config_custom_cursor = $userSettings.value('custom_cursor');
|
||||||
const config_cursor_smoothening = $userSettings.value('cursor_smoothening');
|
const config_cursor_smoothening = $userSettings.value('cursor_smoothening');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user