Files
devstar/templates/devstar-home-vscode-js.tmpl

1097 lines
42 KiB
Handlebars
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- ======================================= Script ==================================-->
<script>
// ===================================== Initialization ===========================
// Global variables
DEVSTAR_DOMAIN = ""
DEVSTAR_DOMAIN_TEST_URL = "" // It is empty by default in the production environment
var LANGUAGE = "zh-cn" // support display language: zh-cn or en
var USERTOKEN = null
var USERNAME = null
var SIGNED = false
// i18n
const i18n = {
languages: {
'en': {
login_reminder: "Please login!",
login_modal: "Login",
username: "Username",
password: "Password",
login_button: "Login",
createNewRepositoryModal: "Create New Repository",
createNewRepositoryButton: "Create New Repository"
},
'zh-cn': {
login_reminder: "请登录!",
login_modal: "登录",
username: "用户名",
password: "密码",
login_button: "确认",
createNewRepositoryModal: "创建新仓库",
createNewRepositoryButton: "创建新仓库",
}
},
init(lang = LANGUAGE) {
this.currentLang = this.languages[lang] ? lang : 'zh-cn';
this.updateAll();
},
// 更新页面的所有翻译
updateAll() {
document.querySelectorAll('.i18n').forEach(el => {
const key = el.dataset.key;
el.textContent = this.getString(key);
});
},
// 安全获取翻译文本如果没有设置则显示key的名称
getString(key) {
return this.languages[this.currentLang]?.[key] || `[${key}]`
}
};
window.onload = async function () {
if ("" === DEVSTAR_DOMAIN_TEST_URL) {
await getDevstarDomainFromVSCode()
.then(async devstarDomain => {
DEVSTAR_DOMAIN = devstarDomain.endsWith("/") ? devstarDomain.substring(0, devstarDomain.length - 1) : devstarDomain
})
.catch(error => {
DEVSTAR_DOMAIN = "https://devstar.cn"
console.error('Failed to get devstar domain: ', error, '. Use default domain: ', DEVSTAR_DOMAIN)
})
} else {
DEVSTAR_DOMAIN = DEVSTAR_DOMAIN_TEST_URL
}
// initiate home
// display language
await getHomeConfigFromVSCode()
.then(async homeConfig => {
LANGUAGE = homeConfig.language
i18n.init()
})
.catch(error => {
console.error("Failed to get home config from vscode ", error)
})
// related to login status
await getUserTokenFromVSCode()
.then(async userToken => {
// verify user token
await verifyToken(userToken)
.then(result => {
// initialize user token
USERTOKEN = userToken
})
.catch(error => {
console.error('Error in verifying token:', error)
})
})
.catch(error => {
console.error("Failed to get user token from vscode: ", error)
})
await getUsernameFromVSCode()
.then(async username => {
USERNAME = username
})
.catch(error => {
console.error('Failed to get user name from vscode: ', error)
})
SIGNED = true ? USERTOKEN && USERNAME : false;
await reloadPageModules()
}
async function getHomeConfigFromVSCode() {
return new Promise(async (resolve, reject) => {
await communicateVSCodeByWebview('getHomeConfig', null)
.then(async data => {
console.log(data)
const config = data.homeConfig
if (config === undefined) {
reject("homeConfig is undefined")
}
resolve(config)
})
.catch(error => {
reject(error)
})
})
}
async function loadCreatingRepoForm() {
// fetch template data
const issueLabels = await fetchTemplateData(DEVSTAR_DOMAIN + `/api/v1/label/templates2`)
const gitignores = await fetchTemplateData(DEVSTAR_DOMAIN + `/api/v1/gitignore/templates`)
const licenses = await fetchTemplateData(DEVSTAR_DOMAIN + `/api/v1/licenses`)
const readmes = await fetchTemplateData(DEVSTAR_DOMAIN + `/api/v1/readme/templates`).then(data => { return data.data })
const object_formats = await fetchTemplateData(DEVSTAR_DOMAIN + `/api/v1/object_formats`).then(data => { return data.data })
// console.log('issueLabels, gitignores, licenses, readmes, object_formats', issueLabels, gitignores, licenses, readmes, object_formats)
// generate menus
const issue_label_menu = document.getElementById('issue_label_menu');
issueLabels.forEach(label => {
const newItem = document.createElement('div');
newItem.classList.add('item');
newItem.setAttribute('data-value', label.DisplayName);
newItem.innerHTML = `${label.DisplayName}<br><i>(${label.Description})</i>`;
issue_label_menu.appendChild(newItem);
});
const gitignore_menu = document.getElementById('gitignore_menu');
gitignores.forEach(gitignore => {
const newItem = document.createElement('div');
newItem.classList.add('item');
newItem.setAttribute('data-value', gitignore)
newItem.innerHTML = `${gitignore}`;
gitignore_menu.appendChild(newItem);
})
const license_menu = document.getElementById('license_menu');
licenses.forEach(license => {
const newItem = document.createElement('div');
newItem.classList.add('item');
newItem.setAttribute('data-value', license.name)
newItem.innerHTML = `${license.name}`;
license_menu.appendChild(newItem);
})
const readme_menu = document.getElementById('readme_menu');
readmes.forEach(readme => {
const newItem = document.createElement('div');
newItem.classList.add('item');
newItem.setAttribute('data-value', readme.name)
newItem.innerHTML = `${readme.name}`;
readme_menu.appendChild(newItem);
})
const defaultBranchElement = document.getElementById('default_branch');
if (defaultBranchElement) {
defaultBranchElement.value = 'main'; // Set the value of the input field to "main"
defaultBranchElement.placeholder = 'main'; // Optionally update the placeholder as well
}
const defaultObjectFormatElement = document.getElementById('object_format_name');
if (defaultObjectFormatElement) {
defaultObjectFormatElement.value = object_formats[0].name
}
const default_object_format_name = document.getElementById('default_object_format_name')
if (default_object_format_name) {
default_object_format_name.innerHTML = object_formats[0].name
}
const object_format_menu = document.getElementById('object_format_menu')
object_formats.forEach(object_format => {
const newItem = document.createElement('div');
newItem.classList.add('item');
newItem.setAttribute('data-value', object_format.name)
newItem.innerHTML = `${object_format.name}`;
object_format_menu.appendChild(newItem);
})
}
async function fetchTemplateData(url) {
return new Promise((resolve, reject) => {
try {
fetch(url, {
method: 'GET',
})
.then(response => {
response.json().then(data => {
if (response.ok) {
resolve(data)
} else {
throw new Error(`Failed to fetch template data from url: ${url} Error: ${data.message}`)
}
})
})
} catch (error) {
reject(error)
console.error(error)
}
})
}
async function getDevstarDomainFromVSCode() {
return new Promise(async (resolve, reject) => {
await communicateVSCodeByWebview('getDevstarDomain', null)
.then(async data => {
const devstarDomain = data.devstarDomain
if (undefined === devstarDomain) {
reject("devstar domain is undefined")
}
resolve(devstarDomain)
})
.catch(error => {
reject(error)
})
})
}
async function getUserTokenFromVSCode() {
return new Promise(async (resolve, reject) => {
await communicateVSCodeByWebview('getUserToken', null)
.then(async data => {
const userToken = data.userToken
if (userToken === undefined) {
reject("userToken is undefined")
}
resolve(userToken)
})
.catch(error => {
reject(error)
})
})
}
async function getUsernameFromVSCode() {
return new Promise(async (resolve, reject) => {
await communicateVSCodeByWebview('getUsername', null)
.then(async data => {
const username = data.username
if (username === undefined) {
reject('username is undefined')
}
resolve(username)
})
.catch(error => {
reject(error)
})
})
}
function verifyToken(token) {
return new Promise((resolve, reject) => {
fetch(DEVSTAR_DOMAIN + '/api/devcontainer/user', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
})
.then(response => {
if (response.ok) {
resolve(response.status)
} else {
reject(new Error("Error code", response.status))
}
})
})
}
async function reloadPageModules() {
// everytime login status change, call this function
if (SIGNED) {
// head
// document.getElementById('signup-link').style.display = 'none'
document.getElementById('signin-link').style.display = 'none'
document.getElementById('logout-link').style.display = 'flex'
// html content
document.getElementById('contentBeforeSigned').style.display = 'none'
document.getElementById('contentAfterSigned').style.display = 'block'
// load data for page
loadRepositories() // repo list
await loadCreatingRepoForm() // form for creating repo
} else {
// document.getElementById('signup-link').style.display = 'flex'
document.getElementById('signin-link').style.display = 'flex'
document.getElementById('logout-link').style.display = 'none'
// html content
document.getElementById('contentBeforeSigned').style.display = 'block'
document.getElementById('contentAfterSigned').style.display = 'none'
}
}
// ===================================== login and logout ===========================
// login model
function openLoginModal() {
// check if user has logged in, only show login modal when user has not logged in
if (SIGNED) {
console.log('User has logged in')
showInformationNotification('已登录')
} else {
document.getElementById('loginModal').style.display = 'block';
}
}
function closeLoginModal() {
document.getElementById('loginModal').style.display = 'none';
}
async function login() {
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
const url = DEVSTAR_DOMAIN + `/api/v1/users/${username}/tokens`;
// Base64编码用户名和密码
const base64Credentials = btoa(username + ':' + password);
const tokenName = generateTokenName(10);
postData = {
"name": tokenName,
"scopes": ["write:user", "write:repository"]
}
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + base64Credentials
},
body: JSON.stringify(postData)
})
.then(response => {
response.json().then(async data => {
if (response.ok) {
// store token in global variable and vscode global state
USERTOKEN = data.sha1;
USERNAME = username;
setUserTokenToVSCode(USERTOKEN);
setUsernameToVSCode(username);
SIGNED = true
await reloadPageModules()
closeLoginModal()
// if user public key exist, meaning that public key has been uploaded
await getUserPublicKeyFromVSCode()
.then(async userPublicKey => {
if (userPublicKey === '') {
await createUserPublicKeyByVSCode()
.then(async () => {
// only upload new created public key
await getUserPublicKeyFromVSCode()
.then(async userPublicKey => {
const dataFromResp = await communicateVSCodeByWebview('getMachineName', {});
const machineName = dataFromResp.machineName;
// key title: username-machine-timestamp
const timestamp = Date.now();
const keyTitle = `${USERNAME}-${machineName}-${timestamp}`
await uploadNewCreatedPublicKey(userPublicKey, keyTitle);
})
.catch(error => {
console.error("Failed to get NEW CREATED user public key from vscode: ", error)
})
})
}
})
.catch(error => {
console.error("Failed to get user public key from vscode: ", error)
})
} else {
showErrorNotification(`登录失败!\nError: ${data.message}`)
throw new Error(`登录失败!\nError: ${data.message}`);
}
})
})
.catch(error => {
console.error('There has been a problem when logging', error);
});
}
function generateTokenName(length = 10) {
// tokenName is random string and number and _ combination (10 characters)
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_';
let name = 'vscode_login_token_';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
name += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return name
}
async function logout() {
// remove token and username from global variable and vscode global state
USERTOKEN = null
USERNAME = null
await setUserTokenToVSCode('')
await setUsernameToVSCode('')
SIGNED = false
await reloadPageModules()
}
async function setUserTokenToVSCode(userToken) {
var removeUserToken = false;
if ('' === userToken) {
removeUserToken = true
}
communicateVSCodeByWebview('setUserToken', { userToken: userToken })
.then(result => {
if (result.ok) {
console.log(removeUserToken ? 'User token has been removed from vscode' : 'User token has been stored in vscode')
} else {
console.error(removeUserToken ? 'Failed to remove user token from vscode' : 'Failed to store user token into vscode')
}
})
.catch(error => {
console.error('Failed to set user token into vscode:', error)
})
}
async function setUsernameToVSCode(username) {
var removeUsername = false;
if ('' === username) {
removeUsername = true;
}
await communicateVSCodeByWebview('setUsername', { username: username })
.then(result => {
if (result.ok) {
console.log(removeUsername ? 'User name has been removed from vscode' : 'User name has been stored in vscode')
} else {
console.error(removeUsername ? 'Failed to removing user name from vscode' : 'Failed to store user name in vscode')
}
})
.catch(error => {
console.error("Error happened when setting user name: ", error)
})
}
async function getUserPublicKeyFromVSCode() {
return new Promise(async (resolve, reject) => {
await communicateVSCodeByWebview('getUserPublicKey', {})
.then(data => {
const publicKey = data.userPublicKey;
resolve(publicKey)
})
.catch(error => {
reject(error)
})
})
}
async function createUserPublicKeyByVSCode() {
await communicateVSCodeByWebview('createUserPublicKey', {})
.then(res => {
if (res.ok) {
console.log('User public key has been created')
} else {
console.error('Failed to create user public key')
}
})
.catch(error => {
console.error('Failed to request to create user public key: ', erro)
})
}
async function uploadNewCreatedPublicKey(userPublicKey, keyTitle) {
const postData = {
"key": userPublicKey,
"title": keyTitle
}
const uploadUrl = DEVSTAR_DOMAIN + `/api/v1/user/keys`;
fetch(uploadUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + USERTOKEN
},
body: JSON.stringify(postData)
})
.then(response => {
response.json().then(data => {
if (response.ok) {
console.log("Successfully upload new created public key.\n", data)
} else {
throw new Error(`Failed to upload new created public key!\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error);
});
}
// ===================================== Repo ===========================
function loadRepositories() {
// clear old data
const repoList = document.getElementById('repo_list')
repoList.innerHTML = '';
// load new data
var url = DEVSTAR_DOMAIN + "/api/v1/user/repos?page=1&limit=20"
var token = USERTOKEN
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
})
.then(response => {
response.json().then(data => {
if (response.ok) {
let repos = data;
repos.forEach((repo, index) => {
const repoFullName = repo.full_name;
const repoURL = repo.html_url;
const repoID = repo.id;
hasDevContainer(repoID)
.then(found => {
addItemForRepoList(repoFullName, repoURL, repoID, found)
})
})
} else {
throw new Error(`Failed to load repos, Error: ${data.message}`);
}
})
})
.catch(error => {
console.error(error);
});
}
/**
* isOpen表示项目是否已经被打开
*/
function addItemForRepoList(repoFullName, repoURL, repoID, hasDevContainer) {
const repoList = document.getElementById('repo_list');
// 创建新的 flex-item 元素
const flexItem = document.createElement('div');
flexItem.classList.add('flex-item');
// 创建 flex-item-main
const flexItemMain = document.createElement('div');
flexItemMain.classList.add('flex-item-main');
// 创建 flex-item-header
const flexItemHeader = document.createElement('div');
flexItemHeader.classList.add('flex-item-header');
// 创建 flex-item-title
const flexItemTitle = document.createElement('div');
flexItemTitle.classList.add('flex-item-title');
// 创建名称部分
const repoNameDiv = document.createElement('div');
repoNameDiv.classList.add('text', 'name');
repoNameDiv.textContent = repoFullName;
// 创建 flex-item-trailing
const flexItemTrailing = document.createElement('div');
flexItemTrailing.classList.add('flex-item-trailing');
// 创建按钮
const createContainerBtn = document.createElement('button');
createContainerBtn.id = `${repoID}_createContainerBtn`
createContainerBtn.classList.add('ui', 'green', 'button');
createContainerBtn.onclick = function () {
createDevContainer(repoID)
}
const openProjectBtn = document.createElement('button');
openProjectBtn.id = `${repoID}_openProjectBtn`
openProjectBtn.classList.add('ui', 'primary', 'button');
openProjectBtn.onclick = function () {
openDevContainer(repoID)
}
const deleteContainerBtn = document.createElement('button');
deleteContainerBtn.id = `${repoID}_deleteContainerBtn`
deleteContainerBtn.classList.add('ui', 'red', 'button');
deleteContainerBtn.onclick = function () {
deleteDevContainer(repoID)
}
// 按钮名称根据语言配置
switch (LANGUAGE) {
case 'zh-cn':
createContainerBtn.textContent = '创建容器';
openProjectBtn.textContent = '打开项目';
deleteContainerBtn.textContent = '删除容器'
break
case 'en':
createContainerBtn.textContent = 'Create Container'
openProjectBtn.textContent = 'Open Project'
deleteContainerBtn.textContent = 'Delete Container'
break;
}
// 创建 flex-item-body
const flexItemBody = document.createElement('div');
flexItemBody.classList.add('flex-item-body');
flexItemBody.textContent = repoURL;
// 组合所有部分
flexItemTitle.appendChild(repoNameDiv);
flexItemTrailing.appendChild(createContainerBtn);
flexItemTrailing.appendChild(openProjectBtn);
flexItemTrailing.appendChild(deleteContainerBtn);
flexItemHeader.appendChild(flexItemTitle);
flexItemHeader.appendChild(flexItemTrailing);
flexItemMain.appendChild(flexItemHeader);
flexItemMain.appendChild(flexItemBody);
flexItem.appendChild(flexItemMain);
// 将新创建的 flex-item 添加到 repo_list 中
repoList.appendChild(flexItem);
// 调整按钮显示
if (hasDevContainer) {
adjustBtnForRepoItem(repoID, 'created')
} else {
adjustBtnForRepoItem(repoID, 'not create')
}
}
function adjustBtnForRepoItem(repoId, status) {
// status: not create, createing, created
const createContainerBtn = document.getElementById(`${repoId}_createContainerBtn`);
const openProjectBtn = document.getElementById(`${repoId}_openProjectBtn`);
const deleteContainerBtn = document.getElementById(`${repoId}_deleteContainerBtn`);
if (status === 'not create') {
createContainerBtn.style.display = 'block';
openProjectBtn.style.display = 'none';
deleteContainerBtn.style.display = 'none';
} else if (status === 'creating') {
createContainerBtn.classList.toggle('disabled')
openProjectBtn.style.display = 'none';
deleteContainerBtn.style.display = 'none';
} else {
createContainerBtn.style.display = 'none';
openProjectBtn.style.display = 'block';
deleteContainerBtn.style.display = 'block';
}
}
// 创建仓库modal
function openModalForCreateRepository() {
document.getElementById('modalForCreatingRepo').style.display = "block";
}
function closeModalForCreatingRepo() {
document.getElementById('modalForCreatingRepo').style.display = "none";
}
// 点击窗外关闭弹窗
window.onclick = function (event) {
if (event.target == document.getElementById('modalForCreatingRepo')) {
closeModalForCreatingRepo();
}
if (event.target == document.getElementById('loginModal')) {
closeLoginModal();
}
}
function createRepo() {
// 这里添加实际的表单提交逻辑
// 模拟表单处理
var repo_name = document.getElementById('repo_name').value;
// 暂时不支持private
// var is_private = document.getElementById('is_private').checked;
var description = document.getElementById('description').value;
var template_url = document.getElementById('devstar_template').value;
var issue_labels = document.getElementById('issue_label').value
var gitignore = document.getElementById('gitignore').value
var license = document.getElementById('license').value
var readme = document.getElementById('readme').value
var default_branch = document.getElementById('default_branch').value;
var object_format_name = document.getElementById('object_format_name').value;
var is_template = document.getElementById('is_template').checked
const url = DEVSTAR_DOMAIN + "/api/v1/user/repos"
var token = USERTOKEN
const postData = {
'name': repo_name,
// 'private': is_private,
'trust_model': 'default',
"auto_init": true
}
if (description) {
postData.description = description
}
if (issue_labels) {
postData.issue_labels = issue_labels
}
if (gitignore) {
postData.gitignore = gitignore
}
if (license) {
postData.license = license
}
if (readme) {
postData.readme = readme
}
if (default_branch) {
postData.default_branch = default_branch
}
if (object_format_name) {
postData.object_format_name = object_format_name
}
if (is_template) {
postData.template = is_template
}
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
body: JSON.stringify(postData)
})
.then(response => {
response.json().then(data => {
if (response.ok) {
showInformationNotification('项目创建成功!')
console.log('项目创建成功!')
loadRepositories()
closeModalForCreatingRepo(); // 关闭创建项目弹窗
} else {
showErrorNotification(`项目创建失败!\nError: ${data.message}`)
throw new Error(`项目创建失败!\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error);
});
return false;
}
// ===================================== Projects ===========================
async function openDevContainer(repoId) {
console.log("opening project")
// open devcontainer through repoId
var url = DEVSTAR_DOMAIN + "/api/devcontainer"
var token = USERTOKEN
const queryParams = new URLSearchParams({
repoId: repoId,
wait: true
}).toString();
const urlWithParams = `${url}?${queryParams}`;
fetch(urlWithParams, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
})
.then(response => {
response.json().then(data => {
const responseCode = data.code
if (response.ok && responseCode === 0) {
// container start successfully
// get devContainer ssh connection information
const projectName = data.data.repoName
const devContainerHost = data.data.devContainerHost
const devContainerUsername = data.data.devContainerUsername
const devContainerPort = data.data.devContainerPort
const devContainerWorkDir = data.data.devContainerWorkDir
// default: open with key
communicateVSCodeByWebview('firstOpenRemoteFolder', {
host: `${projectName}`,
hostname: `${devContainerHost}`,
username: `${devContainerUsername}`,
port: `${devContainerPort}`,
path: `${devContainerWorkDir}`,
})
} else if (response.ok) {
// TODO: 等待的code
console.warn(`打开容器失败code: ${responseCode}\nError: ${data.data.ErrorMsg}`)
showErrorNotification(`打开容器失败!\nError: ${data.data.ErrorMsg}`)
throw new Error(`打开容器失败!\nError: ${data.data.ErrorMsg}`)
} else {
showErrorNotification(`打开容器失败!\nError: ${data.message}`)
throw new Error(`打开容器失败!\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error);
});
}
async function hasDevContainer(repoId) {
return new Promise(async (resolve) => {
url = DEVSTAR_DOMAIN + "/api/devcontainer/user"
token = USERTOKEN
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
})
.then(response => {
response.json().then(data => {
if (response.ok) {
let found = false
const devContainers = data.data.devContainers;
devContainers.forEach((c, index) => {
if (String(c.repoId) === String(repoId)) {
// has devContainer
found = true
resolve(true)
}
});
if (!found) {
resolve(false)
}
} else {
throw new Error(`Failed to fetch devContainer list.\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error);
});
})
}
async function createDevContainer(repoId) {
showInformationNotification("正在创建开发容器...")
// request creating container
const url = DEVSTAR_DOMAIN + "/api/devcontainer"
var token = USERTOKEN
const postData = {
"repoId": repoId.toString(),
// "sshPublicKeyList": [publicKey]
}
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
body: JSON.stringify(postData)
})
.then(response => {
response.json().then(async data => {
const responseCode = data.code
if (response.ok && responseCode === 0) {
// TODO: 根据中英双语显示
showInformationNotification(`创建容器成功!项目初始化中,请等待...`)
adjustBtnForRepoItem(repoId, 'creating')
console.log('Successfully create dev container!')
// 此时等待容器准备完成
try {
await sleep(3000)
const projectStatus = await projectCompletionSign(repoId);
if (projectStatus === 'ok') {
console.log('Project prepared!')
adjustBtnForRepoItem(repoId, 'created')
showInformationNotification(`项目已准备完成!`)
} else {
// projectCompletionSign 抛出了错误或者返回了 'error'
console.warn("项目初始化失败或超时");
}
} catch (error) {
console.error("项目初始化失败:", error);
adjustBtnForRepoItem(repoId, 'created')
}
} else if (response.ok) {
console.warn(responseCode)
showErrorNotification(`创建容器失败!\nError: ${data.data.ErrorMsg}`)
throw new Error(`创建容器失败!\nError: ${data.data.ErrorMsg}`)
} else {
showErrorNotification(`创建容器失败!\nError: ${data.message}`)
throw new Error(`创建容器失败!\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error);
});
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function projectCompletionSign(repoId) {
return new Promise((resolve, reject) => {
const maxRetries = 60; // 最大重试次数
let retryCount = 0;
const intervalId = setInterval(async () => {
var url = DEVSTAR_DOMAIN + "/api/devcontainer";
var token = USERTOKEN;
const queryParams = new URLSearchParams({
repoId: repoId,
wait: false
}).toString();
const urlWithParams = `${url}?${queryParams}`;
try {
const response = await fetch(urlWithParams, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
});
const data = await response.json();
const responseCode = data.code;
const devcontainerStatus = data.data.devContainerStatus
console.log("容器状态: ", response.ok, responseCode, devcontainerStatus)
if (responseCode == 0 && devcontainerStatus == 4) {
// 项目准备完成
clearInterval(intervalId); // 清除定时器
resolve('ok');
return;
} else if (responseCode == 0) {
// 继续
} else {
showErrorNotification(`项目初始化失败!\nError: ${data.message}`);
clearInterval(intervalId);
reject(new Error(`项目初始化失败!\nError: ${data.message}`));
return;
}
} catch (error) {
console.error(error);
// 发生错误时,不清除定时器,继续重试
}
retryCount++;
if (retryCount >= maxRetries) {
clearInterval(intervalId); // 清除定时器
reject(new Error("达到最大重试次数,项目可能未准备好")); // Reject Promise
}
}, 1000); // 每秒检查一次
});
}
function deleteDevContainer(repoId) {
var url = DEVSTAR_DOMAIN + "/api/devcontainer"
var token = USERTOKEN
const queryParams = new URLSearchParams({
repoId: repoId,
}).toString();
const urlWithParams = `${url}?${queryParams}`;
fetch(urlWithParams, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
})
.then(response => {
response.json().then(data => {
const responseCode = data.code
if (response.ok && responseCode === 0) {
showInformationNotification("删除容器成功!")
adjustBtnForRepoItem(repoId, 'not create')
console.log('Successfully delete dev container belong to repoId:', repoId, data)
} else if (response.ok) {
showErrorNotification(`删除容器失败!\nError: ${data.data.ErrorMsg}`)
throw new Error(`删除容器失败!\nError: ${data.data.ErrorMsg}`)
} else {
showErrorNotification(`删除容器失败!\nError: ${data.message}`)
throw new Error(`删除容器失败!\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error)
})
}
function firstOpenRemoteFolder(host, hostname, username, password, port, path) {
const message = {
action: 'firstOpenRemoteFolder',
host: host,
hostname: hostname,
username: username,
password: password,
port: port,
path: path,
}
// 向iframe父页面发送消息
window.parent.postMessage(message, '*');
}
function openRemoteFolder(host, path) {
const message = {
action: 'openRemoteFolder',
host: host,
path: path,
}
// 向iframe父页面发送消息
window.parent.postMessage(message, '*');
}
// ===================================== Utils ===========================
// 统一通知
function showInformationNotification(message) {
communicateVSCodeByWebviewNoReturn('showInformationNotification', { message: message })
}
function showWarningNotification(message) {
communicateVSCodeByWebviewNoReturn('showWarningNotification', { message: message })
}
function showErrorNotification(message) {
communicateVSCodeByWebviewNoReturn('showErrorNotification', { message: message })
}
async function communicateVSCodeByWebviewNoReturn(action, data) {
window.parent.postMessage({ target: "vscode_no_return", action: action, data: data }, "*");
}
async function communicateVSCodeByWebview(action, data) {
return new Promise((resolve, reject) => {
// request to webview
window.parent.postMessage({ target: "vscode", action: action, data: data }, '*');
// response from webview
function handleResponse(event) {
const jsonData = event.data
if (jsonData.action === action) {
// return webview response
console.log("dataFromVSCodeByWebview", jsonData.data)
window.removeEventListener('message', handleResponse) // 清理监听器
resolve(jsonData.data)
}
}
window.addEventListener('message', handleResponse)
setTimeout(() => {
window.removeEventListener('message', handleResponse)
reject('timeout')
}, 10000); // 10秒超时
})
}
</script>