Commit 596c911c authored by 王曜嵚 Wang Yaoqin's avatar 王曜嵚 Wang Yaoqin

dev: websocket 后端传递

parent b2ffb25e
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
padding: 0; padding: 0;
} }
.content{ .content{
background-color: #EFEFEF;
overflow: hidden; overflow: hidden;
height: 100%; height: 100%;
padding: 24px; padding: 24px;
......
<template>
<span>
<slot />
</span>
</template>
\ No newline at end of file
<template> <template>
<div class="bottom-log" v-show="userStore.username"> <div class="bottom-log" v-show="userStore.username">
<div class="log__icon"> <div class="log__icon">
<svg t="1713863250860" fill="gray" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5129" width="16" height="16"><path d="M96 652.8c-19.2 0-38.4-19.2-38.4-38.4V409.6c0-19.2 19.2-38.4 38.4-38.4s38.4 19.2 38.4 38.4v204.8c0 25.6-12.8 38.4-38.4 38.4z" p-id="5130"></path><path d="M1017.6 512c0-51.2-32-89.6-76.8-102.4V160c0-83.2-64-147.2-147.2-147.2H204.8C121.6 12.8 57.6 76.8 57.6 160v19.2c0 19.2 19.2 38.4 38.4 38.4s38.4-19.2 38.4-38.4v-19.2c0-38.4 32-70.4 70.4-70.4h595.2c38.4 0 70.4 32 70.4 70.4V409.6c-38.4 12.8-76.8 57.6-76.8 102.4 0 51.2 32 89.6 76.8 102.4v249.6c0 38.4-32 70.4-70.4 70.4H204.8c-38.4 0-70.4-32-70.4-70.4v-19.2c0-19.2-19.2-38.4-38.4-38.4s-38.4 19.2-38.4 38.4v19.2c0 83.2 64 147.2 147.2 147.2h595.2c83.2 0 147.2-64 147.2-147.2V620.8c44.8-19.2 70.4-57.6 70.4-108.8z m-108.8 51.2c-25.6 0-51.2-19.2-51.2-51.2 0-25.6 19.2-51.2 51.2-51.2s51.2 19.2 51.2 51.2c-6.4 25.6-25.6 51.2-51.2 51.2z" p-id="5131"></path><path d="M659.2 332.8H345.6c-19.2 0-38.4-12.8-38.4-38.4 0-19.2 19.2-38.4 38.4-38.4h313.6c19.2 0 38.4 19.2 38.4 38.4 0 25.6-19.2 38.4-38.4 38.4zM659.2 550.4H345.6c-19.2 0-38.4-12.8-38.4-38.4 0-19.2 19.2-38.4 38.4-38.4h313.6c19.2 0 38.4 19.2 38.4 38.4 0 25.6-19.2 38.4-38.4 38.4zM659.2 768H345.6c-19.2 0-38.4-19.2-38.4-38.4s19.2-38.4 38.4-38.4h313.6c19.2 0 38.4 19.2 38.4 38.4 0 25.6-19.2 38.4-38.4 38.4z" p-id="5132"></path><path d="M153.6 768H44.8c-19.2 0-38.4-19.2-38.4-38.4s19.2-38.4 38.4-38.4h108.8c19.2 0 38.4 19.2 38.4 38.4 0 25.6-19.2 38.4-38.4 38.4z" p-id="5133"></path><path d="M153.6 332.8H44.8c-25.6 0-38.4-12.8-38.4-38.4 0-19.2 12.8-38.4 38.4-38.4h108.8c19.2 0 38.4 19.2 38.4 38.4 0 25.6-19.2 38.4-38.4 38.4z" p-id="5134"></path></svg> <svg t="1713863250860" fill="gray" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5129" width="14" height="14"><path d="M96 652.8c-19.2 0-38.4-19.2-38.4-38.4V409.6c0-19.2 19.2-38.4 38.4-38.4s38.4 19.2 38.4 38.4v204.8c0 25.6-12.8 38.4-38.4 38.4z" p-id="5130"></path><path d="M1017.6 512c0-51.2-32-89.6-76.8-102.4V160c0-83.2-64-147.2-147.2-147.2H204.8C121.6 12.8 57.6 76.8 57.6 160v19.2c0 19.2 19.2 38.4 38.4 38.4s38.4-19.2 38.4-38.4v-19.2c0-38.4 32-70.4 70.4-70.4h595.2c38.4 0 70.4 32 70.4 70.4V409.6c-38.4 12.8-76.8 57.6-76.8 102.4 0 51.2 32 89.6 76.8 102.4v249.6c0 38.4-32 70.4-70.4 70.4H204.8c-38.4 0-70.4-32-70.4-70.4v-19.2c0-19.2-19.2-38.4-38.4-38.4s-38.4 19.2-38.4 38.4v19.2c0 83.2 64 147.2 147.2 147.2h595.2c83.2 0 147.2-64 147.2-147.2V620.8c44.8-19.2 70.4-57.6 70.4-108.8z m-108.8 51.2c-25.6 0-51.2-19.2-51.2-51.2 0-25.6 19.2-51.2 51.2-51.2s51.2 19.2 51.2 51.2c-6.4 25.6-25.6 51.2-51.2 51.2z" p-id="5131"></path><path d="M659.2 332.8H345.6c-19.2 0-38.4-12.8-38.4-38.4 0-19.2 19.2-38.4 38.4-38.4h313.6c19.2 0 38.4 19.2 38.4 38.4 0 25.6-19.2 38.4-38.4 38.4zM659.2 550.4H345.6c-19.2 0-38.4-12.8-38.4-38.4 0-19.2 19.2-38.4 38.4-38.4h313.6c19.2 0 38.4 19.2 38.4 38.4 0 25.6-19.2 38.4-38.4 38.4zM659.2 768H345.6c-19.2 0-38.4-19.2-38.4-38.4s19.2-38.4 38.4-38.4h313.6c19.2 0 38.4 19.2 38.4 38.4 0 25.6-19.2 38.4-38.4 38.4z" p-id="5132"></path><path d="M153.6 768H44.8c-19.2 0-38.4-19.2-38.4-38.4s19.2-38.4 38.4-38.4h108.8c19.2 0 38.4 19.2 38.4 38.4 0 25.6-19.2 38.4-38.4 38.4z" p-id="5133"></path><path d="M153.6 332.8H44.8c-25.6 0-38.4-12.8-38.4-38.4 0-19.2 12.8-38.4 38.4-38.4h108.8c19.2 0 38.4 19.2 38.4 38.4 0 25.6-19.2 38.4-38.4 38.4z" p-id="5134"></path></svg>
</div> </div>
<div class="log__info" @click="handleChangePanel"> <div class="log__info" @click="handleChangePanel">
<span v-html="lastLog"></span> <span v-html="lastLog"></span>
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
<svg t="1713929532584" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4248" width="16" height="16"><path d="M572.16 512l183.466667-183.04a42.666667 42.666667 0 1 0-60.586667-60.586667L512 451.84l-183.04-183.466667a42.666667 42.666667 0 0 0-60.586667 60.586667l183.466667 183.04-183.466667 183.04a42.666667 42.666667 0 0 0 0 60.586667 42.666667 42.666667 0 0 0 60.586667 0l183.04-183.466667 183.04 183.466667a42.666667 42.666667 0 0 0 60.586667 0 42.666667 42.666667 0 0 0 0-60.586667z" p-id="4249"></path></svg> <svg t="1713929532584" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4248" width="16" height="16"><path d="M572.16 512l183.466667-183.04a42.666667 42.666667 0 1 0-60.586667-60.586667L512 451.84l-183.04-183.466667a42.666667 42.666667 0 0 0-60.586667 60.586667l183.466667 183.04-183.466667 183.04a42.666667 42.666667 0 0 0 0 60.586667 42.666667 42.666667 0 0 0 60.586667 0l183.04-183.466667 183.04 183.466667a42.666667 42.666667 0 0 0 60.586667 0 42.666667 42.666667 0 0 0 0-60.586667z" p-id="4249"></path></svg>
</div> </div>
</div> </div>
<div class='logs-content'> <div class='logs-content' ref="logsContent">
<div v-for='item in renderLogs' :key='item'> <div v-for='item in renderLogs' :key='item'>
<p v-html='item'></p> <p v-html='item'></p>
</div> </div>
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { io } from 'socket.io-client'; import { Socket, io } from 'socket.io-client';
import { useUserStore } from "@/store/user" import { useUserStore } from "@/store/user"
const userStore = useUserStore() const userStore = useUserStore()
...@@ -41,10 +41,8 @@ const renderLogs = computed(() => { ...@@ -41,10 +41,8 @@ const renderLogs = computed(() => {
}) })
userStore.$subscribe(function (mutation, state) { userStore.$subscribe(function (mutation, state) {
if (state.username) {
initWebsocket(state.username) initWebsocket(state.username)
} }, { once: true })
})
function addColorfulTag(log: string) { function addColorfulTag(log: string) {
let value = log let value = log
...@@ -55,12 +53,7 @@ function addColorfulTag(log: string) { ...@@ -55,12 +53,7 @@ function addColorfulTag(log: string) {
} }
function initWebsocket (username: string) { function initWebsocket (username: string) {
const socket = io({ let socket: Socket = getWebSocket()
path: '/socket.io',
auth: {
username
}
});
// eslint-disable-next-line no-control-regex // eslint-disable-next-line no-control-regex
const removeLogPrefix = (log: string) => log.replace(/^\[.*?\]\s/, '').replace(/[\u0000-\u0009]/g, '').replace(/[\u000B-\u001F]/g, '') const removeLogPrefix = (log: string) => log.replace(/^\[.*?\]\s/, '').replace(/[\u0000-\u0009]/g, '').replace(/[\u000B-\u001F]/g, '')
...@@ -74,6 +67,9 @@ function initWebsocket (username: string) { ...@@ -74,6 +67,9 @@ function initWebsocket (username: string) {
} }
} }
}) })
socket.on('ClearLog', () => {
logs.value = []
})
} }
function handleChangePanel () { function handleChangePanel () {
......
...@@ -7,46 +7,70 @@ ...@@ -7,46 +7,70 @@
<span title="状态" class="secondary"> <span title="状态" class="secondary">
状态:{{ getStatusLabel(info?.status) }} 状态:{{ getStatusLabel(info?.status) }}
</span> </span>
<span title="分支" class="secondary branch" @click="handleChangeBranch">分支:{{ info?.branch }}</span> <span title="分支" class="secondary branch">分支:{{ info?.branch }}</span>
<span title="目录" class="secondary">目录:/var/logwire-platform</span> <span title="目录" class="secondary">目录:/var/logwire-platform</span>
</div> </div>
<div> <div class="row" style="margin-top: 50px;">
<button v-if="info?.status === 'null'" class="primary" @click="handleInstall">初始化</button> <div class="extra-operation" @click="navigateTo('/git')">
<button v-if="info?.status === 'created' || info?.status === 'compiled'" @click="handleCompile">编译</button> <svg t="1714466723924" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13200" width="200" height="200"><path d="M738.986667 868.693333c0 47.786667 37.546667 85.333333 85.333333 85.333334s85.333333-37.546667 85.333333-85.333334-37.546667-85.333333-85.333333-85.333333-85.333333 39.253333-85.333333 85.333333z" p-id="13201"></path><path d="M824.32 979.626667c-61.44 0-110.933333-49.493333-110.933333-110.933334s49.493333-110.933333 110.933333-110.933333 110.933333 49.493333 110.933333 110.933333-49.493333 110.933333-110.933333 110.933334z m0-170.666667c-32.426667 0-59.733333 27.306667-59.733333 59.733333s27.306667 59.733333 59.733333 59.733334 59.733333-27.306667 59.733333-59.733334-27.306667-59.733333-59.733333-59.733333z" p-id="13202"></path><path d="M269.653333 868.693333c0 47.786667 37.546667 85.333333 85.333334 85.333334s85.333333-37.546667 85.333333-85.333334-37.546667-85.333333-85.333333-85.333333-85.333333 39.253333-85.333334 85.333333z" p-id="13203"></path><path d="M354.986667 979.626667c-61.44 0-110.933333-49.493333-110.933334-110.933334s49.493333-110.933333 110.933334-110.933333 110.933333 49.493333 110.933333 110.933333-49.493333 110.933333-110.933333 110.933334z m0-170.666667c-32.426667 0-59.733333 27.306667-59.733334 59.733333s27.306667 59.733333 59.733334 59.733334 59.733333-27.306667 59.733333-59.733334-27.306667-59.733333-59.733333-59.733333z" p-id="13204"></path><path d="M269.653333 186.026667c0 47.786667 37.546667 85.333333 85.333334 85.333333s85.333333-37.546667 85.333333-85.333333-37.546667-85.333333-85.333333-85.333334-85.333333 39.253333-85.333334 85.333334z" p-id="13205"></path><path d="M354.986667 296.96c-61.44 0-110.933333-49.493333-110.933334-110.933333s49.493333-110.933333 110.933334-110.933334 110.933333 49.493333 110.933333 110.933334-49.493333 110.933333-110.933333 110.933333z m0-170.666667c-32.426667 0-59.733333 27.306667-59.733334 59.733334s27.306667 59.733333 59.733334 59.733333 59.733333-27.306667 59.733333-59.733333-27.306667-59.733333-59.733333-59.733334z" p-id="13206"></path><path d="M824.32 808.96c-13.653333 0-25.6-11.946667-25.6-25.6v-85.333333c0-68.266667-110.933333-114.346667-218.453333-157.013334-75.093333-30.72-150.186667-61.44-199.68-104.106666V785.066667c0 13.653333-11.946667 25.6-25.6 25.6s-25.6-11.946667-25.6-25.6V273.066667c0-13.653333 11.946667-25.6 25.6-25.6s25.6 11.946667 25.6 25.6v63.146666c0 68.266667 110.933333 114.346667 218.453333 157.013334 122.88 51.2 250.88 102.4 250.88 204.8v85.333333c0 13.653333-11.946667 25.6-25.6 25.6z" p-id="13207"></path></svg>
<button v-if="info?.status === 'compiled'" @click="handleExecute">运行</button> 切换分支
<button v-if="info?.status === 'running'" @click="handleStop">停止</button> </div>
<div v-else-if="info?.status?.endsWith('ing')" class="loading-icon"> <div class="extra-operation" @click="navigateTo('/v1')">
<svg t="1714012359955" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4291" width="16" height="16"><path d="M876.864 782.592c3.264 0 6.272-3.2 6.272-6.656 0-3.456-3.008-6.592-6.272-6.592-3.264 0-6.272 3.2-6.272 6.592 0 3.456 3.008 6.656 6.272 6.656z m-140.544 153.344c2.304 2.432 5.568 3.84 8.768 3.84a12.16 12.16 0 0 0 8.832-3.84 13.76 13.76 0 0 0 0-18.56 12.224 12.224 0 0 0-8.832-3.84 12.16 12.16 0 0 0-8.768 3.84 13.696 13.696 0 0 0 0 18.56zM552.32 1018.24c3.456 3.648 8.32 5.76 13.184 5.76a18.368 18.368 0 0 0 13.184-5.76 20.608 20.608 0 0 0 0-27.968 18.368 18.368 0 0 0-13.184-5.824 18.368 18.368 0 0 0-13.184 5.76 20.608 20.608 0 0 0 0 28.032z m-198.336-5.76c4.608 4.8 11.072 7.68 17.6 7.68a24.448 24.448 0 0 0 17.536-7.68 27.456 27.456 0 0 0 0-37.248 24.448 24.448 0 0 0-17.536-7.68 24.448 24.448 0 0 0-17.6 7.68 27.52 27.52 0 0 0 0 37.184z m-175.68-91.84c5.76 6.08 13.824 9.6 21.952 9.6a30.592 30.592 0 0 0 22.016-9.6 34.368 34.368 0 0 0 0-46.592 30.592 30.592 0 0 0-22.016-9.6 30.592 30.592 0 0 0-21.952 9.6 34.368 34.368 0 0 0 0 46.592z m-121.152-159.36c6.912 7.36 16.64 11.648 26.368 11.648a36.736 36.736 0 0 0 26.432-11.584 41.28 41.28 0 0 0 0-55.936 36.736 36.736 0 0 0-26.432-11.584 36.8 36.8 0 0 0-26.368 11.52 41.28 41.28 0 0 0 0 56zM12.736 564.672a42.88 42.88 0 0 0 30.784 13.44 42.88 42.88 0 0 0 30.784-13.44 48.128 48.128 0 0 0 0-65.216 42.88 42.88 0 0 0-30.72-13.44 42.88 42.88 0 0 0-30.848 13.44 48.128 48.128 0 0 0 0 65.216z m39.808-195.392a48.96 48.96 0 0 0 35.2 15.36 48.96 48.96 0 0 0 35.2-15.36 54.976 54.976 0 0 0 0-74.56 48.96 48.96 0 0 0-35.2-15.424 48.96 48.96 0 0 0-35.2 15.424 54.976 54.976 0 0 0 0 74.56zM168.32 212.48c10.368 11.008 24.96 17.408 39.68 17.408 14.592 0 29.184-6.4 39.552-17.408a61.888 61.888 0 0 0 0-83.84 55.104 55.104 0 0 0-39.616-17.408c-14.656 0-29.248 6.4-39.616 17.408a61.888 61.888 0 0 0 0 83.84zM337.344 124.8c11.52 12.16 27.712 19.264 43.968 19.264 16.256 0 32.448-7.04 43.968-19.264a68.672 68.672 0 0 0 0-93.184 61.248 61.248 0 0 0-43.968-19.264 61.248 61.248 0 0 0-43.968 19.264 68.736 68.736 0 0 0 0 93.184z m189.632-1.088c12.672 13.44 30.528 21.248 48.448 21.248s35.712-7.808 48.384-21.248a75.584 75.584 0 0 0 0-102.464A67.392 67.392 0 0 0 575.36 0c-17.92 0-35.776 7.808-48.448 21.248a75.584 75.584 0 0 0 0 102.464z m173.824 86.592c13.824 14.592 33.28 23.104 52.736 23.104 19.584 0 39.04-8.512 52.8-23.104a82.432 82.432 0 0 0 0-111.744 73.472 73.472 0 0 0-52.8-23.168c-19.52 0-38.912 8.512-52.736 23.168a82.432 82.432 0 0 0 0 111.744z m124.032 158.528c14.976 15.872 36.032 25.088 57.216 25.088 21.12 0 42.24-9.216 57.152-25.088a89.344 89.344 0 0 0 0-121.088 79.616 79.616 0 0 0-57.152-25.088c-21.184 0-42.24 9.216-57.216 25.088a89.344 89.344 0 0 0 0 121.088z m50.432 204.032c16.128 17.088 38.784 27.008 61.632 27.008 22.784 0 45.44-9.92 61.568-27.008a96.256 96.256 0 0 0 0-130.432 85.76 85.76 0 0 0-61.568-27.072c-22.848 0-45.44 9.984-61.632 27.072a96.192 96.192 0 0 0 0 130.432z" fill="#262626" p-id="4292"></path></svg> <svg t="1714466827612" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14201" width="200" height="200"><path d="M544.033302 516.000776V96.000564c0-17.672051-14.478655-31.998984-32.150706-31.998984s-32.150706 14.326933-32.150707 31.998984v420.061624c-54.909072 14.251072-95.809105 64.333925-95.809105 123.935554 0 59.691941 40.82056 109.836206 96.090876 124.000579a32.298816 32.298816 0 0 0-0.281771 4.006195v159.99492c0 17.672051 14.478655 31.998984 32.150707 31.998984s32.150706-14.326933 32.150706-31.998984V768.004516a32.280754 32.280754 0 0 0-0.317894-3.941171c55.389526-14.088512 96.397933-64.301413 96.397932-124.058378 0.010837-59.691941-40.809723-109.832593-96.080038-124.004191z m13.221527 169.249989a64.00158 64.00158 0 1 1 18.744945-45.256635 63.578925 63.578925 0 0 1-18.744945 45.260247zM223.678606 196.003711a32.287979 32.287979 0 0 0 0.292607-4.002582V96.000564a31.970084 31.970084 0 1 0-63.940169 0V192.001129a32.371065 32.371065 0 0 0 0.180622 4.060381c-55.270316 14.160761-96.173962 64.301413-96.173961 124.000579s40.910871 109.818143 96.181186 123.996966a32.360228 32.360228 0 0 0-0.184234 3.937558V927.999436a31.970084 31.970084 0 1 0 63.940169 0V448.000226a32.287979 32.287979 0 0 0-0.278158-3.883372c55.385914-14.088512 96.350971-64.301413 96.350971-124.054765S279.071744 210.084998 223.678606 196.003711z m13.575545 169.315013A64.00158 64.00158 0 1 1 255.999097 320.062089a63.578925 63.578925 0 0 1-18.744946 45.256635zM959.99842 320.062089c0-59.775027-41.048144-109.977091-96.45212-124.058378a32.360228 32.360228 0 0 0 0.173397-4.002582V96.000564a31.970084 31.970084 0 1 0-63.940169 0V192.001129a32.291592 32.291592 0 0 0 0.299833 4.060381c-55.270316 14.160761-96.116162 64.301413-96.116163 124.000579s40.82056 109.836206 96.090876 124.000578a32.298816 32.298816 0 0 0-0.281771 3.99897V927.999436a31.970084 31.970084 0 1 0 63.940169 0V448.061637a32.381903 32.381903 0 0 0-0.166172-3.941171c55.407588-14.084899 96.452119-64.286963 96.45212-124.058377z m-82.746526 45.253022a63.997968 63.997968 0 1 1 18.748558-45.253022 63.578925 63.578925 0 0 1-18.748558 45.253022z" p-id="14202"></path></svg>
修改配置
</div>
<div class="extra-operation" @click="handleOpenVscode">
<svg t="1714466933895" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18229" width="200" height="200"><path d="M753.664 28.672V860.16L28.672 751.616l726.016 244.736 241.664-100.352V129.024L753.664 28.672zM499.712 169.984L277.504 389.12 143.36 288.768 89.088 307.2 225.28 441.344 89.088 576.512l55.296 18.432 134.144-100.352 222.208 220.16 133.12-56.32V226.304l-134.144-56.32z m0 156.672v230.4L347.136 441.344l152.576-114.688z" p-id="18230"></path></svg>
打开 Vscode
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="project-card__settings"> <div class="project-card__settings">
<div class="setting-item" @click="handleOpenVscode"> <div class="setting-item" v-if="info?.status === 'null'" title="初始化" @click="handleInstall">
<svg width="32" height="32" t="1713861440945" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5245"><path d="M746.222933 102.239573l-359.799466 330.820267L185.347413 281.4976 102.2464 329.864533l198.20544 182.132054-198.20544 182.132053 83.101013 48.510293 201.076054-151.558826 359.799466 330.676906 175.527254-85.251413V187.4944z m0 217.57952v384.341334l-255.040853-192.177494z" fill="#2196F3" p-id="5246"></path></svg> <svg t="1714465823398" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2319" width="200" height="200"><path d="M56.888889 284.444444h853.333333v682.666667H56.888889V284.444444z m56.888889 56.888889v568.888889h739.555555V341.333333H113.777778zM199.111111 170.666667L125.155556 284.444444H56.888889l113.777778-170.666666h625.777777l113.777778 170.666666h-68.266666l-73.955556-113.777777h-568.888889zM125.155556 284.444444H56.888889l113.777778-170.666666h625.777777l113.777778 170.666666h-68.266666l-73.955556-113.777777h-568.888889L125.155556 284.444444z" p-id="2320"></path><path d="M227.555556 603.022222l39.822222-34.133333 221.866666 193.422222 216.177778-193.422222 39.822222 34.133333-256 227.555556z" p-id="2321"></path><path d="M455.111111 398.222222h56.888889v369.777778H455.111111z" p-id="2322"></path></svg>
</div>
<div class="setting-item" v-if="info?.status === 'created' || info?.status === 'compiled'" title="编译" @click="handleCompile">
<svg t="1714466022297" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7952" width="200" height="200"><path d="M353.536 32l531.008 1.152 6.016 0.512a36.928 36.928 0 0 1 30.912 36.48v232.832l-0.448 6.016a36.928 36.928 0 0 1-36.48 30.912h-232.896l-9.6 9.664v605.504l-0.512 6.016a36.928 36.928 0 0 1-36.48 30.912h-186.24l-6.016-0.512a36.928 36.928 0 0 1-30.912-36.416V349.568l-9.664-9.664h-232.96l-5.76-0.448a36.928 36.928 0 0 1-31.104-36.48v-112l0.448-5.76a36.928 36.928 0 0 1 18.752-26.688L353.536 32z m484.48 75.008H372.672l-191.36 103.04-5.12 8.512v37.824l9.728 9.664H455.68v642.432l9.664 9.728h93.184l9.6-9.728V266.048h269.888l9.6-9.6V116.608l-9.6-9.664z" p-id="7953"></path></svg>
</div>
<div class="setting-item" v-if="info?.status === 'compiled'" title="运行" @click="handleExecute">
<svg t="1714466279198" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1111" width="200" height="200"><path d="M512 193.536c175.616 0 318.464 142.848 318.464 318.464s-142.848 318.464-318.464 318.464S193.536 687.616 193.536 512 336.384 193.536 512 193.536M512 102.4c-226.304 0-409.6 183.296-409.6 409.6s183.296 409.6 409.6 409.6 409.6-183.296 409.6-409.6-183.296-409.6-409.6-409.6z" p-id="1112"></path><path d="M670.208 483.328L456.192 360.96a26.0096 26.0096 0 0 0-38.912 22.528v244.736c0 19.968 21.504 32.256 38.912 22.528l214.016-122.368c17.408-10.24 17.408-35.328 0-45.056z" p-id="1113"></path></svg>
</div> </div>
<div class="setting-item"> <div class="setting-item" v-if="info?.status === 'launched'" title="停止" @click="handleStop">
<svg width="32" height="32" t="1713861304749" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4253"><path d="M439.264 208a16 16 0 0 0-16 16v67.968a239.744 239.744 0 0 0-46.496 26.896l-58.912-34a16 16 0 0 0-21.856 5.856l-80 138.56a16 16 0 0 0 5.856 21.856l58.896 34a242.624 242.624 0 0 0 0 53.728l-58.88 34a16 16 0 0 0-6.72 20.176l0.848 1.68 80 138.56a16 16 0 0 0 21.856 5.856l58.912-34a239.744 239.744 0 0 0 46.496 26.88V800a16 16 0 0 0 16 16h160a16 16 0 0 0 16-16v-67.968a239.744 239.744 0 0 0 46.512-26.896l58.912 34a16 16 0 0 0 21.856-5.856l80-138.56a16 16 0 0 0-4.288-20.832l-1.568-1.024-58.896-34a242.624 242.624 0 0 0 0-53.728l58.88-34a16 16 0 0 0 6.72-20.176l-0.848-1.68-80-138.56a16 16 0 0 0-21.856-5.856l-58.912 34a239.744 239.744 0 0 0-46.496-26.88V224a16 16 0 0 0-16-16h-160z m32 48h96v67.376l28.8 12.576c13.152 5.76 25.632 12.976 37.184 21.52l25.28 18.688 58.448-33.728 48 83.136-58.368 33.68 3.472 31.2a194.624 194.624 0 0 1 0 43.104l-3.472 31.2 58.368 33.68-48 83.136-58.432-33.728-25.296 18.688c-11.552 8.544-24.032 15.76-37.184 21.52l-28.8 12.576V768h-96v-67.376l-28.784-12.576c-13.152-5.76-25.632-12.976-37.184-21.52l-25.28-18.688-58.448 33.728-48-83.136 58.368-33.68-3.472-31.2a194.624 194.624 0 0 1 0-43.104l3.472-31.2-58.368-33.68 48-83.136 58.432 33.728 25.296-18.688a191.744 191.744 0 0 1 37.184-21.52l28.8-12.576V256z m47.28 144a112 112 0 1 0 0 224 112 112 0 0 0 0-224z m0 48a64 64 0 1 1 0 128 64 64 0 0 1 0-128z" fill="#5A626A" p-id="4254"></path></svg> <svg t="1714466309184" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1264" width="200" height="200"><path d="M611.328 638.464H412.672c-14.848 0-27.136-12.288-27.136-27.136V412.672c0-14.848 12.288-27.136 27.136-27.136h198.144c14.848 0 27.136 12.288 27.136 27.136v198.144c0.512 15.36-11.776 27.648-26.624 27.648z" p-id="1265"></path><path d="M512 193.536c175.616 0 318.464 142.848 318.464 318.464s-142.848 318.464-318.464 318.464S193.536 687.616 193.536 512 336.384 193.536 512 193.536M512 102.4c-226.304 0-409.6 183.296-409.6 409.6s183.296 409.6 409.6 409.6 409.6-183.296 409.6-409.6-183.296-409.6-409.6-409.6z" p-id="1266"></path></svg>
</div>
<div v-if="info?.status?.endsWith('ing')" class="setting-item">
<div class="loading-icon">
<svg t="1714012359955" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4291" width="16" height="16"><path d="M876.864 782.592c3.264 0 6.272-3.2 6.272-6.656 0-3.456-3.008-6.592-6.272-6.592-3.264 0-6.272 3.2-6.272 6.592 0 3.456 3.008 6.656 6.272 6.656z m-140.544 153.344c2.304 2.432 5.568 3.84 8.768 3.84a12.16 12.16 0 0 0 8.832-3.84 13.76 13.76 0 0 0 0-18.56 12.224 12.224 0 0 0-8.832-3.84 12.16 12.16 0 0 0-8.768 3.84 13.696 13.696 0 0 0 0 18.56zM552.32 1018.24c3.456 3.648 8.32 5.76 13.184 5.76a18.368 18.368 0 0 0 13.184-5.76 20.608 20.608 0 0 0 0-27.968 18.368 18.368 0 0 0-13.184-5.824 18.368 18.368 0 0 0-13.184 5.76 20.608 20.608 0 0 0 0 28.032z m-198.336-5.76c4.608 4.8 11.072 7.68 17.6 7.68a24.448 24.448 0 0 0 17.536-7.68 27.456 27.456 0 0 0 0-37.248 24.448 24.448 0 0 0-17.536-7.68 24.448 24.448 0 0 0-17.6 7.68 27.52 27.52 0 0 0 0 37.184z m-175.68-91.84c5.76 6.08 13.824 9.6 21.952 9.6a30.592 30.592 0 0 0 22.016-9.6 34.368 34.368 0 0 0 0-46.592 30.592 30.592 0 0 0-22.016-9.6 30.592 30.592 0 0 0-21.952 9.6 34.368 34.368 0 0 0 0 46.592z m-121.152-159.36c6.912 7.36 16.64 11.648 26.368 11.648a36.736 36.736 0 0 0 26.432-11.584 41.28 41.28 0 0 0 0-55.936 36.736 36.736 0 0 0-26.432-11.584 36.8 36.8 0 0 0-26.368 11.52 41.28 41.28 0 0 0 0 56zM12.736 564.672a42.88 42.88 0 0 0 30.784 13.44 42.88 42.88 0 0 0 30.784-13.44 48.128 48.128 0 0 0 0-65.216 42.88 42.88 0 0 0-30.72-13.44 42.88 42.88 0 0 0-30.848 13.44 48.128 48.128 0 0 0 0 65.216z m39.808-195.392a48.96 48.96 0 0 0 35.2 15.36 48.96 48.96 0 0 0 35.2-15.36 54.976 54.976 0 0 0 0-74.56 48.96 48.96 0 0 0-35.2-15.424 48.96 48.96 0 0 0-35.2 15.424 54.976 54.976 0 0 0 0 74.56zM168.32 212.48c10.368 11.008 24.96 17.408 39.68 17.408 14.592 0 29.184-6.4 39.552-17.408a61.888 61.888 0 0 0 0-83.84 55.104 55.104 0 0 0-39.616-17.408c-14.656 0-29.248 6.4-39.616 17.408a61.888 61.888 0 0 0 0 83.84zM337.344 124.8c11.52 12.16 27.712 19.264 43.968 19.264 16.256 0 32.448-7.04 43.968-19.264a68.672 68.672 0 0 0 0-93.184 61.248 61.248 0 0 0-43.968-19.264 61.248 61.248 0 0 0-43.968 19.264 68.736 68.736 0 0 0 0 93.184z m189.632-1.088c12.672 13.44 30.528 21.248 48.448 21.248s35.712-7.808 48.384-21.248a75.584 75.584 0 0 0 0-102.464A67.392 67.392 0 0 0 575.36 0c-17.92 0-35.776 7.808-48.448 21.248a75.584 75.584 0 0 0 0 102.464z m173.824 86.592c13.824 14.592 33.28 23.104 52.736 23.104 19.584 0 39.04-8.512 52.8-23.104a82.432 82.432 0 0 0 0-111.744 73.472 73.472 0 0 0-52.8-23.168c-19.52 0-38.912 8.512-52.736 23.168a82.432 82.432 0 0 0 0 111.744z m124.032 158.528c14.976 15.872 36.032 25.088 57.216 25.088 21.12 0 42.24-9.216 57.152-25.088a89.344 89.344 0 0 0 0-121.088 79.616 79.616 0 0 0-57.152-25.088c-21.184 0-42.24 9.216-57.216 25.088a89.344 89.344 0 0 0 0 121.088z m50.432 204.032c16.128 17.088 38.784 27.008 61.632 27.008 22.784 0 45.44-9.92 61.568-27.008a96.256 96.256 0 0 0 0-130.432 85.76 85.76 0 0 0-61.568-27.072c-22.848 0-45.44 9.984-61.632 27.072a96.192 96.192 0 0 0 0 130.432z" p-id="4292"></path></svg>
</div> </div>
<div style="display: none;">
<button>切换分支</button>
<button>修改配置</button>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Socket, io } from 'socket.io-client';
import { useUserStore } from '~/store/user'; import { useUserStore } from '~/store/user';
let { pending, data: info } = useFetch('/api/devops/v1/getProjectInfo') let { pending, data: info } = useLazyFetch('/api/devops/v1/getProjectInfo')
watch(info, (info) => {
useUserStore().setBranchV1({ branchV1: info?.branch || '' })
})
function handleChangeBranch () { let socket: Socket = getWebSocket()
} onMounted(() => {
socket.on('ChangeV1Status', (e: any) => {
if (info.value) info.value.status = e
})
})
onUnmounted(() => {
socket.close()
})
function handleOpenVscode() { function handleOpenVscode() {
window.open(window.location.protocol + '//' + window.location.hostname + ':' + useUserStore().config.port + '/vscode?folder=/var/logwire-platform', '_blank') window.open(window.location.protocol + '//' + window.location.hostname + ':' + useUserStore().port + '/vscode?folder=/var/logwire-platform', '_blank')
} }
function getStatusLabel(status?: string) { function getStatusLabel(status?: string) {
...@@ -55,6 +79,7 @@ function getStatusLabel(status?: string) { ...@@ -55,6 +79,7 @@ function getStatusLabel(status?: string) {
['creating', '创建中'], ['creating', '创建中'],
['created', "已创建"], ['created', "已创建"],
['running', '运行中'], ['running', '运行中'],
['launched', '正在运行'],
['stopped', '已停止'], ['stopped', '已停止'],
['compiling', '编译中'], ['compiling', '编译中'],
['compiled', '已编译'] ['compiled', '已编译']
...@@ -63,24 +88,17 @@ function getStatusLabel(status?: string) { ...@@ -63,24 +88,17 @@ function getStatusLabel(status?: string) {
} }
async function handleStop() { async function handleStop() {
await $fetch('/api/devops/v1/stop', { method: 'post' }) await $fetch('/api/devops/v1/stop', { method: 'post' })
if (info.value) info.value.status = 'compiled'
} }
async function handleExecute() { async function handleExecute() {
await $fetch('/api/devops/v1/execute', { method: 'post' }) await $fetch('/api/devops/v1/execute', { method: 'post' })
if (info.value) info.value.status = 'running'
} }
async function handleCompile() { async function handleCompile() {
if (info.value) info.value.status = 'compiling'
await $fetch('/api/devops/v1/compile', { method: 'post'}) await $fetch('/api/devops/v1/compile', { method: 'post'})
if (info.value) info.value.status = 'compiled'
} }
async function handleInstall() { async function handleInstall() {
if (info.value) info.value.status = 'creating'
try {
await $fetch('/api/devops/v1/install', { method: 'post' }) await $fetch('/api/devops/v1/install', { method: 'post' })
if (info.value) info.value.status = 'created' if (info.value) {
} catch (err) { info.value.branch = 'master'
info.value!.status = 'null'
} }
} }
</script> </script>
...@@ -119,6 +137,21 @@ async function handleInstall() { ...@@ -119,6 +137,21 @@ async function handleInstall() {
margin-bottom: 10px; margin-bottom: 10px;
font-size: 14px; font-size: 14px;
} }
.project-card__desc > .row > .extra-operation {
font-size: 12px;
color: #999;
margin-right: 15px;
line-height: 16px;
display: inline-block;
}
.project-card__desc > .row > .extra-operation:hover{
color: #2e75e6 !important;
cursor: pointer;
}
.project-card__desc > .row > .extra-operation > svg{
width: 12px;
height: 12px;
}
.project-card__desc > .row > .primary { .project-card__desc > .row > .primary {
font-size: 14px; font-size: 14px;
color: #000; color: #000;
...@@ -138,6 +171,11 @@ async function handleInstall() { ...@@ -138,6 +171,11 @@ async function handleInstall() {
background-color: #eee; background-color: #eee;
cursor: pointer; cursor: pointer;
} }
.setting-item > svg {
width: 20px;
height: 20px;
fill: #999;
}
@keyframes rotate { @keyframes rotate {
from { from {
......
...@@ -7,27 +7,57 @@ ...@@ -7,27 +7,57 @@
<span title="状态" class="secondary"> <span title="状态" class="secondary">
状态:{{ getStatusLabel(info?.status) }} 状态:{{ getStatusLabel(info?.status) }}
</span> </span>
<span title="分支" class="secondary branch" @click="handleChangeBranch">分支:{{ info?.branch }}</span> <span title="分支" class="secondary branch">分支:{{ info?.branch }}</span>
<span title="目录" class="secondary">目录:/var/logwire-backend</span> <span title="目录" class="secondary">目录:/var/logwire-backend</span>
</div> </div>
<div> <div class="row" style="margin-top: 50px;">
<button v-if="info?.status === 'null'" class="primary" @click="handleInstall">初始化</button> <div class="extra-operation" @click="navigateTo('/git')">
<button v-if="info?.status === 'created' || info?.status === 'compiled'" @click="handleCompile">编译</button> <svg t="1714466723924" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13200" width="200" height="200"><path d="M738.986667 868.693333c0 47.786667 37.546667 85.333333 85.333333 85.333334s85.333333-37.546667 85.333333-85.333334-37.546667-85.333333-85.333333-85.333333-85.333333 39.253333-85.333333 85.333333z" p-id="13201"></path><path d="M824.32 979.626667c-61.44 0-110.933333-49.493333-110.933333-110.933334s49.493333-110.933333 110.933333-110.933333 110.933333 49.493333 110.933333 110.933333-49.493333 110.933333-110.933333 110.933334z m0-170.666667c-32.426667 0-59.733333 27.306667-59.733333 59.733333s27.306667 59.733333 59.733333 59.733334 59.733333-27.306667 59.733333-59.733334-27.306667-59.733333-59.733333-59.733333z" p-id="13202"></path><path d="M269.653333 868.693333c0 47.786667 37.546667 85.333333 85.333334 85.333334s85.333333-37.546667 85.333333-85.333334-37.546667-85.333333-85.333333-85.333333-85.333333 39.253333-85.333334 85.333333z" p-id="13203"></path><path d="M354.986667 979.626667c-61.44 0-110.933333-49.493333-110.933334-110.933334s49.493333-110.933333 110.933334-110.933333 110.933333 49.493333 110.933333 110.933333-49.493333 110.933333-110.933333 110.933334z m0-170.666667c-32.426667 0-59.733333 27.306667-59.733334 59.733333s27.306667 59.733333 59.733334 59.733334 59.733333-27.306667 59.733333-59.733334-27.306667-59.733333-59.733333-59.733333z" p-id="13204"></path><path d="M269.653333 186.026667c0 47.786667 37.546667 85.333333 85.333334 85.333333s85.333333-37.546667 85.333333-85.333333-37.546667-85.333333-85.333333-85.333334-85.333333 39.253333-85.333334 85.333334z" p-id="13205"></path><path d="M354.986667 296.96c-61.44 0-110.933333-49.493333-110.933334-110.933333s49.493333-110.933333 110.933334-110.933334 110.933333 49.493333 110.933333 110.933334-49.493333 110.933333-110.933333 110.933333z m0-170.666667c-32.426667 0-59.733333 27.306667-59.733334 59.733334s27.306667 59.733333 59.733334 59.733333 59.733333-27.306667 59.733333-59.733333-27.306667-59.733333-59.733333-59.733334z" p-id="13206"></path><path d="M824.32 808.96c-13.653333 0-25.6-11.946667-25.6-25.6v-85.333333c0-68.266667-110.933333-114.346667-218.453333-157.013334-75.093333-30.72-150.186667-61.44-199.68-104.106666V785.066667c0 13.653333-11.946667 25.6-25.6 25.6s-25.6-11.946667-25.6-25.6V273.066667c0-13.653333 11.946667-25.6 25.6-25.6s25.6 11.946667 25.6 25.6v63.146666c0 68.266667 110.933333 114.346667 218.453333 157.013334 122.88 51.2 250.88 102.4 250.88 204.8v85.333333c0 13.653333-11.946667 25.6-25.6 25.6z" p-id="13207"></path></svg>
<button v-if="info?.status === 'compiled'" @click="handleExecute">运行</button> 切换分支
<button v-if="info?.status === 'running'" @click="handleStop">停止</button> </div>
<div v-else-if="info?.status?.endsWith('ing')" class="loading-icon"> <div class="extra-operation" @click="navigateTo('/v2')">
<svg t="1714012359955" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4291" width="16" height="16"><path d="M876.864 782.592c3.264 0 6.272-3.2 6.272-6.656 0-3.456-3.008-6.592-6.272-6.592-3.264 0-6.272 3.2-6.272 6.592 0 3.456 3.008 6.656 6.272 6.656z m-140.544 153.344c2.304 2.432 5.568 3.84 8.768 3.84a12.16 12.16 0 0 0 8.832-3.84 13.76 13.76 0 0 0 0-18.56 12.224 12.224 0 0 0-8.832-3.84 12.16 12.16 0 0 0-8.768 3.84 13.696 13.696 0 0 0 0 18.56zM552.32 1018.24c3.456 3.648 8.32 5.76 13.184 5.76a18.368 18.368 0 0 0 13.184-5.76 20.608 20.608 0 0 0 0-27.968 18.368 18.368 0 0 0-13.184-5.824 18.368 18.368 0 0 0-13.184 5.76 20.608 20.608 0 0 0 0 28.032z m-198.336-5.76c4.608 4.8 11.072 7.68 17.6 7.68a24.448 24.448 0 0 0 17.536-7.68 27.456 27.456 0 0 0 0-37.248 24.448 24.448 0 0 0-17.536-7.68 24.448 24.448 0 0 0-17.6 7.68 27.52 27.52 0 0 0 0 37.184z m-175.68-91.84c5.76 6.08 13.824 9.6 21.952 9.6a30.592 30.592 0 0 0 22.016-9.6 34.368 34.368 0 0 0 0-46.592 30.592 30.592 0 0 0-22.016-9.6 30.592 30.592 0 0 0-21.952 9.6 34.368 34.368 0 0 0 0 46.592z m-121.152-159.36c6.912 7.36 16.64 11.648 26.368 11.648a36.736 36.736 0 0 0 26.432-11.584 41.28 41.28 0 0 0 0-55.936 36.736 36.736 0 0 0-26.432-11.584 36.8 36.8 0 0 0-26.368 11.52 41.28 41.28 0 0 0 0 56zM12.736 564.672a42.88 42.88 0 0 0 30.784 13.44 42.88 42.88 0 0 0 30.784-13.44 48.128 48.128 0 0 0 0-65.216 42.88 42.88 0 0 0-30.72-13.44 42.88 42.88 0 0 0-30.848 13.44 48.128 48.128 0 0 0 0 65.216z m39.808-195.392a48.96 48.96 0 0 0 35.2 15.36 48.96 48.96 0 0 0 35.2-15.36 54.976 54.976 0 0 0 0-74.56 48.96 48.96 0 0 0-35.2-15.424 48.96 48.96 0 0 0-35.2 15.424 54.976 54.976 0 0 0 0 74.56zM168.32 212.48c10.368 11.008 24.96 17.408 39.68 17.408 14.592 0 29.184-6.4 39.552-17.408a61.888 61.888 0 0 0 0-83.84 55.104 55.104 0 0 0-39.616-17.408c-14.656 0-29.248 6.4-39.616 17.408a61.888 61.888 0 0 0 0 83.84zM337.344 124.8c11.52 12.16 27.712 19.264 43.968 19.264 16.256 0 32.448-7.04 43.968-19.264a68.672 68.672 0 0 0 0-93.184 61.248 61.248 0 0 0-43.968-19.264 61.248 61.248 0 0 0-43.968 19.264 68.736 68.736 0 0 0 0 93.184z m189.632-1.088c12.672 13.44 30.528 21.248 48.448 21.248s35.712-7.808 48.384-21.248a75.584 75.584 0 0 0 0-102.464A67.392 67.392 0 0 0 575.36 0c-17.92 0-35.776 7.808-48.448 21.248a75.584 75.584 0 0 0 0 102.464z m173.824 86.592c13.824 14.592 33.28 23.104 52.736 23.104 19.584 0 39.04-8.512 52.8-23.104a82.432 82.432 0 0 0 0-111.744 73.472 73.472 0 0 0-52.8-23.168c-19.52 0-38.912 8.512-52.736 23.168a82.432 82.432 0 0 0 0 111.744z m124.032 158.528c14.976 15.872 36.032 25.088 57.216 25.088 21.12 0 42.24-9.216 57.152-25.088a89.344 89.344 0 0 0 0-121.088 79.616 79.616 0 0 0-57.152-25.088c-21.184 0-42.24 9.216-57.216 25.088a89.344 89.344 0 0 0 0 121.088z m50.432 204.032c16.128 17.088 38.784 27.008 61.632 27.008 22.784 0 45.44-9.92 61.568-27.008a96.256 96.256 0 0 0 0-130.432 85.76 85.76 0 0 0-61.568-27.072c-22.848 0-45.44 9.984-61.632 27.072a96.192 96.192 0 0 0 0 130.432z" fill="#262626" p-id="4292"></path></svg> <svg t="1714466827612" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14201" width="200" height="200"><path d="M544.033302 516.000776V96.000564c0-17.672051-14.478655-31.998984-32.150706-31.998984s-32.150706 14.326933-32.150707 31.998984v420.061624c-54.909072 14.251072-95.809105 64.333925-95.809105 123.935554 0 59.691941 40.82056 109.836206 96.090876 124.000579a32.298816 32.298816 0 0 0-0.281771 4.006195v159.99492c0 17.672051 14.478655 31.998984 32.150707 31.998984s32.150706-14.326933 32.150706-31.998984V768.004516a32.280754 32.280754 0 0 0-0.317894-3.941171c55.389526-14.088512 96.397933-64.301413 96.397932-124.058378 0.010837-59.691941-40.809723-109.832593-96.080038-124.004191z m13.221527 169.249989a64.00158 64.00158 0 1 1 18.744945-45.256635 63.578925 63.578925 0 0 1-18.744945 45.260247zM223.678606 196.003711a32.287979 32.287979 0 0 0 0.292607-4.002582V96.000564a31.970084 31.970084 0 1 0-63.940169 0V192.001129a32.371065 32.371065 0 0 0 0.180622 4.060381c-55.270316 14.160761-96.173962 64.301413-96.173961 124.000579s40.910871 109.818143 96.181186 123.996966a32.360228 32.360228 0 0 0-0.184234 3.937558V927.999436a31.970084 31.970084 0 1 0 63.940169 0V448.000226a32.287979 32.287979 0 0 0-0.278158-3.883372c55.385914-14.088512 96.350971-64.301413 96.350971-124.054765S279.071744 210.084998 223.678606 196.003711z m13.575545 169.315013A64.00158 64.00158 0 1 1 255.999097 320.062089a63.578925 63.578925 0 0 1-18.744946 45.256635zM959.99842 320.062089c0-59.775027-41.048144-109.977091-96.45212-124.058378a32.360228 32.360228 0 0 0 0.173397-4.002582V96.000564a31.970084 31.970084 0 1 0-63.940169 0V192.001129a32.291592 32.291592 0 0 0 0.299833 4.060381c-55.270316 14.160761-96.116162 64.301413-96.116163 124.000579s40.82056 109.836206 96.090876 124.000578a32.298816 32.298816 0 0 0-0.281771 3.99897V927.999436a31.970084 31.970084 0 1 0 63.940169 0V448.061637a32.381903 32.381903 0 0 0-0.166172-3.941171c55.407588-14.084899 96.452119-64.286963 96.45212-124.058377z m-82.746526 45.253022a63.997968 63.997968 0 1 1 18.748558-45.253022 63.578925 63.578925 0 0 1-18.748558 45.253022z" p-id="14202"></path></svg>
修改配置
</div>
<div class="extra-operation" @click="handleOpenVscode">
<svg t="1714466933895" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18229" width="200" height="200"><path d="M753.664 28.672V860.16L28.672 751.616l726.016 244.736 241.664-100.352V129.024L753.664 28.672zM499.712 169.984L277.504 389.12 143.36 288.768 89.088 307.2 225.28 441.344 89.088 576.512l55.296 18.432 134.144-100.352 222.208 220.16 133.12-56.32V226.304l-134.144-56.32z m0 156.672v230.4L347.136 441.344l152.576-114.688z" p-id="18230"></path></svg>
打开 Vscode
</div>
<div class="extra-operation" @click="handleDebug">
<svg t="1714466907476" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17233" width="200" height="200"><path d="M489.23 81.63A160 160 0 0 0 352 240v21.34h320V240A160 160 0 0 0 489.23 81.63z m383.76 143.64a32.004 32.004 0 0 0-31.1 7.56L750.62 320H273.38l-91.3-87.14a31.984 31.984 0 1 0-44.16 46.28l86.08 82.2v177.35H96a32 32 0 0 0 0 64h128V640c0 52.96 14.302 102.596 39.23 145.22L137.92 904.86a32.005 32.005 0 1 0 44.19 46.31L301.82 836.9a287.104 287.104 0 0 0 178.21 89.34V544a32 32 0 0 1 64 0v382.24a287.424 287.424 0 0 0 178.21-89.34l119.71 114.27a32.032 32.032 0 0 0 44.19-46.31L760.8 785.22A287.04 287.04 0 0 0 800 640v-37.34h128a32 32 0 0 0 0-64H800V361.31l86.08-82.17a32.004 32.004 0 0 0-13.09-53.87z" p-id="17234"></path></svg>
调试
</div>
<div class="extra-operation" @click="handleZip">
<svg t="1714466881817" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16209" width="200" height="200"><path d="M966.208 246.752L534.144 21.408a47.968 47.968 0 0 0-44.128-0.128L58.08 243.136A47.968 47.968 0 0 0 32 285.824V744.64c0 18.208 10.304 34.848 26.592 42.976l432 215.36a48 48 0 0 0 42.816 0l432-215.36A48 48 0 0 0 992 744.672V289.344c0-17.92-9.952-34.304-25.792-42.592zM508.384 463.68l-162.176-79.808 367.36-196.704 158.4 82.624-363.584 193.888z m3.488-381.696l132.992 69.376-369.312 197.76-144.896-71.328 381.216-195.808zM96 332.096l153.216 75.392v168.256a32 32 0 0 0 64 0v-136.736L480 521.024v405.184L96 734.752V332.096z m448 594.112V517.184l384-204.736v422.304l-384 191.456z" p-id="16210"></path></svg>
打包微信压缩文件
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="project-card__settings"> <div class="project-card__settings">
<div class="setting-item" @click="handleOpenVscode"> <div class="setting-item" v-if="info?.status === 'null'" title="初始化" @click="handleInstall">
<svg width="32" height="32" t="1713861440945" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5245"><path d="M746.222933 102.239573l-359.799466 330.820267L185.347413 281.4976 102.2464 329.864533l198.20544 182.132054-198.20544 182.132053 83.101013 48.510293 201.076054-151.558826 359.799466 330.676906 175.527254-85.251413V187.4944z m0 217.57952v384.341334l-255.040853-192.177494z" fill="#2196F3" p-id="5246"></path></svg> <svg t="1714465823398" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2319" width="200" height="200"><path d="M56.888889 284.444444h853.333333v682.666667H56.888889V284.444444z m56.888889 56.888889v568.888889h739.555555V341.333333H113.777778zM199.111111 170.666667L125.155556 284.444444H56.888889l113.777778-170.666666h625.777777l113.777778 170.666666h-68.266666l-73.955556-113.777777h-568.888889zM125.155556 284.444444H56.888889l113.777778-170.666666h625.777777l113.777778 170.666666h-68.266666l-73.955556-113.777777h-568.888889L125.155556 284.444444z" p-id="2320"></path><path d="M227.555556 603.022222l39.822222-34.133333 221.866666 193.422222 216.177778-193.422222 39.822222 34.133333-256 227.555556z" p-id="2321"></path><path d="M455.111111 398.222222h56.888889v369.777778H455.111111z" p-id="2322"></path></svg>
</div> </div>
<div class="setting-item"> <div class="setting-item" v-if="info?.status === 'created' || info?.status === 'compiled'" title="编译" @click="handleCompile">
<svg width="32" height="32" t="1713861304749" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4253"><path d="M439.264 208a16 16 0 0 0-16 16v67.968a239.744 239.744 0 0 0-46.496 26.896l-58.912-34a16 16 0 0 0-21.856 5.856l-80 138.56a16 16 0 0 0 5.856 21.856l58.896 34a242.624 242.624 0 0 0 0 53.728l-58.88 34a16 16 0 0 0-6.72 20.176l0.848 1.68 80 138.56a16 16 0 0 0 21.856 5.856l58.912-34a239.744 239.744 0 0 0 46.496 26.88V800a16 16 0 0 0 16 16h160a16 16 0 0 0 16-16v-67.968a239.744 239.744 0 0 0 46.512-26.896l58.912 34a16 16 0 0 0 21.856-5.856l80-138.56a16 16 0 0 0-4.288-20.832l-1.568-1.024-58.896-34a242.624 242.624 0 0 0 0-53.728l58.88-34a16 16 0 0 0 6.72-20.176l-0.848-1.68-80-138.56a16 16 0 0 0-21.856-5.856l-58.912 34a239.744 239.744 0 0 0-46.496-26.88V224a16 16 0 0 0-16-16h-160z m32 48h96v67.376l28.8 12.576c13.152 5.76 25.632 12.976 37.184 21.52l25.28 18.688 58.448-33.728 48 83.136-58.368 33.68 3.472 31.2a194.624 194.624 0 0 1 0 43.104l-3.472 31.2 58.368 33.68-48 83.136-58.432-33.728-25.296 18.688c-11.552 8.544-24.032 15.76-37.184 21.52l-28.8 12.576V768h-96v-67.376l-28.784-12.576c-13.152-5.76-25.632-12.976-37.184-21.52l-25.28-18.688-58.448 33.728-48-83.136 58.368-33.68-3.472-31.2a194.624 194.624 0 0 1 0-43.104l3.472-31.2-58.368-33.68 48-83.136 58.432 33.728 25.296-18.688a191.744 191.744 0 0 1 37.184-21.52l28.8-12.576V256z m47.28 144a112 112 0 1 0 0 224 112 112 0 0 0 0-224z m0 48a64 64 0 1 1 0 128 64 64 0 0 1 0-128z" fill="#5A626A" p-id="4254"></path></svg> <svg t="1714466022297" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7952" width="200" height="200"><path d="M353.536 32l531.008 1.152 6.016 0.512a36.928 36.928 0 0 1 30.912 36.48v232.832l-0.448 6.016a36.928 36.928 0 0 1-36.48 30.912h-232.896l-9.6 9.664v605.504l-0.512 6.016a36.928 36.928 0 0 1-36.48 30.912h-186.24l-6.016-0.512a36.928 36.928 0 0 1-30.912-36.416V349.568l-9.664-9.664h-232.96l-5.76-0.448a36.928 36.928 0 0 1-31.104-36.48v-112l0.448-5.76a36.928 36.928 0 0 1 18.752-26.688L353.536 32z m484.48 75.008H372.672l-191.36 103.04-5.12 8.512v37.824l9.728 9.664H455.68v642.432l9.664 9.728h93.184l9.6-9.728V266.048h269.888l9.6-9.6V116.608l-9.6-9.664z" p-id="7953"></path></svg>
</div>
<div class="setting-item" v-if="info?.status === 'compiled'" title="运行" @click="handleExecute">
<svg t="1714466279198" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1111" width="200" height="200"><path d="M512 193.536c175.616 0 318.464 142.848 318.464 318.464s-142.848 318.464-318.464 318.464S193.536 687.616 193.536 512 336.384 193.536 512 193.536M512 102.4c-226.304 0-409.6 183.296-409.6 409.6s183.296 409.6 409.6 409.6 409.6-183.296 409.6-409.6-183.296-409.6-409.6-409.6z" p-id="1112"></path><path d="M670.208 483.328L456.192 360.96a26.0096 26.0096 0 0 0-38.912 22.528v244.736c0 19.968 21.504 32.256 38.912 22.528l214.016-122.368c17.408-10.24 17.408-35.328 0-45.056z" p-id="1113"></path></svg>
</div> </div>
<div class="setting-item" v-if="info?.status === 'launched'" title="停止" @click="handleStop">
<svg t="1714466309184" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1264" width="200" height="200"><path d="M611.328 638.464H412.672c-14.848 0-27.136-12.288-27.136-27.136V412.672c0-14.848 12.288-27.136 27.136-27.136h198.144c14.848 0 27.136 12.288 27.136 27.136v198.144c0.512 15.36-11.776 27.648-26.624 27.648z" p-id="1265"></path><path d="M512 193.536c175.616 0 318.464 142.848 318.464 318.464s-142.848 318.464-318.464 318.464S193.536 687.616 193.536 512 336.384 193.536 512 193.536M512 102.4c-226.304 0-409.6 183.296-409.6 409.6s183.296 409.6 409.6 409.6 409.6-183.296 409.6-409.6-183.296-409.6-409.6-409.6z" p-id="1266"></path></svg>
</div>
<div v-if="info?.status?.endsWith('ing')" class="setting-item">
<div class="loading-icon">
<svg t="1714012359955" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4291" width="16" height="16"><path d="M876.864 782.592c3.264 0 6.272-3.2 6.272-6.656 0-3.456-3.008-6.592-6.272-6.592-3.264 0-6.272 3.2-6.272 6.592 0 3.456 3.008 6.656 6.272 6.656z m-140.544 153.344c2.304 2.432 5.568 3.84 8.768 3.84a12.16 12.16 0 0 0 8.832-3.84 13.76 13.76 0 0 0 0-18.56 12.224 12.224 0 0 0-8.832-3.84 12.16 12.16 0 0 0-8.768 3.84 13.696 13.696 0 0 0 0 18.56zM552.32 1018.24c3.456 3.648 8.32 5.76 13.184 5.76a18.368 18.368 0 0 0 13.184-5.76 20.608 20.608 0 0 0 0-27.968 18.368 18.368 0 0 0-13.184-5.824 18.368 18.368 0 0 0-13.184 5.76 20.608 20.608 0 0 0 0 28.032z m-198.336-5.76c4.608 4.8 11.072 7.68 17.6 7.68a24.448 24.448 0 0 0 17.536-7.68 27.456 27.456 0 0 0 0-37.248 24.448 24.448 0 0 0-17.536-7.68 24.448 24.448 0 0 0-17.6 7.68 27.52 27.52 0 0 0 0 37.184z m-175.68-91.84c5.76 6.08 13.824 9.6 21.952 9.6a30.592 30.592 0 0 0 22.016-9.6 34.368 34.368 0 0 0 0-46.592 30.592 30.592 0 0 0-22.016-9.6 30.592 30.592 0 0 0-21.952 9.6 34.368 34.368 0 0 0 0 46.592z m-121.152-159.36c6.912 7.36 16.64 11.648 26.368 11.648a36.736 36.736 0 0 0 26.432-11.584 41.28 41.28 0 0 0 0-55.936 36.736 36.736 0 0 0-26.432-11.584 36.8 36.8 0 0 0-26.368 11.52 41.28 41.28 0 0 0 0 56zM12.736 564.672a42.88 42.88 0 0 0 30.784 13.44 42.88 42.88 0 0 0 30.784-13.44 48.128 48.128 0 0 0 0-65.216 42.88 42.88 0 0 0-30.72-13.44 42.88 42.88 0 0 0-30.848 13.44 48.128 48.128 0 0 0 0 65.216z m39.808-195.392a48.96 48.96 0 0 0 35.2 15.36 48.96 48.96 0 0 0 35.2-15.36 54.976 54.976 0 0 0 0-74.56 48.96 48.96 0 0 0-35.2-15.424 48.96 48.96 0 0 0-35.2 15.424 54.976 54.976 0 0 0 0 74.56zM168.32 212.48c10.368 11.008 24.96 17.408 39.68 17.408 14.592 0 29.184-6.4 39.552-17.408a61.888 61.888 0 0 0 0-83.84 55.104 55.104 0 0 0-39.616-17.408c-14.656 0-29.248 6.4-39.616 17.408a61.888 61.888 0 0 0 0 83.84zM337.344 124.8c11.52 12.16 27.712 19.264 43.968 19.264 16.256 0 32.448-7.04 43.968-19.264a68.672 68.672 0 0 0 0-93.184 61.248 61.248 0 0 0-43.968-19.264 61.248 61.248 0 0 0-43.968 19.264 68.736 68.736 0 0 0 0 93.184z m189.632-1.088c12.672 13.44 30.528 21.248 48.448 21.248s35.712-7.808 48.384-21.248a75.584 75.584 0 0 0 0-102.464A67.392 67.392 0 0 0 575.36 0c-17.92 0-35.776 7.808-48.448 21.248a75.584 75.584 0 0 0 0 102.464z m173.824 86.592c13.824 14.592 33.28 23.104 52.736 23.104 19.584 0 39.04-8.512 52.8-23.104a82.432 82.432 0 0 0 0-111.744 73.472 73.472 0 0 0-52.8-23.168c-19.52 0-38.912 8.512-52.736 23.168a82.432 82.432 0 0 0 0 111.744z m124.032 158.528c14.976 15.872 36.032 25.088 57.216 25.088 21.12 0 42.24-9.216 57.152-25.088a89.344 89.344 0 0 0 0-121.088 79.616 79.616 0 0 0-57.152-25.088c-21.184 0-42.24 9.216-57.216 25.088a89.344 89.344 0 0 0 0 121.088z m50.432 204.032c16.128 17.088 38.784 27.008 61.632 27.008 22.784 0 45.44-9.92 61.568-27.008a96.256 96.256 0 0 0 0-130.432 85.76 85.76 0 0 0-61.568-27.072c-22.848 0-45.44 9.984-61.632 27.072a96.192 96.192 0 0 0 0 130.432z" p-id="4292"></path></svg>
</div>
</div>
<!-- <div class="setting-item" @click="handleOpenVscode">
<svg width="32" height="32" t="1713861440945" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5245"><path d="M746.222933 102.239573l-359.799466 330.820267L185.347413 281.4976 102.2464 329.864533l198.20544 182.132054-198.20544 182.132053 83.101013 48.510293 201.076054-151.558826 359.799466 330.676906 175.527254-85.251413V187.4944z m0 217.57952v384.341334l-255.040853-192.177494z" p-id="5246"></path></svg>
</div>
<div class="setting-item">
<svg width="32" height="32" t="1713861304749" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4253"><path d="M439.264 208a16 16 0 0 0-16 16v67.968a239.744 239.744 0 0 0-46.496 26.896l-58.912-34a16 16 0 0 0-21.856 5.856l-80 138.56a16 16 0 0 0 5.856 21.856l58.896 34a242.624 242.624 0 0 0 0 53.728l-58.88 34a16 16 0 0 0-6.72 20.176l0.848 1.68 80 138.56a16 16 0 0 0 21.856 5.856l58.912-34a239.744 239.744 0 0 0 46.496 26.88V800a16 16 0 0 0 16 16h160a16 16 0 0 0 16-16v-67.968a239.744 239.744 0 0 0 46.512-26.896l58.912 34a16 16 0 0 0 21.856-5.856l80-138.56a16 16 0 0 0-4.288-20.832l-1.568-1.024-58.896-34a242.624 242.624 0 0 0 0-53.728l58.88-34a16 16 0 0 0 6.72-20.176l-0.848-1.68-80-138.56a16 16 0 0 0-21.856-5.856l-58.912 34a239.744 239.744 0 0 0-46.496-26.88V224a16 16 0 0 0-16-16h-160z m32 48h96v67.376l28.8 12.576c13.152 5.76 25.632 12.976 37.184 21.52l25.28 18.688 58.448-33.728 48 83.136-58.368 33.68 3.472 31.2a194.624 194.624 0 0 1 0 43.104l-3.472 31.2 58.368 33.68-48 83.136-58.432-33.728-25.296 18.688c-11.552 8.544-24.032 15.76-37.184 21.52l-28.8 12.576V768h-96v-67.376l-28.784-12.576c-13.152-5.76-25.632-12.976-37.184-21.52l-25.28-18.688-58.448 33.728-48-83.136 58.368-33.68-3.472-31.2a194.624 194.624 0 0 1 0-43.104l3.472-31.2-58.368-33.68 48-83.136 58.432 33.728 25.296-18.688a191.744 191.744 0 0 1 37.184-21.52l28.8-12.576V256z m47.28 144a112 112 0 1 0 0 224 112 112 0 0 0 0-224z m0 48a64 64 0 1 1 0 128 64 64 0 0 1 0-128z" p-id="4254"></path></svg>
</div> -->
<div style="display: none;"> <div style="display: none;">
<button>切换分支</button> <button>切换分支</button>
<button>修改配置</button> <button>修改配置</button>
...@@ -40,16 +70,27 @@ ...@@ -40,16 +70,27 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Socket, io } from 'socket.io-client';
import { useUserStore } from '~/store/user'; import { useUserStore } from '~/store/user';
let { pending, data: info } = useFetch('/api/devops/v2/getProjectInfo') let { pending, data: info } = useLazyFetch('/api/devops/v2/getProjectInfo')
watch(info, (info) => {
useUserStore().setBranchV2({ branchV2: info?.branch || '' })
})
function handleChangeBranch () { let socket: Socket = getWebSocket()
onMounted(() => {
} socket.on('ChangeV2Status', (e: any) => {
console.log('info', info, e)
if (info.value) info.value.status = e
})
})
onUnmounted(() => {
socket.close()
})
function handleOpenVscode() { function handleOpenVscode() {
window.open(window.location.protocol + '//' + window.location.hostname + ':' + useUserStore().config.port + '/vscode?folder=/var/logwire-backend', '_blank') window.open(window.location.protocol + '//' + window.location.hostname + ':' + useUserStore().port + '/vscode?folder=/var/logwire-backend', '_blank')
} }
function getStatusLabel(status?: string) { function getStatusLabel(status?: string) {
...@@ -58,6 +99,7 @@ function getStatusLabel(status?: string) { ...@@ -58,6 +99,7 @@ function getStatusLabel(status?: string) {
['creating', '创建中'], ['creating', '创建中'],
['created', "已创建"], ['created', "已创建"],
['running', '运行中'], ['running', '运行中'],
['launched', '正在运行'],
['stopped', '已停止'], ['stopped', '已停止'],
['compiling', '编译中'], ['compiling', '编译中'],
['compiled', '已编译'] ['compiled', '已编译']
...@@ -66,32 +108,23 @@ function getStatusLabel(status?: string) { ...@@ -66,32 +108,23 @@ function getStatusLabel(status?: string) {
} }
async function handleDebug() { async function handleDebug() {
await $fetch('/api/devops/v2/debug', { method: 'post'}) await $fetch('/api/devops/v2/debug', { method: 'post'})
if (info.value) info.value.status = 'running'
} }
async function handleZip() { async function handleZip() {
await $fetch('/api/backend/downloadWeApp', { method: 'post' }) await $fetch('/api/devops/v2/downloadWeApp', { method: 'post' })
if (info.value) info.value.status = 'running'
} }
async function handleStop() { async function handleStop() {
await $fetch('/api/devops/v2/stop', { method: 'post' }) await $fetch('/api/devops/v2/stop', { method: 'post' })
if (info.value) info.value.status = 'compiled'
} }
async function handleExecute() { async function handleExecute() {
await $fetch('/api/devops/v2/execute', { method: 'post' }) await $fetch('/api/devops/v2/execute', { method: 'post' })
if (info.value) info.value.status = 'running'
} }
async function handleCompile() { async function handleCompile() {
if (info.value) info.value.status = 'compiling'
await $fetch('/api/devops/v2/compile', { method: 'post'}) await $fetch('/api/devops/v2/compile', { method: 'post'})
if (info.value) info.value.status = 'compiled'
} }
async function handleInstall() { async function handleInstall() {
if (info.value) info.value.status = 'creating'
try {
await $fetch('/api/devops/v2/install', { method: 'post' }) await $fetch('/api/devops/v2/install', { method: 'post' })
if (info.value) info.value.status = 'created' if (info.value) {
} catch (err) { info.value.branch = 'master'
info.value!.status = 'null'
} }
} }
</script> </script>
...@@ -104,7 +137,8 @@ async function handleInstall() { ...@@ -104,7 +137,8 @@ async function handleInstall() {
border-radius: 20px; border-radius: 20px;
height: 120px; height: 120px;
display: flex; display: flex;
overflow: hidden overflow: hidden;
margin-bottom: 20px;
} }
.project-card__info{ .project-card__info{
flex: 1; flex: 1;
...@@ -130,6 +164,21 @@ async function handleInstall() { ...@@ -130,6 +164,21 @@ async function handleInstall() {
margin-bottom: 10px; margin-bottom: 10px;
font-size: 14px; font-size: 14px;
} }
.project-card__desc > .row > .extra-operation {
font-size: 12px;
color: #999;
margin-right: 15px;
line-height: 16px;
display: inline-block;
}
.project-card__desc > .row > .extra-operation:hover{
color: #2e75e6 !important;
cursor: pointer;
}
.project-card__desc > .row > .extra-operation > svg{
width: 12px;
height: 12px;
}
.project-card__desc > .row > .primary { .project-card__desc > .row > .primary {
font-size: 14px; font-size: 14px;
color: #000; color: #000;
...@@ -145,6 +194,11 @@ async function handleInstall() { ...@@ -145,6 +194,11 @@ async function handleInstall() {
color: #2e75e6 !important; color: #2e75e6 !important;
cursor: pointer; cursor: pointer;
} }
.setting-item > svg {
width: 20px;
height: 20px;
fill: #999;
}
.setting-item:hover{ .setting-item:hover{
background-color: #eee; background-color: #eee;
cursor: pointer; cursor: pointer;
......
...@@ -50,5 +50,7 @@ ...@@ -50,5 +50,7 @@
#app .maintab{ #app .maintab{
flex: 1; flex: 1;
overflow: hidden; overflow: hidden;
margin: 0 auto;
max-width: 800px;
} }
</style> </style>
import { useUserStore } from "~/store/user"
export default defineNuxtRouteMiddleware((to, from) => { export default defineNuxtRouteMiddleware((to, from) => {
if (to.path === '/') { if (to.path === '/') {
return navigateTo('/login') return navigateTo('/login')
} else if (to.path !== '/login' && !useUserStore().username) {
// 检查是否登录
return navigateTo('/login')
} }
}) })
\ No newline at end of file
...@@ -3,24 +3,6 @@ ...@@ -3,24 +3,6 @@
<div class='content'> <div class='content'>
<div class='container'> <div class='container'>
<form class='form' @submit.prevent> <form class='form' @submit.prevent>
<div class='form-group'>
<div class='form-group__label'>
<label>Email</label>
<p>用户的 Gitlab 邮箱账号</p>
</div>
<div class='form-group__body'>
<input v-model='form.email' />
</div>
</div>
<div class='form-group'>
<div class='form-group__label'>
<label>SSH Key</label>
<p>使用上述的 Email 邮箱生成 Ssh key,用于 Gitlab 中用户认证</p>
</div>
<div class='form-group__body'>
<button class='primary' @click='handleGenerate'>生成 ssh key</button>
</div>
</div>
<div class='form-group'> <div class='form-group'>
<div class='form-group__label'> <div class='form-group__label'>
<label>V2 分支管理</label> <label>V2 分支管理</label>
...@@ -32,16 +14,13 @@ ...@@ -32,16 +14,13 @@
<span>所有分支</span> <span>所有分支</span>
<span> <span>
<select v-model='form.targetBranchV2'> <select v-model='form.targetBranchV2'>
<option v-for='item in branchs' :key='item'>{{ item }}</option> <option v-for='item in branchsV2' :key='item'>{{ item }}</option>
</select> </select>
</span>
<p>
<button @click='handleCheckout("v2")'>切换分支</button> <button @click='handleCheckout("v2")'>切换分支</button>
</p> </span>
</div> </div>
<div class='form-group__item'> <div class='form-group__item'>
<span>刷新分支</span> <span>刷新分支</span>
<p>刷新当前分支</p>
<span><button @click='handleFetch("v2")'>刷新</button></span> <span><button @click='handleFetch("v2")'>刷新</button></span>
</div> </div>
</div> </div>
...@@ -59,14 +38,11 @@ ...@@ -59,14 +38,11 @@
<select v-model='form.targetBranchV1'> <select v-model='form.targetBranchV1'>
<option v-for='item in branchsV1' :key='item'>{{ item }}</option> <option v-for='item in branchsV1' :key='item'>{{ item }}</option>
</select> </select>
</span>
<p>
<button @click='handleCheckout("v1")'>切换分支</button> <button @click='handleCheckout("v1")'>切换分支</button>
</p> </span>
</div> </div>
<div class='form-group__item'> <div class='form-group__item'>
<span>刷新分支</span> <span>刷新分支</span>
<p>刷新当前分支</p>
<span><button @click='handleFetch("v1")'>刷新</button></span> <span><button @click='handleFetch("v1")'>刷新</button></span>
</div> </div>
</div> </div>
...@@ -78,6 +54,7 @@ ...@@ -78,6 +54,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useUserStore } from '~/store/user';
const form = ref({ const form = ref({
email: '', email: '',
...@@ -86,86 +63,46 @@ const form = ref({ ...@@ -86,86 +63,46 @@ const form = ref({
targetBranchV2: '', targetBranchV2: '',
targetBranchV1: '' targetBranchV1: ''
}) })
const branchs = ref([]) const branchsV2 = ref([])
const branchsV1 = ref([]) const branchsV1 = ref([])
onMounted(() => { onMounted(async () => {
getUserInfo() getUserInfo()
getCurrentBranchV2() getAllBranch('v1')
getCurrentBranchV1() getAllBranch('v2')
getAllBranchV2()
getAllBranchV1()
}) })
function getUserInfo() { function getUserInfo() {
$fetch('/api/user/getProjectInfo').then((res: any) => { form.value.email = useUserStore().username + '@greaconsulting.com'
form.value.email = res.username + '@greaconsulting.com' form.value.currentBranchV1 = useUserStore().branchV1
console.log(form) form.value.currentBranchV2 = useUserStore().branchV2
})
}
function getAllBranchV2() {
return $fetch('/api/git/getAllBranchs', { query: { platform: 'v2' } }).then((res: any) => {
branchs.value = res.split('\n').map((o: string) => o.trim()).filter((o: string) => !!o)
})
}
function getAllBranchV1() {
return $fetch('/api/git/getAllBranchs', { query: { platform: 'v1' } }).then((res: any) => {
branchsV1.value = res.split('\n').map((o: string) => o.trim()).filter((o: string) => !!o)
})
}
function getCurrentBranchV2() {
return $fetch('/api/git/getCurrentBranch', { query: { platform: 'v2' } }).then((res: any) => {
form.value.currentBranchV2 = res.trim()
form.value.targetBranchV2 = form.value.currentBranchV2
})
}
function getCurrentBranchV1() {
return $fetch('/api/git/getCurrentBranch', { query: { platform: 'v1' } }).then((res: any) => {
form.value.currentBranchV1 = res.trim()
form.value.targetBranchV1 = form.value.currentBranchV1 form.value.targetBranchV1 = form.value.currentBranchV1
}) form.value.targetBranchV2 = form.value.currentBranchV2
} }
function handleGenerate() { function getAllBranch(platform: 'v1' | 'v2') {
$fetch('/api/git/generateSsh', { method: 'post', body: { email: form.value.email } }).then(res => { return $fetch('/api/git/getAllBranchs', { query: { platform } }).then((res: any) => {
if (res) { if (platform === 'v1') {
const input = document.createElement('input') branchsV1.value = res.split('\n').map((o: string) => o.trim()).filter((o: string) => !!o)
input.value = res } else {
document.body.appendChild(input) branchsV2.value = res.split('\n').map((o: string) => o.trim()).filter((o: string) => !!o)
input.select()
document.execCommand('copy')
alert('已拷贝 SSH Key, 请前往 Gitlab 更新个人 SSH Key 配置')
document.body.removeChild(input)
} }
}) })
} }
async function handleFetch(platform: 'v1' | 'v2') { async function handleFetch(platform: 'v1' | 'v2') {
const branch = platform === 'v2' ? form.value.currentBranchV2 : form.value.currentBranchV1 const branch = platform === 'v2' ? form.value.currentBranchV2 : form.value.currentBranchV1
if (!branch) return
await $fetch('/api/git/fetch', { method: 'post', body: { branch, platform }}) await $fetch('/api/git/fetch', { method: 'post', body: { branch, platform }})
if (platform === 'v1') { await getAllBranch(platform)
await getCurrentBranchV1()
await getAllBranchV1()
} else {
await getCurrentBranchV2()
await getAllBranchV2()
}
alert('刷新分支完成,请重新编译运行')
} }
async function handleCheckout(platform: 'v1' | 'v2') { async function handleCheckout(platform: 'v1' | 'v2') {
try {
const branch = platform === 'v1' ? form.value.targetBranchV1 : form.value.targetBranchV2 const branch = platform === 'v1' ? form.value.targetBranchV1 : form.value.targetBranchV2
await $fetch('/api/git/checkout', { method: 'post', body: { platform, branch } }) await $fetch('/api/git/checkout', { method: 'post', body: { platform, branch } })
if (platform === 'v1') { if (platform === 'v1') {
await getCurrentBranchV1()
form.value.currentBranchV1 = form.value.targetBranchV1 form.value.currentBranchV1 = form.value.targetBranchV1
} else { } else {
await getCurrentBranchV2()
form.value.currentBranchV2 = form.value.targetBranchV2 form.value.currentBranchV2 = form.value.targetBranchV2
} }
alert('切换分支完成,请重新编译运行') alert('切换分支完成,请重新编译运行')
// 后端更新完毕
} catch (err) {
console.log(err)
}
} }
</script> </script>
...@@ -176,4 +113,8 @@ async function handleCheckout(platform: 'v1' | 'v2') { ...@@ -176,4 +113,8 @@ async function handleCheckout(platform: 'v1' | 'v2') {
height: 100%; height: 100%;
padding: 24px; padding: 24px;
} }
.content select{
width: 200px;
margin-right: 10px;
}
</style> </style>
\ No newline at end of file
...@@ -8,79 +8,6 @@ ...@@ -8,79 +8,6 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useUserStore } from '~/store/user';
const username = ref('')
const status = ref('null') // null | created | stopped | running | creating | compiling
const statusV1 = ref('null')
const loading = ref(false)
const logLevel = ref('progress') // info | progress
const logs = ref([] as string[])
const port = ref(0)
onMounted(async () => {
await getUserInfomation()
})
async function getUserInfomation() {
const res = await $fetch('/api/user/getProjectInfo')
if (res) {
useUserStore().setUserConfig(res.username, { port: res["port"] })
}
}
function handleInstallV1() {
if (loading.value) return
logs.value = []
loading.value = true
statusV1.value = 'creating'
$fetch('/api/devops/v1/install', { method: 'post' })
.then(getUserInfomation, getUserInfomation)
.finally(() => {
loading.value = false
})
}
function handleCompileV1() {
if (loading.value) return
loading.value = true
logs.value = []
resetV2()
$fetch('/api/devops/v1/compile', { method: 'post'}).then(() => {
return getUserInfomation()
}).finally(() => {
loading.value = false
})
}
function handleExecuteV1() {
if (loading.value) return
loading.value = true
logs.value = []
resetV2()
$fetch('/api/devops/v1/execute', { method: 'post'}).then(() => {
return getUserInfomation()
}).finally(() => {
loading.value = false
})
}
function handleStopV1() {
if (loading.value) return
logs.value = []
loading.value = true
$fetch('/api/devops/v1/stop', { method: 'post'}).then(() => {
return getUserInfomation()
}).finally(() => {
loading.value = false
})
}
function handleOpenVscodeV1() {
window.open(window.location.protocol + '//' + window.location.hostname + ':' + port + '/vscode?folder=/var/logwire-platform', '_blank')
}
function resetV1() {
statusV1.value = 'created'
}
function resetV2() {
status.value = 'created'
}
</script> </script>
<style scoped> <style scoped>
......
...@@ -32,13 +32,11 @@ function handleLogin () { ...@@ -32,13 +32,11 @@ function handleLogin () {
$fetch('/api/user/login', { $fetch('/api/user/login', {
method: 'post', method: 'post',
body: { username: username.value } body: { username: username.value }
}).then((value) => { }).then(value => {
if (value) { userStore.setUserName({ username: value!.username })
userStore.setUserConfig(username.value, { port: value["port"] }) userStore.setPort({ port: value!.port })
setWebSocket(username.value, true)
navigateTo('/home') navigateTo('/home')
} else {
navigateTo('/prepare')
}
}) })
} }
</script> </script>
......
<template>
<div class="prepare">
<span>准备数据库服务中......</span>
</div>
</template>
<script setup lang="ts">
import { useUserStore } from '~/store/user';
const { data } = useLazyFetch('/api/user/prepare', { method: 'post' })
watch(data, function (value) {
if (value) {
useUserStore().setUserConfig(value.username, { port: value['port'] })
navigateTo('/home')
}
})
</script>
<style scoped>
.prepare{
height: 100vh;
width: 100vw;
text-align: center;
line-height: 100vh;
}
span{
font-weight: bold;
}
</style>
\ No newline at end of file
...@@ -5,8 +5,9 @@ export default defineEventHandler(async (event) => { ...@@ -5,8 +5,9 @@ export default defineEventHandler(async (event) => {
let username = event.context.username let username = event.context.username
try { try {
let docker = createDockerFactory(username) let docker = createDockerFactory(username)
LogUtil.clear(username)
setPgTableData( 'v1', username, 'status', 'compiling') await changeProjectStatus( 'v1', username, 'status', 'compiling')
// 每次编译前把已有的 tenants_config 文件夹拷贝到一个地方,编译完成后,再拷贝回来 // 每次编译前把已有的 tenants_config 文件夹拷贝到一个地方,编译完成后,再拷贝回来
let container = await docker.checkContainer( username + '.node') let container = await docker.checkContainer( username + '.node')
...@@ -37,9 +38,9 @@ export default defineEventHandler(async (event) => { ...@@ -37,9 +38,9 @@ export default defineEventHandler(async (event) => {
await docker.execContainerCommand({ container, cmd: 'rm ../tenants.tar.gz', dir: '/var/logwire-platform/dist'}) await docker.execContainerCommand({ container, cmd: 'rm ../tenants.tar.gz', dir: '/var/logwire-platform/dist'})
await copyAndCreateServerPropertiesV1InDocker(username) await copyAndCreateServerPropertiesV1InDocker(username)
LogUtil.printInfo(username, '编译完成') LogUtil.printInfo(username, '编译完成')
await setPgTableData( 'v1', username, 'status', 'compiled') await changeProjectStatus( 'v1', username, 'status', 'compiled')
} catch (err) { } catch (err) {
await setPgTableData( 'v1', username, 'status', 'created') await changeProjectStatus( 'v1', username, 'status', 'created')
LogUtil.printError(username, err instanceof Error ? err.message : JSON.stringify(err)) LogUtil.printError(username, err instanceof Error ? err.message : JSON.stringify(err))
setResponseStatus(event, 500) setResponseStatus(event, 500)
return err return err
......
...@@ -2,12 +2,15 @@ import LogUtil from "~/server/utils/log" ...@@ -2,12 +2,15 @@ import LogUtil from "~/server/utils/log"
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
let username = event.context.username let username = event.context.username
try {
LogUtil.clear(username)
let docker = createDockerFactory(username) let docker = createDockerFactory(username)
let container = await docker.checkContainer( username + '.node') let container = await docker.checkContainer( username + '.node')
const row = await getPgTableData( 'user', username) const row = await getPgTableData( 'user', username)
const port = row?.["port"] const port = row?.["port"]
LogUtil.printInfo(username, '程序启动中') LogUtil.printInfo(username, '程序启动中')
await changeProjectStatus( 'v1', username, 'status', 'running')
if (!container) { if (!container) {
throw new Error('没有创建容器,请先初始化容器') throw new Error('没有创建容器,请先初始化容器')
} }
...@@ -22,6 +25,10 @@ export default defineEventHandler(async (event) => { ...@@ -22,6 +25,10 @@ export default defineEventHandler(async (event) => {
LogUtil.print(username, `[progress] [[1;34mInfo[m] 程序运行中...... \n`) LogUtil.print(username, `[progress] [[1;34mInfo[m] 程序运行中...... \n`)
LogUtil.print(username, `[progress] [[1;34mInfo[m] 请代理后端请求到 <strong>${HOST}:${port}</strong> 上 \n`) LogUtil.print(username, `[progress] [[1;34mInfo[m] 请代理后端请求到 <strong>${HOST}:${port}</strong> 上 \n`)
await changeProjectStatus( 'v1', username, 'status', 'launched')
await setPgTableData( 'v1', username, 'status', 'running') } catch (err) {
await changeProjectStatus( 'v2', username, 'status', 'compiled')
LogUtil.print(username, '[error] [[1;31mError[m]] ' + (err instanceof Error ? err.message : JSON.stringify(err)))
setResponseStatus(event, 500)
}
}) })
\ No newline at end of file
...@@ -6,6 +6,7 @@ export default defineEventHandler(async (event) => { ...@@ -6,6 +6,7 @@ export default defineEventHandler(async (event) => {
let username = event.context.username let username = event.context.username
let pgClient = await createPgClientFactory(username) let pgClient = await createPgClientFactory(username)
try { try {
LogUtil.clear(username)
let docker = createDockerFactory(username) let docker = createDockerFactory(username)
const rowV2 = await getPgTableData( 'v2', username) const rowV2 = await getPgTableData( 'v2', username)
...@@ -14,7 +15,7 @@ export default defineEventHandler(async (event) => { ...@@ -14,7 +15,7 @@ export default defineEventHandler(async (event) => {
} }
let rowV1 = await getPgTableData('v1', username) let rowV1 = await getPgTableData('v1', username)
if (!rowV1) await insertPgTableData('v1', username) if (!rowV1) await insertPgTableData('v1', username)
await setPgTableData( 'v1', username, 'status', 'creating') await changeProjectStatus( 'v1', username, 'status', 'creating')
let container = await docker.checkContainer( username + '.node') as Dockerode.Container let container = await docker.checkContainer( username + '.node') as Dockerode.Container
if (!container) { if (!container) {
throw new Error('没有创建容器,请先初始化容器') throw new Error('没有创建容器,请先初始化容器')
...@@ -62,11 +63,11 @@ export default defineEventHandler(async (event) => { ...@@ -62,11 +63,11 @@ export default defineEventHandler(async (event) => {
await docker.execContainerCommand({ container, cmd: 'tar -zvxf book.tar -C /var/logwire-platform/dist/projects/demo', dir: '/var/logwire-demo'}) await docker.execContainerCommand({ container, cmd: 'tar -zvxf book.tar -C /var/logwire-platform/dist/projects/demo', dir: '/var/logwire-demo'})
}) })
await setPgTableData( 'v1', username, 'status', 'created') await changeProjectStatus( 'v1', username, 'status', 'created')
await stopPgClient(pgClient) await stopPgClient(pgClient)
LogUtil.printSuccess(username, 'installed') LogUtil.printSuccess(username, 'installed')
} catch (err: any) { } catch (err: any) {
await setPgTableData( 'v1', username, 'status', 'null') await changeProjectStatus( 'v1', username, 'status', 'null')
await stopPgClient(pgClient) await stopPgClient(pgClient)
LogUtil.printError(username, (err instanceof Error ? err.message : JSON.stringify(err))) LogUtil.printError(username, (err instanceof Error ? err.message : JSON.stringify(err)))
setResponseStatus(event, 500) setResponseStatus(event, 500)
......
...@@ -2,6 +2,7 @@ import LogUtil from "~/server/utils/log" ...@@ -2,6 +2,7 @@ import LogUtil from "~/server/utils/log"
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
let username = event.context.username let username = event.context.username
LogUtil.clear(username)
let docker = createDockerFactory(username) let docker = createDockerFactory(username)
let container = await docker.checkContainer( username + '.node') let container = await docker.checkContainer( username + '.node')
if (!container) { if (!container) {
...@@ -10,5 +11,5 @@ export default defineEventHandler(async (event) => { ...@@ -10,5 +11,5 @@ export default defineEventHandler(async (event) => {
LogUtil.printInfo(username, `程序停止中......`) LogUtil.printInfo(username, `程序停止中......`)
await docker.execContainerCommand({ container, cmd: 'pm2 delete platform', quiet: true }) await docker.execContainerCommand({ container, cmd: 'pm2 delete platform', quiet: true })
LogUtil.printInfo(username, `程序已停止`) LogUtil.printInfo(username, `程序已停止`)
await setPgTableData( 'v1', username, 'status', 'compiled') await changeProjectStatus( 'v1', username, 'status', 'compiled')
}) })
\ No newline at end of file
...@@ -5,9 +5,10 @@ export default defineEventHandler(async (event) => { ...@@ -5,9 +5,10 @@ export default defineEventHandler(async (event) => {
let username = event.context.username let username = event.context.username
try { try {
let username = event.context.username let username = event.context.username
LogUtil.clear(username)
let docker = createDockerFactory(username) let docker = createDockerFactory(username)
await setPgTableData( 'v2', username, 'status', 'compiling') await changeProjectStatus( 'v2', username, 'status', 'compiling')
// TODO: 在后端发送 websocket 通知前端状态变更 // TODO: 在后端发送 websocket 通知前端状态变更
// 每次编译前把已有的 tenants_config 文件夹拷贝到一个地方,编译完成后,再拷贝回来 // 每次编译前把已有的 tenants_config 文件夹拷贝到一个地方,编译完成后,再拷贝回来
...@@ -36,9 +37,9 @@ export default defineEventHandler(async (event) => { ...@@ -36,9 +37,9 @@ export default defineEventHandler(async (event) => {
copyAndCreateGatewayPropertiesV2InDocker(username) copyAndCreateGatewayPropertiesV2InDocker(username)
LogUtil.print(username, `[progress] [[1;34mInfo[m] 编译完成 \n`) LogUtil.print(username, `[progress] [[1;34mInfo[m] 编译完成 \n`)
await setPgTableData( 'v2', username, 'status', 'compiled') await changeProjectStatus( 'v2', username, 'status', 'compiled')
} catch (err) { } catch (err) {
await setPgTableData( 'v2', username, 'status', 'created') await changeProjectStatus( 'v2', username, 'status', 'created')
LogUtil.print(username, '[error] [[1;31mError[m]] ' + (err instanceof Error ? err.message : JSON.stringify(err))) LogUtil.print(username, '[error] [[1;31mError[m]] ' + (err instanceof Error ? err.message : JSON.stringify(err)))
setResponseStatus(event, 500) setResponseStatus(event, 500)
} }
......
...@@ -3,17 +3,17 @@ import LogUtil from "~/server/utils/log" ...@@ -3,17 +3,17 @@ import LogUtil from "~/server/utils/log"
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
// 先判断服务是否启动,启动则关闭,然后调用新的命令启动 java 端 // 先判断服务是否启动,启动则关闭,然后调用新的命令启动 java 端
let username = event.context.username let username = event.context.username
LogUtil.clear(username)
let docker = createDockerFactory(username) let docker = createDockerFactory(username)
let container = await docker.checkContainer( username + '.node') let container = await docker.checkContainer( username + '.node')
if (!container) { if (!container) {
throw new Error('没有创建容器,请先初始化容器') throw new Error('没有创建容器,请先初始化容器')
} }
const rowV2 = await getPgTableData( 'v2', username) const rowV2 = await getPgTableData( 'v2', username)
const status = rowV2?.status const tenants = rowV2!.tenants
const tenants = rowV2?.tenants
const rowUser = await getPgTableData( 'user', username) const rowUser = await getPgTableData( 'user', username)
const port = rowUser?.["port"] const port = rowUser!.port
const debug = rowV2?.debug const debug = rowV2!.debug
// 有可能本身就关闭了 // 有可能本身就关闭了
try { try {
...@@ -63,5 +63,5 @@ export default defineEventHandler(async (event) => { ...@@ -63,5 +63,5 @@ export default defineEventHandler(async (event) => {
LogUtil.print(username, `[progress] [[1;34mInfo[m] 请设置开发环境域名和端口号 <strong>${tenants?.host}</strong> 上\n`) LogUtil.print(username, `[progress] [[1;34mInfo[m] 请设置开发环境域名和端口号 <strong>${tenants?.host}</strong> 上\n`)
LogUtil.print(username, `[progress] [[1;34mInfo[m] 调试程序已启动,请IP地址为 <strong>${debug?.host}</strong> 后端访问 <strong>${HOST}:${port}</strong> 进行调试\n`) LogUtil.print(username, `[progress] [[1;34mInfo[m] 调试程序已启动,请IP地址为 <strong>${debug?.host}</strong> 后端访问 <strong>${HOST}:${port}</strong> 进行调试\n`)
await setPgTableData( 'v2', username, 'status', 'running') await changeProjectStatus( 'v2', username, 'status', 'running')
}) })
\ No newline at end of file
...@@ -2,9 +2,7 @@ import LogUtil from "~/server/utils/log" ...@@ -2,9 +2,7 @@ import LogUtil from "~/server/utils/log"
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
let username = event.context.username let username = event.context.username
let pgClient = await createPgClientFactory(username) LogUtil.clear(username)
try {
let username = event.context.username
let docker = createDockerFactory(username) let docker = createDockerFactory(username)
// 创建 node 容器 // 创建 node 容器
let container = await docker.checkContainer( username + '.node') let container = await docker.checkContainer( username + '.node')
...@@ -18,9 +16,4 @@ export default defineEventHandler(async (event) => { ...@@ -18,9 +16,4 @@ export default defineEventHandler(async (event) => {
} else { } else {
throw new Error('没有创建容器,请先初始化容器') throw new Error('没有创建容器,请先初始化容器')
} }
} catch (err) {
await stopPgClient(pgClient)
setResponseStatus(event, 500)
return err
}
}) })
\ No newline at end of file
...@@ -4,6 +4,7 @@ export default defineEventHandler(async (event) => { ...@@ -4,6 +4,7 @@ export default defineEventHandler(async (event) => {
let username = event.context.username let username = event.context.username
try { try {
let username = event.context.username let username = event.context.username
LogUtil.clear(username)
let docker = createDockerFactory(username) let docker = createDockerFactory(username)
let container = await docker.checkContainer( username + '.node') let container = await docker.checkContainer( username + '.node')
let rowUser = await getPgTableData( 'user', username) let rowUser = await getPgTableData( 'user', username)
...@@ -12,6 +13,7 @@ export default defineEventHandler(async (event) => { ...@@ -12,6 +13,7 @@ export default defineEventHandler(async (event) => {
let port = rowUser?.["port"] let port = rowUser?.["port"]
LogUtil.printInfo(username, `程序初始化中......`) LogUtil.printInfo(username, `程序初始化中......`)
await changeProjectStatus( 'v2', username, 'status', 'running')
if (!container) { if (!container) {
throw new Error('没有创建容器,请先初始化容器') throw new Error('没有创建容器,请先初始化容器')
} }
...@@ -32,8 +34,9 @@ export default defineEventHandler(async (event) => { ...@@ -32,8 +34,9 @@ export default defineEventHandler(async (event) => {
LogUtil.printInfo(username, `请代理后端请求到 <strong>${HOST}:${port}</strong> 上`) LogUtil.printInfo(username, `请代理后端请求到 <strong>${HOST}:${port}</strong> 上`)
LogUtil.printInfo(username, `请设置开发环境域名和端口号 <strong>${tenants?.host}</strong> 上`) LogUtil.printInfo(username, `请设置开发环境域名和端口号 <strong>${tenants?.host}</strong> 上`)
await setPgTableData( 'v2', username, 'status', 'running') await changeProjectStatus( 'v2', username, 'status', 'launched')
} catch (err) { } catch (err) {
await changeProjectStatus( 'v2', username, 'status', 'compiled')
LogUtil.printError(username, (err instanceof Error ? err.message : JSON.stringify(err))) LogUtil.printError(username, (err instanceof Error ? err.message : JSON.stringify(err)))
setResponseStatus(event, 500) setResponseStatus(event, 500)
return err return err
......
import Dockerode from "dockerode" import Dockerode from "dockerode"
import LogUtil from "~/server/utils/log" import LogUtil, { sendProjectStatus } from "~/server/utils/log"
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import { changeProjectStatus } from "~/server/utils/postgres"
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
let username = event.context.username let username = event.context.username
let pgClient = await createPgClientFactory('backend_helper') let pgClient = await createPgClientFactory('backend_helper')
try { try {
LogUtil.clear(username)
let docker = createDockerFactory(username) let docker = createDockerFactory(username)
let host = docker.host let host = docker.host
let container: Dockerode.Container = {} as Dockerode.Container let container: Dockerode.Container = {} as Dockerode.Container
const port = (await getPgTableData( 'user', username))?.port const port = (await getPgTableData( 'user', username))?.port
const row = await getPgTableData('v2', username) const row = await getPgTableData('v2', username)
if (!row) await insertPgTableData('v2', username) if (!row) await insertPgTableData('v2', username)
await setPgTableData( 'v2', username, 'status', 'creating') await changeProjectStatus( 'v2', username, 'status', 'creating')
// 必须要保证创建容器是首先执行 // 必须要保证创建容器是首先执行
// node 容器根据 dev/prod 环境与否,决定在哪里创建,远程容器还是本机容器 // node 容器根据 dev/prod 环境与否,决定在哪里创建,远程容器还是本机容器
await LogUtil.run(username, '创建 node 容器', async () => { await LogUtil.run(username, '创建 node 容器', async () => {
...@@ -186,11 +188,11 @@ export default defineEventHandler(async (event) => { ...@@ -186,11 +188,11 @@ export default defineEventHandler(async (event) => {
} }
await setPgTableData('v2', username, 'debug', JSON.stringify(debug)) await setPgTableData('v2', username, 'debug', JSON.stringify(debug))
}) })
await setPgTableData( 'v2', username, 'status', 'created') await changeProjectStatus( 'v2', username, 'status', 'created')
await stopPgClient(pgClient) await stopPgClient(pgClient)
LogUtil.printSuccess(username, '安装成功') LogUtil.printSuccess(username, '安装成功')
} catch (err: any) { } catch (err: any) {
await setPgTableData( 'v2', username, 'status', 'null') await changeProjectStatus( 'v2', username, 'status', 'null')
await stopPgClient(pgClient) await stopPgClient(pgClient)
LogUtil.printError(username, (err instanceof Error ? err.message : JSON.stringify(err))) LogUtil.printError(username, (err instanceof Error ? err.message : JSON.stringify(err)))
setResponseStatus(event, 500) setResponseStatus(event, 500)
......
...@@ -2,6 +2,7 @@ import LogUtil from "~/server/utils/log" ...@@ -2,6 +2,7 @@ import LogUtil from "~/server/utils/log"
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
let username = event.context.username let username = event.context.username
LogUtil.clear(username)
let docker = createDockerFactory(username) let docker = createDockerFactory(username)
let container = await docker.checkContainer( username + '.node') let container = await docker.checkContainer( username + '.node')
if (!container) { if (!container) {
...@@ -12,5 +13,5 @@ export default defineEventHandler(async (event) => { ...@@ -12,5 +13,5 @@ export default defineEventHandler(async (event) => {
await docker.execContainerCommand({ container, cmd: 'pm2 delete gateway', quiet: true }) await docker.execContainerCommand({ container, cmd: 'pm2 delete gateway', quiet: true })
LogUtil.print(username, `程序已停止`) LogUtil.print(username, `程序已停止`)
await setPgTableData( 'v2', username, 'status', 'compiled') await changeProjectStatus( 'v2', username, 'status', 'compiled')
}) })
\ No newline at end of file
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const username = event.context.username const username = event.context.username
let client = await createPgClientFactory('backend_helper')
let result = await getPgTableData( 'user', username) let result = await getPgTableData( 'user', username)
return result return result
}) })
\ No newline at end of file
import { PgPort } from "~/server/utils/postgres" import { TableUser } from "~/server/utils/postgres"
import { SESSION_PASSWORD } from "../../utils" import { SESSION_PASSWORD } from "../../utils"
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
try {
const body = await readBody(event) const body = await readBody(event)
const username = body.username const username = body.username
const session = await useSession(event, { password: SESSION_PASSWORD }) const session = await useSession(event, { password: SESSION_PASSWORD })
await session.update({ username }) await session.update({ username })
// 生产环境要求必须是先创建好服务器,再启动服务
if (process.env.NODE_ENV === 'development') { let user = await getPgTableData('user', username)
return false if (!user) {
} else { const port = await getAvailableNodePort(HOST)
let result = await getPgTableData('user', username) await insertPgTableData('user', username)
return result await setPgTableData('user', username, 'port', port)
} user = await getPgTableData('user', username)
} catch (err) {
throw createError({
statusCode: 500,
statusMessage: '登录失败'
})
} }
return user
}) })
\ No newline at end of file
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
export default defineEventHandler(async (event) => { export default defineNitroPlugin(async (nitroApp) => {
let username = event.context.username let docker = createDockerFactory('ADMIN')
let docker = createDockerFactory(username)
let postgres = await docker.checkContainer('postgres_12') let postgres = await docker.checkContainer('postgres_12')
if (!postgres) { if (!postgres) {
postgres = await docker.checkAndCreateContainer({ name: 'postgres_12', img: 'postgres:12', env: ["POSTGRES_PASSWORD=postgres"], portBindings: { '5432/tcp': [{ HostPort: PgPort.toString() }] } }) postgres = await docker.checkAndCreateContainer({ name: 'postgres_12', img: 'postgres:12', env: ["POSTGRES_PASSWORD=postgres"], portBindings: { '5432/tcp': [{ HostPort: PgPort.toString() }] } })
...@@ -13,25 +12,16 @@ export default defineEventHandler(async (event) => { ...@@ -13,25 +12,16 @@ export default defineEventHandler(async (event) => {
await docker.startContainer({ container: postgres }) await docker.startContainer({ container: postgres })
await sleep(3000) await sleep(3000)
} }
let client = await createPgClientFactory() let client = await createPgClientFactory()
let result = await executePgQuery({ client, query: "SELECT u.datname FROM pg_catalog.pg_database u where u.datname='backend_helper';" }) let result = await executePgQuery({ client, query: "SELECT u.datname FROM pg_catalog.pg_database u where u.datname='backend_helper';" })
if (result.rows.length === 0) { if (result.rows.length === 0) {
await executePgQuery({ client, query: 'CREATE DATABASE backend_helper'}) await executePgQuery({ client, query: fs.readFileSync(path.resolve('./public/files/postgres/create_backend_helper.sql'), { encoding: 'utf-8'})})
await client.end() await client.end()
client = await createPgClientFactory('backend_helper') client = await createPgClientFactory('backend_helper')
const sqlCreateUser = fs
await executePgQuery({ client, query: fs.readFileSync(path.resolve('./public/files/postgres/create_user.sql'), { encoding: 'utf-8' })}) await executePgQuery({ client, query: fs.readFileSync(path.resolve('./public/files/postgres/create_user.sql'), { encoding: 'utf-8' })})
await executePgQuery({ client, query: fs.readFileSync(path.resolve('./public/files/postgres/create_v2.sql'), { encoding: 'utf-8' })}) await executePgQuery({ client, query: fs.readFileSync(path.resolve('./public/files/postgres/create_v2.sql'), { encoding: 'utf-8' })})
await executePgQuery({ client, query: fs.readFileSync(path.resolve('./public/files/postgres/create_v1.sql'), { encoding: 'utf-8'})}) await executePgQuery({ client, query: fs.readFileSync(path.resolve('./public/files/postgres/create_v1.sql'), { encoding: 'utf-8'})})
} else { await client.end()
client = await createPgClientFactory('backend_helper')
}
let user = await getPgTableData('user', username)
if (result.rowCount === 0) {
const port = await getAvailableNodePort(HOST)
await executePgQuery({ client, query: `INSERT INTO public."user"(username, port) VALUES ('${username}', '${port}');`})
user = await getPgTableData('user', username)
} }
return user
}) })
\ No newline at end of file
...@@ -12,7 +12,6 @@ export default defineNitroPlugin((nitroApp: NitroApp) => { ...@@ -12,7 +12,6 @@ export default defineNitroPlugin((nitroApp: NitroApp) => {
io.on("connection", (socket: any) => { io.on("connection", (socket: any) => {
let username = socket.handshake.auth['username'] let username = socket.handshake.auth['username']
console.log('connected success!', username)
setWebsocketIo(username, socket) setWebsocketIo(username, socket)
}); });
......
declare module global { declare module global {
namespace NodeJS { namespace NodeJS {
interface ProcessEnv { interface ProcessEnv {
HOST: string DEVHOST: string
NODE_ENV: string NODE_ENV: string
PGUSER: string PGUSER: string
postgres: string postgres: string
......
import fs from 'fs' import fs from 'fs'
export const HOST = process.env.NODE_ENV === 'production' ? '192.168.0.4' : (process.env.HOST || '192.168.0.190') export const HOST = process.env.NODE_ENV === 'production' ? '192.168.0.4' : (process.env.DEVHOST || '192.168.0.190')
export function readJson(path: string): Record<string, any> { export function readJson(path: string): Record<string, any> {
try { try {
......
...@@ -13,6 +13,12 @@ export function getWebsocketIo (username: string) { ...@@ -13,6 +13,12 @@ export function getWebsocketIo (username: string) {
return websocket.get(username) as Socket return websocket.get(username) as Socket
} }
export function sendProjectStatus (platform: 'v1' | 'v2', username: string, status: string) {
const eventName = platform === 'v1' ? 'ChangeV1Status' : 'ChangeV2Status'
const socket = getWebsocketIo(username)
socket.emit(eventName, status)
}
export default class LogUtil { export default class LogUtil {
// 根据 log 判断用户是否已经执行过 // 根据 log 判断用户是否已经执行过
static async run (username: string, log: string, cb: () => Promise<void>) { static async run (username: string, log: string, cb: () => Promise<void>) {
...@@ -40,4 +46,8 @@ export default class LogUtil { ...@@ -40,4 +46,8 @@ export default class LogUtil {
static async printWarning (username: string, log: string) { static async printWarning (username: string, log: string) {
} }
static clear (username: string) {
let socket = getWebsocketIo(username)
socket.emit('ClearLog')
}
} }
import pg from 'pg' import pg from 'pg'
import { sendProjectStatus } from './log'
export const PgPort = process.env.NODE_ENV?.trim() === 'production' ? 25556 : 30001 export const PgPort = process.env.NODE_ENV?.trim() === 'production' ? 25556 : 30001
export const PgHost = process.env.NODE_ENV?.trim() === 'production' ? '192.168.0.4' : process.env.HOST export const PgHost = process.env.NODE_ENV?.trim() === 'production' ? '192.168.0.4' : process.env.DEVHOST
let pool: pg.Pool = new pg.Pool({ let pool: pg.Pool = new pg.Pool({
host: PgHost, host: PgHost,
...@@ -41,7 +42,7 @@ export type TableUser = { username: string, port: number } ...@@ -41,7 +42,7 @@ export type TableUser = { username: string, port: number }
export type TableV2 = { export type TableV2 = {
username: string username: string
status: 'null' | 'created' | 'stopped' | 'running' | 'creating' | 'compiling' | 'compiled' status: 'null' | 'created' | 'stopped' | 'running' | 'launched' | 'creating' | 'compiling' | 'compiled'
tenants: Record<string, any> tenants: Record<string, any>
debug: Record<string, any> debug: Record<string, any>
has_debugged: boolean has_debugged: boolean
...@@ -50,7 +51,7 @@ export type TableV2 = { ...@@ -50,7 +51,7 @@ export type TableV2 = {
export type TableV1 = { export type TableV1 = {
username: string username: string
status: 'null' | 'created' | 'stopped' | 'running' | 'creating' | 'compiling' | 'compiled' status: 'null' | 'created' | 'stopped' | 'running' | 'launched' | 'creating' | 'compiling' | 'compiled'
serverProperties: { id: any, key: string, value: string }[] serverProperties: { id: any, key: string, value: string }[]
} }
...@@ -65,7 +66,7 @@ export async function getPgTableData (table: string, username: string) { ...@@ -65,7 +66,7 @@ export async function getPgTableData (table: string, username: string) {
export async function setPgTableData (table: 'user', username: string, key: keyof TableUser, value: TableUser[keyof TableUser]): Promise<void>; export async function setPgTableData (table: 'user', username: string, key: keyof TableUser, value: TableUser[keyof TableUser]): Promise<void>;
export async function setPgTableData (table: 'v2', username: string, key: keyof TableV2, value: TableV2[keyof TableV2]): Promise<void>; export async function setPgTableData (table: 'v2', username: string, key: keyof TableV2, value: TableV2[keyof TableV2]): Promise<void>;
export async function setPgTableData (table: 'v1', username: string, key: keyof TableV1, value: TableV1[keyof TableV1]): Promise<void>; export async function setPgTableData (table: 'v1', username: string, key: keyof TableV1, value: TableV1[keyof TableV1]): Promise<void>;
export async function setPgTableData (table: string, username: string, key: string, value: any) { export async function setPgTableData (table: 'v1' | 'v2' | 'user', username: string, key: string, value: any) {
await pool.query(`UPDATE public.${table} SET "${key}"='${value}' WHERE username='${username}'`) await pool.query(`UPDATE public.${table} SET "${key}"='${value}' WHERE username='${username}'`)
} }
...@@ -76,3 +77,10 @@ export async function insertPgTableData (table: string, username: string) { ...@@ -76,3 +77,10 @@ export async function insertPgTableData (table: string, username: string) {
await pool.query(`INSERT INTO public."${table}"(username) VALUES ('${username}');`) await pool.query(`INSERT INTO public."${table}"(username) VALUES ('${username}');`)
} }
export async function changeProjectStatus (table: 'v2', username: string, key: keyof TableV2, value: TableV2[keyof TableV2]): Promise<void>;
export async function changeProjectStatus (table: 'v1', username: string, key: keyof TableV1, value: TableV1[keyof TableV1]): Promise<void>;
export async function changeProjectStatus (table: string, username: string, key: string, value: any) {
await setPgTableData(table as any, username, key as any, value)
sendProjectStatus(table as any, username, value)
}
...@@ -2,15 +2,22 @@ import { defineStore } from 'pinia' ...@@ -2,15 +2,22 @@ import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', () => { export const useUserStore = defineStore('user', () => {
const username = ref('') const username = ref('')
const config = ref<{ port?: string }>({}) const port = ref(0)
const branchV1 = ref('')
const branchV2 = ref('')
function setUserName (name: string, p?: string) { function setUserName (payload: { username: string }) {
username.value = name username.value = payload.username
} }
function setUserConfig (name: string, obj: Object) { function setPort (payload: { port: number }) {
config.value = obj port.value = payload.port
username.value = name }
function setBranchV1 (payload: { branchV1: string }) {
branchV1.value = payload.branchV1
}
function setBranchV2 (payload: { branchV2: string }) {
branchV2.value = payload.branchV2
} }
return { username, config, setUserName, setUserConfig } return { username, port, branchV1, branchV2, setUserName, setPort , setBranchV1, setBranchV2 }
}) })
\ No newline at end of file
import { io, Socket } from "socket.io-client";
let socket: Socket
export function setWebSocket (username: string, force?: boolean) {
if (!socket || force) {
socket = io({
path: '/socket.io',
auth: {
username
}
});
}
}
export function getWebSocket () {
return socket
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment