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;
+}