149 lines
4.6 KiB
Vue
149 lines
4.6 KiB
Vue
<template>
|
|
<div class="login-page">
|
|
<div class="auth-container">
|
|
<div class="login-card">
|
|
<div class="brand-block">
|
|
<div class="brand-mark">
|
|
<i class="mdi mdi-note-text-outline" aria-hidden="true"></i>
|
|
</div>
|
|
<h1 class="brand-title">Notely</h1>
|
|
</div>
|
|
|
|
<h2 class="auth-title">Login</h2>
|
|
|
|
<form @submit.prevent="handleLogin">
|
|
<div class="mb-3">
|
|
<label for="email" class="form-label">Email</label>
|
|
<input id="email" v-model="form.email" type="email" class="form-control" required />
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="password" class="form-label">Password</label>
|
|
<input id="password" v-model="form.password" type="password" class="form-control" required />
|
|
</div>
|
|
|
|
<div v-if="error" class="alert alert-danger">{{ error }}</div>
|
|
|
|
<button type="submit" class="btn btn-primary w-100 auth-submit">Login</button>
|
|
</form>
|
|
|
|
<div v-if="providerLoginEnabled && providers.length" class="mt-4">
|
|
<div class="oauth-divider"><span>or continue with</span></div>
|
|
<div class="d-grid gap-2 mt-3">
|
|
<button v-for="provider in providers" :key="provider.id" type="button" class="btn btn-outline-dark auth-provider-btn" @click="startProviderLogin(provider.id)">
|
|
Sign in with {{ provider.name }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<p v-if="registrationEnabled" class="text-center mt-4 mb-0 auth-switch-link">
|
|
Don't have an account?
|
|
<router-link to="/register">Register here</router-link>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { onMounted, ref } from "vue";
|
|
import { useRouter } from "vue-router";
|
|
import { useAuthStore } from "../stores/authStore";
|
|
import { useSettingsStore } from "../stores/settingsStore";
|
|
import apiClient from "../services/apiClient";
|
|
|
|
const router = useRouter();
|
|
const authStore = useAuthStore();
|
|
const settingsStore = useSettingsStore();
|
|
|
|
const form = ref({
|
|
email: "",
|
|
password: "",
|
|
});
|
|
const error = ref("");
|
|
const providers = ref([]);
|
|
const registrationEnabled = ref(true);
|
|
const providerLoginEnabled = ref(true);
|
|
|
|
const handleLogin = async () => {
|
|
error.value = "";
|
|
try {
|
|
await authStore.login(form.value.email, form.value.password);
|
|
router.push("/");
|
|
} catch (err) {
|
|
error.value = err;
|
|
}
|
|
};
|
|
|
|
const loadProviders = async () => {
|
|
try {
|
|
const response = await apiClient.get("/api/v1/auth/providers");
|
|
providers.value = response.data.providers || [];
|
|
} catch {
|
|
providers.value = [];
|
|
}
|
|
};
|
|
|
|
const startProviderLogin = (providerId) => {
|
|
window.location.href = `${apiClient.defaults.baseURL}/api/v1/auth/providers/${providerId}/start`;
|
|
};
|
|
|
|
const completeOAuthRedirect = async () => {
|
|
const params = new URLSearchParams(window.location.search);
|
|
const status = params.get("status");
|
|
|
|
if (status === "oauth_error") {
|
|
error.value = params.get("message") || "Provider sign-in failed.";
|
|
return true;
|
|
}
|
|
|
|
if (status !== "oauth_success") {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
await authStore.ensureInitialized();
|
|
} catch {
|
|
error.value = "Unable to restore provider session.";
|
|
return true;
|
|
}
|
|
|
|
if (authStore.isAuthenticated) {
|
|
await router.replace("/");
|
|
return true;
|
|
}
|
|
|
|
error.value = "Provider sign-in returned an incomplete session.";
|
|
|
|
return true;
|
|
};
|
|
|
|
onMounted(async () => {
|
|
const flags = await settingsStore.loadFeatureFlags();
|
|
registrationEnabled.value = !!flags.registration_enabled;
|
|
providerLoginEnabled.value = !!flags.provider_login_enabled;
|
|
|
|
await authStore.ensureInitialized();
|
|
|
|
if (authStore.isAuthenticated) {
|
|
await router.replace("/");
|
|
return;
|
|
}
|
|
|
|
const handledOAuthCallback = await completeOAuthRedirect();
|
|
if (!handledOAuthCallback && providerLoginEnabled.value) {
|
|
await loadProviders();
|
|
}
|
|
|
|
const queryMessage = typeof router.currentRoute.value.query.message === "string" ? router.currentRoute.value.query.message : "";
|
|
if (!error.value && queryMessage) {
|
|
error.value = queryMessage;
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style scoped src="../assets/styles/scoped/pages/Login.css"></style>
|
|
|
|
|
|
|