feat: improve OAuth2 support
This commit is contained in:
@@ -28,10 +28,6 @@ jobs:
|
||||
AI_MODEL: ${{ secrets.AI_MODEL || vars.AI_MODEL || '@cf/meta/llama-3.1-8b-instruct-fast' }}
|
||||
CLOUDFLARE_EMAIL: ${{ secrets.CF_EMAIL || vars.CF_EMAIL || false }}
|
||||
ANALYSIS_CACHE: ${{ secrets.ANALYSIS_CACHE || vars.ANALYSIS_CACHE || false }}
|
||||
LINUXDO_SWITCH: ${{ secrets.LINUXDO_SWITCH || vars.LINUXDO_SWITCH }}
|
||||
GITHUB_SWITCH: ${{ secrets.GITHUB_SWITCH || vars.GITHUB_SWITCH }}
|
||||
GITLAB_SWITCH: ${{ secrets.GITLAB_SWITCH || vars.GITLAB_SWITCH }}
|
||||
GOOGLE_SWITCH: ${{ secrets.GOOGLE_SWITCH || vars.GOOGLE_SWITCH }}
|
||||
|
||||
|
||||
outputs:
|
||||
@@ -107,10 +103,6 @@ jobs:
|
||||
sed -i '/^project_link = /d' "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
if [ -z "$LINUXDO_CLIENT_ID" ] || [ -z "$LINUXDO_CLIENT_SECRET" ]; then
|
||||
sed -i '/^linuxdo_client_id = /,/^linuxdo_switch = /d' "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
if [ -z "$CUSTOM_DOMAIN" ]; then
|
||||
sed -i '/\[\[routes\]\]/,/^$/d' "$CONFIG_FILE"
|
||||
fi
|
||||
@@ -135,7 +127,6 @@ jobs:
|
||||
sed -i "s|\${LINUXDO_CLIENT_ID}|${LINUXDO_CLIENT_ID}|g" "$CONFIG_FILE"
|
||||
sed -i "s|\${LINUXDO_CLIENT_SECRET}|${LINUXDO_CLIENT_SECRET}|g" "$CONFIG_FILE"
|
||||
sed -i "s|\${LINUXDO_CALLBACK_URL}|${LINUXDO_CALLBACK_URL}|g" "$CONFIG_FILE"
|
||||
sed -i "s|\${LINUXDO_SWITCH}|${LINUXDO_SWITCH}|g" "$CONFIG_FILE"
|
||||
|
||||
echo "✅ Environment setup completed."
|
||||
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ With only one domain, you can create multiple different email addresses, similar
|
||||
|
||||
- **🤖 CAPTCHA**: Integrated with Turnstile CAPTCHA to prevent automated registration.
|
||||
|
||||
- **🔑 OAuth2 Login**: Support LinuxDo / GitHub / GitLab / Google third-party login.
|
||||
- **🔑 OAuth2 Login**: Support LinuxDo / GitHub / Google third-party login.
|
||||
|
||||
- **📜 More Features**: Under development...
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
|
||||
- **🤖 人机验证**:集成Turnstile人机验证,防止人机批量注册
|
||||
|
||||
- **🔑 OAuth2 登录**:支持 LinuxDo / GitHub / GitLab / Google 第三方登录
|
||||
- **🔑 OAuth2 登录**:支持 LinuxDo / GitHub / Google 第三方登录
|
||||
|
||||
- **📜 更多功能**:正在开发中...
|
||||
|
||||
|
||||
@@ -18,10 +18,6 @@
|
||||
| `ADMIN` | ✅ | 您的管理员邮箱地址(例如 `[email protected]`) |
|
||||
| `JWT_SECRET` | ✅ | 用于生成和验证 JWT 的随机长字符串 |
|
||||
| `PROJECT_LINK` | ❌ | (可选)是否在登录页显示 GitHub 项目链接(`true` / `false`) | |
|
||||
| `LINUXDO_SWITCH` | ❌ | LinuxDo OAuth 启用开关(`true` / `false`) | |
|
||||
| `GITHUB_SWITCH` | ❌ | GitHub OAuth 启用开关(`true` / `false`) | |
|
||||
| `GITLAB_SWITCH` | ❌ | GitLab OAuth 启用开关(`true` / `false`) | |
|
||||
| `GOOGLE_SWITCH` | ❌ | Google OAuth 启用开关(`true` / `false`) |
|
||||
| `INIT_URL` | ❌ | (可选)部署后用于初始化数据库的 Worker URL(格式参考下述手动初始化) |
|
||||
|
||||
---
|
||||
|
||||
@@ -343,7 +343,6 @@ const en = {
|
||||
oauthSetting: 'OAuth',
|
||||
clientId: 'Client ID',
|
||||
clientSecret: 'Client Secret',
|
||||
callbackBase: 'https://domain',
|
||||
notOwner: 'Base email does not belong to you',
|
||||
}
|
||||
|
||||
|
||||
@@ -343,7 +343,6 @@ const zh = {
|
||||
oauthSetting: 'OAuth',
|
||||
clientId: '客户端 ID',
|
||||
clientSecret: '客户端密钥',
|
||||
callbackBase: 'https://domain',
|
||||
notOwner: '基础邮箱不属于您',
|
||||
}
|
||||
export default zh
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
import http from '@/axios/index.js';
|
||||
|
||||
export function oauthLinuxDoLogin(code) {
|
||||
return http.post('/oauth/linuxDo/login',{code})
|
||||
export function oauthLinuxDoLogin(code, redirectUri) {
|
||||
return http.post('/oauth/linuxDo/login',{code, redirectUri})
|
||||
}
|
||||
|
||||
export function oauthGithubLogin(code) {
|
||||
return http.post('/oauth/github/login',{code})
|
||||
export function oauthGithubLogin(code, redirectUri) {
|
||||
return http.post('/oauth/github/login',{code, redirectUri})
|
||||
}
|
||||
|
||||
export function oauthGitlabLogin(code) {
|
||||
return http.post('/oauth/gitlab/login',{code})
|
||||
}
|
||||
|
||||
export function oauthGoogleLogin(code) {
|
||||
return http.post('/oauth/google/login',{code})
|
||||
export function oauthGoogleLogin(code, redirectUri) {
|
||||
return http.post('/oauth/google/login',{code, redirectUri})
|
||||
}
|
||||
|
||||
export function oauthBindUser(form) {
|
||||
|
||||
@@ -59,26 +59,6 @@ const routes = [
|
||||
name: 'login',
|
||||
component: () => import('@/views/login/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/login/github',
|
||||
name: 'loginGithub',
|
||||
component: () => import('@/views/login/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/login/gitlab',
|
||||
name: 'loginGitlab',
|
||||
component: () => import('@/views/login/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/login/google',
|
||||
name: 'loginGoogle',
|
||||
component: () => import('@/views/login/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/login/linuxdo',
|
||||
name: 'loginLinuxdo',
|
||||
component: () => import('@/views/login/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/test',
|
||||
name: 'test',
|
||||
|
||||
@@ -167,7 +167,7 @@ import {cvtR2Url} from "@/utils/convert.js";
|
||||
import {loginUserInfo} from "@/request/my.js";
|
||||
import {permsToRouter} from "@/perm/perm.js";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {oauthBindUser, oauthLinuxDoLogin, oauthGithubLogin, oauthGitlabLogin, oauthGoogleLogin} from "@/request/ouath.js";
|
||||
import {oauthBindUser, oauthLinuxDoLogin, oauthGithubLogin, oauthGoogleLogin} from "@/request/ouath.js";
|
||||
|
||||
const {t} = useI18n();
|
||||
const accountStore = useAccountStore();
|
||||
@@ -181,19 +181,22 @@ const oauthLoading = ref(false);
|
||||
const showBindForm = ref(false);
|
||||
const show = ref('login')
|
||||
|
||||
const oauthKeys = ['linuxdo', 'github', 'google']
|
||||
|
||||
const oauthProvider = computed(() => {
|
||||
const match = route.path.match(/^\/login\/(.+)/)
|
||||
return match ? match[1] : null
|
||||
const fromState = route.query.state
|
||||
if (oauthKeys.includes(fromState)) return fromState
|
||||
const fromStore = sessionStorage.getItem('oauthProvider')
|
||||
return oauthKeys.includes(fromStore) ? fromStore : null
|
||||
})
|
||||
|
||||
const oauthProviders = computed(() => {
|
||||
const allProviders = [
|
||||
{ key: 'linuxdo', label: 'LinuxDo', icon: '/image/linuxdo.webp', iconType: 'image' },
|
||||
{ key: 'github', label: 'GitHub', icon: 'mingcute:github-fill', iconType: 'iconify' },
|
||||
{ key: 'gitlab', label: 'GitLab', icon: 'mingcute:gitlab-fill', iconType: 'iconify' },
|
||||
{ key: 'google', label: 'Google', icon: 'mingcute:google-fill', iconType: 'iconify' },
|
||||
]
|
||||
return allProviders.filter(p => settingStore.settings[p.key + 'Switch'])
|
||||
return allProviders.filter(p => settingStore.settings[p.key + 'Switch'] === 0)
|
||||
})
|
||||
|
||||
const bindForm = reactive({
|
||||
@@ -284,12 +287,12 @@ const getEmailName = (email) => {
|
||||
|
||||
function oauthLogin(provider) {
|
||||
const clientId = settingStore.settings[provider + 'ClientId']
|
||||
const redirectUri = encodeURIComponent(window.location.origin + '/login/' + provider)
|
||||
const redirectUri = encodeURIComponent(window.location.origin + '/login')
|
||||
sessionStorage.setItem('oauthProvider', provider)
|
||||
const authorizeUrls = {
|
||||
linuxdo: `https://connect.linux.do/oauth2/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=openid+profile+email`,
|
||||
github: `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=user:email`,
|
||||
gitlab: `https://gitlab.com/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=read_user`,
|
||||
google: `https://accounts.google.com/o/oauth2/v2/auth?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=openid+profile+email`,
|
||||
linuxdo: `https://connect.linux.do/oauth2/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=openid+profile+email&state=${provider}`,
|
||||
github: `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=user:email&state=${provider}`,
|
||||
google: `https://accounts.google.com/o/oauth2/v2/auth?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=openid+profile+email&state=${provider}`,
|
||||
}
|
||||
window.location.href = authorizeUrls[provider]
|
||||
}
|
||||
@@ -297,7 +300,6 @@ function oauthLogin(provider) {
|
||||
const loginFns = {
|
||||
linuxdo: oauthLinuxDoLogin,
|
||||
github: oauthGithubLogin,
|
||||
gitlab: oauthGitlabLogin,
|
||||
google: oauthGoogleLogin,
|
||||
}
|
||||
|
||||
@@ -305,15 +307,16 @@ oauthGetUser();
|
||||
|
||||
async function oauthGetUser() {
|
||||
|
||||
if (!oauthProvider.value) return
|
||||
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
const code = params.get('code')
|
||||
if (!code || !oauthProvider.value) return
|
||||
|
||||
if (code) {
|
||||
|
||||
const provider = oauthProvider.value
|
||||
oauthLoading.value = true
|
||||
loginFns[oauthProvider.value](code).then(data => {
|
||||
sessionStorage.removeItem('oauthProvider')
|
||||
window.history.replaceState({}, '', window.location.origin + window.location.pathname)
|
||||
|
||||
loginFns[provider](code, window.location.origin + '/login').then(data => {
|
||||
|
||||
bindForm.oauthUserId = data.userInfo.oauthUserId;
|
||||
|
||||
@@ -333,10 +336,6 @@ async function oauthGetUser() {
|
||||
}).catch(() => {
|
||||
oauthLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const cleanUrl = window.location.origin + window.location.pathname
|
||||
window.history.replaceState({}, '', cleanUrl)
|
||||
}
|
||||
|
||||
function bind() {
|
||||
|
||||
@@ -353,22 +353,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OAuth Login Card -->
|
||||
<div class="settings-card">
|
||||
<div class="card-title">{{ $t('oauthLogin') }}</div>
|
||||
<div class="card-content">
|
||||
<div class="setting-item" v-for="p in oauthPlatforms" :key="p.key">
|
||||
<div><span>{{ p.label }}</span></div>
|
||||
<div class="forward">
|
||||
<span>{{ setting[p.key + 'Switch'] ? $t('enabled') : $t('disabled') }}</span>
|
||||
<el-button class="opt-button" size="small" type="primary" :disabled="!setting[p.key + 'Switch']" @click="openOauthSetting(p)">
|
||||
<Icon icon="fluent:settings-48-regular" width="18" height="18"/>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-card">
|
||||
<div class="card-title">{{ $t('noticeTitle') }}</div>
|
||||
<div class="card-content">
|
||||
@@ -413,6 +397,22 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OAuth Login Card -->
|
||||
<div class="settings-card">
|
||||
<div class="card-title">{{ $t('oauthLogin') }}</div>
|
||||
<div class="card-content">
|
||||
<div class="setting-item" v-for="p in oauthPlatforms" :key="p.key">
|
||||
<div><span>{{ p.label }}</span></div>
|
||||
<div class="forward">
|
||||
<span>{{ setting[p.key + 'Switch'] === 0 ? $t('enabled') : $t('disabled') }}</span>
|
||||
<el-button class="opt-button" size="small" type="primary" @click="openOauthSetting(p)">
|
||||
<Icon icon="fluent:settings-48-regular" width="18" height="18"/>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-card about">
|
||||
<div class="card-title">{{ $t('about') }}</div>
|
||||
<div class="card-content">
|
||||
@@ -504,17 +504,18 @@
|
||||
</form>
|
||||
</el-dialog>
|
||||
<el-dialog v-model="oauthSettingShow" :title="$t('oauthSetting') + ' - ' + oauthForm.label" width="340"
|
||||
@closed="oauthForm.clientId = ''; oauthForm.clientSecret = ''; oauthForm.callbackBase = ''">
|
||||
@closed="oauthForm.clientId = ''; oauthForm.clientSecret = ''; oauthForm.switch = 1">
|
||||
<div class="dialog-content">
|
||||
<el-input type="text" :placeholder="$t('clientId')" v-model="oauthForm.clientId"/>
|
||||
<el-input type="text" style="margin-top: 15px" :placeholder="$t('clientSecret')" v-model="oauthForm.clientSecret"/>
|
||||
<el-input type="text" style="margin-top: 15px" :placeholder="$t('callbackBase')" v-model="oauthForm.callbackBase">
|
||||
<template #append>/login/{{ oauthForm.key }}</template>
|
||||
</el-input>
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 15px;">
|
||||
<el-button type="primary" @click="saveOauth">{{ $t('save') }}</el-button>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-switch v-model="oauthForm.switch" :active-value="0" :inactive-value="1" :active-text="$t('enable')"
|
||||
:inactive-text="$t('disable')"/>
|
||||
<el-button type="primary" :loading="settingLoading" @click="saveOauth">{{ $t('save') }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
v-model="showSetBackground"
|
||||
@@ -919,7 +920,6 @@ const turnstileForm = reactive({
|
||||
const oauthPlatforms = [
|
||||
{ key: 'linuxdo', label: 'LinuxDo' },
|
||||
{ key: 'github', label: 'GitHub' },
|
||||
{ key: 'gitlab', label: 'GitLab' },
|
||||
{ key: 'google', label: 'Google' },
|
||||
]
|
||||
const oauthSettingShow = ref(false)
|
||||
@@ -928,7 +928,7 @@ const oauthForm = reactive({
|
||||
label: '',
|
||||
clientId: '',
|
||||
clientSecret: '',
|
||||
callbackBase: '',
|
||||
switch: 1,
|
||||
})
|
||||
|
||||
const s3 = reactive({
|
||||
@@ -1393,9 +1393,7 @@ function openOauthSetting(p) {
|
||||
oauthForm.label = p.label
|
||||
oauthForm.clientId = setting.value[p.key + 'ClientId'] || ''
|
||||
oauthForm.clientSecret = setting.value[p.key + 'ClientSecret'] || ''
|
||||
const suffix = '/login/' + p.key
|
||||
const fullUrl = setting.value[p.key + 'CallbackUrl'] || ''
|
||||
oauthForm.callbackBase = fullUrl.endsWith(suffix) ? fullUrl.slice(0, -suffix.length) : fullUrl
|
||||
oauthForm.switch = setting.value[p.key + 'Switch'] ?? 1
|
||||
oauthSettingShow.value = true
|
||||
}
|
||||
|
||||
@@ -1403,9 +1401,8 @@ function saveOauth() {
|
||||
const form = {}
|
||||
form[oauthForm.key + 'ClientId'] = oauthForm.clientId
|
||||
form[oauthForm.key + 'ClientSecret'] = oauthForm.clientSecret
|
||||
form[oauthForm.key + 'CallbackUrl'] = oauthForm.callbackBase + '/login/' + oauthForm.key
|
||||
form[oauthForm.key + 'Switch'] = oauthForm.switch
|
||||
editSetting(form)
|
||||
oauthSettingShow.value = false
|
||||
}
|
||||
|
||||
function saveTurnstileKey() {
|
||||
@@ -1571,6 +1568,7 @@ function editSetting(settingForm, refreshStatus = true) {
|
||||
addS3Show.value = false
|
||||
emailPrefixShow.value = false
|
||||
aiCodeFilterShow.value = false
|
||||
oauthSettingShow.value = false
|
||||
}).catch((e) => {
|
||||
loginOpacity.value = setting.value.loginOpacity
|
||||
setting.value = {...setting.value, ...JSON.parse(backup)}
|
||||
|
||||
@@ -12,11 +12,6 @@ app.post('/oauth/github/login', async (c) => {
|
||||
return c.json(result.ok(loginInfo))
|
||||
});
|
||||
|
||||
app.post('/oauth/gitlab/login', async (c) => {
|
||||
const loginInfo = await oauthService.gitlabLogin(c, await c.req.json());
|
||||
return c.json(result.ok(loginInfo))
|
||||
});
|
||||
|
||||
app.post('/oauth/google/login', async (c) => {
|
||||
const loginInfo = await oauthService.googleLogin(c, await c.req.json());
|
||||
return c.json(result.ok(loginInfo))
|
||||
|
||||
@@ -55,15 +55,12 @@ export const setting = sqliteTable('setting', {
|
||||
aiCodeFilter: text('ai_code_filter').default('').notNull(),
|
||||
linuxdoClientId: text('linuxdo_client_id').default('').notNull(),
|
||||
linuxdoClientSecret: text('linuxdo_client_secret').default('').notNull(),
|
||||
linuxdoCallbackUrl: text('linuxdo_callback_url').default('').notNull(),
|
||||
linuxdoSwitch: integer('linuxdo_switch').default(1).notNull(),
|
||||
githubClientId: text('github_client_id').default('').notNull(),
|
||||
githubClientSecret: text('github_client_secret').default('').notNull(),
|
||||
githubCallbackUrl: text('github_callback_url').default('').notNull(),
|
||||
gitlabClientId: text('gitlab_client_id').default('').notNull(),
|
||||
gitlabClientSecret: text('gitlab_client_secret').default('').notNull(),
|
||||
gitlabCallbackUrl: text('gitlab_callback_url').default('').notNull(),
|
||||
githubSwitch: integer('github_switch').default(1).notNull(),
|
||||
googleClientId: text('google_client_id').default('').notNull(),
|
||||
googleClientSecret: text('google_client_secret').default('').notNull(),
|
||||
googleCallbackUrl: text('google_callback_url').default('').notNull()
|
||||
googleSwitch: integer('google_switch').default(1).notNull()
|
||||
});
|
||||
export default setting
|
||||
|
||||
@@ -15,6 +15,7 @@ const en = {
|
||||
noOsDomainSendAtt: 'Cannot send attachments: object storage domain not configured',
|
||||
noOsSendAtt: 'Cannot send attachments: object storage not configured',
|
||||
disabledSend: 'Email sending feature is disabled',
|
||||
oauthDisabled: 'This OAuth login is not enabled',
|
||||
daySendLimit: 'Daily send limit reached',
|
||||
totalSendLimit: 'Total send limit reached',
|
||||
daySendLack: 'Not enough remaining sends today',
|
||||
|
||||
@@ -15,6 +15,7 @@ const zh = {
|
||||
noOsDomainSendAtt: '对象存储域名未配置不能发送附件',
|
||||
noOsSendAtt: '对象存储未配置不能发送附件',
|
||||
disabledSend: '邮件发送功能已停用',
|
||||
oauthDisabled: '该 OAuth 登录未启用',
|
||||
daySendLimit: '发送次数已到达每日限制',
|
||||
totalSendLimit: '发送次数已到达限制',
|
||||
daySendLack: '当日剩余发送次数不足',
|
||||
|
||||
@@ -31,16 +31,29 @@ const dbInit = {
|
||||
await this.v3_0DB(c);
|
||||
await this.v3_1DB(c);
|
||||
await this.v3_2DB(c);
|
||||
await this.v3_3DB(c);
|
||||
await settingService.refresh(c);
|
||||
return c.text('success');
|
||||
},
|
||||
|
||||
async v3_3DB(c) {
|
||||
const oauthSwitchFields = [
|
||||
'linuxdo_switch', 'github_switch', 'google_switch',
|
||||
];
|
||||
for (const field of oauthSwitchFields) {
|
||||
try {
|
||||
await c.env.db.prepare(`ALTER TABLE setting ADD COLUMN ${field} INTEGER NOT NULL DEFAULT 1;`).run();
|
||||
} catch (e) {
|
||||
console.warn(`跳过字段:${e.message}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async v3_2DB(c) {
|
||||
const oauthFields = [
|
||||
'linuxdo_client_id', 'linuxdo_client_secret', 'linuxdo_callback_url',
|
||||
'github_client_id', 'github_client_secret', 'github_callback_url',
|
||||
'gitlab_client_id', 'gitlab_client_secret', 'gitlab_callback_url',
|
||||
'google_client_id', 'google_client_secret', 'google_callback_url',
|
||||
'linuxdo_client_id', 'linuxdo_client_secret',
|
||||
'github_client_id', 'github_client_secret',
|
||||
'google_client_id', 'google_client_secret',
|
||||
];
|
||||
for (const field of oauthFields) {
|
||||
try {
|
||||
|
||||
@@ -6,6 +6,7 @@ import userService from "./user-service";
|
||||
import loginService from "./login-service";
|
||||
import cryptoUtils from "../utils/crypto-utils";
|
||||
import settingService from "./setting-service";
|
||||
import {t} from '../i18n/i18n';
|
||||
|
||||
const oauthService = {
|
||||
|
||||
@@ -33,15 +34,16 @@ const oauthService = {
|
||||
|
||||
async linuxDoLogin(c, params) {
|
||||
|
||||
const { code } = params;
|
||||
const { code, redirectUri } = params;
|
||||
|
||||
const setting = await settingService.query(c);
|
||||
this.assertEnabled(setting, 'linuxdoSwitch');
|
||||
|
||||
const reqParams = new URLSearchParams()
|
||||
reqParams.append('client_id', setting.linuxdoClientId)
|
||||
reqParams.append('client_secret', setting.linuxdoClientSecret)
|
||||
reqParams.append('code', code)
|
||||
reqParams.append('redirect_uri', setting.linuxdoCallbackUrl)
|
||||
reqParams.append('redirect_uri', redirectUri)
|
||||
reqParams.append('grant_type', 'authorization_code')
|
||||
|
||||
const tokenRes = await fetch("https://connect.linux.do/oauth2/token", {
|
||||
@@ -80,9 +82,10 @@ const oauthService = {
|
||||
|
||||
async githubLogin(c, params) {
|
||||
|
||||
const { code } = params;
|
||||
const { code, redirectUri } = params;
|
||||
|
||||
const setting = await settingService.query(c);
|
||||
this.assertEnabled(setting, 'githubSwitch');
|
||||
|
||||
const tokenRes = await fetch("https://github.com/login/oauth/access_token", {
|
||||
method: "POST",
|
||||
@@ -94,7 +97,7 @@ const oauthService = {
|
||||
client_id: setting.githubClientId,
|
||||
client_secret: setting.githubClientSecret,
|
||||
code: code,
|
||||
redirect_uri: setting.githubCallbackUrl
|
||||
redirect_uri: redirectUri
|
||||
})
|
||||
});
|
||||
|
||||
@@ -129,63 +132,18 @@ const oauthService = {
|
||||
return await this.saveAndLogin(c, userInfo);
|
||||
},
|
||||
|
||||
async gitlabLogin(c, params) {
|
||||
|
||||
const { code } = params;
|
||||
|
||||
const setting = await settingService.query(c);
|
||||
|
||||
const reqParams = new URLSearchParams()
|
||||
reqParams.append('client_id', setting.gitlabClientId)
|
||||
reqParams.append('client_secret', setting.gitlabClientSecret)
|
||||
reqParams.append('code', code)
|
||||
reqParams.append('redirect_uri', setting.gitlabCallbackUrl)
|
||||
reqParams.append('grant_type', 'authorization_code')
|
||||
|
||||
const tokenRes = await fetch("https://gitlab.com/oauth/token", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
body: reqParams.toString()
|
||||
});
|
||||
|
||||
if (!tokenRes.ok) {
|
||||
throw new BizError(tokenRes.statusText);
|
||||
}
|
||||
|
||||
const token = await tokenRes.json();
|
||||
|
||||
const userRes = await fetch('https://gitlab.com/api/v4/user', {
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + token.access_token
|
||||
}
|
||||
});
|
||||
|
||||
if (!userRes.ok) {
|
||||
throw new BizError(userRes.statusText);
|
||||
}
|
||||
|
||||
const userInfo = await userRes.json();
|
||||
|
||||
userInfo.oauthUserId = String(userInfo.id);
|
||||
userInfo.username = userInfo.username;
|
||||
userInfo.name = userInfo.name;
|
||||
userInfo.avatar = userInfo.avatar_url;
|
||||
userInfo.platform = 'gitlab';
|
||||
|
||||
return await this.saveAndLogin(c, userInfo);
|
||||
},
|
||||
|
||||
async googleLogin(c, params) {
|
||||
|
||||
const { code } = params;
|
||||
const { code, redirectUri } = params;
|
||||
|
||||
const setting = await settingService.query(c);
|
||||
this.assertEnabled(setting, 'googleSwitch');
|
||||
|
||||
const reqParams = new URLSearchParams()
|
||||
reqParams.append('client_id', setting.googleClientId)
|
||||
reqParams.append('client_secret', setting.googleClientSecret)
|
||||
reqParams.append('code', code)
|
||||
reqParams.append('redirect_uri', setting.googleCallbackUrl)
|
||||
reqParams.append('redirect_uri', redirectUri)
|
||||
reqParams.append('grant_type', 'authorization_code')
|
||||
|
||||
const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
|
||||
@@ -246,6 +204,12 @@ const oauthService = {
|
||||
|
||||
},
|
||||
|
||||
assertEnabled(setting, switchKey) {
|
||||
if (setting[switchKey] !== 0) {
|
||||
throw new BizError(t('oauthDisabled'));
|
||||
}
|
||||
},
|
||||
|
||||
async getById(c, oauthUserId) {
|
||||
return await orm(c).select().from(oauth).where(eq(oauth.oauthUserId, oauthUserId)).get();
|
||||
},
|
||||
|
||||
@@ -59,11 +59,6 @@ const settingService = {
|
||||
|
||||
setting.projectLink = projectLink;
|
||||
|
||||
setting.linuxdoSwitch = this.parseBoolean(c.env.linuxdo_switch);
|
||||
setting.githubSwitch = this.parseBoolean(c.env.github_switch);
|
||||
setting.gitlabSwitch = this.parseBoolean(c.env.gitlab_switch);
|
||||
setting.googleSwitch = this.parseBoolean(c.env.google_switch);
|
||||
|
||||
setting.emailPrefixFilter = setting.emailPrefixFilter.split(",").filter(Boolean);
|
||||
|
||||
c.set?.('setting', setting);
|
||||
@@ -131,15 +126,6 @@ const settingService = {
|
||||
|
||||
params.resendTokens = JSON.stringify(resendTokens);
|
||||
|
||||
const callbackPlatforms = ['linuxdo', 'github', 'gitlab', 'google'];
|
||||
for (const platform of callbackPlatforms) {
|
||||
const key = platform + 'CallbackUrl';
|
||||
const expectedSuffix = '/login/' + platform;
|
||||
if (params[key] && !params[key].endsWith(expectedSuffix)) {
|
||||
throw new BizError(`Invalid callback URL for ${platform}: must end with ${expectedSuffix}`);
|
||||
}
|
||||
}
|
||||
|
||||
await orm(c).update(setting).set({ ...params }).returning().get();
|
||||
await this.refresh(c);
|
||||
},
|
||||
@@ -229,28 +215,16 @@ const settingService = {
|
||||
notice: settingRow.notice,
|
||||
loginDomain: settingRow.loginDomain,
|
||||
linuxdoClientId: settingRow.linuxdoClientId,
|
||||
linuxdoCallbackUrl: settingRow.linuxdoCallbackUrl,
|
||||
linuxdoSwitch: settingRow.linuxdoSwitch,
|
||||
githubClientId: settingRow.githubClientId,
|
||||
githubCallbackUrl: settingRow.githubCallbackUrl,
|
||||
githubSwitch: settingRow.githubSwitch,
|
||||
gitlabClientId: settingRow.gitlabClientId,
|
||||
gitlabCallbackUrl: settingRow.gitlabCallbackUrl,
|
||||
gitlabSwitch: settingRow.gitlabSwitch,
|
||||
googleClientId: settingRow.googleClientId,
|
||||
googleCallbackUrl: settingRow.googleCallbackUrl,
|
||||
googleSwitch: settingRow.googleSwitch,
|
||||
minEmailPrefix: settingRow.minEmailPrefix,
|
||||
projectLink: settingRow.projectLink
|
||||
};
|
||||
},
|
||||
|
||||
parseBoolean(value) {
|
||||
if (typeof value === 'string' && value === 'true') return true;
|
||||
if (value === true) return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default settingService;
|
||||
|
||||
@@ -44,10 +44,5 @@ admin = "${ADMIN}"
|
||||
jwt_secret = "${JWT_SECRET}"
|
||||
project_link = "${PROJECT_LINK}"
|
||||
|
||||
linuxdo_switch = "${LINUXDO_SWITCH}"
|
||||
github_switch = "${GITHUB_SWITCH}"
|
||||
gitlab_switch = "${GITLAB_SWITCH}"
|
||||
google_switch = "${GOOGLE_SWITCH}"
|
||||
|
||||
[build]
|
||||
command = "pnpm --prefix ../mail-vue install && pnpm --prefix ../mail-vue run build"
|
||||
|
||||
@@ -42,10 +42,6 @@ crons = ["0 16 * * *"] #每天晚上12点执行每日任务,刷新分析缓存
|
||||
#domain = [] #邮件域名可可配置多个 示例: ["example1.com","example2.com"]
|
||||
#admin = "" #管理员的邮箱 示例: [email protected]
|
||||
#jwt_secret = "" #jwt令牌的密钥,随便填一串字符串
|
||||
#linuxdo_switch = "" #LinuxDo OAuth 开关
|
||||
#github_switch = "" #GitHub OAuth 开关
|
||||
#gitlab_switch = "" #GitLab OAuth 开关
|
||||
#google_switch = "" #Google OAuth 开关
|
||||
|
||||
[build]
|
||||
command = "pnpm --prefix ../mail-vue install && pnpm --prefix ../mail-vue run build"
|
||||
|
||||
Reference in New Issue
Block a user