4
0

LoginView.vue 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. <template>
  2. <Section title="Login to OliveTin" class="small">
  3. <div class="login-form">
  4. <div v-if="!hasOAuth && !hasLocalLogin" class="login-disabled">
  5. <span>This server is not configured with either OAuth, or local users, so you cannot login.</span>
  6. </div>
  7. <div v-if="hasOAuth" class="login-oauth2">
  8. <h3>OAuth Login</h3>
  9. <div class="oauth-providers">
  10. <button v-for="provider in oauthProviders" :key="provider.key" class="oauth-button"
  11. @click="loginWithOAuth(provider)">
  12. <span v-if="provider.icon" class="provider-icon" v-html="provider.icon"></span>
  13. <span class="provider-name">Login with {{ provider.title }}</span>
  14. </button>
  15. </div>
  16. </div>
  17. <div v-if="hasLocalLogin" class="login-local">
  18. <h3>Local Login</h3>
  19. <form @submit.prevent="handleLocalLogin" class="local-login-form">
  20. <div v-if="loginError" class="bad">
  21. {{ loginError }}
  22. </div>
  23. <input id="username" v-model="username" type="text" name="username" autocomplete="username" required placeholder="Username" />
  24. <input id="password" v-model="password" type="password" name="password" autocomplete="current-password" placeholder="Password"
  25. required />
  26. <button type="submit" :disabled="loading" class="login-button">
  27. {{ loading ? 'Logging in...' : 'Login' }}
  28. </button>
  29. </form>
  30. </div>
  31. </div>
  32. </Section>
  33. </template>
  34. <script setup>
  35. import { ref, onMounted, watch } from 'vue'
  36. import { useRouter } from 'vue-router'
  37. import Section from 'picocrank/vue/components/Section.vue'
  38. const router = useRouter()
  39. const username = ref('')
  40. const password = ref('')
  41. const loading = ref(false)
  42. const loginError = ref('')
  43. const hasOAuth = ref(false)
  44. const hasLocalLogin = ref(false)
  45. const oauthProviders = ref([])
  46. function loadLoginOptions() {
  47. // Use the init response data that was loaded in App.vue
  48. if (window.initResponse) {
  49. hasOAuth.value = window.initResponse.oAuth2Providers && window.initResponse.oAuth2Providers.length > 0
  50. hasLocalLogin.value = window.initResponse.authLocalLogin
  51. if (hasOAuth.value) {
  52. oauthProviders.value = window.initResponse.oAuth2Providers
  53. }
  54. } else {
  55. console.warn('Init response not available yet, login options will be empty')
  56. }
  57. }
  58. async function handleLocalLogin() {
  59. loading.value = true
  60. loginError.value = ''
  61. try {
  62. const response = await window.client.localUserLogin({
  63. username: username.value,
  64. password: password.value
  65. })
  66. if (response.success) {
  67. // Re-initialize to get updated user context
  68. try {
  69. const initResponse = await window.client.init({})
  70. window.initResponse = initResponse
  71. window.initError = false
  72. window.initErrorMessage = ''
  73. window.initCompleted = true
  74. // Update the header with new user info
  75. if (window.updateHeaderFromInit) {
  76. window.updateHeaderFromInit()
  77. }
  78. } catch (initErr) {
  79. console.error('Failed to reinitialize after login:', initErr)
  80. }
  81. // Redirect to home page on successful login
  82. router.push('/')
  83. } else {
  84. loginError.value = 'Login failed. Please check your credentials.'
  85. }
  86. } catch (err) {
  87. console.error('Login error:', err)
  88. loginError.value = err.message || 'Network error. Please try again.'
  89. } finally {
  90. loading.value = false
  91. }
  92. }
  93. function loginWithOAuth(provider) {
  94. if (!provider.key) {
  95. console.error('OAuth provider missing key:', provider)
  96. return
  97. }
  98. const providerKey = encodeURIComponent(provider.key)
  99. window.location.href = `/oauth/login?provider=${providerKey}`
  100. }
  101. onMounted(() => {
  102. loadLoginOptions()
  103. // Also watch for when init response becomes available
  104. const stopWatcher = watch(() => window.initResponse, () => {
  105. loadLoginOptions()
  106. }, { immediate: true })
  107. })
  108. </script>
  109. <style scoped>
  110. section {
  111. margin: auto;
  112. }
  113. .login-view {
  114. min-height: 100vh;
  115. display: flex;
  116. align-items: center;
  117. justify-content: center;
  118. padding: 1rem;
  119. }
  120. form {
  121. grid-template-columns: 1fr;
  122. gap: 1em;
  123. }
  124. .provider-icon {
  125. width: 1em;
  126. height: 1em;
  127. margin-right: .4em;
  128. display: inline-flex;
  129. vertical-align: middle;
  130. }
  131. </style>