2025-09-20 01:56:37 +00:00
|
|
|
|
{{template "base/head" .}}
|
|
|
|
|
|
<div role="main" aria-label="{{.Title}}" class="page-content repository wiki pages">
|
|
|
|
|
|
{{template "repo/header" .}}
|
|
|
|
|
|
<div class="ui container">
|
|
|
|
|
|
{{template "base/alert" .}}
|
|
|
|
|
|
<!-- 开始:Dev Container 正文 -->
|
|
|
|
|
|
<div class="issue-content">
|
|
|
|
|
|
<!-- 开始:Dev Container 正文内容 - 左侧主展示区 -->
|
|
|
|
|
|
<div class="issue-content-left">
|
|
|
|
|
|
{{if not .HasDevContainerConfiguration}}
|
|
|
|
|
|
|
|
|
|
|
|
<div class="empty-placeholder">
|
|
|
|
|
|
{{svg "octicon-container" 48}}
|
|
|
|
|
|
<h2>{{ctx.Locale.Tr "repo.dev_container_empty"}}</h2>
|
|
|
|
|
|
{{if .isAdmin}}
|
|
|
|
|
|
<form method="get" action="{{.CreateDevcontainerSettingUrl}}" class="ui edit form">
|
|
|
|
|
|
<button class="ui primary button" type="submit">Create</button>
|
|
|
|
|
|
</form>
|
|
|
|
|
|
{{end}}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{{else}}
|
|
|
|
|
|
|
|
|
|
|
|
<div class="ui container">
|
2025-10-31 07:44:19 +00:00
|
|
|
|
|
2025-09-20 01:56:37 +00:00
|
|
|
|
<form class="ui edit form">
|
|
|
|
|
|
<div class="repo-editor-header">
|
|
|
|
|
|
<div class="ui breadcrumb field">
|
|
|
|
|
|
<a class="section" href="{{$.BranchLink}}">{{.Repository.Name}}</a>
|
|
|
|
|
|
{{range $i, $v := .TreeNames}}
|
|
|
|
|
|
<div class="breadcrumb-divider">/</div>
|
|
|
|
|
|
<span class="section"><a href="{{$.BranchLink}}/{{index $.TreePaths $i | PathEscapeSegments}}">{{$v}}</a></span>
|
|
|
|
|
|
{{end}}
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<a href="{{.EditDevcontainerConfigurationUrl}}"><div class="ui primary button" style="margin-left: 10px;width: 4em;height: 1em;">Edit</div></a>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
</form>
|
2025-10-31 07:44:19 +00:00
|
|
|
|
{{if and .ValidateDevContainerConfiguration .HasDevContainer}}
|
2025-09-20 01:56:37 +00:00
|
|
|
|
<iframe id="webTerminalContainer" src="{{.WebSSHUrl}}" width="100%" style="height: 100vh; display: none;" frameborder="0">您的浏览器不支持iframe</iframe>
|
2025-10-31 07:44:19 +00:00
|
|
|
|
{{end}}
|
2025-09-20 01:56:37 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
{{end}}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<!-- 结束:Dev Container 正文内容 - 左侧主展示区 -->
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 开始:Dev Container 正文内容 - 右侧展示区 -->
|
|
|
|
|
|
<div class="issue-content-right ui segment">
|
2025-10-18 08:53:50 +00:00
|
|
|
|
<strong>{{ctx.Locale.Tr "repo.dev_container_control"}}</strong>
|
2025-09-20 01:56:37 +00:00
|
|
|
|
<div class="ui relaxed list">
|
|
|
|
|
|
|
2025-10-31 07:44:19 +00:00
|
|
|
|
{{if and .ValidateDevContainerConfiguration .HasDevContainer}}
|
2025-09-20 01:56:37 +00:00
|
|
|
|
<div style=" display: none;" id="deleteContainer" class="item"><a class="delete-button flex-text-inline" data-modal="#delete-repo-devcontainer-of-user-modal" href="#" data-url="{{.Repository.Link}}/devcontainer/delete">{{svg "octicon-trash" 14}}{{ctx.Locale.Tr "repo.dev_container_control.delete"}}</a></div>
|
|
|
|
|
|
{{if .isAdmin}}
|
|
|
|
|
|
<div style=" display: none;" id="updateContainer" class="item"><a class="delete-button flex-text-inline" style="color:black; " data-modal-id="updatemodal" href="#">{{svg "octicon-database"}}{{ctx.Locale.Tr "repo.dev_container_control.update"}}</a></div>
|
|
|
|
|
|
{{end}}
|
|
|
|
|
|
|
2025-10-18 08:53:50 +00:00
|
|
|
|
<div style=" display: none;" id="restartContainer" class="item"><button class="flex-text-inline" style="color:black; " >{{svg "octicon-terminal" 14 "tw-mr-2"}}{{ctx.Locale.Tr "repo.dev_container_control.start"}}</button></div>
|
|
|
|
|
|
<div style=" display: none;" id="stopContainer" class="item"><button class="flex-text-inline" style="color:black; " >{{svg "octicon-terminal" 14 "tw-mr-2"}}{{ctx.Locale.Tr "repo.dev_container_control.stop"}} </button></div>
|
2025-09-20 01:56:37 +00:00
|
|
|
|
|
|
|
|
|
|
<div style=" display: none;" id="webTerminal" class="item"><a class="flex-text-inline" style="color:black; " href="{{.WebSSHUrl}}" target="_blank">{{svg "octicon-code" 14}}open with WebTerminal</a></div>
|
|
|
|
|
|
<div style=" display: none;" id="vsTerminal" class="item"><a class="flex-text-inline" style="color:black; " onclick="window.location.href = '{{.VSCodeUrl}}'">{{svg "octicon-code" 14}}open with VSCode</a ></div>
|
|
|
|
|
|
<div style=" display: none;" id="cursorTerminal" class="item"><a class="flex-text-inline" style="color:black; " onclick="window.location.href = '{{.CursorUrl}}'">{{svg "octicon-code" 14}}open with Cursor</a ></div>
|
|
|
|
|
|
<div style=" display: none;" id="windsurfTerminal" class="item"><a class="flex-text-inline" style="color:black;" onclick="window.location.href = '{{.WindsurfUrl}}'">{{svg "octicon-code" 14}}open with Windsurf</a ></div>
|
2025-10-18 08:53:50 +00:00
|
|
|
|
|
|
|
|
|
|
{{end}}
|
|
|
|
|
|
{{if .ValidateDevContainerConfiguration}}
|
|
|
|
|
|
<div style=" display: none;" id="createContainer" class="item">
|
2025-09-20 01:56:37 +00:00
|
|
|
|
<div>
|
|
|
|
|
|
<form method="get" action="{{.Repository.Link}}/devcontainer/create" class="ui edit form">
|
2025-10-31 07:44:19 +00:00
|
|
|
|
<button class="flex-text-inline" type="submit">{{svg "octicon-terminal" 14 "tw-mr-2"}} {{ctx.Locale.Tr "repo.dev_container_control.create"}}</button>
|
2025-09-20 01:56:37 +00:00
|
|
|
|
</form>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-10-18 08:53:50 +00:00
|
|
|
|
<div id="loading" class="loading"></div>
|
2025-09-20 01:56:37 +00:00
|
|
|
|
{{end}}
|
|
|
|
|
|
{{if not .ValidateDevContainerConfiguration}}
|
|
|
|
|
|
<div class="item">{{svg "octicon-alert" 16 "tw-mr-2"}} {{ctx.Locale.Tr "repo.dev_container_invalid_config_prompt"}} </div>
|
|
|
|
|
|
{{end}}
|
2025-10-18 08:53:50 +00:00
|
|
|
|
|
2025-09-20 01:56:37 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<!-- 结束:Dev Container 正文内容 - 右侧展示区 -->
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 结束Dev Container 正文内容 -->
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-10-31 07:44:19 +00:00
|
|
|
|
<!-- 自定义警告框 -->
|
|
|
|
|
|
<div id="customAlert" class="custom-alert">
|
|
|
|
|
|
<div class="alert-content">
|
|
|
|
|
|
<div class="alert-header">
|
|
|
|
|
|
<strong>提示信息</strong>
|
|
|
|
|
|
<button class="alert-close" onclick="closeCustomAlert()">×</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div id="alertText" class="alert-body"></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-09-20 01:56:37 +00:00
|
|
|
|
|
|
|
|
|
|
<!-- 确认删除 Dev Container 模态对话框 -->
|
|
|
|
|
|
<div class="ui g-modal-confirm delete modal" id="delete-repo-devcontainer-of-user-modal">
|
|
|
|
|
|
<div class="header">
|
|
|
|
|
|
{{svg "octicon-trash"}}
|
|
|
|
|
|
{{ctx.Locale.Tr "repo.dev_container_control.delete"}}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="content">
|
|
|
|
|
|
<p>{{ctx.Locale.Tr "repo.dev_container_control.deletion_desc"}}</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{{template "base/modal_actions_confirm" .}}
|
|
|
|
|
|
</div>
|
2025-10-31 07:44:19 +00:00
|
|
|
|
<!-- 保存 Dev Container 模态对话框 -->
|
2025-09-20 01:56:37 +00:00
|
|
|
|
<div class="ui g-modal-confirm delete modal" style="width: 35%" id="updatemodal">
|
|
|
|
|
|
<div class="header">
|
|
|
|
|
|
{{ctx.Locale.Tr "repo.dev_container_control.update"}}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="content">
|
|
|
|
|
|
<form class="ui form tw-max-w-2xl tw-m-auto" id="updateForm" onsubmit="submitForm(event)">
|
2025-10-31 07:44:19 +00:00
|
|
|
|
|
2025-09-20 01:56:37 +00:00
|
|
|
|
<div class="required field ">
|
|
|
|
|
|
<label for="RepositoryAddress">Registry:</label>
|
|
|
|
|
|
<input style="border: 1px solid black;" type="text" id="RepositoryAddress" name="RepositoryAddress" value="{{.RepositoryAddress}}">
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="required field ">
|
|
|
|
|
|
<label for="RepositoryUsername">Registry Username:</label>
|
|
|
|
|
|
<input style="border: 1px solid black;" type="text" id="RepositoryUsername" name="RepositoryUsername" value="{{.RepositoryUsername}}">
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="required field ">
|
|
|
|
|
|
<label for="RepositoryPassword">Registry Password:</label>
|
2025-10-31 07:44:19 +00:00
|
|
|
|
<div style="position: relative; display: inline-block; width: 100%;">
|
|
|
|
|
|
<input style="border: 1px solid black; width: 100%; padding-right: 80px;"
|
|
|
|
|
|
type="password"
|
|
|
|
|
|
id="RepositoryPassword"
|
|
|
|
|
|
name="RepositoryPassword"
|
|
|
|
|
|
required
|
|
|
|
|
|
autocomplete="current-password">
|
|
|
|
|
|
<button type="button"
|
|
|
|
|
|
style="position: absolute; right: 5px; top: 50%; transform: translateY(-50%);
|
|
|
|
|
|
background: none; border: none; cursor: pointer; color: #666;
|
|
|
|
|
|
font-size: 12px; padding: 5px 8px;"
|
|
|
|
|
|
onclick="togglePasswordVisibility('RepositoryPassword', this)">
|
|
|
|
|
|
显示密码
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
2025-09-20 01:56:37 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="required field ">
|
|
|
|
|
|
<label for="ImageName">Image(name:tag):</label>
|
|
|
|
|
|
<input style="border: 1px solid black;" type="text" id="ImageName" name="ImageName" value="{{.ImageName}}">
|
|
|
|
|
|
</div>
|
2025-10-31 07:44:19 +00:00
|
|
|
|
<div class="inline field">
|
|
|
|
|
|
<div class="ui checkbox">
|
|
|
|
|
|
{{if not .HasDevContainerDockerfile}}
|
|
|
|
|
|
<input type="checkbox" id="SaveMethod" name="SaveMethod" disabled>
|
|
|
|
|
|
<label for="SaveMethod">There is no Dockerfile</label>
|
|
|
|
|
|
{{else}}
|
|
|
|
|
|
<input type="checkbox" id="SaveMethod" name="SaveMethod" value="on">
|
|
|
|
|
|
<label for="SaveMethod">Build From Dockerfile: {{.DockerfilePath}}</label>
|
|
|
|
|
|
{{end}}
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-09-20 01:56:37 +00:00
|
|
|
|
<div class="actions">
|
|
|
|
|
|
<button class="ui primary button" type="submit" id="updateSubmitButton" >Submit</button>
|
|
|
|
|
|
<button class="ui cancel button" id="updateCloseButton">Close</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
</form>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
2025-10-31 07:44:19 +00:00
|
|
|
|
document.getElementById('updateSubmitButton').addEventListener('click', function() {
|
|
|
|
|
|
const form = document.getElementById('updateForm');
|
|
|
|
|
|
const formData = new FormData(form);
|
|
|
|
|
|
var RepositoryAddress = formData.get('RepositoryAddress');
|
|
|
|
|
|
var RepositoryUsername = formData.get('RepositoryUsername');
|
|
|
|
|
|
var RepositoryPassword = formData.get('RepositoryPassword');
|
|
|
|
|
|
var SaveMethod = formData.get('SaveMethod');
|
|
|
|
|
|
var ImageName = formData.get('ImageName');
|
|
|
|
|
|
if(ImageName != "" && SaveMethod != "" && RepositoryPassword != "" && RepositoryUsername != "" && RepositoryAddress != ""){
|
|
|
|
|
|
document.getElementById('updatemodal').classList.add('is-loading')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-09-20 01:56:37 +00:00
|
|
|
|
var status = '-1'
|
|
|
|
|
|
var intervalID
|
|
|
|
|
|
const createContainer = document.getElementById('createContainer');
|
|
|
|
|
|
const deleteContainer = document.getElementById('deleteContainer');
|
|
|
|
|
|
const updateContainer = document.getElementById('updateContainer');
|
|
|
|
|
|
const restartContainer = document.getElementById('restartContainer');
|
|
|
|
|
|
const stopContainer = document.getElementById('stopContainer');
|
|
|
|
|
|
const webTerminal = document.getElementById('webTerminal');
|
|
|
|
|
|
const vsTerminal = document.getElementById('vsTerminal');
|
|
|
|
|
|
const cursorTerminal = document.getElementById('cursorTerminal');
|
|
|
|
|
|
const windsurfTerminal = document.getElementById('windsurfTerminal');
|
|
|
|
|
|
const webTerminalContainer = document.getElementById('webTerminalContainer');
|
2025-10-18 08:53:50 +00:00
|
|
|
|
const loadingElement = document.getElementById('loading');
|
2025-09-20 01:56:37 +00:00
|
|
|
|
|
|
|
|
|
|
function concealElement(){
|
2025-10-18 08:53:50 +00:00
|
|
|
|
if (createContainer){
|
|
|
|
|
|
createContainer.style.display = 'none';
|
|
|
|
|
|
}
|
2025-09-20 01:56:37 +00:00
|
|
|
|
if (deleteContainer){
|
|
|
|
|
|
deleteContainer.style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (updateContainer) {
|
|
|
|
|
|
updateContainer.style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (restartContainer) {
|
|
|
|
|
|
restartContainer.style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (stopContainer) {
|
|
|
|
|
|
stopContainer.style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (webTerminal) {
|
|
|
|
|
|
webTerminal.style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (vsTerminal) {
|
|
|
|
|
|
vsTerminal.style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (cursorTerminal) {
|
|
|
|
|
|
cursorTerminal.style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (windsurfTerminal) {
|
|
|
|
|
|
windsurfTerminal.style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (webTerminalContainer) {
|
|
|
|
|
|
webTerminalContainer.style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
function displayElement(){
|
|
|
|
|
|
if (deleteContainer){
|
|
|
|
|
|
deleteContainer.style.display = 'block';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (updateContainer) {
|
|
|
|
|
|
updateContainer.style.display = 'block';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (restartContainer) {
|
|
|
|
|
|
restartContainer.style.display = 'block';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (stopContainer) {
|
|
|
|
|
|
stopContainer.style.display = 'block';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (webTerminal) {
|
|
|
|
|
|
webTerminal.style.display = 'block';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (vsTerminal) {
|
|
|
|
|
|
vsTerminal.style.display = 'block';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (cursorTerminal) {
|
|
|
|
|
|
cursorTerminal.style.display = 'block';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (windsurfTerminal) {
|
|
|
|
|
|
windsurfTerminal.style.display = 'block';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (webTerminalContainer) {
|
|
|
|
|
|
webTerminalContainer.style.display = 'block';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getStatus() {
|
|
|
|
|
|
fetch(
|
|
|
|
|
|
'{{.Repository.Link}}'+'/devcontainer/status'
|
|
|
|
|
|
)
|
|
|
|
|
|
.then(response => response.json())
|
|
|
|
|
|
.then(data => {
|
2025-10-18 08:53:50 +00:00
|
|
|
|
if(status !== '9' && status !== '-1' && data.status == '9'){
|
|
|
|
|
|
window.location.reload();
|
|
|
|
|
|
}
|
2025-10-31 07:44:19 +00:00
|
|
|
|
else if(status !== '-1' && data.status == '-1'){
|
2025-10-18 08:53:50 +00:00
|
|
|
|
window.location.reload();
|
|
|
|
|
|
}
|
2025-10-31 07:44:19 +00:00
|
|
|
|
else if(status !== '4' && status !== '-1' && data.status == '4'){
|
|
|
|
|
|
//window.location.reload();
|
2025-10-18 08:53:50 +00:00
|
|
|
|
}
|
2025-10-31 07:44:19 +00:00
|
|
|
|
else if (data.status == '-1' || data.status == '') {
|
2025-10-18 08:53:50 +00:00
|
|
|
|
if (loadingElement) {
|
|
|
|
|
|
loadingElement.style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (createContainer){
|
|
|
|
|
|
createContainer.style.display = 'block';
|
|
|
|
|
|
}
|
2025-09-20 01:56:37 +00:00
|
|
|
|
clearInterval(intervalID);
|
|
|
|
|
|
} else if (data.status == '0' || data.status == '1' || data.status == '2') {
|
|
|
|
|
|
concealElement();
|
|
|
|
|
|
if (webTerminalContainer) {
|
|
|
|
|
|
webTerminalContainer.style.display = 'block';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (deleteContainer){
|
|
|
|
|
|
deleteContainer.style.display = 'block';
|
|
|
|
|
|
}
|
2025-10-18 08:53:50 +00:00
|
|
|
|
if (loadingElement) {
|
|
|
|
|
|
loadingElement.style.display = 'block';
|
|
|
|
|
|
}
|
2025-09-20 01:56:37 +00:00
|
|
|
|
}else if (data.status == '3') {
|
|
|
|
|
|
concealElement();
|
|
|
|
|
|
if (deleteContainer){
|
|
|
|
|
|
deleteContainer.style.display = 'block';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (webTerminalContainer) {
|
|
|
|
|
|
webTerminalContainer.style.display = 'block';
|
|
|
|
|
|
}
|
2025-10-18 08:53:50 +00:00
|
|
|
|
if (loadingElement) {
|
|
|
|
|
|
loadingElement.style.display = 'block';
|
|
|
|
|
|
}
|
2025-09-20 01:56:37 +00:00
|
|
|
|
}else if (data.status == '4') {
|
|
|
|
|
|
displayElement();
|
2025-10-18 08:53:50 +00:00
|
|
|
|
if (loadingElement) {
|
|
|
|
|
|
loadingElement.style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (restartContainer) {
|
|
|
|
|
|
restartContainer.style.display = 'none';
|
|
|
|
|
|
}
|
2025-09-20 01:56:37 +00:00
|
|
|
|
clearInterval(intervalID);
|
|
|
|
|
|
}else if (data.status == '5') {
|
|
|
|
|
|
concealElement();
|
2025-10-18 08:53:50 +00:00
|
|
|
|
if (loadingElement) {
|
|
|
|
|
|
loadingElement.style.display = 'block';
|
|
|
|
|
|
}
|
2025-09-20 01:56:37 +00:00
|
|
|
|
}else if (data.status == '6') {
|
|
|
|
|
|
concealElement();
|
|
|
|
|
|
if (deleteContainer){
|
|
|
|
|
|
deleteContainer.style.display = 'block';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (updateContainer) {
|
|
|
|
|
|
updateContainer.style.display = 'block';
|
|
|
|
|
|
}
|
2025-10-18 08:53:50 +00:00
|
|
|
|
if (loadingElement) {
|
|
|
|
|
|
loadingElement.style.display = 'block';
|
|
|
|
|
|
}
|
2025-09-20 01:56:37 +00:00
|
|
|
|
}else if (data.status == '7') {
|
|
|
|
|
|
concealElement();
|
|
|
|
|
|
if (deleteContainer){
|
|
|
|
|
|
deleteContainer.style.display = 'block';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (updateContainer) {
|
|
|
|
|
|
updateContainer.style.display = 'block';
|
|
|
|
|
|
}
|
2025-10-18 08:53:50 +00:00
|
|
|
|
if (loadingElement) {
|
|
|
|
|
|
loadingElement.style.display = 'block';
|
|
|
|
|
|
}
|
2025-09-20 01:56:37 +00:00
|
|
|
|
}else if (data.status == '8') {
|
|
|
|
|
|
concealElement();
|
|
|
|
|
|
if (deleteContainer){
|
|
|
|
|
|
deleteContainer.style.display = 'block';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (updateContainer) {
|
|
|
|
|
|
updateContainer.style.display = 'block';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (restartContainer) {
|
|
|
|
|
|
restartContainer.style.display = 'block';
|
|
|
|
|
|
}
|
2025-10-18 08:53:50 +00:00
|
|
|
|
if (loadingElement) {
|
|
|
|
|
|
loadingElement.style.display = 'none';
|
|
|
|
|
|
}
|
2025-09-20 01:56:37 +00:00
|
|
|
|
clearInterval(intervalID);
|
|
|
|
|
|
}else if (data.status == '9') {
|
|
|
|
|
|
concealElement();
|
2025-10-18 08:53:50 +00:00
|
|
|
|
if (loadingElement) {
|
|
|
|
|
|
loadingElement.style.display = 'block';
|
|
|
|
|
|
}
|
2025-09-20 01:56:37 +00:00
|
|
|
|
}
|
2025-10-18 08:53:50 +00:00
|
|
|
|
|
2025-09-20 01:56:37 +00:00
|
|
|
|
status = data.status
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(error => {
|
|
|
|
|
|
console.error('Error:', error);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-10-31 07:44:19 +00:00
|
|
|
|
intervalID = setInterval(getStatus, 5000);
|
2025-09-20 01:56:37 +00:00
|
|
|
|
if (restartContainer) {
|
|
|
|
|
|
restartContainer.addEventListener('click', function(event) {
|
|
|
|
|
|
// 处理点击逻辑
|
|
|
|
|
|
concealElement();
|
2025-10-18 08:53:50 +00:00
|
|
|
|
if (loadingElement) {
|
|
|
|
|
|
loadingElement.style.display = 'block';
|
|
|
|
|
|
}
|
2025-09-20 01:56:37 +00:00
|
|
|
|
fetch('{{.Repository.Link}}' + '/devcontainer/restart')
|
2025-10-31 07:44:19 +00:00
|
|
|
|
.then(response => {intervalID = setInterval(getStatus, 5000);})
|
2025-09-20 01:56:37 +00:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
if (stopContainer) {
|
|
|
|
|
|
stopContainer.addEventListener('click', function(event) {
|
|
|
|
|
|
concealElement();
|
2025-10-18 08:53:50 +00:00
|
|
|
|
if (loadingElement) {
|
|
|
|
|
|
loadingElement.style.display = 'block';
|
|
|
|
|
|
}
|
2025-09-20 01:56:37 +00:00
|
|
|
|
// 处理点击逻辑
|
|
|
|
|
|
fetch('{{.Repository.Link}}' + '/devcontainer/stop')
|
2025-10-31 07:44:19 +00:00
|
|
|
|
.then(response => {intervalID = setInterval(getStatus, 5000);})
|
2025-09-20 01:56:37 +00:00
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
if (deleteContainer) {
|
|
|
|
|
|
deleteContainer.addEventListener('click', function(event) {
|
|
|
|
|
|
setInterval(getStatus, 3000);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-31 07:44:19 +00:00
|
|
|
|
|
|
|
|
|
|
function togglePasswordVisibility(passwordFieldId, button) {
|
|
|
|
|
|
const passwordInput = document.getElementById(passwordFieldId);
|
|
|
|
|
|
|
|
|
|
|
|
if (passwordInput.type === 'password') {
|
|
|
|
|
|
passwordInput.type = 'text';
|
|
|
|
|
|
button.textContent = '隐藏密码';
|
|
|
|
|
|
button.style.color = '#2185d0'; // 主色调,表示激活状态
|
|
|
|
|
|
} else {
|
|
|
|
|
|
passwordInput.type = 'password';
|
|
|
|
|
|
button.textContent = '显示密码';
|
|
|
|
|
|
button.style.color = '#666'; // 恢复默认颜色
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
function showCustomAlert(message, title = "提示信息") {
|
|
|
|
|
|
const alertBox = document.getElementById('customAlert');
|
|
|
|
|
|
const alertText = document.getElementById('alertText');
|
|
|
|
|
|
const alertHeader = alertBox.querySelector('.alert-header strong');
|
|
|
|
|
|
|
|
|
|
|
|
alertHeader.textContent = title;
|
|
|
|
|
|
alertText.textContent = message;
|
|
|
|
|
|
alertBox.style.display = 'block';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function closeCustomAlert() {
|
|
|
|
|
|
document.getElementById('customAlert').style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 点击背景关闭
|
|
|
|
|
|
document.getElementById('customAlert').addEventListener('click', function(e) {
|
|
|
|
|
|
if (e.target === this) {
|
|
|
|
|
|
closeCustomAlert();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-09-20 01:56:37 +00:00
|
|
|
|
function submitForm(event) {
|
|
|
|
|
|
event.preventDefault(); // 阻止默认的表单提交行为
|
|
|
|
|
|
const {csrfToken} = window.config;
|
|
|
|
|
|
const {appSubUrl} = window.config;
|
2025-10-31 07:44:19 +00:00
|
|
|
|
const formModal = document.getElementById('updatemodal');
|
2025-09-20 01:56:37 +00:00
|
|
|
|
const form = document.getElementById('updateForm');
|
|
|
|
|
|
const submitButton = document.getElementById('updateSubmitButton');
|
|
|
|
|
|
const closeButton = document.getElementById('updateCloseButton');
|
|
|
|
|
|
submitButton.disabled = true;
|
|
|
|
|
|
const formData = new FormData(form);
|
|
|
|
|
|
fetch('{{.Repository.Link}}'+'/devcontainer/update', {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
'x-csrf-token': csrfToken, // 如果需要认证
|
|
|
|
|
|
'content-type' : 'application/json',
|
|
|
|
|
|
},
|
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
|
RepositoryAddress: formData.get('RepositoryAddress'),
|
|
|
|
|
|
RepositoryUsername: formData.get('RepositoryUsername'),
|
|
|
|
|
|
RepositoryPassword: formData.get('RepositoryPassword'),
|
|
|
|
|
|
SaveMethod: formData.get('SaveMethod'),
|
|
|
|
|
|
ImageName: formData.get('ImageName'),
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
.then(response => response.json())
|
|
|
|
|
|
.then(data => {
|
|
|
|
|
|
submitButton.disabled = false;
|
2025-10-31 07:44:19 +00:00
|
|
|
|
formModal.classList.remove('is-loading')
|
|
|
|
|
|
showCustomAlert(data.message);
|
2025-09-20 01:56:37 +00:00
|
|
|
|
if(data.redirect){
|
2025-10-31 07:44:19 +00:00
|
|
|
|
closeCustomAlert()
|
2025-09-20 01:56:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
intervalID = setInterval(getStatus, 3000);
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch((error) => {
|
|
|
|
|
|
submitButton.disabled = false;
|
|
|
|
|
|
alert('提交失败,请重试。');
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
2025-10-18 08:53:50 +00:00
|
|
|
|
<style>
|
|
|
|
|
|
.loading{
|
|
|
|
|
|
width:60px;
|
|
|
|
|
|
height:60px;
|
|
|
|
|
|
border-radius:150px;
|
|
|
|
|
|
border:8px solid #fff;
|
|
|
|
|
|
border-top-color:rgba(0,0,0,0.3);
|
|
|
|
|
|
box-sizing:border-box;
|
|
|
|
|
|
margin-left:calc(50% - 30px);
|
|
|
|
|
|
animation:loading 1.2s linear infinite;
|
|
|
|
|
|
-webkit-animation:loading 1.2s linear infinite;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes loading{
|
|
|
|
|
|
0%{transform:rotate(0deg)}
|
|
|
|
|
|
100%{transform:rotate(360deg)}
|
|
|
|
|
|
}
|
|
|
|
|
|
@-webkit-keyframes loading{
|
|
|
|
|
|
0%{-webkit-transform:rotate(0deg)}
|
|
|
|
|
|
100%{-webkit-transform:rotate(360deg)}
|
|
|
|
|
|
}
|
2025-10-31 07:44:19 +00:00
|
|
|
|
.custom-alert {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
background: rgba(0,0,0,0.5);
|
|
|
|
|
|
z-index: 10000;
|
|
|
|
|
|
}
|
|
|
|
|
|
.alert-content {
|
|
|
|
|
|
color: black;
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 50%;
|
|
|
|
|
|
left: 50%;
|
|
|
|
|
|
transform: translate(-50%, -50%);
|
|
|
|
|
|
background: white;
|
|
|
|
|
|
padding: 0; /* 移除内边距,在内部元素中设置 */
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
width: 80%;
|
|
|
|
|
|
max-width: 600px;
|
|
|
|
|
|
max-height: 80%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
|
|
|
|
}
|
|
|
|
|
|
.alert-header {
|
|
|
|
|
|
padding: 15px 20px;
|
|
|
|
|
|
border-bottom: 1px solid #eee;
|
|
|
|
|
|
background: #f8f9fa;
|
|
|
|
|
|
border-radius: 8px 8px 0 0;
|
|
|
|
|
|
position: sticky;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
z-index: 10;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.alert-close {
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
width: 30px;
|
|
|
|
|
|
height: 30px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
}
|
|
|
|
|
|
.alert-close:hover {
|
|
|
|
|
|
background: #e9ecef;
|
|
|
|
|
|
color: #000;
|
|
|
|
|
|
}
|
|
|
|
|
|
.alert-body {
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
max-height: calc(80vh - 100px); /* 减去头部高度 */
|
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
|
word-wrap: break-word;
|
|
|
|
|
|
}
|
2025-10-18 08:53:50 +00:00
|
|
|
|
</style>
|
2025-09-20 01:56:37 +00:00
|
|
|
|
{{template "base/footer" .}}
|