import Dockerode from "dockerode" import LogUtil, { sendProjectStatus, sendSocketMessage } from "~/server/utils/log" import fs from 'fs' import path from 'path' import { changeProjectStatus } from "~/server/utils/postgres" export default defineEventHandler(async (event) => { let username = event.context.username let isClonedSuccess = true let sshKey = '' try { LogUtil.clear(username) let docker = createDockerFactory(username) let container: Dockerode.Container = {} as Dockerode.Container const port = (await getPgTableData( 'user', username))?.port const row = await getPgTableData('v2', username) if (!row) await insertPgTableData('v2', username) await changeProjectStatus( 'v2', username, 'status', 'creating') // 必须要保证创建容器是首先执行 // node 容器根据 dev/prod 环境与否,决定在哪里创建,远程容器还是本机容器 await LogUtil.run(username, '创建 node 容器', async () => { container = await docker.checkAndCreateContainer({ name: username + '.node', img: 'node:16-bullseye', exposedPorts: { '8080/tcp': {} }, portBindings: { '8080/tcp': [{ HostPort: port }] } }) await docker.startContainer({ container }) }) await LogUtil.run(username, '检查 SSH Key', async () => { sshKey = await checkSshExists(username) 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 () => { 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) } 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}) await stopPgClient(client) client = await createPgClientFactory(username) await executePgQuery({ client, query: 'CREATE SCHEMA library' }) await stopPgClient(client) } }) await LogUtil.run(username, '创建 redis 容器', async () => { if (process.env.NODE_ENV?.trim() !== 'production') { let redisInfo = await docker.checkContainer('redis') if (!redisInfo) { 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 (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 (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 (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 }) let info = await rocketmqbroker.inspect() if (info?.State.Status === 'created') { let brokerText = fs.readFileSync(path.resolve('./public/files/v2/broker.conf'), { encoding: 'utf-8' }) await docker.execContainerCommand({ container: rocketmqbroker, cmd: 'mkdir /home/rocketmq/conf -p' }) await docker.execContainerCommand({ container: rocketmqbroker, cmd: 'touch /home/rocketmq/conf/broker.conf' }) await docker.writeFile({ container: rocketmqbroker, path: '/home/rocketmq/conf/broker.conf', text: "'" +brokerText + "'" }) await docker.execContainerCommand({ container: rocketmqbroker, cmd: 'mqbroker -c /home/rocketmq/conf/broker.conf' }) } } }) await LogUtil.run(username, '克隆仓库', async () => { isClonedSuccess = false await docker.execContainerCommand({ container, cmd: ['git', 'config' ,'--global', 'core.sshCommand', 'ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'] }) await docker.execContainerCommand({ container, cmd: 'rm -rf /var/logwire-backend' }) await docker.execContainerCommand({ container, cmd: 'git clone ssh://git@gitlab.logwire.cn:13389/logwire2/logwire-backend.git', dir: '/var' }) isClonedSuccess = true }) // await LogUtil.run(username, '添加源', async () => { // let txt = await docker.getFile({ container, path: '/etc/apt/sources.list' }) // if (txt && !txt.includes('deb http://security.debian.org/debian-security bullseye-security main')) { // await docker.appendFile({ container, path: '/etc/apt/sources.list', text: '\'deb http://security.debian.org/debian-security bullseye-security main\'' }) // } // }) await LogUtil.run(username, '更新源', async () => { await docker.execContainerCommand({ container, cmd: 'apt-get update' }) }) await LogUtil.run(username, '安装 openjdk ', async () => { 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 -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 () => { await docker.execContainerCommand({ container, cmd: 'apt-get install -y maven' }) }) await LogUtil.run(username, '修改 maven 源', async () => { let mavenSettingXml = fs.readFileSync(path.resolve('./public/files/settings.xml'), { encoding: 'utf-8' }) await docker.writeFile({ container, path: '/etc/maven/settings.xml', text: '\'' + mavenSettingXml + '\'' }) let mavenSecurityXml = fs.readFileSync(path.resolve('./public/files/settings-security.xml'), { encoding: 'utf-8' }) try { let xml = await docker.getFile({ container, path: '/root/.m2/settings-security.xml' }) } catch (err) { await docker.execContainerCommand({ container, cmd: 'mkdir .m2', dir: '/root' }) await docker.writeFile({ container, path: '/root/.m2/settings-security.xml', text: '\'' + mavenSecurityXml + '\'' }) } }) // wetty 不好用,去掉了,逃生通道留一个 VSCODE 即可 await LogUtil.run(username, '安装 code-server ', async () => { try { let result = await docker.execContainerCommand({ container, cmd: 'which code-server' }) } catch (err) { await docker.execContainerCommand({ container, cmd: 'apt-get install -y build-essential pkg-config python3' }) await docker.execContainerCommand({ container, cmd: 'npm config set python python3' }) await docker.execContainerCommand({ container, cmd: 'yarn global add code-server@4.6.0' }) } }) await LogUtil.run(username, '安装 nginx ', async () => { await docker.execContainerCommand({ container, cmd: 'apt-get install -y nginx'}) }) await LogUtil.run(username, '安装 pm2 ', async () => { await docker.execContainerCommand({ container, cmd: 'npm install --global pm2' }) }) await LogUtil.run(username, '安装 zip', async () => { await docker.execContainerCommand({ container, cmd: 'apt-get install -y zip' }) }) await LogUtil.run(username, '配置 nginx ', async () => { let nginxConfigText = fs.readFileSync(path.resolve('./public/files/nginx.conf'), { encoding: 'utf-8' }) await docker.writeFile({ container, path: '/etc/nginx/nginx.conf', text: '\'' + nginxConfigText + '\'' }) }) await LogUtil.run(username, '启动 code-server ', async () => { try { let result = 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' }) } catch (err) { console.log('code-server err', err) } } }) await LogUtil.run(username, '启动 nginx ', async () => { try { let result = await docker.execContainerCommand({ container, cmd: 'lsof -i:80' }) } catch (err) { try { await docker.execContainerCommand({ container, cmd: 'nginx' }) } catch (err) { console.log('nginx err', err) } } }) 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 changeProjectStatus( 'v2', username, 'status', 'created') LogUtil.printSuccess(username, '安装成功') } catch (err: any) { await changeProjectStatus( 'v2', username, 'status', 'null') if (!isClonedSuccess) { const message = `初始化中克隆仓库失败,请将 <strong>${sshKey.replace(/\n/g, '')}</strong> 放入 Gitlab SSH 设置中并重试` const type = 'Tip' const eventData = { type, message } sendSocketMessage(username, 'v2', JSON.stringify(eventData)) } LogUtil.printError(username, (err instanceof Error ? err.message : JSON.stringify(err))) setResponseStatus(event, 500) return err } })