diff --git a/packages/ui/certd-client/src/layout/layout-basic.vue b/packages/ui/certd-client/src/layout/layout-basic.vue
index 22b83a645..df15dbeac 100644
--- a/packages/ui/certd-client/src/layout/layout-basic.vue
+++ b/packages/ui/certd-client/src/layout/layout-basic.vue
@@ -35,7 +35,13 @@ const menus = computed(() => [
const avatar = computed(() => {
const avt = userStore.getUserInfo?.avatar;
- return avt ? `/api/basic/file/download?key=${avt}` : "";
+ if (!avt) {
+ return "";
+ }
+ if (avt.startsWith("http")) {
+ return avt;
+ }
+ return `/api/basic/file/download?key=${avt}`;
});
async function handleLogout() {
diff --git a/packages/ui/certd-client/src/views/certd/mine/user-profile.vue b/packages/ui/certd-client/src/views/certd/mine/user-profile.vue
index 61c0bf546..2b87efb95 100644
--- a/packages/ui/certd-client/src/views/certd/mine/user-profile.vue
+++ b/packages/ui/certd-client/src/views/certd/mine/user-profile.vue
@@ -8,7 +8,7 @@
{{ userInfo.username }}
{{ userInfo.nickName }}
-
+
{{ userInfo.username }}
@@ -108,6 +108,17 @@ async function bind(type: string) {
window.location.href = loginUrl;
}
+const userAvatar = computed(() => {
+ if (isEmpty(userInfo.value.avatar)) {
+ return "";
+ }
+ if (userInfo.value.avatar.startsWith("http")) {
+ return userInfo.value.avatar;
+ }
+
+ return "api/basic/file/download?&key=" + userInfo.value.avatar;
+});
+
onMounted(async () => {
await getUserInfo();
await loadOauthBounds();
diff --git a/packages/ui/certd-server/src/plugins/plugin-oauth/index.ts b/packages/ui/certd-server/src/plugins/plugin-oauth/index.ts
index 8d853c0ff..3a43d4351 100644
--- a/packages/ui/certd-server/src/plugins/plugin-oauth/index.ts
+++ b/packages/ui/certd-server/src/plugins/plugin-oauth/index.ts
@@ -2,4 +2,6 @@ export * from './api.js'
export * from './oidc/plugin-oidc.js'
export * from './wx/plugin-wx.js'
export * from './oauth2/plugin-gitee.js'
-export * from './oauth2/plugin-clogin.js'
\ No newline at end of file
+export * from './oauth2/plugin-clogin.js'
+export * from './oauth2/plugin-github.js'
+export * from './oauth2/plugin-google.js'
\ No newline at end of file
diff --git a/packages/ui/certd-server/src/plugins/plugin-oauth/oauth2/plugin-github.ts b/packages/ui/certd-server/src/plugins/plugin-oauth/oauth2/plugin-github.ts
new file mode 100644
index 000000000..0cf9958d3
--- /dev/null
+++ b/packages/ui/certd-server/src/plugins/plugin-oauth/oauth2/plugin-github.ts
@@ -0,0 +1,103 @@
+import { AddonInput, BaseAddon, IsAddon } from "@certd/lib-server";
+import { BuildLoginUrlReq, BuildLogoutUrlReq, IOauthProvider, OnCallbackReq } from "../api.js";
+
+@IsAddon({
+ addonType: "oauth",
+ name: 'github',
+ title: 'GitHub认证',
+ desc: 'GitHub OAuth2登录',
+ icon:"simple-icons:github",
+ showTest: false,
+})
+export class GithubOauthProvider extends BaseAddon implements IOauthProvider {
+
+ @AddonInput({
+ title: "ClientId",
+ helper: "[GitHub Developer Settings](https://github.com/settings/developers)创建应用后获取",
+ required: true,
+ })
+ clientId = "";
+
+ @AddonInput({
+ title: "ClientSecretKey",
+ component: {
+ placeholder: "ClientSecretKey / appSecretKey",
+ },
+ required: true,
+ })
+ clientSecretKey = "";
+
+ async buildLoginUrl(params: BuildLoginUrlReq) {
+
+ let scope = "user:email" // Scope of the access request
+ let state:any = {
+ forType: params.forType || 'login',
+ }
+ state = this.ctx.utils.hash.base64(JSON.stringify(state))
+
+ const authorizeEndpoint = "https://github.com/login/oauth/authorize"
+ const redirectUrl = encodeURIComponent(params.redirectUri)
+ const loginUrl = `${authorizeEndpoint}?client_id=${this.clientId}&redirect_uri=${redirectUrl}&response_type=code&scope=${scope}&state=${state}`
+ return {
+ loginUrl,
+ ticketValue: {
+ state,
+ },
+ };
+ }
+
+ async onCallback(req: OnCallbackReq) {
+
+ const code = req.code || ""
+
+ const tokenEndpoint = "https://github.com/login/oauth/access_token"
+
+ const uri = new URL(req.currentURL)
+ const redirectUri = `${uri.origin}${uri.pathname}`
+ const res = await this.ctx.utils.http.request( {
+ url: tokenEndpoint,
+ method: "post",
+ headers: {
+ "Accept": "application/json"
+ },
+ data:{
+ client_id: this.clientId,
+ client_secret: this.clientSecretKey,
+ code,
+ redirect_uri: redirectUri
+ }
+ })
+
+ const tokens = res
+
+ const userInfoEndpoint = "https://api.github.com/user"
+
+ // 获取用户信息
+ const userInfoRes = await this.ctx.utils.http.request( {
+ url: userInfoEndpoint,
+ method: "get",
+ headers: {
+ "Authorization": `Bearer ${tokens.access_token}`,
+ "Accept": "application/json"
+ }
+ })
+ const userInfo = userInfoRes
+
+ return {
+ token:{
+ accessToken: tokens.access_token,
+ refreshToken: tokens.refresh_token,
+ expiresIn: tokens.expires_in,
+ },
+ userInfo: {
+ openId: userInfo.id,
+ nickName: userInfo.login || userInfo.name || "",
+ avatar: userInfo.avatar_url,
+ },
+ }
+ };
+
+ async buildLogoutUrl(params: BuildLogoutUrlReq) {
+ return {};
+ }
+}
\ No newline at end of file
diff --git a/packages/ui/certd-server/src/plugins/plugin-oauth/oauth2/plugin-google.ts b/packages/ui/certd-server/src/plugins/plugin-oauth/oauth2/plugin-google.ts
new file mode 100644
index 000000000..6d1c12241
--- /dev/null
+++ b/packages/ui/certd-server/src/plugins/plugin-oauth/oauth2/plugin-google.ts
@@ -0,0 +1,103 @@
+import { AddonInput, BaseAddon, IsAddon } from "@certd/lib-server";
+import { BuildLoginUrlReq, BuildLogoutUrlReq, IOauthProvider, OnCallbackReq } from "../api.js";
+
+@IsAddon({
+ addonType: "oauth",
+ name: 'google',
+ title: 'Google认证',
+ desc: 'Google OAuth2登录',
+ icon:"simple-icons:google",
+ showTest: false,
+})
+export class GoogleOauthProvider extends BaseAddon implements IOauthProvider {
+
+ @AddonInput({
+ title: "ClientId",
+ helper: "[Google Cloud Console](https://console.cloud.google.com/apis/credentials)创建应用后获取",
+ required: true,
+ })
+ clientId = "";
+
+ @AddonInput({
+ title: "ClientSecretKey",
+ component: {
+ placeholder: "ClientSecretKey / appSecretKey",
+ },
+ required: true,
+ })
+ clientSecretKey = "";
+
+ async buildLoginUrl(params: BuildLoginUrlReq) {
+
+ let scope = "email profile" // Scope of the access request
+ let state:any = {
+ forType: params.forType || 'login',
+ }
+ state = this.ctx.utils.hash.base64(JSON.stringify(state))
+
+ const authorizeEndpoint = "https://accounts.google.com/o/oauth2/auth"
+ const redirectUrl = encodeURIComponent(params.redirectUri)
+ const loginUrl = `${authorizeEndpoint}?client_id=${this.clientId}&redirect_uri=${redirectUrl}&response_type=code&scope=${scope}&state=${state}`
+ return {
+ loginUrl,
+ ticketValue: {
+ state,
+ },
+ };
+ }
+
+ async onCallback(req: OnCallbackReq) {
+
+ const code = req.code || ""
+
+ const tokenEndpoint = "https://oauth2.googleapis.com/token"
+
+ const uri = new URL(req.currentURL)
+ const redirectUri = `${uri.origin}${uri.pathname}`
+ const res = await this.ctx.utils.http.request( {
+ url: tokenEndpoint,
+ method: "post",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded"
+ },
+ data:{
+ client_id: this.clientId,
+ client_secret: this.clientSecretKey,
+ code,
+ redirect_uri: redirectUri,
+ grant_type: "authorization_code"
+ }
+ })
+
+ const tokens = res
+
+ const userInfoEndpoint = "https://www.googleapis.com/oauth2/v3/userinfo"
+
+ // 获取用户信息
+ const userInfoRes = await this.ctx.utils.http.request( {
+ url: userInfoEndpoint,
+ method: "get",
+ headers: {
+ "Authorization": `Bearer ${tokens.access_token}`
+ }
+ })
+ const userInfo = userInfoRes
+
+ return {
+ token:{
+ accessToken: tokens.access_token,
+ refreshToken: tokens.refresh_token,
+ expiresIn: tokens.expires_in,
+ },
+ userInfo: {
+ openId: userInfo.sub,
+ nickName: userInfo.name || userInfo.email || "",
+ avatar: userInfo.picture,
+ },
+ }
+ };
+
+ async buildLogoutUrl(params: BuildLogoutUrlReq) {
+ return {};
+ }
+}
\ No newline at end of file