diff --git a/src/api/controllers/index.ts b/src/api/controllers/index.ts index 514f8634..c04b1719 100644 --- a/src/api/controllers/index.ts +++ b/src/api/controllers/index.ts @@ -1,7 +1,9 @@ import * as Configuration from './configuration'; import * as Ding from './ding'; import * as GitHub from './github'; +import * as Auth from './oauth'; import * as Proxy from './proxy'; +import * as OpenSumiRun from './run'; import * as Static from './static'; import * as Webhook from './webhook'; @@ -12,4 +14,6 @@ export const registerBlueprint = (hono: THono) => { Webhook.route(hono); Static.route(hono); Configuration.route(hono); + Auth.route(hono); + OpenSumiRun.route(hono); }; diff --git a/src/api/controllers/oauth.ts b/src/api/controllers/oauth.ts new file mode 100644 index 00000000..2bbd92ba --- /dev/null +++ b/src/api/controllers/oauth.ts @@ -0,0 +1,60 @@ +import { GitHubKVManager } from '@/kv/github'; + +export function route(hono: THono) { + hono.get('/auth/callback/:id', async (c) => { + const code = c.req.query('code'); + const state = c.req.query('state'); + const config = await GitHubKVManager.instance().getOauthAppConfig( + c.req.param('id'), + ); + + if (!config) { + return c.html('error', 500); + } + + const res = await fetch('https://github.com/login/oauth/access_token', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + client_id: config.clientId, + client_secret: config.clientSecret, + code, + }), + }) + .then((res) => res.json<{ access_token: string }>()) + .catch((err) => { + console.log('request github error: ', err); + }); + + if (res && state) { + const [, originalUrl] = state.split('|'); + // 获取 state 参数,重定向到原始页面 + // 携带 access_token 参数,前端自行缓存后请求 + return c.redirect(`${originalUrl}?access_token=${res.access_token}`); + } + + return c.html('error', 500); + }); + + hono.get('/auth/github/:id', async (c) => { + const config = await GitHubKVManager.instance().getOauthAppConfig( + c.req.param('id'), + ); + + if (!config) { + return c.html('error', 500); + } + + // 重定向到 github 登录页面 + // 透传 state 参数,用于登录后重定向到原始页面 + // state: originalState|originalUrl + return c.redirect( + `https://github.com/login/oauth/authorize?client_id=${ + config.clientId + }&scope=read:user%20repo&state=${c.req.query('state')}`, + ); + }); +} diff --git a/src/api/controllers/run.ts b/src/api/controllers/run.ts new file mode 100644 index 00000000..52d82d2b --- /dev/null +++ b/src/api/controllers/run.ts @@ -0,0 +1,45 @@ +import { html } from 'hono/html'; + +import Environment from '@/env'; +import { OpenSumiRunKVManager } from '@/kv/run'; + +export function route(hono: THono) { + hono.get('/ide/:group/:project', async (c) => { + const env = Environment.instance().environment; + const version = await OpenSumiRunKVManager.instance().getCdnVersion(); + const originTrial = await OpenSumiRunKVManager.instance().getTrialToken( + env, + ); + + const cdnBase = + env === 'prod' + ? 'https://g.alicdn.com/opensumi/run' + : 'https://dev.g.alicdn.com/opensumi/run'; + + return c.html( + html` + + + + OpenSumi + + + + + + +
+ + + `, + ); + }); +} diff --git a/src/kv/constants.ts b/src/kv/constants.ts index b8f8a168..26c8b2b4 100644 --- a/src/kv/constants.ts +++ b/src/kv/constants.ts @@ -14,6 +14,14 @@ export namespace DingCommon { export namespace GitHubCommon { export const GITHUB_SETTINGS_PREFIX = 'github/settings/'; export const GITHUB_APP_SETTINGS_PREFIX = 'github/app/settings/'; + export const GITHUB_OAUTH_SETTINGS_PREFIX = 'github/oauth/settings/'; +} + +export namespace OpenSumiRunCommon { + export const OPENSUMI_RUN_CDN_VERSION_PREFIX = 'opensumi/run/cdn-version'; + + export const OPENSUMI_RUN_ORIGINAL_TRIAL_TOKEN_PREFIX = + 'opensumi/run/original-trial-token'; } export namespace Common { diff --git a/src/kv/github.ts b/src/kv/github.ts index ace3ff13..a11a2034 100644 --- a/src/kv/github.ts +++ b/src/kv/github.ts @@ -1,10 +1,11 @@ import { GitHubCommon, KVManager } from '@/kv'; -import { AppSetting, ISetting } from './types'; +import { AppSetting, IGitHubOauthAppConfig, ISetting } from './types'; export class GitHubKVManager { appSettingsKV: KVManager; settingsKV: KVManager; + oauthKV: KVManager; private constructor() { this.appSettingsKV = KVManager.for( @@ -13,6 +14,9 @@ export class GitHubKVManager { this.settingsKV = KVManager.for( GitHubCommon.GITHUB_SETTINGS_PREFIX, ); + this.oauthKV = KVManager.for( + GitHubCommon.GITHUB_OAUTH_SETTINGS_PREFIX, + ); } private static _instance: GitHubKVManager; @@ -45,4 +49,8 @@ export class GitHubKVManager { return webhooks; }; + + getOauthAppConfig = async (id: string) => { + return await this.oauthKV.getJSON(id); + }; } diff --git a/src/kv/run.ts b/src/kv/run.ts new file mode 100644 index 00000000..6aef7224 --- /dev/null +++ b/src/kv/run.ts @@ -0,0 +1,37 @@ +import { KVManager, OpenSumiRunCommon } from '@/kv'; + +import { IOpenSumiRunConfig, IOpenSumiRunOriginalTrialToken } from './types'; + +export class OpenSumiRunKVManager { + cdnVersion: KVManager; + + originalTrialToken: KVManager; + + constructor() { + this.cdnVersion = KVManager.for( + OpenSumiRunCommon.OPENSUMI_RUN_CDN_VERSION_PREFIX, + ); + + this.originalTrialToken = KVManager.for( + OpenSumiRunCommon.OPENSUMI_RUN_ORIGINAL_TRIAL_TOKEN_PREFIX, + ); + } + + private static _instance: OpenSumiRunKVManager; + static instance() { + if (!this._instance) { + this._instance = new OpenSumiRunKVManager(); + } + return this._instance; + } + + getCdnVersion = async () => { + const cdnVersionData = await this.cdnVersion.getJSON(''); + return cdnVersionData?.version || '0.0.1'; + }; + + getTrialToken = async (env: 'local' | 'prod' | 'unittest') => { + const trialTokenData = await this.originalTrialToken.getJSON(''); + return trialTokenData ? trialTokenData[env] : undefined; + }; +} diff --git a/src/kv/types.ts b/src/kv/types.ts index 755af793..47ec609a 100644 --- a/src/kv/types.ts +++ b/src/kv/types.ts @@ -138,3 +138,18 @@ export interface IAdminInfo { */ tokenByScope?: Record; } + +export interface IOpenSumiRunConfig { + version: string; +} + +export interface IOpenSumiRunOriginalTrialToken { + local: string; + prod: string; + unittest: string; +} + +export interface IGitHubOauthAppConfig { + clientId: string; + clientSecret: string; +}