Compare commits

17 Commits

Author SHA1 Message Date
Levi Yan
781518976e release 0.3.9 2025-06-22 16:22:55 +08:00
Levi Yan
c18fc24016 refactor(main): if devstarDomain_sessionId exist and isn't null, use it first 2025-06-22 16:22:33 +08:00
Levi Yan
41b97301f7 refactor(remote-container): bring devstarDomain param for /openProjectSkippingLoginCheck only when devstarDomain_sessionId exists and isn't null 2025-06-22 16:20:20 +08:00
Levi Yan
1afb9a8085 refactor(user): create user ssh key only when user has beed logged 2025-06-22 16:16:09 +08:00
Levi Yan
548ccdc575 refactor(user): isLogged add validity verification for username and token 2025-06-22 16:15:14 +08:00
Levi Yan
6e75693220 refactor(user): login function store token, username only after verifying token as true 2025-06-22 16:12:50 +08:00
Levi Yan
ed7ee8cce3 refactor(user): rename setDevstarDomainAndHostname as setDevstarDomain 2025-06-22 16:08:41 +08:00
Levi Yan
12e950bf3e refactor: no longer set username and userToken as member variables in user class and get them only by global state 2025-06-22 16:02:28 +08:00
Levi Yan
4ab517bc44 refactor: use standard format for domain 2025-06-22 15:56:01 +08:00
Levi Yan
353790d693 feat: support open_with_vscode link bringing devstarDomain param 2025-06-22 11:20:40 +08:00
Levi Yan
b713e4515a feat(remote-container): setUser 2025-06-22 11:11:09 +08:00
Levi Yan
5f38aafeed feat(user): function setDevstarDomainAndHostname, getDevstarDomain; add member varibale devstarDomain 2025-06-22 11:10:26 +08:00
Levi Yan
9b56dba1e2 feat(user): two constructors used for domain which come from user config or param in open_with_vscode link 2025-06-22 11:05:39 +08:00
Levi Yan
dd106a1ecb feat(home): function setUser, setRemoteContainer, setDevstarDomain 2025-06-22 10:57:26 +08:00
Levi Yan
9141d67894 feat(home): two constructors used for domain which come from user config or param in open_with_vscode link 2025-06-22 10:56:55 +08:00
Levi Yan
e6c159520a feat(devstar-api): two constructors used for domain which come from user config or param in open_with_vscode link 2025-06-22 10:56:07 +08:00
Levi Yan
00fd73d41b style: formatting 2025-06-22 10:50:06 +08:00
6 changed files with 319 additions and 133 deletions

View File

