| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- <template>
- <Section title="Login to OliveTin" class="small">
- <div class="login-form">
- <div v-if="!hasOAuth && !hasLocalLogin" class="login-disabled">
- <span>This server is not configured with either OAuth, or local users, so you cannot login.</span>
- </div>
- <div v-if="hasOAuth" class="login-oauth2">
- <h3>OAuth Login</h3>
- <div class="oauth-providers">
- <button v-for="provider in oauthProviders" :key="provider.key" class="oauth-button"
- @click="loginWithOAuth(provider)">
- <span v-if="provider.icon" class="provider-icon" v-html="provider.icon"></span>
- <span class="provider-name">Login with {{ provider.title }}</span>
- </button>
- </div>
- </div>
- <div v-if="hasLocalLogin" class="login-local">
- <h3>Local Login</h3>
- <form @submit.prevent="handleLocalLogin" class="local-login-form">
- <div v-if="loginError" class="bad">
- {{ loginError }}
- </div>
- <input id="username" v-model="username" type="text" name="username" autocomplete="username" required placeholder="Username" />
- <input id="password" v-model="password" type="password" name="password" autocomplete="current-password" placeholder="Password"
- required />
- <button type="submit" :disabled="loading" class="login-button">
- {{ loading ? 'Logging in...' : 'Login' }}
- </button>
- </form>
- </div>
- </div>
- </Section>
- </template>
- <script setup>
- import { ref, onMounted, watch } from 'vue'
- import { useRouter } from 'vue-router'
- import Section from 'picocrank/vue/components/Section.vue'
- const router = useRouter()
- const username = ref('')
- const password = ref('')
- const loading = ref(false)
- const loginError = ref('')
- const hasOAuth = ref(false)
- const hasLocalLogin = ref(false)
- const oauthProviders = ref([])
- function loadLoginOptions() {
- // Use the init response data that was loaded in App.vue
- if (window.initResponse) {
- hasOAuth.value = window.initResponse.oAuth2Providers && window.initResponse.oAuth2Providers.length > 0
- hasLocalLogin.value = window.initResponse.authLocalLogin
- if (hasOAuth.value) {
- oauthProviders.value = window.initResponse.oAuth2Providers
- }
- } else {
- console.warn('Init response not available yet, login options will be empty')
- }
- }
- async function handleLocalLogin() {
- loading.value = true
- loginError.value = ''
- try {
- const response = await window.client.localUserLogin({
- username: username.value,
- password: password.value
- })
- if (response.success) {
- // Re-initialize to get updated user context
- try {
- const initResponse = await window.client.init({})
- window.initResponse = initResponse
- window.initError = false
- window.initErrorMessage = ''
- window.initCompleted = true
-
- // Update the header with new user info
- if (window.updateHeaderFromInit) {
- window.updateHeaderFromInit()
- }
- } catch (initErr) {
- console.error('Failed to reinitialize after login:', initErr)
- }
-
- // Redirect to home page on successful login
- router.push('/')
- } else {
- loginError.value = 'Login failed. Please check your credentials.'
- }
- } catch (err) {
- console.error('Login error:', err)
- loginError.value = err.message || 'Network error. Please try again.'
- } finally {
- loading.value = false
- }
- }
- function loginWithOAuth(provider) {
- if (!provider.key) {
- console.error('OAuth provider missing key:', provider)
- return
- }
-
- const providerKey = encodeURIComponent(provider.key)
- window.location.href = `/oauth/login?provider=${providerKey}`
- }
- onMounted(() => {
- loadLoginOptions()
-
- // Also watch for when init response becomes available
- const stopWatcher = watch(() => window.initResponse, () => {
- loadLoginOptions()
- }, { immediate: true })
- })
- </script>
- <style scoped>
- section {
- margin: auto;
- }
- .login-view {
- min-height: 100vh;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 1rem;
- }
- form {
- grid-template-columns: 1fr;
- gap: 1em;
- }
- .provider-icon {
- width: 1em;
- height: 1em;
- margin-right: .4em;
- display: inline-flex;
- vertical-align: middle;
- }
- </style>
|