mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-05-10 17:43:31 +08:00
feat(oauth2): add SVG icon support for OAuth2 providers (#825)
- Add optional `icon` field to UserOauth2Settings type - Include preset SVG icons for GitHub, Linux Do, and Authentik templates - Render icons on OAuth2 login buttons - Add icon configuration UI with preview in admin panel Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
export type UserOauth2Settings = {
|
||||
name: string;
|
||||
icon?: string; // SVG icon string for the provider
|
||||
clientID: string;
|
||||
clientSecret: string;
|
||||
authorizationURL: string;
|
||||
|
||||
@@ -83,7 +83,7 @@ export const useGlobalState = createGlobalState(
|
||||
fetched: false,
|
||||
enable: false,
|
||||
enableMailVerify: false,
|
||||
/** @type {{ clientID: string, name: string }[]} */
|
||||
/** @type {{ clientID: string, name: string, icon?: string }[]} */
|
||||
oauth2ClientIDs: [],
|
||||
});
|
||||
const userSettings = ref({
|
||||
|
||||
@@ -25,6 +25,8 @@ const { t } = useI18n({
|
||||
mailAllowList: 'Mail Address Allow List',
|
||||
addOauth2: 'Add Oauth2',
|
||||
name: 'Name',
|
||||
icon: 'Icon (SVG, please ensure trusted source)',
|
||||
iconPreview: 'Preview',
|
||||
oauth2Type: 'Oauth2 Type',
|
||||
enableEmailFormat: 'Enable Email Format',
|
||||
userEmailFormat: 'Email Regex Pattern',
|
||||
@@ -42,6 +44,8 @@ const { t } = useI18n({
|
||||
mailAllowList: '邮件地址白名单',
|
||||
addOauth2: '添加 Oauth2',
|
||||
name: '名称',
|
||||
icon: '图标 (SVG, 请确保来源可信)',
|
||||
iconPreview: '预览',
|
||||
oauth2Type: 'Oauth2 类型',
|
||||
enableEmailFormat: '启用邮箱格式转换',
|
||||
userEmailFormat: '邮箱正则表达式',
|
||||
@@ -52,6 +56,12 @@ const { t } = useI18n({
|
||||
}
|
||||
});
|
||||
|
||||
const OAUTH2_ICONS: Record<string, string> = {
|
||||
github: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z"/></svg>',
|
||||
linuxdo: '<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em"><g><path d="m7.44,0s.09,0,.13,0c.09,0,.19,0,.28,0,.14,0,.29,0,.43,0,.09,0,.18,0,.27,0q.12,0,.25,0t.26.08c.15.03.29.06.44.08,1.97.38,3.78,1.47,4.95,3.11.04.06.09.12.13.18.67.96,1.15,2.11,1.3,3.28q0,.19.09.26c0,.15,0,.29,0,.44,0,.04,0,.09,0,.13,0,.09,0,.19,0,.28,0,.14,0,.29,0,.43,0,.09,0,.18,0,.27,0,.08,0,.17,0,.25q0,.19-.08.26c-.03.15-.06.29-.08.44-.38,1.97-1.47,3.78-3.11,4.95-.06.04-.12.09-.18.13-.96.67-2.11,1.15-3.28,1.3q-.19,0-.26.09c-.15,0-.29,0-.44,0-.04,0-.09,0-.13,0-.09,0-.19,0-.28,0-.14,0-.29,0-.43,0-.09,0-.18,0-.27,0-.08,0-.17,0-.25,0q-.19,0-.26-.08c-.15-.03-.29-.06-.44-.08-1.97-.38-3.78-1.47-4.95-3.11q-.07-.09-.13-.18c-.67-.96-1.15-2.11-1.3-3.28q0-.19-.09-.26c0-.15,0-.29,0-.44,0-.04,0-.09,0-.13,0-.09,0-.19,0-.28,0-.14,0-.29,0-.43,0-.09,0-.18,0-.27,0-.08,0-.17,0-.25q0-.19.08-.26c.03-.15.06-.29.08-.44.38-1.97,1.47-3.78,3.11-4.95.06-.04.12-.09.18-.13C4.42.73,5.57.26,6.74.1,7,.07,7.15,0,7.44,0Z" fill="#EFEFEF"/><path d="m1.27,11.33h13.45c-.94,1.89-2.51,3.21-4.51,3.88-1.99.59-3.96.37-5.8-.57-1.25-.7-2.67-1.9-3.14-3.3Z" fill="#FEB005"/><path d="m12.54,1.99c.87.7,1.82,1.59,2.18,2.68H1.27c.87-1.74,2.33-3.13,4.2-3.78,2.44-.79,5-.47,7.07,1.1Z" fill="#1D1D1F"/></g></svg>',
|
||||
authentik: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 2.18l7 3.12v4.7c0 4.83-3.23 9.36-7 10.57-3.77-1.21-7-5.74-7-10.57V6.3l7-3.12zM11 7v6h2V7h-2zm0 8v2h2v-2h-2z"/></svg>',
|
||||
};
|
||||
|
||||
const mailAllowOptions = constant.COMMOM_MAIL.map((item) => {
|
||||
return { label: item, value: item }
|
||||
})
|
||||
@@ -91,6 +101,7 @@ const addNewOauth2 = () => {
|
||||
userInfoURL: 'https://api.github.com/user',
|
||||
userEmailKey: 'email',
|
||||
scope: 'user:email',
|
||||
icon: OAUTH2_ICONS.github,
|
||||
},
|
||||
linuxdo: {
|
||||
authorizationURL: 'https://connect.linux.do/oauth2/authorize',
|
||||
@@ -102,6 +113,7 @@ const addNewOauth2 = () => {
|
||||
enableEmailFormat: true,
|
||||
userEmailFormat: '^(.+)$',
|
||||
userEmailReplace: 'linux_do_$1@oauth.linux.do',
|
||||
icon: OAUTH2_ICONS.linuxdo,
|
||||
},
|
||||
authentik: {
|
||||
authorizationURL: 'https://youdomain/application/o/authorize/',
|
||||
@@ -110,12 +122,14 @@ const addNewOauth2 = () => {
|
||||
userInfoURL: 'https://youdomain/application/o/userinfo/',
|
||||
userEmailKey: 'email',
|
||||
scope: 'email openid',
|
||||
icon: OAUTH2_ICONS.authentik,
|
||||
},
|
||||
custom: {},
|
||||
}
|
||||
const template = templates[newOauth2Type.value] || {}
|
||||
userOauth2Settings.value.push({
|
||||
name: newOauth2Name.value,
|
||||
icon: '',
|
||||
clientID: '',
|
||||
clientSecret: '',
|
||||
authorizationURL: '',
|
||||
@@ -198,6 +212,13 @@ onMounted(async () => {
|
||||
<n-form-item-row :label="t('name')" required>
|
||||
<n-input v-model:value="item.name" />
|
||||
</n-form-item-row>
|
||||
<n-form-item-row :label="t('icon')">
|
||||
<n-input v-model:value="item.icon" type="textarea"
|
||||
:autosize="{ minRows: 2, maxRows: 5 }" style="width: 100%;" />
|
||||
</n-form-item-row>
|
||||
<n-form-item-row v-if="item.icon" :label="t('iconPreview')">
|
||||
<span class="oauth2-icon-preview" v-html="item.icon"></span>
|
||||
</n-form-item-row>
|
||||
<n-form-item-row label="Client ID" required>
|
||||
<n-input v-model:value="item.clientID" />
|
||||
</n-form-item-row>
|
||||
@@ -276,4 +297,20 @@ onMounted(async () => {
|
||||
place-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.oauth2-icon-preview {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 1px solid var(--n-border-color);
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.oauth2-icon-preview :deep(svg) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -233,6 +233,9 @@ onMounted(async () => {
|
||||
</n-button>
|
||||
<n-button @click="oauth2Login(item.clientID)" v-for="item in userOpenSettings.oauth2ClientIDs"
|
||||
:key="item.clientID" block secondary strong>
|
||||
<template #icon v-if="item.icon">
|
||||
<span class="oauth2-icon" v-html="item.icon"></span>
|
||||
</template>
|
||||
{{ t('loginWith', { provider: item.name }) }}
|
||||
</n-button>
|
||||
</n-form>
|
||||
@@ -305,4 +308,17 @@ onMounted(async () => {
|
||||
.n-button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.oauth2-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.oauth2-icon :deep(svg) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -146,6 +146,7 @@ export class WebhookSettings {
|
||||
|
||||
export type UserOauth2Settings = {
|
||||
name: string;
|
||||
icon?: string; // SVG icon string for the provider
|
||||
clientID: string;
|
||||
clientSecret: string;
|
||||
authorizationURL: string;
|
||||
|
||||
@@ -11,13 +11,14 @@ export default {
|
||||
openSettings: async (c: Context<HonoCustomType>) => {
|
||||
const value = await getJsonSetting(c, CONSTANTS.USER_SETTINGS_KEY);
|
||||
const settings = new UserSettings(value);
|
||||
const oauth2ClientIDs = [] as { clientID: string, name: string }[];
|
||||
const oauth2ClientIDs = [] as { clientID: string, name: string, icon?: string }[];
|
||||
try {
|
||||
const oauth2Settings = await getJsonSetting<UserOauth2Settings[]>(c, CONSTANTS.OAUTH2_SETTINGS_KEY);
|
||||
oauth2ClientIDs.push(
|
||||
...oauth2Settings?.map(s => ({
|
||||
clientID: s.clientID,
|
||||
name: s.name
|
||||
name: s.name,
|
||||
icon: s.icon,
|
||||
})) || []
|
||||
);
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user