@@ -2,7 +2,7 @@
"name": "devstar", "name": "devstar",
"displayName": "%displayName%", "displayName": "%displayName%",
"description": "%description%", "description": "%description%",
"version": "0.3.8", "version": "0.3.9",
"keywords": [], "keywords": [],
"publisher": "mengning", "publisher": "mengning",
"engines": { "engines": {

View File

@@ -7,52 +7,68 @@ export default class DevstarAPIHandler {
private devstarDomain: string; private devstarDomain: string;
constructor() { /**
// 获取domain * domain使用用户配置
const devstarDomainFromUserConfig = utils.devstarDomain() */
if (undefined == devstarDomainFromUserConfig || "" == devstarDomainFromUserConfig) { constructor();
this.devstarDomain = "https://devstar.cn";
/**
* open with vscode链接传入devstarDomain
* @param devstarDomainURL
*/
constructor(devstarDomainURL: string);
constructor(devstarDomainURL?: string) {
if (devstarDomainURL == undefined || devstarDomainURL == "") {
// 获取domain
const devstarDomainFromUserConfig: string | undefined = utils.devstarDomain()
if (undefined == devstarDomainFromUserConfig || "" == devstarDomainFromUserConfig) {
this.devstarDomain = "https://devstar.cn";
} else {
this.devstarDomain = devstarDomainFromUserConfig.endsWith('/') ? devstarDomainFromUserConfig.slice(0, -1) : devstarDomainFromUserConfig;
}
} else { } else {
this.devstarDomain = devstarDomainFromUserConfig.endsWith('/') ? devstarDomainFromUserConfig.slice(0, -1) : devstarDomainFromUserConfig; // open with vscode传入
this.devstarDomain = devstarDomainURL.endsWith('/') ? devstarDomainURL.slice(0, -1) : devstarDomainURL
} }
} }
public async verifyToken(token: string, username: string): Promise<boolean> { public async verifyToken(token: string, username: string): Promise<boolean> {
try { try {
const response = await fetch(this.devstarDomain + `/api/devcontainer/user`, { const response = await fetch(this.devstarDomain + `/api/devcontainer/user`, {
method: 'GET', method: 'GET',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Authorization': 'token ' + token 'Authorization': 'token ' + token
}
});
// 处理非200响应状态码
if (!response.ok) {
const text = await response.text(); // 先读取文本防止json解析失败
if (response.status == 401) {
throw new Error('Token错误')
} else {
throw new Error(`HTTP Error: ${response.status} - ${text}`);
}
} }
});
const responseData = await response.json(); // 处理非200响应状态码
const data = responseData.data if (!response.ok) {
if (data.username == undefined || data.username == "") { const text = await response.text(); // 先读取文本防止json解析失败
throw new Error('Token对应用户不存在') if (response.status == 401) {
throw new Error('Token错误')
} else { } else {
// 验证用户名匹配 throw new Error(`HTTP Error: ${response.status} - ${text}`);
if (data.username !== username) {
throw new Error('Token与用户名不符');
}
} }
return true;
} catch (error) {
console.error(error)
return false
} }
const responseData = await response.json();
const data = responseData.data
if (data.username == undefined || data.username == "") {
throw new Error('Token对应用户不存在')
} else {
// 验证用户名匹配
if (data.username !== username) {
throw new Error('Token与用户名不符');
}
}
return true;
} catch (error) {
console.error(error)
return false
}
} }
// 上传公钥 // 上传公钥

View File

@@ -11,16 +11,56 @@ export default class DSHome {
private devstarHomePageUrl: string; private devstarHomePageUrl: string;
private devstarDomain: string | undefined private devstarDomain: string | undefined
constructor(context: vscode.ExtensionContext, user: User) {
/**
* 配置项提供devstarDomain
* @param context
* @param user
*/
constructor(context: vscode.ExtensionContext, user: User)
/**
* open with vscode链接提供devstarDomain
* @param context
* @param user
* @param devstarDomain
*/
constructor(context: vscode.ExtensionContext, user: User, devstarDomain: string)
constructor(context: vscode.ExtensionContext, user: User, devstarDomain?: string) {
this.context = context; this.context = context;
this.user = user; this.user = user;
this.remoteContainer = new RemoteContainer(user); this.remoteContainer = new RemoteContainer(user);
this.devstarDomain = utils.devstarDomain() if (devstarDomain != undefined && devstarDomain != "") {
if (undefined == this.devstarDomain || "" == this.devstarDomain) { this.devstarDomain = devstarDomain.endsWith('/') ? devstarDomain.slice(0, -1) : devstarDomain
this.devstarHomePageUrl = "https://devstar.cn/devstar-home" this.devstarHomePageUrl = this.devstarDomain + "/devstar-home"
} else { } else {
this.devstarHomePageUrl = this.devstarDomain.endsWith('/') ? this.devstarDomain + "devstar-home" : this.devstarDomain + "/devstar-home" const devstarDomainFromConfig = utils.devstarDomain()
if (devstarDomainFromConfig != undefined && devstarDomainFromConfig != "") {
this.devstarDomain = devstarDomainFromConfig.endsWith('/') ? devstarDomainFromConfig.slice(0, -1) : devstarDomainFromConfig
this.devstarHomePageUrl = this.devstarDomain + "/devstar-home"
} else {
this.devstarDomain = "https://devstar.cn"
this.devstarHomePageUrl = "https://devstar.cn/devstar-home"
}
}
}
setUser(user: User) {
this.user = user
}
setRemoteContainer(remoteContainer: RemoteContainer) {
this.remoteContainer = remoteContainer
}
setDevstarDomainAndHomePageURL(devstarDomain: string) {
if (devstarDomain != undefined && devstarDomain != "") {
this.devstarDomain = devstarDomain.endsWith('/') ? devstarDomain.slice(0, -1) : devstarDomain
this.devstarHomePageUrl = devstarDomain.endsWith('/') ? this.devstarDomain + "devstar-home" : devstarDomain + "/devstar-home"
} else {
console.error("devstarDomain is undefined or null")
} }
} }
@@ -49,7 +89,7 @@ export default class DSHome {
const config = { const config = {
language: vscode.env.language language: vscode.env.language
} }
panel.webview.postMessage({command: 'getHomeConfig', data: {homeConfig: config}}) panel.webview.postMessage({ command: 'getHomeConfig', data: { homeConfig: config } })
break; break;
case 'getUserToken': case 'getUserToken':
const userToken = this.user.getUserTokenFromLocal() const userToken = this.user.getUserTokenFromLocal()

View File

@@ -14,11 +14,6 @@ export class DevStarExtension {
dsHome: DSHome; dsHome: DSHome;
constructor(private context: vscode.ExtensionContext) { constructor(private context: vscode.ExtensionContext) {
this.user = new User(context);
// 只保持一个User实例
this.remoteContainer = new RemoteContainer(this.user);
this.dsHome = new DSHome(context, this.user);
// 确定local系统是否为win如果是保存powershell版本 // 确定local系统是否为win如果是保存powershell版本
if (vscode.env.remoteName === undefined) { if (vscode.env.remoteName === undefined) {
if (os.platform() === 'win32') { if (os.platform() === 'win32') {
@@ -29,11 +24,40 @@ export class DevStarExtension {
} }
} }
// support for open with vscode in web // 只保持一个User实例
// 如果global state中devstarDomain的值不为空则在global state中存储一个键值对为devstarDomain_<sessionId>和devstar domain并把devstarDomain的值置空.
// 这时如果remote窗口需要打开项目且global state中的devstarDomain_<sessionId>键存在、值不为空,
// 则取出其值并通过参数将devstar domain传递给/openProjectSkippingLoginCheck
//
// 如果global state中devstarDomain_<sessionId>存在,直接使用
const devstarDomain_sessionId: string | undefined = context.globalState.get('devstarDomain_' + vscode.env.sessionId)
const devstarDomain: string | undefined = context.globalState.get('devstarDomain')
if (devstarDomain_sessionId != undefined && devstarDomain_sessionId != "") {
this.user = new User(context, devstarDomain_sessionId)
this.remoteContainer = new RemoteContainer(this.user);
this.dsHome = new DSHome(context, this.user, devstarDomain_sessionId)
} else if (devstarDomain != undefined && devstarDomain != "") {
console.log('domain in global state', devstarDomain)
// global state中存在devstarDomain
this.user = new User(context, devstarDomain)
this.remoteContainer = new RemoteContainer(this.user);
this.dsHome = new DSHome(context, this.user, devstarDomain)
context.globalState.update('devstarDomain_' + vscode.env.sessionId, devstarDomain)
context.globalState.update('devstarDomain', "")
} else {
this.user = new User(context);
this.remoteContainer = new RemoteContainer(this.user);
this.dsHome = new DSHome(context, this.user);
}
const handler = vscode.window.registerUriHandler({ const handler = vscode.window.registerUriHandler({
handleUri: async (uri: vscode.Uri) => { handleUri: async (uri: vscode.Uri) => {
const devstarAPIHandler = new DevstarAPIHandler() /**
* 支持open with vscode的入口
*/
if (uri.path === '/openProject') { if (uri.path === '/openProject') {
const params = new URLSearchParams(uri.query); const params = new URLSearchParams(uri.query);
const host = params.get('host'); const host = params.get('host');
@@ -41,47 +65,62 @@ export class DevStarExtension {
const port = params.get('port'); const port = params.get('port');
const username = params.get('username'); const username = params.get('username');
const path = params.get('path'); const path = params.get('path');
const access_token = params.get('access_token'); const accessToken = params.get('access_token');
const devstar_username = params.get('devstar_username'); const devstarUsername = params.get('devstar_username');
const devstarDomain = params.get('devstar_domain');
if (host && hostname && port && username && path) { if (host && hostname && port && username && path) {
const container_host = host; const containerHost = host;
const container_hostname = hostname const containerHostname = hostname
const container_port = parseInt(port, 10); const containerPort = parseInt(port, 10);
const container_username = username; const containerUsername = username;
const project_path = decodeURIComponent(path); const projectPath = decodeURIComponent(path);
if (access_token && devstar_username) { if (accessToken && devstarUsername && devstarDomain) {
if (!this.user.isLogged()) { // 修改user、remote-container、home中的devstar domain和hostname
this.user.setDevstarDomain(devstarDomain)
this.remoteContainer.setUser(this.user)
this.dsHome.setDevstarDomainAndHomePageURL(devstarDomain)
this.dsHome.setUser(this.user)
this.dsHome.setRemoteContainer(this.remoteContainer)
vscode.commands.executeCommand('devstar.showHome');
// 将devstar domain存在global state中
context.globalState.update('devstarDomain', devstarDomain)
if (!await this.user.isLogged()) {
// 如果没有用户登录,则直接登录; // 如果没有用户登录,则直接登录;
const res = await this.user.login(access_token, devstar_username) const res = await this.user.login(accessToken, devstarUsername)
if (res === 'ok') { if (res === 'ok') {
await this.remoteContainer.firstOpenProject(container_host, container_hostname, container_port, container_username, project_path, this.context) await this.remoteContainer.firstOpenProject(containerHost, containerHostname, containerPort, containerUsername, projectPath, this.context)
} }
} else if (devstar_username === this.user.getUsernameFromLocal()) { } else if (devstarUsername === this.user.getUsernameFromLocal()) {
// 如果同用户已经登录,则忽略,直接打开项目 // 如果同用户已经登录,则忽略,直接打开项目
await this.remoteContainer.firstOpenProject(container_host, container_hostname, container_port, container_username, project_path, this.context) await this.remoteContainer.firstOpenProject(containerHost, containerHostname, containerPort, containerUsername, projectPath, this.context)
} else { } else {
// 如果不是同用户,可以选择切换用户,或者不切换登录用户,直接打开容器 // 如果不是同用户,可以选择切换用户,或者不切换登录用户,直接打开容器
const selection = await vscode.window.showWarningMessage(`已登录用户:${this.user.getUsernameFromLocal()},是否切换用户?`, const selection = await vscode.window.showWarningMessage(`已登录用户:${this.user.getUsernameFromLocal()},是否切换用户?`,
'Yes', 'No',); 'Yes', 'No',);
if (selection === 'Yes') { if (selection === 'Yes') {
// 如果没有用户登录,则直接登录; // 如果没有用户登录,则直接登录;
const res = await this.user.login(access_token, devstar_username) const res = await this.user.login(accessToken, devstarUsername)
if (res === 'ok') { if (res === 'ok') {
await this.remoteContainer.firstOpenProject(container_host, container_hostname, container_port, container_username, project_path, this.context) await this.remoteContainer.firstOpenProject(containerHost, containerHostname, containerPort, containerUsername, projectPath, this.context)
} }
} else if (selection === 'No') { } else if (selection === 'No') {
await openProjectWithoutLogging(container_host, container_port, container_username, project_path); await openProjectWithoutLogging(containerHost, containerPort, containerUsername, projectPath);
} }
} }
} else { } else {
await openProjectWithoutLogging(container_host, container_port, container_username, project_path); await openProjectWithoutLogging(containerHost, containerPort, containerUsername, projectPath);
} }
} else { } else {
vscode.window.showErrorMessage('Missing required parameters.'); vscode.window.showErrorMessage('Missing required parameters.');
} }
} else if (uri.path === "/openProjectSkippingLoginCheck") { } else if (uri.path === "/openProjectSkippingLoginCheck") {
/**
* 支持remote打开local窗口后再打开项目的入口
*/
// 仅有已登录、不用改变登录状态时,用此流程 // 仅有已登录、不用改变登录状态时,用此流程
const params = new URLSearchParams(uri.query); const params = new URLSearchParams(uri.query);
const host = params.get('host'); const host = params.get('host');
@@ -89,6 +128,7 @@ export class DevStarExtension {
const port = params.get('port'); const port = params.get('port');
const username = params.get('username'); const username = params.get('username');
const path = params.get('path'); const path = params.get('path');
const devstarDomain = params.get('devstar_domain')
if (host && hostname && port && username && path) { if (host && hostname && port && username && path) {
const container_host = host; const container_host = host;
@@ -97,7 +137,23 @@ export class DevStarExtension {
const container_username = username; const container_username = username;
const project_path = decodeURIComponent(path); const project_path = decodeURIComponent(path);
await this.remoteContainer.firstOpenProject(container_host, container_hostname, container_port, container_username, project_path, this.context) if (devstarDomain != undefined && devstarDomain != "") {
// 修改user、remote-container、home中的devstar domain和hostname
this.user.setDevstarDomain(devstarDomain)
this.remoteContainer.setUser(this.user)
this.dsHome.setDevstarDomainAndHomePageURL(devstarDomain)
this.dsHome.setUser(this.user)
this.dsHome.setRemoteContainer(this.remoteContainer)
vscode.commands.executeCommand('devstar.showHome');
// 将devstar domain存在global state中
context.globalState.update('devstarDomain', devstarDomain)
await this.remoteContainer.firstOpenProject(container_host, container_hostname, container_port, container_username, project_path, this.context)
} else {
// devstarDomain参数不存在则不存储使用默认用户配置
await this.remoteContainer.firstOpenProject(container_host, container_hostname, container_port, container_username, project_path, this.context)
}
} }
} }
} }

View File

@@ -16,6 +16,10 @@ export default class RemoteContainer {
this.user = user this.user = user
} }
public setUser(user: User) {
this.user = user
}
/** /**
* 第一次打开远程项目 * 第一次打开远程项目
* *
@@ -33,19 +37,37 @@ export default class RemoteContainer {
vscode.commands.executeCommand('workbench.action.terminal.newLocal').then(() => { vscode.commands.executeCommand('workbench.action.terminal.newLocal').then(() => {
const terminal = vscode.window.terminals[vscode.window.terminals.length - 1]; const terminal = vscode.window.terminals[vscode.window.terminals.length - 1];
if (terminal) { if (terminal) {
let devstarDomain: string | undefined = context.globalState.get("devstarDomain_" + vscode.env.sessionId)
if (devstarDomain == undefined || devstarDomain == "")
devstarDomain = undefined
// vscode协议 // vscode协议
// 根据系统+命令行版本确定命令 // 根据系统+命令行版本确定命令
const semver = require('semver') const semver = require('semver')
const powershellVersion = context.globalState.get('powershellVersion') const powershellVersion = context.globalState.get('powershellVersion')
const powershell_semver_compatible_version = semver.coerce(powershellVersion) const powershell_semver_compatible_version = semver.coerce(powershellVersion)
if (powershellVersion === undefined)
terminal.sendText(`code --new-window && code --open-url "vscode://mengning.devstar/openProjectSkippingLoginCheck?host=${host}&hostname=${hostname}&port=${port}&username=${username}&path=${path}"`) if (devstarDomain === undefined) {
else if (semver.satisfies(powershell_semver_compatible_version, ">=5.1.26100")) { // 不传递devstarDomain
// win & powershell >= 5.1.26100.0 if (powershellVersion === undefined)
terminal.sendText(`code --new-window ; code --% --open-url "vscode://mengning.devstar/openProjectSkippingLoginCheck?host=${host}&hostname=${hostname}&port=${port}&username=${username}&path=${path}"`) terminal.sendText(`code --new-window && code --open-url "vscode://mengning.devstar/openProjectSkippingLoginCheck?host=${host}&hostname=${hostname}&port=${port}&username=${username}&path=${path}"`)
else if (semver.satisfies(powershell_semver_compatible_version, ">=5.1.26100")) {
// win & powershell >= 5.1.26100.0
terminal.sendText(`code --new-window ; code --% --open-url "vscode://mengning.devstar/openProjectSkippingLoginCheck?host=${host}&hostname=${hostname}&port=${port}&username=${username}&path=${path}"`)
} else {
// win & powershell < 5.1.26100.0
terminal.sendText(`code --new-window && code --open-url "vscode://mengning.devstar/openProjectSkippingLoginCheck?host=${host}&hostname=${hostname}&port=${port}&username=${username}&path=${path}"`)
}
} else { } else {
// win & powershell < 5.1.26100.0 if (powershellVersion === undefined)
terminal.sendText(`code --new-window && code --open-url "vscode://mengning.devstar/openProjectSkippingLoginCheck?host=${host}&hostname=${hostname}&port=${port}&username=${username}&path=${path}"`) terminal.sendText(`code --new-window && code --open-url "vscode://mengning.devstar/openProjectSkippingLoginCheck?host=${host}&hostname=${hostname}&port=${port}&username=${username}&path=${path}&devstar_domain=${devstarDomain}"`)
else if (semver.satisfies(powershell_semver_compatible_version, ">=5.1.26100")) {
// win & powershell >= 5.1.26100.0
terminal.sendText(`code --new-window ; code --% --open-url "vscode://mengning.devstar/openProjectSkippingLoginCheck?host=${host}&hostname=${hostname}&port=${port}&username=${username}&path=${path}&devstar_domain=${devstarDomain}"`)
} else {
// win & powershell < 5.1.26100.0
terminal.sendText(`code --new-window && code --open-url "vscode://mengning.devstar/openProjectSkippingLoginCheck?host=${host}&hostname=${hostname}&port=${port}&username=${username}&path=${path}&devstar_domain=${devstarDomain}"`)
}
} }
} }
}) })

View File

@@ -3,7 +3,7 @@ import * as path from 'path';
import * as os from 'os'; import * as os from 'os';
import * as fs from 'fs'; import * as fs from 'fs';
import DevstarAPIHandler from './devstar-api'; import DevstarAPIHandler from './devstar-api';
import { showErrorNotification } from './utils'; import * as utils from './utils';
const { const {
generateKeyPairSync, generateKeyPairSync,
createHash createHash
@@ -12,58 +12,100 @@ const sshpk = require('sshpk');
export default class User { export default class User {
private context: vscode.ExtensionContext; private context: vscode.ExtensionContext;
private username: string | undefined; // 修改devstarDomain会影响hostname, username key, user token key
private userToken: string | undefined; private devstarDomain: string;
private usernameKey: string = 'devstarUsername'
private userTokenKey: string = 'devstarUserToken'
private localUserPrivateKeyPath: string = ''
private devstarHostname: string; private devstarHostname: string;
private usernameKey: string; // devstarUsername_<devstarDomain>
private userTokenKey: string; // devstarUserToken_<devstarDomain>
constructor(context: vscode.ExtensionContext) { /**
* devstarDomain由配置项提供
* @param context
*/
constructor(context: vscode.ExtensionContext);
/**
* devstarDomain由open with vscode链接提供
* @param context
* @param devstarDomain
*/
constructor(context: vscode.ExtensionContext, devstarDomain: string);
constructor(context: vscode.ExtensionContext, devstarDomain?: string) {
this.context = context; this.context = context;
this.username = this.context.globalState.get(this.usernameKey);
this.userToken = this.context.globalState.get(this.userTokenKey);
// 提取devstar domain的主域名用于本地ssh key的命名 // 提取devstar domain的主域名用于本地ssh key的命名
let devstarDomainFromConfig: string | undefined; if (devstarDomain != undefined && devstarDomain != "") {
let devstarDomainURL: string; // open with vscode链接提供域名
devstarDomainFromConfig = vscode.workspace.getConfiguration('devstar').get('devstarDomain') this.devstarDomain = devstarDomain.endsWith('/') ? devstarDomain.slice(0, -1) : devstarDomain
// 如果没有配置devstar domain则默认domain为https://devstar.cn let parsedUrl = new URL(devstarDomain);
devstarDomainURL = (devstarDomainFromConfig === undefined || devstarDomainFromConfig === "") ? 'https://devstar.cn' : devstarDomainFromConfig; this.devstarHostname = parsedUrl.hostname.replace(/\./g, '_'); //提取hostname并用下划线替换.
let parsedUrl = new URL(devstarDomainURL); } else {
this.devstarHostname = parsedUrl.hostname.replace(/\./g, '_'); //提取hostname并用下划线替换. const devstarDomainFromConfig: string | undefined = utils.devstarDomain();
if (devstarDomainFromConfig != undefined && devstarDomainFromConfig != "") {
// 用户配置项提供域名
this.devstarDomain = devstarDomainFromConfig.endsWith('/') ? devstarDomainFromConfig.slice(0, -1) : devstarDomainFromConfig
let parsedUrl = new URL(this.devstarDomain);
this.devstarHostname = parsedUrl.hostname.replace(/\./g, '_'); //提取hostname并用下划线替换.
} else {
// 如果没有配置devstar domain则默认domain为https://devstar.cn
this.devstarDomain = "https://devstar.cn"
this.devstarHostname = "devstar_cn"
}
}
this.usernameKey = "devstarUsername_" + this.devstarDomain
this.userTokenKey = "devstarUserToken_" + this.devstarDomain
} }
public async login(token: string, username: string) { setDevstarDomain(devstarDomain: string) {
const devstarAPIHandler = new DevstarAPIHandler() // 修改devstar domain会影响hostname、usertoken key、username key
if (devstarDomain != "") {
this.devstarDomain = devstarDomain.endsWith('/') ? devstarDomain.slice(0, -1) : devstarDomain
const parsedUrl = new URL(devstarDomain)
this.devstarHostname = parsedUrl.hostname.replace(/\./g, '_');
this.usernameKey = "devstarUsername_" + this.devstarDomain
this.userTokenKey = "devstarUserToken_" + this.devstarDomain
} else {
console.error("devstar domain is null")
}
}
getDevstarDomain(): string {
return this.devstarDomain
}
public async login(token: string, username: string): Promise<string> {
const devstarAPIHandler = new DevstarAPIHandler(this.devstarDomain)
try { try {
const res = await devstarAPIHandler.verifyToken(token, username) const res = await devstarAPIHandler.verifyToken(token, username)
if (!res) { if (!res) {
throw new Error('Token verification failed') throw new Error('Token verification failed')
} } else {
// token与用户名验证通过
// 插件登录存储token与用户名
this.setUserTokenToLocal(token)
this.setUsernameToLocal(username)
// token与用户名验证通过 // 检查本地是否有用户所属公钥,没有则创建
// 插件登录存储token与用户名 if (!this.existUserPublicKey()) {
this.setUserTokenToLocal(token) await this.createUserSSHKey()
this.setUsernameToLocal(username)
// 检查本地是否有用户所属公钥,没有则创建 // 上传公钥
if (!this.existUserPublicKey()) { const uploadResult = await devstarAPIHandler.uploadUserPublicKey(this)
await this.createUserSSHKey() if (uploadResult !== 'ok') {
throw new Error('Upload user public key failed')
// 上传公钥 }
const uploadResult = await devstarAPIHandler.uploadUserPublicKey(this)
if (uploadResult !== 'ok') {
throw new Error('Upload user public key failed')
} }
vscode.window.showInformationMessage(vscode.l10n.t('User login successfully!'))
return 'ok'
} }
vscode.window.showInformationMessage(vscode.l10n.t('User login successfully!'))
return 'ok'
} catch (error) { } catch (error) {
console.error(error) console.error(error)
await showErrorNotification('用户登录失败!') await utils.showErrorNotification('用户登录失败!')
return 'login failed' return 'login failed'
} }
} }
@@ -74,34 +116,34 @@ export default class User {
vscode.window.showInformationMessage(vscode.l10n.t("User has logged out!")) vscode.window.showInformationMessage(vscode.l10n.t("User has logged out!"))
} }
public isLogged() { public async isLogged(): Promise<boolean> {
var existUsername = false; const username: string|undefined = this.context.globalState.get(this.usernameKey)
var existUserToken = false; const userToken: string|undefined = this.context.globalState.get(this.userTokenKey)
if (this.username != undefined && this.username != '') { if ((username != undefined && username != '') && (userToken != undefined && userToken != '')) {
existUsername = true; const devstarAPIHandler = new DevstarAPIHandler(this.devstarDomain)
}
if (this.userToken != undefined && this.userToken != '') {
existUserToken = true;
}
if (existUsername && existUserToken) { const res = await devstarAPIHandler.verifyToken(userToken, username)
return true; if (!res) {
console.error("username or token is error")
return false;
} else {
return true;
}
} else { } else {
return false; return false;
} }
} }
public getUsernameFromLocal(): string | undefined { public getUsernameFromLocal(): string | undefined {
return this.username; return this.context.globalState.get(this.usernameKey);
} }
public getUserTokenFromLocal(): string | undefined { public getUserTokenFromLocal(): string | undefined {
return this.userToken; return this.context.globalState.get(this.userTokenKey);
} }
public setUsernameToLocal(username: string) { public setUsernameToLocal(username: string) {
this.context.globalState.update(this.usernameKey, username); this.context.globalState.update(this.usernameKey, username);
this.username = username;
if (username !== "") { if (username !== "") {
console.log('Username has been stored.') console.log('Username has been stored.')
} else { } else {
@@ -111,7 +153,6 @@ export default class User {
public setUserTokenToLocal(userToken: string) { public setUserTokenToLocal(userToken: string) {
this.context.globalState.update(this.userTokenKey, userToken) this.context.globalState.update(this.userTokenKey, userToken)
this.userToken = userToken
if (userToken !== "") { if (userToken !== "") {
console.log('Token has been stored.'); console.log('Token has been stored.');
} else { } else {
@@ -128,19 +169,23 @@ export default class User {
} }
public getUserPrivateKeyPath(): string { public getUserPrivateKeyPath(): string {
if (!this.isLogged) { if (!this.isLogged()) {
return ''; return '';
} else {
const username: string|undefined = this.context.globalState.get(this.usernameKey)
// islogged为trueusername不为空
return path.join(os.homedir(), '.ssh', `id_rsa_${username}_${this.devstarHostname}`)
} }
return path.join(os.homedir(), '.ssh', `id_rsa_${this.username}_${this.devstarHostname}`)
} }
public getUserPublicKeyPath(): string { public getUserPublicKeyPath(): string {
if (!this.isLogged) { if (!this.isLogged()) {
return ''; return '';
} else {
const username: string|undefined = this.context.globalState.get(this.usernameKey)
// islogged为trueusername不为空
return path.join(os.homedir(), '.ssh', `id_rsa_${username}_${this.devstarHostname}.pub`)
} }
return path.join(os.homedir(), '.ssh', `id_rsa_${this.username}_${this.devstarHostname}.pub`)
} }
public existUserPublicKey(): boolean { public existUserPublicKey(): boolean {
@@ -172,6 +217,12 @@ export default class User {
return; return;
} }
if (!await this.isLogged()) {
// 如果用户没有登录则拒绝创建ssh key
console.error("User hasn't been logged, refuse to craete user ssh key")
return;
}
const { const {
publicKey, publicKey,
privateKey, privateKey,
@@ -196,7 +247,7 @@ export default class User {
if (!fs.existsSync(publicKeyDir)) { if (!fs.existsSync(publicKeyDir)) {
console.log(`Directory ${publicKeyDir} does not exist, creating it...`); console.log(`Directory ${publicKeyDir} does not exist, creating it...`);
// 公钥与私钥的目录一样,所以只用创建一次 // 公钥与私钥的目录一样,所以只用创建一次
fs.mkdirSync(publicKeyDir, {recursive: true}) fs.mkdirSync(publicKeyDir, { recursive: true })
} }
fs.writeFileSync(this.getUserPublicKeyPath(), publicKeyStr); fs.writeFileSync(this.getUserPublicKeyPath(), publicKeyStr);
@@ -208,7 +259,8 @@ export default class User {
this.updateLocalUserPrivateKeyPath(this.getUserPrivateKeyPath()) this.updateLocalUserPrivateKeyPath(this.getUserPrivateKeyPath())
console.log(`Update local user private key path.`) console.log(`Update local user private key path.`)
} catch (error) { } catch (error) {
console.error(`Failed to write public/private key into the user(${this.username}) ssh public/key file: `, error); const username: string|undefined = this.context.globalState.get(this.usernameKey)
console.error(`Failed to write public/private key into the user(${username}) ssh public/key file: `, error);
} }
} }
} }