merge code from home.html

This commit is contained in:
Levi Yan
2024-12-18 09:50:36 +08:00
repo.diff.committed_by 戴明辰
repo.diff.parent 52fbc5c556
repo.diff.commit 36a2f71dc7

repo.diff.view_file

@@ -1,51 +1,922 @@
{{template "base/head" .}}
<!DOCTYPE html>
<html lang="{{ctx.Locale.Lang}}" data-theme="{{UserThemeName .SignedUser}}">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{if .Title}}{{.Title}} - {{end}}{{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}}</title>
{{if .ManifestData}}<link rel="manifest" href="data:{{.ManifestData}}">{{end}}
<meta name="author" content="{{if .Repository}}{{.Owner.Name}}{{else}}{{MetaAuthor}}{{end}}">
<meta name="description" content="{{if .Repository}}{{.Repository.Name}}{{if .Repository.Description}} - {{.Repository.Description}}{{end}}{{else}}{{MetaDescription}}{{end}}">
<meta name="keywords" content="{{MetaKeywords}}">
<meta name="referrer" content="no-referrer">
{{if .GoGetImport}}
<meta name="go-import" content="{{.GoGetImport}} git {{.RepoCloneLink.HTTPS}}">
<meta name="go-source" content="{{.GoGetImport}} _ {{.GoDocDirectory}} {{.GoDocFile}}">
{{end}}
{{if and .EnableFeed .FeedURL}}
<link rel="alternate" type="application/atom+xml" title="" href="{{.FeedURL}}.atom">
<link rel="alternate" type="application/rss+xml" title="" href="{{.FeedURL}}.rss">
{{end}}
<link rel="icon" href="{{AssetUrlPrefix}}/img/favicon.svg" type="image/svg+xml">
<link rel="alternate icon" href="{{AssetUrlPrefix}}/img/favicon.png" type="image/png">
{{template "base/head_script" .}}
<noscript>
<style>
.dropdown:hover > .menu { display: block; }
.ui.secondary.menu .dropdown.item > .menu { margin-top: 0; }
</style>
</noscript>
{{template "base/head_opengraph" .}}
{{template "base/head_style" .}}
</head>
<body hx-headers='{"x-csrf-token": "{{.CsrfToken}}"}' hx-swap="outerHTML" hx-ext="morph" hx-push-url="false">
<div class="full height">
<noscript>{{ctx.Locale.Tr "enable_javascript"}}</noscript>
{{template "custom/body_inner_pre" .}}
{{$notificationUnreadCount := 0}}
{{if and .IsSigned .NotificationUnreadCount}}
{{$notificationUnreadCount = call .NotificationUnreadCount}}
{{end}}
<nav id="navbar" aria-label="{{ctx.Locale.Tr "aria.navbar"}}">
<div class="navbar-left">
<!-- the logo -->
<a class="item" id="navbar-logo" href="{{AppSubUrl}}/" aria-label="{{if .IsSigned}}{{ctx.Locale.Tr "dashboard"}}{{else}}{{ctx.Locale.Tr "home"}}{{end}}">
<img width="auto" height="30" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{ctx.Locale.Tr "logo"}}" aria-hidden="true">
</a>
<!-- mobile right menu, it must be here because in mobile view, each item is a flex column, the first item is a full row column -->
<div class="ui secondary menu item navbar-mobile-right only-mobile">
<!-- {{if and .IsSigned EnableTimetracking .ActiveStopwatch}}-->
<!-- <a id="mobile-stopwatch-icon" class="active-stopwatch item tw-mx-0" href="{{.ActiveStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}" data-seconds="{{.ActiveStopwatch.Seconds}}">-->
<!-- <div class="tw-relative">-->
<!-- {{svg "octicon-stopwatch"}}-->
<!-- <span class="header-stopwatch-dot"></span>-->
<!-- </div>-->
<!-- </a>-->
<!-- {{end}}-->
{{if .IsSigned}}
<a id="mobile-notifications-icon" class="item tw-w-auto tw-p-2" href="{{AppSubUrl}}/notifications" data-tooltip-content="{{ctx.Locale.Tr "notifications"}}" aria-label="{{ctx.Locale.Tr "notifications"}}">
<div class="tw-relative">
{{svg "octicon-bell"}}
<span class="notification_count{{if not $notificationUnreadCount}} tw-hidden{{end}}">{{$notificationUnreadCount}}</span>
</div>
</a>
{{end}}
<button class="item tw-w-auto ui icon mini button tw-p-2 tw-m-0" id="navbar-expand-toggle" aria-label="{{ctx.Locale.Tr "home.nav_menu"}}">{{svg "octicon-three-bars"}}</button>
</div>
<!-- navbar links non-mobile -->
{{if and .IsSigned .MustChangePassword}}
{{/* No links */}}
{{else if .IsSigned}}
<!-- {{if not ctx.Consts.RepoUnitTypeIssues.UnitGlobalDisabled}}-->
<!-- <a class="item{{if .PageIsIssues}} active{{end}}" href="{{AppSubUrl}}/issues">{{ctx.Locale.Tr "issues"}}</a>-->
<!-- {{end}}-->
<!-- {{if not ctx.Consts.RepoUnitTypePullRequests.UnitGlobalDisabled}}-->
<!-- <a class="item{{if .PageIsPulls}} active{{end}}" href="{{AppSubUrl}}/pulls">{{ctx.Locale.Tr "pull_requests"}}</a>-->
<!-- {{end}}-->
<!-- {{if not (and ctx.Consts.RepoUnitTypeIssues.UnitGlobalDisabled ctx.Consts.RepoUnitTypePullRequests.UnitGlobalDisabled)}}-->
<!-- {{if .ShowMilestonesDashboardPage}}-->
<!-- <a class="item{{if .PageIsMilestonesDashboard}} active{{end}}" href="{{AppSubUrl}}/milestones">{{ctx.Locale.Tr "milestones"}}</a>-->
<!-- {{end}}-->
<!-- {{end}}-->
<!-- <a class="item{{if .PageIsExplore}} active{{end}}" href="{{AppSubUrl}}/explore/repos">{{ctx.Locale.Tr "explore"}}</a>-->
{{else if .IsLandingPageOrganizations}}
<!-- <a class="item{{if .PageIsExplore}} active{{end}}" href="{{AppSubUrl}}/explore/organizations">{{ctx.Locale.Tr "explore"}}</a>-->
{{else}}
<!-- <a class="item{{if .PageIsExplore}} active{{end}}" href="{{AppSubUrl}}/explore/repos">{{ctx.Locale.Tr "explore"}}</a>-->
{{end}}
{{template "custom/extra_links" .}}
{{if not .IsSigned}}
<!-- <a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com">{{ctx.Locale.Tr "help"}}</a>-->
{{end}}
</div>
<!-- the full dropdown menus -->
<div class="navbar-right">
<a id="signup-link" class="item active" href="{{AppSubUrl}}/user/sign_up">
{{svg "octicon-person"}} {{ctx.Locale.Tr "register"}}
</a>
<a id="signin-link" class="item active" rel="nofollow" href="javascript:openLoginModal()">
{{svg "octicon-sign-in"}} {{ctx.Locale.Tr "sign_in"}}
</a>
<a id="logout-link" class="item active" href="javascript:logout()">
{{svg "octicon-sign-out"}}
{{ctx.Locale.Tr "sign_out"}}
</a>
{{if and .IsSigned .MustChangePassword}}
{{else if .IsSigned}}
{{else}}
{{end}}
</div><!-- end full right menu -->
</nav>
<div role="main" aria-label="{{if .IsSigned}}{{ctx.Locale.Tr "dashboard"}}{{else}}{{ctx.Locale.Tr "home"}}{{end}}" class="page-content home">
<div class="tw-mb-8 tw-px-8">
<div class="center">
<img class="logo" width="220" height="220" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{ctx.Locale.Tr "logo"}}">
<div class="hero">
<h1 class="ui icon header title">
{{AppName}}
</h1>
<h2>{{ctx.Locale.Tr "startpage.app_desc"}}</h2>
<!--<h1>DevStar Home</h1>-->
<div class="ui middle very relaxed page">
<!-- login -->
<div id="loginModal" class="ui modal" style=" width:50%; left: 25%; top: 20%">
<div class="close right" onclick="closeLoginModal()">X</div>
<div class="content">
<div class="ui container column fluid">
<h2 class="ui top attached header center">Login</h2>
<div class="ui attached segment">
<form id="loginForm" class="ui form" action="javascript:login()">
<div class="required field">
<label for="username">Username</label>
<input type="text" id="username" name="username" required>
</div>
<div class="required field">
<label for="password">Password</label>
<input type="password" id="password" name="password" required>
</div>
<button class="ui button primary center">Login</button>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="ui stackable middle very relaxed page grid">
<div class="eight wide center column">
<h1 class="hero ui icon header">
{{svg "octicon-flame"}} {{ctx.Locale.Tr "startpage.install"}}
</h1>
<p class="large">
{{ctx.Locale.Tr "startpage.install_desc"}}
</p>
<div class="column">
<div class="ui container">
<button class="ui button primary" onclick="openModalForCreateRepository()">Create New Repository</button>
</div>
<div class="divider"></div>
<div class="ui container">
<h2 class="header">Repositories</h2>
<table class="ui table" id="reposTable">
<thead>
<tr>
<th>repoName</th>
<th>repoURL</th>
<th>Operation</th>
</tr>
</thead>
<tbody>
<!-- 动态加载 -->
</tbody>
</table>
</div>
</div>
<div class="eight wide center column">
<h1 class="hero ui icon header">
{{svg "octicon-device-desktop"}} {{ctx.Locale.Tr "startpage.platform"}}
</h1>
<p class="large">
{{ctx.Locale.Tr "startpage.platform_desc"}}
</p>
</div>
</div>
<div class="ui stackable middle very relaxed page grid">
<div class="eight wide center column">
<h1 class="hero ui icon header">
{{svg "octicon-rocket"}} {{ctx.Locale.Tr "startpage.lightweight"}}
</h1>
<p class="large">
{{ctx.Locale.Tr "startpage.lightweight_desc"}}
</p>
</div>
<div class="eight wide center column">
<h1 class="hero ui icon header">
{{svg "octicon-code"}} {{ctx.Locale.Tr "startpage.license"}}
</h1>
<p class="large">
{{ctx.Locale.Tr "startpage.license_desc"}}
</p>
<div id="modalForCreatingRepo" class="ui modal">
<!-- <div class="modal-content">-->
<!-- <span class="close" onclick="closeModalForCreatingRepo()">&times;</span>-->
<!-- <h2>Create New Project</h2>-->
<!-- <form class="ui form" action="javascript:">-->
<!-- &lt;!&ndash; project settings &ndash;&gt;-->
<!-- <label class="required" for="projectName">Name</label>-->
<!-- <input type="text" id="projectName" name="projectName"><br><br>-->
<!-- <label for="projectDesc">Description</label>-->
<!-- <textarea id="projectDesc" name="projectDesc" placeholder="(Optional)"></textarea><br><br>-->
<!-- <label for="repoURL">Repo URL</label>-->
<!-- <input type="text" id="repoURL" name="repoURL"-->
<!-- placeholder="(Optional) Project repository you want to clone"><br><br>-->
<!-- <hr>-->
<!-- &lt;!&ndash; repo settings&ndash;&gt;-->
<!-- <label for="template">-->
<!-- As template?-->
<!-- <input type="checkbox" id="template">-->
<!-- </label>-->
<!-- <button type="button" onclick="submitRepo()">Create</button>-->
<!-- </form>-->
</div>
<div id="alertBox" class="ui alert"></div>
<!-- ====================== Created Repository ==========================-->
<!-- <button onclick="loadRepositories()">Load Repositories</button>-->
</div>
</div>
</body>
</html>
<!-- ======================================= Script ==================================-->
<script>
// ===================================== Initialization ===========================
// Global variables
DEVSTAR_HOME = "https://devstar.cn"
var USERTOKEN = null
var USERNAME = null
var SIGNED = false
window.onload = async function () {
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;
loadPageModules()
}
function loadPageModules() {
if (SIGNED) {
// head
document.getElementById('signup-link').style.display = 'none'
document.getElementById('signin-link').style.display = 'none'
document.getElementById('logout-link').style.display = 'flex'
// repo
loadRepositories()
} else {
document.getElementById('signup-link').style.display = 'flex'
document.getElementById('signin-link').style.display = 'flex'
document.getElementById('logout-link').style.display = 'none'
// repo
loadRepositories()
}
}
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_HOME + '/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))
}
})
})
}
// ===================================== 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')
showAlert('已登录', 3000) // 消息显示3秒后消失
} 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_HOME + `/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 => {
if (!response.ok) {
throw new Error('Error in logging ' + response.statusText);
closeLoginModal()
}
return response.json();
})
.then(async data => {
// store token in global variable and vscode global state
USERTOKEN = data.sha1;
USERNAME = username;
setUserTokenToVSCode(USERTOKEN);
setUsernameToVSCode(username);
SIGNED = true
loadPageModules()
// 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;
const keyTitle = `${USERNAME}-${machineName}`
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)
})
closeLoginModal()
})
.catch(error => {
closeLoginModal()
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 = '';
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
loadPageModules()
// location.reload()
}
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_HOME + `/api/v1/user/keys`;
fetch(uploadUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + USERTOKEN
},
body: JSON.stringify(postData)
})
.then(response => {
if (!response.ok) {
throw new Error('Error in logging ' + response.statusText);
}
return response.json();
})
.then(async data => {
console.log("Successfully upload new created public key.\n", data)
})
.catch(error => {
console.error('There has been a problem with your fetch operation:', error);
});
}
// ===================================== Repo ===========================
function loadRepositories() {
// clear old data
const tableBody = document.getElementById('reposTable').getElementsByTagName('tbody')[0];
tableBody.innerHTML = '';
// load new data
var url = DEVSTAR_HOME + "/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 => {
if (!response.ok) {
throw new Error('Network response was not ok when loading repos' + response.statusText);
}
return response.json();
})
.then(data => {
var repos = data;
repos.forEach((repo, index) => {
var row = tableBody.insertRow(-1); // 在表格末尾插入一行
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
var cell3 = row.insertCell(2);
const repoFullName = repo.full_name;
const repoURL = repo.html_url;
const repoID = repo.id;
cell1.textContent = repoFullName;
cell2.textContent = repoURL;
cell3.innerHTML = `<button class="ui button primary" onclick="openProject('${repoID}')">Open Project</button>
<button class="ui red button" onclick="deleteDevContainer('${repoID}')">Delete Container</button>
`;
});
})
.catch(error => {
console.error('There has been a problem with your fetch operation:', error);
});
}
// 打开弹窗
function openModalForCreateRepository() {
// make sure login first
if (!USERTOKEN || !USERNAME) {
showAlert('请先登录!', 3000)
return
}
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 submitRepo() {
// 这里添加实际的表单提交逻辑
// 模拟表单处理
var projectName = document.getElementById('projectName').value;
var projectDesc = document.getElementById('projectDesc').value;
var projectTemplate = document.getElementById('template').checked;
// check required fields
if (projectName == '') {
showAlert('请填写必要的项目信息!', 3000) // 消息显示3秒后消失
} else {
}
const url = DEVSTAR_HOME + "/api/v1/user/repos"
var token = USERTOKEN
const postData = {
"name": projectName,
"description": projectDesc,
"template": projectTemplate,
}
fetch(url, {
method: 'POST', // 或者 'POST', 根据API要求
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
body: JSON.stringify(postData)
})
.then(response => {
console.log(response)
if (!response.ok) {
throw new Error('Network response was not ok when creating project' + response.statusText);
}
return response.json();
})
.then(data => {
console.log(data);
showAlert('项目创建成功!', 1500) // 消息显示1.5秒后消失
loadRepositories()
closeModalForCreatingRepo(); // 关闭创建项目弹窗
})
.catch(error => {
console.error('There has been a problem with your fetch operation:', error);
});
}
// ===================================== Projects ===========================
async function openProject(repoId) {
var newCreated = false;
// check if container exist
await hasDevContainer(repoId)
.then(async hasDevContainer => {
if (!hasDevContainer) {
showAlert("正在创建开发容器...", 1500)
await createDevContainer(repoId)
.then(res => {
newCreated = true;
showAlert("创建容器成功!", 1500)
console.log(`Succeed to create dev container for repo ${repoId}`)
})
.catch(error => {
showAlert("创建容器失败!", 1500)
console.log(`Fail to create dev container for repo ${repoId}: `, error)
return;
})
}
}).catch(error => {
console.log("There has a problem when check if the repo has devContainer:", error)
return;
})
console.log("opening project")
// open devcontainer through repoId
var url = DEVSTAR_HOME + "/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 => {
if (!response.ok) {
throw new Error('Network response was not ok when querying devContainer by repoId' + response.statusText);
}
return response.json();
})
.then(data => {
const responseCode = data.code
const reponseMsg = data.msg
if (responseCode == 0) {
// container start successfully
// get devContainer ssh connection information
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: `${devContainerHost}`,
username: `${devContainerUsername}`,
port: `${devContainerPort}`,
path: `${devContainerWorkDir}`
})
} else {
// show Error to User
showAlert("打开容器失败!", 1500)
const responseErrorMsg = data.data.ErrorMsg
console.error("Error happen when starting dev container", responseErrorMsg)
}
})
.catch(error => {
console.error('There has been a problem with your fetch operation:', error);
});
}
async function hasDevContainer(repoId) {
return new Promise((resolve, reject) => {
url = DEVSTAR_HOME + "/api/devcontainer/user"
token = USERTOKEN
fetch(url, {
method: 'GET', // 或者 'POST', 根据API要求
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok when querying devContainer list' + response.statusText);
}
return response.json();
})
.then(data => {
const devContainers = data.data.devContainers;
devContainers.forEach((c, index) => {
if (c.repoId == repoId) {
// has devContainer
resolve(true)
return
}
});
resolve(false)
})
.catch(error => {
console.error('There has been a problem with your fetch operation:', error);
reject(error)
});
})
}
async function createDevContainer(repoId) {
return new Promise(async (resolve, reject) => {
// request creating container
const url = DEVSTAR_HOME + "/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 => {
if (!response.ok) {
throw new Error(`Network response was not ok when creating devContainer ${repoId}` + response.statusText);
}
return response.json();
})
.then(data => {
const responseCode = data.code
if (responseCode == 0) {
resolve("success")
} else {
reject(data.data.ErrorMsg)
}
})
.catch(error => {
console.error('There has been a problem with your fetch operation:', error);
reject(error)
});
})
}
async function getDefaultPublicKeyFromVSCode() {
try {
const data = await communicateVSCodeByWebview('getDefaultPublicKey', null);
const defaultPublicKey = data.defaultPublicKey;
return defaultPublicKey;
} catch (error) {
console.log("Failed to get default public key: ", error)
}
}
function deleteDevContainer(repoId) {
var url = DEVSTAR_HOME + "/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 => {
if (!response.ok) {
throw new Error('Network response was not ok when DELETEING devContainer by repoId' + response.statusText);
}
return response.json();
})
.then(data => {
const respCode = data.code;
if (0 == respCode) {
console.log('Successfully delete dev container belong to repoId:', repoId, data)
showAlert("删除容器成功!", 1500)
} else {
const errorMsg = data.data.ErrorMsg
throw new Error(errorMsg)
}
})
.catch(error => {
showAlert(`删除容器失败\n${error}`, 3000)
console.error(`Failed to delete dev container belong to repoId: ${repoId}\n`, error)
})
}
function firstOpenRemoteFolder(host, username, password, port, path) {
const message = {
action: 'firstOpenRemoteFolder',
host: host,
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 showAlert(alertText, duration) {
document.getElementById('alertBox').innerHTML = alertText;
document.getElementById('alertBox').style.display = 'block';
setTimeout(function () {
document.getElementById('alertBox').style.display = 'none';
}, duration); // 消息显示 duration/1000 秒后消失
}
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')
}, 5000); // 5秒超时
})
}
</script>
{{template "base/footer" .}}