Commit b2ffb25e authored by 王曜嵚 Wang Yaoqin's avatar 王曜嵚 Wang Yaoqin

dev: v2和 v1 成功启动

parent 5d12e416
<template>
<div class='container'>
<NuxtPage />
<BottomLog></BottomLog>
</div>
</template>
......
......@@ -89,6 +89,9 @@ button{
-webkit-appearance: none;
appearance: none;
}
button + button{
margin-left: 10px;
}
button{
background-color: #f6f8fa;
color: #24292f;
......
<template>
<div class="bottom-log" v-show="userStore.username">
<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>
</div>
<div class="log__info" @click="handleChangePanel">
<span v-html="lastLog"></span>
</div>
<div class="log__panel" v-if="visible">
<div class='header'>
<h3>日志</h3>
<div class="icon-close" @click="handleClosePanel">
<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 class='logs-content'>
<div v-for='item in renderLogs' :key='item'>
<p v-html='item'></p>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { io } from 'socket.io-client';
import { useUserStore } from "@/store/user"
const userStore = useUserStore()
const logLevel = ref('progress') // info | progress
const logs = ref([] as string[])
const visible = ref(false)
const lastLog = computed(() => {
return renderLogs.value.slice().pop()
})
const renderLogs = computed(() => {
let str = logs.value.join('')
return str.split('\n').filter(o => !!o.trim()).map((o) => addColorfulTag(o))
})
userStore.$subscribe(function (mutation, state) {
if (state.username) {
initWebsocket(state.username)
}
})
function addColorfulTag(log: string) {
let value = log
.replace(/\[1;34m(.*?)\[m/g, '<span style="color: #5a74f5;">$1</span>')
.replace(/\[1;31m(.*?)\[m/g, '<span style="color: red">$1</span>')
.replace(/\[1m(.*?)\[m/g, '<span style="color: gray">$1</span>')
return value
}
function initWebsocket (username: string) {
const socket = io({
path: '/socket.io',
auth: {
username
}
});
// eslint-disable-next-line no-control-regex
const removeLogPrefix = (log: string) => log.replace(/^\[.*?\]\s/, '').replace(/[\u0000-\u0009]/g, '').replace(/[\u000B-\u001F]/g, '')
const replaceLineFlag = (log: string) => log.replaceAll('\n', '<br />')
socket.on('Log', (log) => {
if (logLevel.value === 'info') {
logs.value.push(removeLogPrefix(log))
} else {
if (!log.startsWith('[info]')) {
logs.value.push(removeLogPrefix(log))
}
}
})
}
function handleChangePanel () {
visible.value = !visible.value
}
function handleClosePanel () {
visible.value = false
}
</script>
<style scoped>
.bottom-log{
position: fixed;
inset: auto 0 0 0;
height: 20px;
background-color: #000;
display: flex;
color: #fff;
}
.log__icon{
width: 20px;
height: 20px;
text-align: center;
padding: 2px 0;
}
.log__info{
flex: 1;
color: #ddd;
font-size: 12px;
line-height: 20px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.log__info:hover{
background-color: #333;
cursor: pointer;
}
.log__panel{
position: fixed;
bottom: 20px;
top: auto;
left: 0;
right: 0;
height: 200px;
border-top: 1px solid #999;
background-color: #fff;
color: #333;
padding: 10px;
display: flex;
flex-direction: column;
}
.log__panel .header{
display: flex;
justify-content: space-between;
}
.log__panel .header .icon-close{
cursor: pointer;
}
.log__panel .header .icon-close:hover{
background-color: #eee;
}
.logs-content{
font-size: 12px;
margin-bottom: 5px;
overflow: auto;
flex: 1;
}
</style>
<template>
<div class="project-card project-v2">
<div class="project-card__info">
<div class="project-card__desc">
<div class="row">
<span title="项目" class="primary">logwire-v1</span>
<span title="状态" class="secondary">
状态:{{ getStatusLabel(info?.status) }}
</span>
<span title="分支" class="secondary branch" @click="handleChangeBranch">分支:{{ info?.branch }}</span>
<span title="目录" class="secondary">目录:/var/logwire-platform</span>
</div>
<div>
<button v-if="info?.status === 'null'" class="primary" @click="handleInstall">初始化</button>
<button v-if="info?.status === 'created' || info?.status === 'compiled'" @click="handleCompile">编译</button>
<button v-if="info?.status === 'compiled'" @click="handleExecute">运行</button>
<button v-if="info?.status === 'running'" @click="handleStop">停止</button>
<div v-else-if="info?.status?.endsWith('ing')" 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" fill="#262626" p-id="4292"></path></svg>
</div>
</div>
</div>
</div>
<div class="project-card__settings">
<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" fill="#2196F3" 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" fill="#5A626A" p-id="4254"></path></svg>
</div>
<div style="display: none;">
<button>切换分支</button>
<button>修改配置</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useUserStore } from '~/store/user';
let { pending, data: info } = useFetch('/api/devops/v1/getProjectInfo')
function handleChangeBranch () {
}
function handleOpenVscode() {
window.open(window.location.protocol + '//' + window.location.hostname + ':' + useUserStore().config.port + '/vscode?folder=/var/logwire-platform', '_blank')
}
function getStatusLabel(status?: string) {
const map = new Map([
['null', "未初始化"],
['creating', '创建中'],
['created', "已创建"],
['running', '运行中'],
['stopped', '已停止'],
['compiling', '编译中'],
['compiled', '已编译']
])
return status ? map.get(status) : ''
}
async function handleStop() {
await $fetch('/api/devops/v1/stop', { method: 'post' })
if (info.value) info.value.status = 'compiled'
}
async function handleExecute() {
await $fetch('/api/devops/v1/execute', { method: 'post' })
if (info.value) info.value.status = 'running'
}
async function handleCompile() {
if (info.value) info.value.status = 'compiling'
await $fetch('/api/devops/v1/compile', { method: 'post'})
if (info.value) info.value.status = 'compiled'
}
async function handleInstall() {
if (info.value) info.value.status = 'creating'
try {
await $fetch('/api/devops/v1/install', { method: 'post' })
if (info.value) info.value.status = 'created'
} catch (err) {
info.value!.status = 'null'
}
}
</script>
<style scoped>
.project-card{
border: 1px solid #fff;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 20px;
height: 120px;
display: flex;
overflow: hidden
}
.project-card__info{
flex: 1;
overflow: hidden;
padding: 20px;
}
.project-card__settings{
width: 60px;
border-left: 1px solid #ddd;
display: flex;
flex-direction: column;
}
.project-card__settings > div{
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.project-card__settings > div:nth-child(n+1) {
border-top: 1px solid #ddd;
}
.project-card__desc > .row{
margin-bottom: 10px;
font-size: 14px;
}
.project-card__desc > .row > .primary {
font-size: 14px;
color: #000;
margin-right: 15px;
}
.project-card__desc > .row > .secondary {
font-size: 12px;
color: #999;
margin-right: 15px;
}
.branch:hover{
color: #2e75e6 !important;
cursor: pointer;
}
.setting-item:hover{
background-color: #eee;
cursor: pointer;
}
@keyframes rotate {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
.loading-icon svg{
animation: rotate infinite 3s linear;
}
</style>
\ No newline at end of file
<template>
<div class="project-card project-v2">
<div class="project-card__info">
<div class="project-card__desc">
<div class="row">
<span title="项目" class="primary">logwire-v2</span>
<span title="状态" class="secondary">
状态:{{ getStatusLabel(info?.status) }}
</span>
<span title="分支" class="secondary branch" @click="handleChangeBranch">分支:{{ info?.branch }}</span>
<span title="目录" class="secondary">目录:/var/logwire-backend</span>
</div>
<div>
<button v-if="info?.status === 'null'" class="primary" @click="handleInstall">初始化</button>
<button v-if="info?.status === 'created' || info?.status === 'compiled'" @click="handleCompile">编译</button>
<button v-if="info?.status === 'compiled'" @click="handleExecute">运行</button>
<button v-if="info?.status === 'running'" @click="handleStop">停止</button>
<div v-else-if="info?.status?.endsWith('ing')" 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" fill="#262626" p-id="4292"></path></svg>
</div>
</div>
</div>
</div>
<div class="project-card__settings">
<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" fill="#2196F3" 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" fill="#5A626A" p-id="4254"></path></svg>
</div>
<div style="display: none;">
<button>切换分支</button>
<button>修改配置</button>
<button>打包微信压缩文件</button>
<button>调试</button>
<button>打开 Vscode</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useUserStore } from '~/store/user';
let { pending, data: info } = useFetch('/api/devops/v2/getProjectInfo')
function handleChangeBranch () {
}
function handleOpenVscode() {
window.open(window.location.protocol + '//' + window.location.hostname + ':' + useUserStore().config.port + '/vscode?folder=/var/logwire-backend', '_blank')
}
function getStatusLabel(status?: string) {
const map = new Map([
['null', "未初始化"],
['creating', '创建中'],
['created', "已创建"],
['running', '运行中'],
['stopped', '已停止'],
['compiling', '编译中'],
['compiled', '已编译']
])
return status ? map.get(status) : ''
}
async function handleDebug() {
await $fetch('/api/devops/v2/debug', { method: 'post'})
if (info.value) info.value.status = 'running'
}
async function handleZip() {
await $fetch('/api/backend/downloadWeApp', { method: 'post' })
if (info.value) info.value.status = 'running'
}
async function handleStop() {
await $fetch('/api/devops/v2/stop', { method: 'post' })
if (info.value) info.value.status = 'compiled'
}
async function handleExecute() {
await $fetch('/api/devops/v2/execute', { method: 'post' })
if (info.value) info.value.status = 'running'
}
async function handleCompile() {
if (info.value) info.value.status = 'compiling'
await $fetch('/api/devops/v2/compile', { method: 'post'})
if (info.value) info.value.status = 'compiled'
}
async function handleInstall() {
if (info.value) info.value.status = 'creating'
try {
await $fetch('/api/devops/v2/install', { method: 'post' })
if (info.value) info.value.status = 'created'
} catch (err) {
info.value!.status = 'null'
}
}
</script>
<style scoped>
.project-card{
border: 1px solid #fff;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 20px;
height: 120px;
display: flex;
overflow: hidden
}
.project-card__info{
flex: 1;
overflow: hidden;
padding: 20px;
}
.project-card__settings{
width: 60px;
border-left: 1px solid #ddd;
display: flex;
flex-direction: column;
}
.project-card__settings > div{
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.project-card__settings > div:nth-child(n+1) {
border-top: 1px solid #ddd;
}
.project-card__desc > .row{
margin-bottom: 10px;
font-size: 14px;
}
.project-card__desc > .row > .primary {
font-size: 14px;
color: #000;
margin-right: 15px;
}
.project-card__desc > .row > .secondary {
font-size: 12px;
color: #999;
margin-right: 15px;
}
.branch:hover{
color: #2e75e6 !important;
cursor: pointer;
}
.setting-item:hover{
background-color: #eee;
cursor: pointer;
}
@keyframes rotate {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
.loading-icon svg{
animation: rotate infinite 3s linear;
}
</style>
/**
* useWaitingFetch
* 等待一定时间后再返回,避免接口时间太短引起页面渲染时的一闪而过的情况
*/
import nitropack from 'nitropack'
export default async function<R extends nitropack.NitroFetchRequest> (
url: R,
options: nitropack.NitroFetchOptions<R> & { wait: number }
): ReturnType<nitropack.$Fetch> {
const startTimestamp = +new Date()
const result = await $fetch(url, options)
const endTimestamp = +new Date()
const gap = endTimestamp - startTimestamp
if (gap < options.wait) {
await sleep(options.wait - gap)
}
return result
}
\ No newline at end of file
......@@ -127,7 +127,15 @@ function getCurrentBranchV1() {
}
function handleGenerate() {
$fetch('/api/git/generateSsh', { method: 'post', body: { email: form.value.email } }).then(res => {
alert('生成 ssh key: [' + res + '], 请放入 gitlab 的 SSH Key 配置中')
if (res) {
const input = document.createElement('input')
input.value = res
document.body.appendChild(input)
input.select()
document.execCommand('copy')
alert('已拷贝 SSH Key, 请前往 Gitlab 更新个人 SSH Key 配置')
document.body.removeChild(input)
}
})
}
async function handleFetch(platform: 'v1' | 'v2') {
......
This diff is collapsed.
......@@ -33,8 +33,12 @@ function handleLogin () {
method: 'post',
body: { username: username.value }
}).then((value) => {
userStore.setUserName(username.value)
if (value) {
userStore.setUserConfig(username.value, { port: value["port"] })
navigateTo('/home')
} else {
navigateTo('/prepare')
}
})
}
</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
......@@ -3,48 +3,13 @@
<div class='content'>
<div class='container'>
<form class='form' ref='form'>
<div class='form-group'>
<div class='form-group__label'>
<label for="none">
<span>postgresV1</span>
<button class='primary' @click='handleSaveConfig("postgresV1")'>保存</button>
</label>
<p>postgresV1 数据库的配置信息</p>
</div>
<div class='form-group__body'>
<div class='form-group__item'><span>数据库</span><span><input v-model='form.postgresV1.database' /></span>
</div>
<div class='form-group__item'><span>用户名</span><span><input v-model='form.postgresV1.username' /></span>
</div>
<div class='form-group__item'><span>密码</span><span><input v-model='form.postgresV1.password' /></span>
</div>
<div class='form-group__item'><span>服务 IP</span><span><input v-model='form.postgresV1.ip' /></span></div>
<div class='form-group__item'><span>服务 PORT</span><span><input v-model='form.postgresV1.port' /></span>
</div>
</div>
</div>
<div class='form-group'>
<div class='form-group__label'>
<label for="none">
<span>redisV1</span>
<button class='primary' @click='handleSaveConfig("redisV1")'>保存</button>
</label>
<p>redisV1 数据库的配置信息</p>
</div>
<div class='form-group__body'>
<div class='form-group__item'><span>服务 IP</span><span><input v-model='form.redisV1.ip' /></span></div>
<div class='form-group__item'><span>服务 PORT</span><span><input v-model='form.redisV1.port' /></span></div>
</div>
</div>
<div class="form-group">
<div class="form-group__label">
<label for="none">
<span>其他配置</span>
<div>
<button type="button" style="margin-right: 10px;" @click.stop="handleAddCustomConfig">新增</button>
<button type="button" class="primary" @click="handleSaveConfig('serverPropertiesV1')">保存</button>
<button type="button" class="primary" @click="handleSaveConfig('serverProperties')">保存</button>
</div>
</label>
</div>
......@@ -58,7 +23,7 @@
</tr>
</thead>
<tbody>
<tr v-for="item in form.serverPropertiesV1" :key="item.id">
<tr v-for="item in form.serverProperties" :key="item.id">
<td><input type="text" v-model="item.key"></td>
<td><input type="text" v-model="item.value"></td>
<td><button @click="handleDeleteServerProperty(item)">删除</button></td>
......@@ -75,29 +40,7 @@
<script lang="ts" setup>
const form = ref<Record<string, any>>({
postgresV1: {
visible: false,
username: 'postgres',
password: 'postgres',
ip: '192.168.0.4',
port: '25556',
database: 'logwirev1',
schema: 'library'
},
redisV1: {
visible: false,
ip: '192.168.0.4',
port: '25557'
},
tenantsV1: {
visible: false,
id: 'wongyaqi',
port: 8080
},
debug: {
host: '192.168.0.190'
},
serverPropertiesV1: []
serverProperties: []
})
onMounted(() => {
......@@ -114,8 +57,8 @@ function getData() {
})
}
function handleSaveConfig(key: string) {
let value = form.value[key]
$fetch('/api/config/setUserSetting', { method: 'post', body: { key, value }}).then(() => {
const value = JSON.stringify(form.value[key])
$fetch('/api/config/setUserSetting', { method: 'post', body: { table: 'v1', key, value }}).then(() => {
alert('保存成功')
}).catch(() => {
alert('保存失败!!!')
......@@ -123,15 +66,15 @@ function handleSaveConfig(key: string) {
}
function handleAddCustomConfig() {
form.value.serverPropertiesV1.push({
form.value.serverProperties.push({
id: Math.random(),
key: '',
value: ''
})
}
function handleDeleteServerProperty(item: any) {
const index = form.value.serverPropertiesV1.indexOf(item)
form.value.serverPropertiesV1.splice(index, 1)
const index = form.value.serverProperties.indexOf(item)
form.value.serverProperties.splice(index, 1)
}
</script>
......
......@@ -3,64 +3,6 @@
<div class='content'>
<div class='container'>
<form class='form' @submit.prevent>
<div class='form-group'>
<div class='form-group__label'>
<label for="none">
<span>Postgres</span>
<button class='primary' @click='handleSaveConfig("postgres")'>保存</button>
</label>
<p>Postgres 数据库的配置信息</p>
</div>
<div class='form-group__body'>
<div class='form-group__item'><span>用户名</span><span><input v-model='form.postgres.username' /></span></div>
<div class='form-group__item'><span>密码</span><span><input v-model='form.postgres.password' /></span></div>
<div class='form-group__item'><span>服务 IP</span><span><input v-model='form.postgres.ip' /></span></div>
<div class='form-group__item'><span>服务 PORT</span><span><input v-model='form.postgres.port' /></span></div>
</div>
</div>
<div class='form-group'>
<div class='form-group__label'>
<label for="none">
<span>Redis</span>
<button class='primary' @click='handleSaveConfig("redis")'>保存</button>
</label>
<p>Redis 数据库的配置信息</p>
</div>
<div class='form-group__body'>
<div class='form-group__item'><span>服务 IP</span><span><input v-model='form.redis.ip' /></span></div>
<div class='form-group__item'><span>服务 PORT</span><span><input v-model='form.redis.port' /></span></div>
</div>
</div>
<div class='form-group'>
<div class='form-group__label'>
<label for="none">
<span>Zookeeper</span>
<button class='primary' @click='handleSaveConfig("zookeeper")'>保存</button>
</label>
<p>Zookeeper 的配置信息</p>
</div>
<div class='form-group__body'>
<div class='form-group__item'><span>服务 IP</span><span><input v-model='form.zookeeper.ip' /></span></div>
<div class='form-group__item'><span>服务 PORT</span><span><input v-model='form.zookeeper.port' /></span></div>
</div>
</div>
<div class='form-group'>
<div class='form-group__label'>
<label for="none">
<span>Rocket MQ</span>
<button class='primary' @click='handleSaveConfig("rocketmq")'>保存</button>
</label>
<p>Rocket MQ 的配置信息</p>
</div>
<div class='form-group__body'>
<div class='form-group__item'><span>服务 IP</span><span><input v-model='form.rocketmq.ip' /></span></div>
<div class='form-group__item'><span>服务 PORT</span><span><input v-model='form.rocketmq.port' /></span></div>
</div>
</div>
<div class='form-group'>
<div class='form-group__label'>
<label for="none">
......@@ -171,7 +113,7 @@ onMounted(() => {
})
function getData () {
$fetch('/api/user/getProjectInfo').then((res: any) => {
$fetch('/api/devops/v2/getProjectInfo').then((res: any) => {
Object.keys(form.value).forEach(key => {
if (res[key]) {
form.value[key] = res[key]
......@@ -180,9 +122,8 @@ function getData () {
})
}
function handleSaveConfig (key: string) {
const value = form.value[key]
console.log(key, value)
$fetch('/api/user/setUserSetting', { method: 'post', body: { key, value } }).then(() => {
const value = JSON.stringify(form.value[key])
$fetch('/api/user/setUserSetting', { method: 'post', body: { table: 'v2', key, value } }).then(() => {
alert('保存成功')
}).catch(() => {
alert('保存失败!!!')
......
{
"postgres": {
"visible": false,
"username": "postgres",
"password": "postgres",
"ip": "192.168.0.4",
"port": "25556",
"database": "login",
"schema": "library"
},
"redis": {
"visible": false,
"ip": "192.168.0.4",
"port": "25557"
},
"zookeeper": {
"visible": false,
"ip": "192.168.0.4",
"port": "2182"
},
"rocketmq": {
"visible": false,
"ip": "192.168.0.4",
"port": "9876"
},
"tenants": {
"visible": false,
"id": "login",
"host": "a.test.com:23339,a.test.com:23340",
"database-schema": "library",
"primary-namespace": "library"
},
"postgresV1": {
"visible": false,
"username": "postgres",
"password": "postgres",
"ip": "192.168.0.4",
"port": "25556",
"database": "logwirev2",
"schema": "library"
},
"redisV1": {
"visible": false,
"ip": "192.168.0.4",
"port": "25557"
},
"tenantsV1": {
"visible": false,
"id": "wongyaqi",
"port": 8080
},
"debug": {
"host": "192.168.1.94"
},
"node-port": 30003,
"username": "login"
}
\ No newline at end of file
{
"postgres": {
"visible": false,
"username": "postgres",
"password": "postgres",
"ip": "localhost",
"port": "25556",
"database": "wyq",
"schema": "library"
},
"redis": {
"visible": false,
"ip": "localhost",
"port": "25557"
},
"zookeeper": {
"visible": false,
"ip": "localhost",
"port": "2182"
},
"rocketmq": {
"visible": false,
"ip": "localhost",
"port": "9876"
},
"tenants": {
"visible": false,
"id": "wyq",
"host": "a.test.com:23333,a.test.com:23334",
"database-schema": "library",
"primary-namespace": "library"
},
"postgresV1": {
"visible": false,
"username": "postgres",
"password": "postgres",
"ip": "192.168.0.4",
"port": "25556",
"database": "logwirev2",
"schema": "library"
},
"redisV1": {
"visible": false,
"ip": "192.168.0.4",
"port": "25557"
},
"tenantsV1": {
"visible": false,
"id": "wongyaqi",
"port": 8080
},
"debug": {
"host": "192.168.1.94"
},
"node-port": 30000,
"username": "wyq",
"status": "created",
"InstallSteps": [
"创建 node 容器",
"检查 SSH Key",
"创建 postgres 容器",
"创建 redis 容器",
"创建 zookeeper 容器",
"创建 rocketmq serv 容器",
"创建 rocketmq broker 容器",
"创建 node 容器",
"检查 SSH Key",
"创建 postgres 容器",
"创建 redis 容器",
"创建 zookeeper 容器",
"创建 rocketmq serv 容器",
"创建 rocketmq broker 容器",
"克隆仓库"
]
}
\ No newline at end of file
-- Database: backend_helper
-- DROP DATABASE IF EXISTS backend_helper;
CREATE DATABASE backend_helper
WITH
OWNER = postgres
ENCODING = 'UTF8'
LC_COLLATE = 'en_US.utf8'
LC_CTYPE = 'en_US.utf8'
TABLESPACE = pg_default
CONNECTION LIMIT = -1
IS_TEMPLATE = False;
\ No newline at end of file
-- Table: public.user
-- DROP TABLE IF EXISTS public."user";
CREATE TABLE IF NOT EXISTS public."user"
(
username character varying(20) COLLATE pg_catalog."default" NOT NULL,
port character varying(10) COLLATE pg_catalog."default",
CONSTRAINT user_pkey PRIMARY KEY (username)
)
TABLESPACE pg_default;
ALTER TABLE IF EXISTS public."user"
OWNER to postgres;
\ No newline at end of file
-- Table: public.v1
-- DROP TABLE IF EXISTS public.v1;
CREATE TABLE IF NOT EXISTS public.v1
(
username character varying(50) COLLATE pg_catalog."default" NOT NULL,
status character varying(50) COLLATE pg_catalog."default",
"serverProperties" json,
CONSTRAINT v1_pkey PRIMARY KEY (username)
)
TABLESPACE pg_default;
ALTER TABLE IF EXISTS public.v1
OWNER to postgres;
\ No newline at end of file
-- Table: public.v2
-- DROP TABLE IF EXISTS public.v2;
CREATE TABLE IF NOT EXISTS public.v2
(
username character varying(50) COLLATE pg_catalog."default" NOT NULL,
tenants json,
debug json,
has_debugged boolean,
status character varying(50) COLLATE pg_catalog."default",
"serverProperties" json,
CONSTRAINT v2_pkey PRIMARY KEY (username)
)
TABLESPACE pg_default;
ALTER TABLE IF EXISTS public.v2
OWNER to postgres;
\ No newline at end of file
......@@ -10,7 +10,7 @@ spring.redis.database=0
# redis ??
#spring.redis.password=
# redis ??
spring.redis.port=25557
spring.redis.port=6379
# redis host
spring.redis.host=192.168.0.4
......@@ -50,11 +50,11 @@ logwire.tenants[0].mail.password=flzx.3qc
logwire.tenants[0].mail.protocol=smtp
logwire.tenants[0].mail.encoding=utf-8
#obs
logwire.document-storage-type=obs
logwire.obs.bucket-name=platform-dev
logwire.obs.end-point=obs.cn-east-3.myhuaweicloud.com
logwire.obs.access-key-id=BLVYTFIPDZDN57ZTCQ5K
logwire.obs.access-key-secret=kPaWoZdyaLQVGYQo0PPTIpwHQi0SaDIE1pmE0itZ
#logwire.document-storage-type=obs
#logwire.obs.bucket-name=platform-dev
#logwire.obs.end-point=obs.cn-east-3.myhuaweicloud.com
#logwire.obs.access-key-id=BLVYTFIPDZDN57ZTCQ5K
#logwire.obs.access-key-secret=kPaWoZdyaLQVGYQo0PPTIpwHQi0SaDIE1pmE0itZ
#azure-blob
#logwire.document-storage-type=blob
#logwire.blob.account-name=logwireblob
......
......@@ -20,7 +20,7 @@ spring.redis.database=0
# redis 密码
spring.redis.password=
# redis 端口
spring.redis.port=30002
spring.redis.port=6379
# redis host
spring.redis.host=192.168.0.190
# 连接超时时间(单位:毫秒)
......@@ -316,3 +316,9 @@ logwire.tenants[0].primary-namespace=library
#logwire.tenants[0].security.csp.frame-ancestors='self'
# 设计器密码
logwire.security.encryption.logwire-kms.master-key-passwords[0]=logwire
logwire.tenants[0].designer.trust-ip=127.0.0.1
logwire.security.encryption.key-management-system=logwire-kms
\ No newline at end of file
import LogUtil from "~/server/utils/log"
// 检测容器运行状态。如果服务器突然被关停,会导致个人服务一直显示“运行中”,但是实际停掉了
export default defineEventHandler(async (event) => {
try {
// 根据传入的端口号,检测
let username = event.context.username
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
if (!container) {
throw new Error('没有创建容器,请先初始化容器')
}
let info = await container.inspect()
if (info.State.Running) {
// 正在运行,则认为正常
return
} else {
LogUtil.print(username, `[progress] [[1;34mInfo[m] 服务器异常关闭,正在重启...... \n`)
await docker.startContainer({ container })
// 依次打开 nginx, vscode-web
try {
await docker.execContainerCommand({ container, cmd: 'lsof -i:80' })
} catch (err) {
try {
await docker.execContainerCommand({ container, cmd: 'nginx' })
LogUtil.print(username, `[progress] [[1;34mInfo[m] Nginx 重启成功...... \n`)
} catch (err) {
throw err
}
}
try {
await docker.execContainerCommand({ container, cmd: 'lsof -i:8000' })
} catch (err) {
try {
docker.execContainerCommand({ container, cmd: 'code-server --bind-addr 127.0.0.1:8000 --auth none' })
LogUtil.print(username, `[progress] [[1;34mInfo[m] Vscode 重启成功...... \n`)
} catch (err) {
throw err
}
}
try { await docker.execContainerCommand({ container, cmd: 'pm2 delete backend' }) } catch (err) {}
try { await docker.execContainerCommand({ container, cmd: 'pm2 delete gateway' }) } catch (err) {}
try { await docker.execContainerCommand({ container, cmd: 'pm2 delete platform' }) } catch (err) {}
const status = getUserConfig(username, 'status')
if (status === 'running') {
setUserConfig(username, 'status', 'stopped')
}
const statusV1 = getUserConfig(username, 'v1-status')
if (statusV1 === 'running') {
setUserConfig(username, 'v1-status', 'stopped')
}
LogUtil.printSuccess(username, '所有服务重启成功')
}
} catch (err) {
setResponseStatus(event, 500)
return err
}
})
\ No newline at end of file
......@@ -2,15 +2,14 @@ import LogUtil from "~/server/utils/log"
import path from 'path'
export default defineEventHandler(async (event) => {
try {
let username = event.context.username
try {
let docker = createDockerFactory(username)
let port = getUserConfig(username, 'node-port')
setUserConfig(username, 'v1-status', 'compiling')
setPgTableData( 'v1', username, 'status', 'compiling')
// 每次编译前把已有的 tenants_config 文件夹拷贝到一个地方,编译完成后,再拷贝回来
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
let container = await docker.checkContainer( username + '.node')
if (!container) {
throw new Error('没有创建容器,请先初始化容器')
}
......@@ -19,14 +18,13 @@ export default defineEventHandler(async (event) => {
await docker.preStartSystem({ container, username, platform: 'v1' })
LogUtil.printInfo(username, '编译中')
const pomDir = '/var/logwire-platform/logwire-build/logwire-server/logwire-libs/logwire-web'
await docker.putArchive({ container, tarPath: path.resolve(__dirname, './files/v1/pomWithoutWeb.tar'), targetPath: pomDir })
await docker.putArchive({ container, tarPath: path.resolve('./public/files/v1/pomWithoutWeb.tar'), targetPath: pomDir })
await docker.execContainerCommand({ container, cmd: ['/bin/bash', '-c', 'export LANG=zh_CN.UTF-8;mvn clean package -Dmaven.test.skip=true'], dir: '/var/logwire-platform/logwire-build'})
await docker.putArchive({ container, tarPath: path.resolve(__dirname, './files/v1/pomWithWeb.tar'), targetPath: pomDir })
await docker.putArchive({ container, tarPath: path.resolve('./public/files/v1/pomWithWeb.tar'), targetPath: pomDir })
const pomVersionStr = await docker.getFile({ container, path: '/var/logwire-platform/logwire-build/logwire-version/pom.xml' })
const version = /<logwire-starter\.version>(.*?)<\/logwire-starter\.version>/.exec(pomVersionStr)?.[1]
const versionWeb = /<logwire-libs\.version>(.*?)<\/logwire-libs\.version>/.exec(pomVersionStr)?.[1]
const projectDir = getUserConfig(username, 'serverPropertiesV1')?.find((o: { key: string, value: string }) => o.key === 'logwire.tenants[0].dir')?.value?.trim() || './projects/demo'
await docker.execContainerCommand({ container, cmd: 'tar -zvcf ../tenants.tar.gz ./projects/demo', dir: '/var/logwire-platform/dist'})
await docker.execContainerCommand({ container, cmd: 'rm -rf dist', dir: '/var/logwire-platform'})
......@@ -39,11 +37,9 @@ export default defineEventHandler(async (event) => {
await docker.execContainerCommand({ container, cmd: 'rm ../tenants.tar.gz', dir: '/var/logwire-platform/dist'})
await copyAndCreateServerPropertiesV1InDocker(username)
LogUtil.printInfo(username, '编译完成')
setUserConfig(username, 'v1-status', 'created')
await setPgTableData( 'v1', username, 'status', 'compiled')
} catch (err) {
console.log(err)
let username = event.context.username
setUserConfig(username, 'v1-status', 'created')
await setPgTableData( 'v1', username, 'status', 'created')
LogUtil.printError(username, err instanceof Error ? err.message : JSON.stringify(err))
setResponseStatus(event, 500)
return err
......
import LogUtil from "~/server/utils/log"
export default defineEventHandler(async (event) => {
try {
let username = event.context.username
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
let port = getUserConfig(username, 'node-port')
let container = await docker.checkContainer( username + '.node')
const row = await getPgTableData( 'user', username)
const port = row?.["port"]
LogUtil.printInfo(username, '程序启动中')
if (!container) {
......@@ -21,13 +21,7 @@ export default defineEventHandler(async (event) => {
await docker.execContainerCommand({ container, cmd: `pm2 start --name platform --no-autorestart java -- -Xms512m -Xmx512m -XX:MaxMetaspaceSize=200m -Dfile.encoding=UTF-8 -Dloader.path=lib -jar logwire-starter-${version}.jar > logwire.log`, dir: '/var/logwire-platform/dist', quiet: true })
LogUtil.print(username, `[progress] [[1;34mInfo[m] 程序运行中...... \n`)
LogUtil.print(username, `[progress] [[1;34mInfo[m] 请代理后端请求到 <strong>192.168.0.4:${port}</strong> 上 \n`)
LogUtil.print(username, `[progress] [[1;34mInfo[m] 请代理后端请求到 <strong>${HOST}:${port}</strong> 上 \n`)
setUserConfig(username, 'v1-status', 'running')
} catch (err) {
let username = event.context.username
LogUtil.print(username, '[error] [[1;31mError[m]] ' + (err instanceof Error ? err.message : JSON.stringify(err)))
setResponseStatus(event, 500)
return err
}
await setPgTableData( 'v1', username, 'status', 'running')
})
\ No newline at end of file
import { TableV1, TableV2 } from "~/server/utils/postgres"
export default defineEventHandler(async (event) => {
let username = event.context.username
let docker = createDockerFactory(username)
const row = await getPgTableData( 'v1', username)
const info: Partial<TableV1> & { branch: string } = { ...row, branch: '' }
info.status = row?.status || 'null'
try {
let container = await docker.checkContainer(username + '.node')
if (container) {
await docker.startContainer({ container })
let result = await docker.execContainerCommand({ container, cmd: 'git branch', dir: getPlatformRootFolder('v1') })
const arrays = result.split('\n')
let currentBranch = arrays.find(o => o.includes('*'))
currentBranch = currentBranch?.replace(/^.*?\*/, '').trim() || ''
info.branch = removeUnreadCharacter(currentBranch)
} else {
info.branch = '无'
}
} catch (err) {
// 出现任何错误都认为没有未初始化分支
info.branch = '无'
}
return info
})
\ No newline at end of file
......@@ -2,58 +2,32 @@ import Dockerode from "dockerode"
import LogUtil from "~/server/utils/log"
import path from 'path'
function sleep (ms: number) {
return new Promise<void>((resolve, reject) => {
setTimeout(resolve, ms)
})
}
export default defineEventHandler(async (event) => {
try {
let username = event.context.username
let pgClient = await createPgClientFactory(username)
try {
let docker = createDockerFactory(username)
let host = process.env['DOCKER_ENV']?.trim() === 'prod' ? '192.168.0.4' : 'localhost'
let port = getUserConfig(username, 'node-port').toString()
const rowV2 = await getPgTableData( 'v2', username)
const statusV2 = getUserConfig(username, 'status')
if (statusV2 === 'null' || statusV2 === undefined) {
if (!rowV2 || rowV2?.status === 'null' || !rowV2?.status) {
throw new Error('请先初始化 V2')
}
setUserConfig(username, 'v1-status', 'creating')
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node') as Dockerode.Container
let rowV1 = await getPgTableData('v1', username)
if (!rowV1) await insertPgTableData('v1', username)
await setPgTableData( 'v1', username, 'status', 'creating')
let container = await docker.checkContainer( username + '.node') as Dockerode.Container
if (!container) {
throw new Error('没有创建容器,请先初始化容器')
}
// 创建 postgres 不一定要在本机,即使是 dev 环境也可以联远程的容器
await LogUtil.run(username, 'v1-创建 postgres 容器', async () => {
if (getUserConfig(username, 'postgresV1.ip') !== '192.168.0.4') {
let postgres = await docker.checkAndCreateContainer({ name: 'postgres', img: 'postgres:12', env: ["POSTGRES_PASSWORD=postgres"], portBindings: { '5432/tcp': [{ HostPort: '30001' }] } })
let info = await postgres.inspect()
await docker.startContainer({ container: postgres })
if (info?.State.Status === 'created') {
info = await postgres.inspect()
await sleep(3000)
let client = await createPgClient({ host: host, port: 30001 })
await executePgQuery({ client, query: 'CREATE DATABASE logwirev2'})
await executePgQuery({ client, query: 'CREATE SCHEMA library' })
await client.end()
}
} else {
// 如果是服务器环境,检查是否存在对应的数据库表,根据不同用户创建不同的数据库
let postgres = await docker.checkContainer('postgres_12')
if (!postgres) {
throw new Error('没有创建服务器上的 Postgres 容器, 请先创建')
}
const datname = username + '_v1'
let client = await createPgClient({ host: host, port: 25556 })
let result = await executePgQuery({ client, query: "SELECT u.datname FROM pg_catalog.pg_database u where u.datname='" + datname + "';" })
const database = username + '_v1'
let result = await executePgQuery({ client: pgClient, query: "SELECT u.datname FROM pg_catalog.pg_database u where u.datname='" + database + "';" })
if (result.rows.length === 0) {
await executePgQuery({ client, query: 'CREATE DATABASE ' + datname })
client = await createPgClient({ host: host, port: 25556, database: datname })
await executePgQuery({ client: pgClient, query: 'CREATE DATABASE ' + database })
const client = await createPgClient({ host: HOST, port: PgPort, database: database })
await executePgQuery({ client, query: 'CREATE SCHEMA library' })
await client.end()
setUserConfig(username, 'postgresV1.database', datname)
}
}
})
......@@ -71,7 +45,7 @@ export default defineEventHandler(async (event) => {
})
await LogUtil.run(username, 'v1-下载字符集', async () => {
await docker.execContainerCommand({ container, cmd: 'rm /etc/locale.gen'})
await docker.execContainerCommand({ container, cmd: 'rm -f /etc/locale.gen'})
await docker.execContainerCommand({ container, cmd: 'touch /etc/locale.gen' })
await docker.writeFile({ container, path: '/etc/locale.gen', text: '\'\nzh_CN.UTF-8 UTF-8\''})
await docker.execContainerCommand({ container, cmd: 'apt-get install -y locales' })
......@@ -82,17 +56,20 @@ export default defineEventHandler(async (event) => {
await docker.execContainerCommand({ container, cmd: 'rm -rf /var/logwire-demo' })
await docker.execContainerCommand({ container, cmd: 'mkdir logwire-demo', dir: '/var'})
await docker.execContainerCommand({ container, cmd: 'mkdir -p dist/projects/demo', dir: '/var/logwire-platform'})
// 避免上传太大的压缩文件,需要先在 node 服务器的 /var 目录下,上传该文件
await docker.putArchive({ container, tarPath: path.resolve('/var/book.tar'), targetPath: '/var/logwire-demo' })
await docker.execContainerCommand({ container, cmd: 'tar -zvcf ../book.tar .', dir: '/var/logwire-demo/book'})
await docker.execContainerCommand({ container, cmd: 'tar -zvxf book.tar -C /var/logwire-platform/dist/projects/demo', dir: '/var/logwire-demo'})
})
setUserConfig(username, 'v1-status', 'created')
await setPgTableData( 'v1', username, 'status', 'created')
await stopPgClient(pgClient)
LogUtil.printSuccess(username, 'installed')
} catch (err: any) {
let username = event.context.username
setUserConfig(username, 'v1-status', 'null')
await setPgTableData( 'v1', username, 'status', 'null')
await stopPgClient(pgClient)
LogUtil.printError(username, (err instanceof Error ? err.message : JSON.stringify(err)))
setResponseStatus(event, 500)
return err
}
})
\ No newline at end of file
import LogUtil from "~/server/utils/log"
export default defineEventHandler(async (event) => {
try {
let username = event.context.username
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
let container = await docker.checkContainer( username + '.node')
if (!container) {
throw new Error('没有创建容器,请先初始化容器')
}
LogUtil.printInfo(username, `程序停止中......`)
await docker.execContainerCommand({ container, cmd: 'pm2 delete platform', quiet: true })
LogUtil.printInfo(username, `程序已停止`)
setUserConfig(username, 'v1-status', 'stopped')
} catch (err) {
setResponseStatus(event, 500)
return err
}
await setPgTableData( 'v1', username, 'status', 'compiled')
})
\ No newline at end of file
......@@ -2,15 +2,16 @@ import LogUtil from "~/server/utils/log"
import { copyAndCreateGatewayPropertiesV2InDocker, copyAndCreateServerPropertiesV2InDocker } from "~/server/utils/server"
export default defineEventHandler(async (event) => {
let username = event.context.username
try {
let username = event.context.username
let docker = createDockerFactory(username)
let port = getUserConfig(username, 'node-port')
setUserConfig(username, 'status', 'compiling')
await setPgTableData( 'v2', username, 'status', 'compiling')
// TODO: 在后端发送 websocket 通知前端状态变更
// 每次编译前把已有的 tenants_config 文件夹拷贝到一个地方,编译完成后,再拷贝回来
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
let container = await docker.checkContainer( username + '.node')
if (!container) {
throw new Error('没有创建容器,请先初始化容器')
}
......@@ -35,10 +36,9 @@ export default defineEventHandler(async (event) => {
copyAndCreateGatewayPropertiesV2InDocker(username)
LogUtil.print(username, `[progress] [[1;34mInfo[m] 编译完成 \n`)
setUserConfig(username, 'status', 'created')
await setPgTableData( 'v2', username, 'status', 'compiled')
} catch (err) {
let username = event.context.username
await setPgTableData( 'v2', username, 'status', 'created')
LogUtil.print(username, '[error] [[1;31mError[m]] ' + (err instanceof Error ? err.message : JSON.stringify(err)))
setResponseStatus(event, 500)
}
......
......@@ -2,17 +2,18 @@ import LogUtil from "~/server/utils/log"
export default defineEventHandler(async (event) => {
// 先判断服务是否启动,启动则关闭,然后调用新的命令启动 java 端
try {
let username = event.context.username
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
let container = await docker.checkContainer( username + '.node')
if (!container) {
throw new Error('没有创建容器,请先初始化容器')
}
const status = getUserConfig(username, 'status')
const tenants = getUserConfig(username, 'tenants')
const port = getUserConfig(username, 'node-port')
const debug = getUserConfig(username, 'debug') || { host: '192.168.0.190' }
const rowV2 = await getPgTableData( 'v2', username)
const status = rowV2?.status
const tenants = rowV2?.tenants
const rowUser = await getPgTableData( 'user', username)
const port = rowUser?.["port"]
const debug = rowV2?.debug
// 有可能本身就关闭了
try {
......@@ -21,9 +22,7 @@ export default defineEventHandler(async (event) => {
} catch (err) {}
await LogUtil.run(username, '初始化调试功能', async () => {
let key = 'InstallSteps'
let steps: string[] = getUserConfig(username, key) || []
if (steps.includes('初始化调试功能')) return
if (rowV2?.has_debugged) return
try {
if (container) {
LogUtil.print(username, `[progress] [Loading] 下载 Nginx ...... \n`)
......@@ -37,6 +36,8 @@ export default defineEventHandler(async (event) => {
await docker.execContainerCommand({ container, cmd: 'make', dir: '/var/tmp/nginx-1.18.0'})
LogUtil.print(username, `[progress] [Loading] 替换 Nginx 文件 ...... \n`)
await docker.execContainerCommand({ container, cmd: 'cp /var/tmp/nginx-1.18.0/objs/nginx /usr/sbin/'})
await setPgTableData( 'v2', username, 'has_debugged', true)
}
} catch (err) {
console.log('err2', err)
......@@ -58,13 +59,9 @@ export default defineEventHandler(async (event) => {
await docker.execContainerCommand({ container, cmd: 'pm2 start --name backend --no-autorestart java -- -Xms128m -Xmx128m -XX:+UseG1GC -DVALIDATE_XML_ENABLED=false --add-opens java.base/sun.util.locale.provider=ALL-UNNAMED -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=:6666,suspend=n -jar logwire-backend-starter.jar', dir: '/var/logwire-backend/build-output/backend', quiet: true })
LogUtil.print(username, `[progress] [[1;34mInfo[m] 程序运行中...... \n`)
LogUtil.print(username, `[progress] [[1;34mInfo[m] 请代理后端请求到 <strong>192.168.0.4:${port}</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>192.168.0.4:${port}</strong> 进行调试\n`)
LogUtil.print(username, `[progress] [[1;34mInfo[m] 请代理后端请求到 <strong>${HOST}:${port}</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`)
setUserConfig(username, 'status', 'running')
} catch (err) {
setResponseStatus(event, 500)
return err
}
await setPgTableData( 'v2', username, 'status', 'running')
})
\ No newline at end of file
import LogUtil from "~/server/utils/log"
export default defineEventHandler(async (event) => {
let username = event.context.username
let pgClient = await createPgClientFactory(username)
try {
let username = event.context.username
let docker = createDockerFactory(username)
// 创建 node 容器
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
let port = getUserConfig(username, 'node-port')
let container = await docker.checkContainer( username + '.node')
let row = await getPgTableData( 'user', username)
let port = row?.["port"]
if (container) {
LogUtil.print(username, `[progress] [[1;34mInfo[m] 打包微信小程序zip \n`)
await docker.execContainerCommand({ container, cmd: 'rm -f weapp.zip', dir: '/var/logwire-backend/build-output/backend' })
......@@ -16,6 +19,7 @@ export default defineEventHandler(async (event) => {
throw new Error('没有创建容器,请先初始化容器')
}
} catch (err) {
await stopPgClient(pgClient)
setResponseStatus(event, 500)
return err
}
......
import LogUtil from "~/server/utils/log"
export default defineEventHandler(async (event) => {
let username = event.context.username
try {
let username = event.context.username
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
let tenants = getUserConfig(username, 'tenants')
let port = getUserConfig(username, 'node-port')
let container = await docker.checkContainer( username + '.node')
let rowUser = await getPgTableData( 'user', username)
let rowV2 = await getPgTableData( 'v2', username)
let tenants = rowV2?.tenants
let port = rowUser?.["port"]
LogUtil.printInfo(username, `程序初始化中......`)
if (!container) {
......@@ -26,12 +29,11 @@ export default defineEventHandler(async (event) => {
await docker.execContainerCommand({ container, cmd: `pm2 start --name backend --no-autorestart java -- -Xms128m -Xmx128m -XX:+UseG1GC -DVALIDATE_XML_ENABLED=false --add-opens java.base/sun.util.locale.provider=ALL-UNNAMED -jar logwire-backend-starter.jar`, dir: '/var/logwire-backend/build-output/backend', quiet: true })
LogUtil.printInfo(username, `程序运行中......`)
LogUtil.printInfo(username, `请代理后端请求到 <strong>192.168.0.4:${port}</strong> 上`)
LogUtil.printInfo(username, `请设置开发环境域名和端口号 <strong>${tenants.host}</strong> 上`)
LogUtil.printInfo(username, `请代理后端请求到 <strong>${HOST}:${port}</strong> 上`)
LogUtil.printInfo(username, `请设置开发环境域名和端口号 <strong>${tenants?.host}</strong> 上`)
setUserConfig(username, 'status', 'running')
await setPgTableData( 'v2', username, 'status', 'running')
} catch (err) {
let username = event.context.username
LogUtil.printError(username, (err instanceof Error ? err.message : JSON.stringify(err)))
setResponseStatus(event, 500)
return err
......
import { TableV2 } from "~/server/utils/postgres"
export default defineEventHandler(async (event) => {
let username = event.context.username
let docker = createDockerFactory(username)
let pgClient = await createPgClientFactory('backend_helper')
const row = await getPgTableData( 'v2', username)
const info: Partial<TableV2> & { branch: string } = { ...row, branch: '' }
info.status = row?.status || 'null'
try {
let container = await docker.checkContainer(username + '.node')
if (container) {
await docker.startContainer({ container })
let result = await docker.execContainerCommand({ container, cmd: 'git branch', dir: getPlatformRootFolder('v2') })
const arrays = result.split('\n')
let currentBranch = arrays.find(o => o.includes('*'))
currentBranch = currentBranch?.replace(/^.*?\*/, '').trim() || ''
info.branch = removeUnreadCharacter(currentBranch)
} else {
info.branch = '无'
}
} catch (err) {
// 出现任何错误都认为没有未初始化分支
info.branch = '无'
}
return info
})
\ No newline at end of file
import Dockerode from "dockerode"
import LogUtil from "~/server/utils/log"
import { createPgClient, executePgQuery } from "~/server/utils/postgres"
import { checkSshExists } from "~/server/utils/server"
import fs from 'fs'
import path from 'path'
function sleep (ms: number) {
return new Promise<void>((resolve, reject) => {
setTimeout(resolve, ms)
})
}
export default defineEventHandler(async (event) => {
try {
let username = event.context.username
let pgClient = await createPgClientFactory('backend_helper')
try {
let docker = createDockerFactory(username)
let host = docker.host
let port = getUserConfig(username, 'node-port').toString()
let container: Dockerode.Container = {} as Dockerode.Container
setUserConfig(username, 'status', 'creating')
const port = (await getPgTableData( 'user', username))?.port
const row = await getPgTableData('v2', username)
if (!row) await insertPgTableData('v2', username)
await setPgTableData( 'v2', username, 'status', 'creating')
// 必须要保证创建容器是首先执行
// node 容器根据 dev/prod 环境与否,决定在哪里创建,远程容器还是本机容器
await LogUtil.run(username, '创建 node 容器', async () => {
......@@ -36,58 +30,47 @@ export default defineEventHandler(async (event) => {
LogUtil.printWarning(username, `${username}@greaconsulting.com 用户 SSH key 为 <strong>${sshKey.replace(/\n/g, '')}</strong>, 若克隆仓库失败, 请检查是否放入 Gitlab SSH 设置中并重试\n`)
})
// 创建 postgres 不一定要在本机,即使是 dev 环境也可以联远程的容器
await LogUtil.run(username, '创建 postgres 容器', async () => {
if (getUserConfig(username, 'postgres.ip') !== '192.168.0.4') {
let postgres = await docker.checkAndCreateContainer({ name: 'postgres', img: 'postgres:12', env: ["POSTGRES_PASSWORD=postgres"], portBindings: { '5432/tcp': [{ HostPort: '30001' }] } })
let info = await postgres.inspect()
await docker.startContainer({ container: postgres })
if (info?.State.Status === 'created') {
info = await postgres.inspect()
await sleep(3000)
let client = await createPgClient({ host: host, port: 30001 })
await executePgQuery({ client, query: 'CREATE DATABASE logwirev2'})
await executePgQuery({ client, query: 'CREATE SCHEMA library' })
await client.end()
}
} else {
// 如果是服务器环境,检查是否存在对应的数据库表,根据不同用户创建不同的数据库
await LogUtil.run(username, '配置 postgres 容器', async () => {
let postgres = await docker.checkContainer('postgres_12')
if (!postgres) {
throw new Error('没有创建服务器上的 Postgres 容器, 请先创建')
postgres = await docker.checkAndCreateContainer({ name: 'postgres_12', img: 'postgres:12', env: ["POSTGRES_PASSWORD=postgres"], portBindings: { '5432/tcp': [{ HostPort: PgPort.toString() }] } })
await docker.startContainer({ container: postgres })
await sleep(3000)
}
let client = await createPgClient({ host: host, port: 25556 })
let client = await createPgClientFactory()
let result = await executePgQuery({ client, query: "SELECT u.datname FROM pg_catalog.pg_database u where u.datname='" +username+ "';" })
if (result.rows.length === 0) {
await executePgQuery({ client, query: 'CREATE DATABASE ' + username})
client = await createPgClient({ host: host, port: 25556, database: username })
await stopPgClient(client)
client = await createPgClientFactory(username)
await executePgQuery({ client, query: 'CREATE SCHEMA library' })
await client.end()
}
await stopPgClient(client)
}
})
await LogUtil.run(username, '创建 redis 容器', async () => {
if (getUserConfig(username, 'redis.ip') !== '192.168.0.4') {
let redis = await docker.checkAndCreateContainer({ name: 'redis', img: 'redis', portBindings: { '6379/tcp': [{ HostPort: '30002' }] } })
if (process.env.NODE_ENV?.trim() !== 'production') {
let redis = await docker.checkAndCreateContainer({ name: 'redis', img: 'redis', portBindings: { '6379/tcp': [{ HostPort: '6379' }] } })
await docker.startContainer({ container: redis })
}
})
await LogUtil.run(username, '创建 zookeeper 容器', async () => {
if (getUserConfig(username, 'zookeeper.ip') !== '192.168.0.4') {
let zookeeper = await docker.checkAndCreateContainer({ name: 'zookeeper', img: 'zookeeper', portBindings: { '2181/tcp': [{ HostPort: '30003' }] } })
if (process.env.NODE_ENV?.trim() !== 'production') {
let zookeeper = await docker.checkAndCreateContainer({ name: 'zookeeper', img: 'zookeeper', portBindings: { '2181/tcp': [{ HostPort: '2181' }] } })
await docker.startContainer({ container: zookeeper })
}
})
// 检查本机 rocketmq 端口是否被占用,被占用说明已经有 rockqtmq 服务启动,这时候就不安装容器了
await LogUtil.run(username, '创建 rocketmq serv 容器', async () => {
if (getUserConfig(username, 'rocketmq.ip') !== '192.168.0.4') {
if (process.env.NODE_ENV?.trim() !== 'production') {
let rockermqsrv = await docker.checkAndCreateContainer({ name: 'rocketmq.srv', img: 'foxiswho/rocketmq:4.8.0', portBindings: { '9876/tcp': [{ 'HostPort': '9876' }] /** , '10909/tcp': [], '10911/tcp': [], '10912/tcp': []*/ }, env: ['JAVA_OPT_EXT=-Xms512M -Xmx512M -Xmn128m'] , cmd: ['bash', '-c', 'mqnamesrv'] })
await docker.startContainer({ container: rockermqsrv })
}
})
await LogUtil.run(username, '创建 rocketmq broker 容器', async () => {
if (getUserConfig(username, 'rocketmq.ip') !== '192.168.0.4') {
if (process.env.NODE_ENV?.trim() !== 'production') {
let rocketmqbroker = await docker.checkAndCreateContainer({ name: 'rocketmq.broker', img: 'foxiswho/rocketmq:4.8.0', portBindings: { '10909/tcp': [{ 'HostPort': '10909' }], '10911/tcp': [{ 'HostPort': '10911' }] /** , '9876/tcp': [],'10912/tcp': [] */ }, env: ['JAVA_OPT_EXT=-Xms512M -Xmx512M -Xmn128m'] })
await docker.startContainer({ container: rocketmqbroker })
......@@ -117,11 +100,16 @@ export default defineEventHandler(async (event) => {
await docker.execContainerCommand({ container, cmd: 'apt-get update' })
})
await LogUtil.run(username, '安装 openjdk ', async () => {
await docker.putArchive({ container, tarPath: '/var/openlogic-openjdk-17.0.10+7-linux-x64.tar.gz', targetPath: '/var' })
const openjdkFilePath = '/var/openlogic-openjdk-17.0.10+7-linux-x64.tar.gz'
const stat = fs.statSync(openjdkFilePath, { throwIfNoEntry: false })
if (!stat) {
throw new Error('未找到 ' + openjdkFilePath + ', 请下载文件并放置到根目录 var 文件夹内')
}
await docker.putArchive({ container, tarPath: openjdkFilePath , targetPath: '/var' })
await docker.execContainerCommand({ container, cmd: 'cp -r openlogic-openjdk-17.0.10+7-linux-x64/ java-17-openjdk', dir: '/var' })
await docker.execContainerCommand({ container, cmd: 'rm -rf openlogic-openjdk-17.0.10+7-linux-x64', dir: '/var' })
await docker.execContainerCommand({ container, cmd: 'ln -s /etc/alternatives/java /usr/bin/java' })
await docker.execContainerCommand({ container, cmd: 'ln -sf /var/java-17-openjdk/bin/java /usr/bin/java' })
// await docker.execContainerCommand({ container, cmd: 'ln -s /etc/alternatives/java /usr/bin/java' })
// await docker.execContainerCommand({ container, cmd: 'apt-get install -y openjdk-17-jdk' })
})
await LogUtil.run(username, '安装 maven ', async () => {
......@@ -184,12 +172,26 @@ export default defineEventHandler(async (event) => {
}
}
})
setUserConfig(username, 'status', 'created')
LogUtil.printSuccess(username, 'Installed')
await LogUtil.run(username, '配置租户信息', async () => {
const tenants = {
id: username,
host: 'a.test.com:23335,a.test.com:23336',
'database-schema': 'library'
}
await setPgTableData('v2', username, 'tenants', JSON.stringify(tenants))
})
await LogUtil.run(username, '配置调试信息', async () => {
const debug = {
host: '192.168.1.94'
}
await setPgTableData('v2', username, 'debug', JSON.stringify(debug))
})
await setPgTableData( 'v2', username, 'status', 'created')
await stopPgClient(pgClient)
LogUtil.printSuccess(username, '安装成功')
} catch (err: any) {
console.log(err)
let username = event.context.username
setUserConfig(username, 'status', 'null')
await setPgTableData( 'v2', username, 'status', 'null')
await stopPgClient(pgClient)
LogUtil.printError(username, (err instanceof Error ? err.message : JSON.stringify(err)))
setResponseStatus(event, 500)
return err
......
import LogUtil from "~/server/utils/log"
export default defineEventHandler(async (event) => {
try {
let username = event.context.username
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
let container = await docker.checkContainer( username + '.node')
if (!container) {
throw new Error('没有创建容器,请先初始化容器')
}
......@@ -13,9 +12,5 @@ export default defineEventHandler(async (event) => {
await docker.execContainerCommand({ container, cmd: 'pm2 delete gateway', quiet: true })
LogUtil.print(username, `程序已停止`)
setUserConfig(username, 'status', 'stopped')
} catch (err) {
setResponseStatus(event, 500)
return err
}
await setPgTableData( 'v2', username, 'status', 'compiled')
})
\ No newline at end of file
......@@ -6,7 +6,7 @@ export default defineEventHandler(async (event) => {
let username = event.context.username
let docker = createDockerFactory(username)
let branch = body.branch
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
let container = await docker.checkContainer( username + '.node')
const dir = getPlatformRootFolder(body.platform)
if (container) {
await docker.startContainer({ container })
......
import { getPlatformRootFolder } from "~/server/utils/server"
export default defineEventHandler(async (event) => {
try {
const body = await readBody(event)
let username = event.context.username
let docker = createDockerFactory(username)
let branch = body.branch
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
let container = await docker.checkContainer( username + '.node')
const dir = getPlatformRootFolder(body.platform)
if (container) {
await docker.startContainer({ container })
......@@ -22,8 +21,4 @@ export default defineEventHandler(async (event) => {
} else {
throw new Error('没有创建容器,请先初始化容器')
}
} catch (err) {
setResponseStatus(event, 500)
return err
}
})
\ No newline at end of file
export default defineEventHandler(async (event) => {
try{
const body = await readBody(event)
let gitEmail = body.email
let username = event.context.username
let docker = createDockerFactory(username)
// 创建 node 容器
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
let container = await docker.checkContainer( username + '.node')
if (container) {
await docker.startContainer({ container })
await docker.execContainerCommand({ container, cmd: 'rm -rf /root/.ssh' })
await docker.execContainerCommand({ container, cmd: `ssh-keygen -f /root/.ssh/id_rsa -t ed25519 -C "${gitEmail}"` })
let sshKey = await docker.getFile({ container, path: '/root/.ssh/id_rsa.pub' })
return sshKey
} else {
throw new Error('没有创建容器,请先初始化容器')
}
} catch (err) {
setResponseStatus(event, 500)
return err
}
})
\ No newline at end of file
......@@ -5,7 +5,7 @@ export default defineEventHandler(async (event) => {
const query = getQuery(event)
let username = event.context.username
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
let container = await docker.checkContainer( username + '.node')
const platform = query.platform as 'v1' | 'v2'
if (container) {
await docker.startContainer({ container })
......
......@@ -6,7 +6,7 @@ export default defineEventHandler(async (event) => {
const { platform } = query
let username = event.context.username
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
let container = await docker.checkContainer(username + '.node')
if (container) {
await docker.startContainer({ container })
let result = await docker.execContainerCommand({ container, cmd: 'git branch', dir: getPlatformRootFolder(platform as 'v1' | 'v2') })
......
type UserConfig = {
"postgres": {
"visible": boolean,
"username": string,
"password": string,
"ip": string,
"port": string,
"database": string,
"schema": string
},
"redis": {
"visible": boolean,
"ip": string,
"port": string
},
"zookeeper": {
"visible": boolean,
"ip": string,
"port": string
},
"rocketmq": {
"visible": boolean,
"ip": string,
"port": string
},
"tenants": {
"visible": boolean,
"id": string,
"host": string,
"database-schema": string,
"primary-namespace": string
},
"postgresV1": {
"visible": boolean,
"username": string,
"password": string,
"ip": string,
"port": string,
"database": string,
"schema": string
},
"redisV1": {
"visible": boolean,
"ip": string,
"port": string
},
"tenantsV1": {
"visible": boolean,
"id": string,
"port": number
},
"debug": {
"host": string
},
"node-port": number,
"username": string,
"status": string,
"InstallSteps": string[]
}
export default defineEventHandler(async (event) => {
const username = event.context.username
const configs = getUserAllConfigs(username)
return configs as unknown as UserConfig
let client = await createPgClientFactory('backend_helper')
let result = await getPgTableData( 'user', username)
return result
})
\ No newline at end of file
import { SESSION_PASSWORD, readJson, writeJson } from "../../utils"
import { getAvailableNodePort, getUserAllConfigs, getUserConfig } from "../../utils/server"
import { PgPort } from "~/server/utils/postgres"
import { SESSION_PASSWORD } from "../../utils"
export default defineEventHandler(async (event) => {
try {
const body = await readBody(event)
const username = body.username
// 当用户数据文件没有 node-port 时,认为该用户还没有注册过
if (getUserConfig(username, 'node-port') === undefined) {
let host = process.env.NODE_ENV?.trim() === 'production' ? '192.168.0.4' : 'localhost'
const port = await getAvailableNodePort(host)
const userDefaultSetting = readJson('./public/files/default-user-setting.json')
const userSetting = Object.assign({}, userDefaultSetting, { "node-port": port })
userSetting['postgres']['database'] = username
userSetting['tenants']['id'] = username
userSetting['tenants']['host'] = `a.test.com:${23333+ (port - 30000)*2},a.test.com:${23334+(port-30000)*2}`
userSetting.username = username
const userDataPath = `./public/data/${username}.json`
writeJson(userDataPath, userSetting)
console.log('usetSetting', userSetting)
}
const session = await useSession(event, { password: SESSION_PASSWORD })
await session.update({ username })
return session.data
// 生产环境要求必须是先创建好服务器,再启动服务
if (process.env.NODE_ENV === 'development') {
return false
} else {
let result = await getPgTableData('user', username)
return result
}
} catch (err) {
console.log(err)
throw createError({
statusCode: 500,
statusMessage: '登录失败'
......
import fs from 'fs'
import path from 'path'
export default defineEventHandler(async (event) => {
let username = event.context.username
let docker = createDockerFactory(username)
let postgres = await docker.checkContainer('postgres_12')
if (!postgres) {
postgres = await docker.checkAndCreateContainer({ name: 'postgres_12', img: 'postgres:12', env: ["POSTGRES_PASSWORD=postgres"], portBindings: { '5432/tcp': [{ HostPort: PgPort.toString() }] } })
await docker.startContainer({ container: postgres })
await sleep(3000)
} else {
await docker.startContainer({ container: postgres })
await sleep(3000)
}
let client = await createPgClientFactory()
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) {
await executePgQuery({ client, query: 'CREATE DATABASE backend_helper'})
await client.end()
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_v2.sql'), { encoding: 'utf-8' })})
await executePgQuery({ client, query: fs.readFileSync(path.resolve('./public/files/postgres/create_v1.sql'), { encoding: 'utf-8'})})
} else {
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
export default defineEventHandler(async (event) => {
try {
const body = await readBody(event)
const { key, value } = body
let username = event.context.username
if (typeof key === 'string') {
setUserConfig(username, key, value)
}
} catch(err) {
setResponseStatus(event, 500)
return err
}
const body = await readBody(event)
const { table, key, value } = body
await setPgTableData( table, username, key, value)
})
\ No newline at end of file
......@@ -9,7 +9,6 @@
import Dockerode from 'dockerode'
import { chmod, createReadStream, createWriteStream, mkdirSync, readFileSync, rmdirSync, rmSync } from 'fs'
import { resolve } from 'path'
import { getUserConfig, setUserConfig } from './server'
import { removeUnreadCharacter } from '.'
import LogUtil from './log'
import tar from 'tar-fs'
......@@ -26,7 +25,7 @@ class Docker {
this.env = []
}
async checkContainer (name: string) {
let targetContainerName = name
let targetContainerName = 'logwire_backend_helper.' + name
let containers = await this.docker.listContainers({ filters: JSON.stringify({ status: ['created', 'restarting', 'running', 'removing', 'paused', 'exited', 'dead' ], name: [targetContainerName] }) })
if (containers.length) {
let container = await this.docker.getContainer(containers[0].Id)
......@@ -116,9 +115,7 @@ class Docker {
}
async putArchive ({ container, tarPath, targetPath }: { container: Dockerode.Container, tarPath: string, targetPath: string }) {
return new Promise((resolve) => {
container.putArchive(tarPath, { path: targetPath }).then(resolve)
})
return container.putArchive(tarPath, { path: targetPath })
}
async writeFile ({ container, path, text }: { container: Dockerode.Container, path: string, text: string }) {
await this.execContainerCommand({ container, cmd: ['bash', '-c', 'echo ' + text + ' > ' + path] })
......@@ -150,11 +147,11 @@ class Docker {
}
async switchJavaVersion ({ container, username, version }: { container: Dockerode.Container, username: string, version: 17 | 8}) {
LogUtil.printInfo(username, '更换 Java 环境')
await this.execContainerCommand({ container, cmd: 'rm /etc/alternatives/java'})
await this.execContainerCommand({ container, cmd: 'rm /usr/bin/java'})
if (version === 8) {
await this.execContainerCommand({ container, cmd: 'ln -s /var/java-8-openjdk/bin/java /etc/alternatives/java'})
await this.execContainerCommand({ container, cmd: 'ln -sf /var/java-8-openjdk/bin/java /usr/bin/java' })
} else {
await this.execContainerCommand({ container, cmd: 'ln -s /var/java-17-openjdk/bin/java /etc/alternatives/java'})
await this.execContainerCommand({ container, cmd: 'ln -sf /var/java-17-openjdk/bin/java /usr/bin/java' })
}
}
async preStartSystem ({ container, username, platform }: { container: Dockerode.Container, username: string, platform: 'v1' | 'v2' }) {
......@@ -172,12 +169,12 @@ class Docker {
try { await this.execContainerCommand({ container, cmd: 'pm2 delete platform' }) } catch (err) {}
if (platform === 'v1') {
await this.switchJavaVersion({ container, username, version: 8 })
const statusV2 = getUserConfig(username, 'status')
if (statusV2) setUserConfig(username, 'status', 'created') // 如果系统在运行,回退到已创建的状态
const statusV2 = (await getPgTableData( 'v2', username))?.status
if (statusV2) setPgTableData( 'v2', username, 'status', 'created') // 如果系统在运行,回退到已创建的状态
} else {
await this.switchJavaVersion({ container, username, version: 17 })
const statusV1 = getUserConfig(username, 'v1-status')
if (statusV1) setUserConfig(username, 'v1-status', 'created') // 如果系统在运行,回退到已创建的状态
const statusV1 = (await getPgTableData( 'v1', username))?.status
if (statusV1) setPgTableData( 'v1', username, 'status', 'created') // 如果系统在运行,回退到已创建的状态
}
}
}
......
declare module global {
namespace NodeJS {
interface ProcessEnv {
HOST: string
NODE_ENV: string
PGUSER: string
postgres: string
}
}
}
\ No newline at end of file
import fs from 'fs'
import path from 'path'
export const HOST = process.env.NODE_ENV === 'production' ? '192.168.0.4' : (process.env.HOST || '192.168.0.190')
export function readJson(path: string): Record<string, any> {
try {
......@@ -24,3 +25,9 @@ export function removeUnreadCharacter (str: string) {
}
export const SESSION_PASSWORD = '80d42cfb-1cd2-462c-8f17-e3237d9027e9'
export function sleep (ms: number) {
return new Promise<void>((resolve, reject) => {
setTimeout(resolve, ms)
})
}
\ No newline at end of file
......@@ -3,8 +3,6 @@
* 使用 LogsModule 包裹后,为某个安装流程增加日志处理,并且对外还是返回 Promise 对象
*/
import { setUserConfig, getUserConfig } from "./server"
import { Socket } from "socket.io";
let websocket: Map<string, Socket> = new Map()
......@@ -18,13 +16,9 @@ export function getWebsocketIo (username: string) {
export default class LogUtil {
// 根据 log 判断用户是否已经执行过
static async run (username: string, log: string, cb: () => Promise<void>) {
let key = 'InstallSteps'
let steps: string[] = getUserConfig(username, key) || []
let socket = getWebsocketIo(username)
socket.emit('Log', '[progress] [1m[Loading][m ' + log + '中...\n')
await cb()
steps.push(log)
setUserConfig(username, key, steps)
socket.emit('Log', '[progress] [1m[Info][m ' + log + '完成\n')
}
static async print(username: string, log: string) {
......
import pg from 'pg'
export async function createPgClient ({ host, port, database }: { host: string, port: number, database?: string }) {
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
let pool: pg.Pool = new pg.Pool({
host: PgHost,
port: PgPort,
database: 'backend_helper'
})
export async function createPgClient (payload: {
user?: string
password?: string
database?: string
host?: string
port?: number
}) {
let client = new pg.Client({
user: 'postgres',
password: 'postgres',
database,
host,
port
user: payload.user,
password: payload.password,
database: payload.database,
host: payload.host || PgHost,
port: payload.port || PgPort
})
await client.connect()
return client
......@@ -15,4 +30,49 @@ export async function createPgClient ({ host, port, database }: { host: string,
export function executePgQuery ({ client, query }: { client: pg.Client, query: string }) {
return client.query(query)
}
export function createPgClientFactory (database?: string) {
return createPgClient({ database })
}
export async function stopPgClient (pgClient: pg.Client) {
await pgClient.end()
}
export type TableUser = { username: string, port: number }
export type TableV2 = {
username: string
status: 'null' | 'created' | 'stopped' | 'running' | 'creating' | 'compiling' | 'compiled'
tenants: Record<string, any>
debug: Record<string, any>
has_debugged: boolean
serverProperties: { id: any, key: string, value: string }[]
}
export type TableV1 = {
username: string
status: 'null' | 'created' | 'stopped' | 'running' | 'creating' | 'compiling' | 'compiled'
serverProperties: { id: any, key: string, value: string }[]
}
export async function getPgTableData (table: 'v1', username: string): Promise<TableV1 | undefined>;
export async function getPgTableData (table: 'v2', username: string): Promise<TableV2 | undefined>;
export async function getPgTableData (table: 'user', username: string): Promise<TableUser | undefined>;
export async function getPgTableData (table: string, username: string) {
const result = await pool.query(`select * from public.${table} where username = '${username}'`)
return result.rows[0]
}
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: 'v1', username: string, key: keyof TableV1, value: TableV1[keyof TableV1]): Promise<void>;
export async function setPgTableData (table: string, username: string, key: string, value: any) {
await pool.query(`UPDATE public.${table} SET "${key}"='${value}' WHERE username='${username}'`)
}
export async function insertPgTableData (table: 'v1', username: string): Promise<void>;
export async function insertPgTableData (table: 'v2', username: string): Promise<undefined>;
export async function insertPgTableData (table: 'user', username: string): Promise<undefined>;
export async function insertPgTableData (table: string, username: string) {
await pool.query(`INSERT INTO public."${table}"(username) VALUES ('${username}');`)
}
This diff is collapsed.
......@@ -2,9 +2,9 @@ import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', () => {
const username = ref('')
const config = ref({})
const config = ref<{ port?: string }>({})
function setUserName (name: string) {
function setUserName (name: string, p?: string) {
username.value = name
}
function setUserConfig (name: string, obj: Object) {
......
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