Files
Auto-ssl/frontend/src/views/Login.vue
T
cnbugs 2d70a15307 feat: add login authentication and certificate expiry notifications
- JWT-based login with ADMIN_USER/ADMIN_PASSWORD env config
- Login page with auth guard for Vue frontend
- Feishu bot webhook notification for expiring certificates
- SMTP email notification for expiring certificates
- Daily 8:00 AM cron for expiry checks
- Startup health check notification
2026-07-23 16:25:02 +08:00

146 lines
3.3 KiB
Vue

<template>
<div class="login-container">
<div class="login-card">
<div class="login-header">
<el-icon :size="32" color="#1890ff"><Link /></el-icon>
<h2>AutoSSL 证书管理</h2>
<p class="login-desc">请登录以继续</p>
</div>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="0"
@keyup.enter="handleLogin"
>
<el-form-item prop="username">
<el-input
v-model="form.username"
placeholder="用户名"
size="large"
:prefix-icon="User"
/>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="form.password"
type="password"
placeholder="密码"
size="large"
show-password
:prefix-icon="Lock"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
size="large"
style="width: 100%"
:loading="loading"
@click="handleLogin"
>
</el-button>
</el-form-item>
</el-form>
<div class="login-footer">
<p v-if="errorMsg" class="error-msg">{{ errorMsg }}</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
import { User, Lock } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { authApi } from '../api'
const router = useRouter()
const formRef = ref()
const loading = ref(false)
const errorMsg = ref('')
const form = reactive({
username: '',
password: '',
})
const rules = {
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
}
const handleLogin = async () => {
const valid = await formRef.value.validate().catch(() => false)
if (!valid) return
loading.value = true
errorMsg.value = ''
try {
const res = await authApi.login(form.username, form.password)
const { token, username, expires_at } = res.data
localStorage.setItem('token', token)
localStorage.setItem('username', username)
localStorage.setItem('token_expires_at', String(expires_at))
ElMessage.success(`欢迎回来, ${username}!`)
router.push('/')
} catch (e: any) {
errorMsg.value = e.response?.data?.error || '登录失败,请检查用户名和密码'
} finally {
loading.value = false
}
}
</script>
<style scoped>
.login-container {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background-size: cover;
}
.login-card {
width: 400px;
background: white;
border-radius: 12px;
padding: 40px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}
.login-header {
text-align: center;
margin-bottom: 32px;
}
.login-header h2 {
margin-top: 12px;
font-size: 24px;
color: #1a1a2e;
}
.login-desc {
margin-top: 8px;
color: #999;
font-size: 14px;
}
.login-footer {
text-align: center;
margin-top: 8px;
}
.error-msg {
color: #f56c6c;
font-size: 13px;
}
:deep(.el-input__prefix) {
font-size: 18px;
}
</style>