feat: add amis jssdk page

Signed-off-by: MiaoWoo <admin@yumc.pw>
This commit is contained in:
MiaoWoo 2019-12-25 15:41:27 +08:00
parent cabf6cf613
commit aed21b2ff6
27 changed files with 522 additions and 66 deletions

View File

@ -25,6 +25,7 @@
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^4.17.0", "@types/express": "^4.17.0",
"@types/mongodb": "^3.3.13",
"@types/socket.io": "^2.1.2", "@types/socket.io": "^2.1.2",
"nodemon": "^1.19.1", "nodemon": "^1.19.1",
"rimraf": "^2.6.3", "rimraf": "^2.6.3",
@ -32,7 +33,7 @@
"typescript": "^3.5.2" "typescript": "^3.5.2"
}, },
"nodemonConfig": { "nodemonConfig": {
"verbose": true, "verbose": false,
"ignore": [ "ignore": [
".git", ".git",
"public" "public"

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>编辑器</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<link rel="stylesheet" href="https://houtai.baidu.com/v2/csssdk">
<style>
html,
body,
.app-wrapper {
position: relative;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="root" class="app-wrapper"></div>
<script src="https://houtai.baidu.com/v2/jssdk"></script>
<script src="index.js"></script>
</body>
</html>

View File

@ -0,0 +1,51 @@
(async function() {
var amis = amisRequire('amis/embed');
amis.embed('#root', {
definitions: {
"editor-page": {
"position": "right",
"resizable": true,
"title": `<% if (data.name) { print('页面 '+data.name+' 详情') } else { print ('新增页面') } %>`,
"size": "lg",
"body": {
"type": "form",
"submitText": '',
"title": "",
"controls": [
{
"name": "name",
"type": "text",
},
{
"name": "content",
"type": "editor",
"language": "json",
"editorTheme": "vs-dark",
"height": "800",
"label": false
},
{
"type": "button",
"label": "保存",
"actionType": "ajax",
"api": {
"url": "/page/manager/${_id}",
"data": {
"name": "${name}",
"content": "${content}"
}
}
}
]
}
}
},
type: 'page',
title: '页面管理',
body: {
name: "editor",
type: "service",
schemaApi: "get:/page/manager/editor"
}
});
})();

View File

@ -0,0 +1,28 @@
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/socket.io-client@2.2.0/dist/socket.io.js"> </script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/xterm.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/addons/fullscreen/fullscreen.css">
<script src="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/xterm.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/addons/fit/fit.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/addons/attach/attach.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/addons/fullscreen/fullscreen.js"></script>
<style>
#terminal-container .terminal.xterm {
height: 100%;
}
#terminal-container .xterm-viewport {
height: 100% !important;
}
</style>
</head>
<body>
<div id="terminal" style="height: 100%;"></div>
<script type="text/javascript" src="js/index.js"></script>
</body>
</html>

View File

@ -1,28 +1,30 @@
<html> <!DOCTYPE html>
<html lang="zh">
<head> <head>
<script src="https://cdn.jsdelivr.net/npm/socket.io-client@2.2.0/dist/socket.io.js"> </script> <meta charset="UTF-8">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/xterm.css" /> <title>四喜服务部署平台</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/addons/fullscreen/fullscreen.css"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<script src="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/xterm.js"></script> <meta http-equiv="X-UA-Compatible" content="IE=Edge">
<script src="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/addons/fit/fit.js"></script> <link rel="stylesheet" href="https://houtai.baidu.com/v2/csssdk">
<script src="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/addons/attach/attach.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/addons/fullscreen/fullscreen.js"></script>
<style> <style>
#terminal-container .terminal.xterm { html,
body,
.app-wrapper {
position: relative;
width: 100%;
height: 100%; height: 100%;
} margin: 0;
padding: 0;
#terminal-container .xterm-viewport {
height: 100% !important;
} }
</style> </style>
</head> </head>
<body> <body>
<div id="terminal" style="height: 100%;"></div> <div id="root" class="app-wrapper"></div>
<script type="text/javascript" src="js/index.js"></script> <script src="https://houtai.baidu.com/v2/jssdk"></script>
<script src="index.js"></script>
</body> </body>
</html> </html>

View File

@ -0,0 +1,8 @@
(async function() {
var amis = amisRequire('amis/embed');
let page = window.location.hash.substring(1);
amis.embed('#root', {
type: "service",
schemaApi: "get:/page/manager/" + page
});
})();

View File

@ -0,0 +1 @@
POST https://dayu-api.miaowoo.cc/service/swirl_swirl/restart

View File

@ -0,0 +1,21 @@
import { controller, httpGet, httpPost } from 'inversify-express-utils';
import * as docker from '@dayu/docker-api'
@controller('/node')
class NodeController {
@httpGet('/list')
public async list() {
let nodes = await docker.node.list();
return {
status: 0,
data: nodes.map(n => ({
id: n.ID,
manager: n.ManagerStatus,
hostname: n.Description.Hostname,
version: n.Description.Engine.EngineVersion,
status: n.Status,
raw: JSON.stringify(n)
}))
}
}
}

View File

@ -0,0 +1,75 @@
import { controller, post, get, requestParam, queryParam } from '@cc-server/binding';
import * as docker from '@dayu/docker-api'
@controller('/service')
class ServiceController {
@get('/list')
public async list(@queryParam('page') page: number, @queryParam('perPage') perPage: number, ) {
let services = await docker.service.list();
return {
status: 0,
msg: '',
data: services.map(s => ({
"service-name": s.Spec.Name,
image: s.Spec.TaskTemplate.ContainerSpec.Image.split('@')[0],
updated_at: s.UpdatedAt,
mode: s.Spec.Mode,
update_status: s.UpdateStatus
}))
};
}
@post('/:id/restart')
public async restart(@requestParam("id") id: string) {
let service = await docker.service.inspect(id);
service.Spec.TaskTemplate.ForceUpdate++
return {
status: 0,
msg: '重启指令已发送',
data: docker.service.update(id, { version: service.Version.Index }, service.Spec)
}
}
@post('/:id/rollback')
public async rollback(@requestParam("id") id: string) {
let service = await docker.service.inspect(id);
if (!service.PreviousSpec) {
return {
status: 1,
msg: '服务自启动后从未重启<br>无法回滚!'
}
}
return {
status: 0,
msg: '回滚指令已发送',
data: docker.service.update(id, { version: service.Version.Index, rollback: 'previous' }, service.Spec)
}
}
@post('/delete')
public async delete() {
return {
status: 0,
msg: '删除成功!',
}
}
@get('/:id')
public async details(@requestParam('id') id: string) {
let service = await docker.service.inspect(id);
return {
status: 0,
data: {
id: service.ID,
name: service.Spec.Name,
env: (service.Spec.TaskTemplate.ContainerSpec.Env ?? []).map(e => {
let args = e.split('=');
return { key: args[0], value: args[1] }
}),
networks: service.Spec.TaskTemplate.Networks ?? [],
raw: JSON.stringify(service)
}
}
}
}

View File

@ -1,11 +1,11 @@
import { controller, httpGet, httpPost, requestParam } from 'inversify-express-utils'; import { controller, get, post, requestParam } from '@cc-server/binding';
import * as docker from '@dayu/docker-api' import * as docker from '@dayu/docker-api'
const STACK_LABEL = 'com.docker.stack.namespace'; const STACK_LABEL = 'com.docker.stack.namespace';
@controller('/stack') @controller('/stack')
class StackController { class StackController {
@httpGet('/list') @get('/list')
public async list(): Promise<any> { public async list(): Promise<any> {
let stacks: { [key: string]: string[] } = {}; let stacks: { [key: string]: string[] } = {};
let result = []; let result = [];
@ -24,10 +24,13 @@ class StackController {
stack.push(service.Spec.Name); stack.push(service.Spec.Name);
} }
} }
return result; return {
status: 0,
data: result
};
} }
@httpGet('/:stack') @get('/:stack')
public async details(@requestParam('stack') stack: string) { public async details(@requestParam('stack') stack: string) {
let filter: any = {} let filter: any = {}
filter[`${STACK_LABEL}=${stack}`] = true filter[`${STACK_LABEL}=${stack}`] = true

View File

@ -0,0 +1,21 @@
import { controller, post, get, requestParam, queryParam } from '@cc-server/binding';
import * as docker from '@dayu/docker-api'
@controller('/task')
class TaskController {
@get('/list')
public async list(@queryParam('page') page: number, @queryParam('perPage') perPage: number, ) {
let tasks = await docker.task.list();
return {
status: 0,
msg: '',
data: tasks.map(s => ({
id: s.ID,
image: s.Spec.ContainerSpec.Image.split('@')[0],
status: s.Status,
updated_at: s.UpdatedAt,
raw: JSON.stringify(s),
}))
};
}
}

View File

@ -0,0 +1,46 @@
import { controller, get, post, requestParam } from '@cc-server/binding';
import * as docker from '@dayu/docker-api'
const GROUP_LABEL = 'pw.yumc.group.name'
@controller('/group')
class GroupController {
@get('/list')
public async list(): Promise<any> {
let stacks: { [key: string]: string[] } = {};
let result = [];
let services = await docker.service.list();
for (const service of services) {
let stackName = service.Spec.Labels[GROUP_LABEL]
if (stackName) {
let stack = stacks[stackName];
if (!stack) {
result.push({
name: stackName,
services: stack = []
})
stacks[stackName] = stack;
}
stack.push(service.Spec.Name);
}
}
return result;
}
@get('/:name')
public async details(@requestParam('name') stack: string) {
let filter: any = {}
filter[`${GROUP_LABEL}=${stack}`] = true
let filterOpt = {
filters: JSON.stringify({
"label": filter
})
}
let services = await docker.service.list(filterOpt)
let networks = await docker.network.list(filterOpt)
return {
services: services.map(service => service.Spec.Name),
networks: networks.map(network => network.Name),
}
}
}

View File

@ -1,10 +0,0 @@
import { controller, httpGet, httpPost } from 'inversify-express-utils';
import * as docker from '@dayu/docker-api'
@controller('/node')
class NodeController {
@httpGet('/list')
public async list() {
return await docker.node.list();
}
}

View File

@ -0,0 +1,79 @@
import { controller, get, post } from "@cc-server/binding";
@controller('/page')
class PageDashboardController {
@get('')
async page() {
return {
type: 'page',
title: '大禹容器管理',
body: [{
type: "button",
label: "刷新页面",
level: "dark",
actionType: "reload",
target: "editor"
}, {
type: 'divider'
}, {
"name": "editor",
"type": "service",
"className": "m-t",
"schemaApi": "/page/editor"
}]
}
}
@post('/editor')
async editor() {
return {
status: 0,
msg: '',
data: {
"type": "form",
"initApi": "post:/page/service/list",
"title": "",
"controls": [{
"name": "api",
"type": "editor",
"language": "json",
"label": "JSON",
}]
}
}
}
@post('/dashboard')
async dashboard() {
return {
status: 0,
msg: '',
data: {
"type": "form",
"api": "https://houtai.baidu.com/api/form/saveForm?waitSeconds=2",
"title": "常规模式",
"mode": "normal",
"controls": [{
"type": "email",
"name": "email",
"required": true,
"placeholder": "请输入邮箱",
"label": "邮箱"
}, {
"type":
"password",
"name": "password",
"label": "密码",
"required": true,
"placeholder": "请输入密码"
}, {
"type": "checkbox",
"name": "rememberMe",
"label": "记住登录"
}, {
"type": "submit",
"btnClassName": "btn-default",
"label": "登录"
}]
}
}
}
}

View File

@ -0,0 +1,58 @@
import { DBClient } from '@cc-server/db'
import { controller, get, post, httpDelete, requestParam, requestBody, BaseHttpController } from "@cc-server/binding";
import { inject, named } from '@cc-server/ioc'
class Page {
_id: string;
name: string;
content: string;
}
@controller('/page/manager')
class PageManagerController extends BaseHttpController {
@inject(DBClient)
@named('page')
private client: DBClient<Page>
@get('/list')
async list() {
let pages = await this.client.find({})
return {
status: 0,
data: pages
}
}
@get('/:name')
async index(@requestParam('name') name: string) {
let page = await this.client.findOne({ name })
if (!page) {
return {
status: 404,
msg: `未找到 ${name} 的页面配置数据!`
}
}
return {
status: 0,
data: JSON.parse(page.content)
}
}
@post('/')
async add(@requestBody() page: Page) {
let result = this.client.insertOne(page);
return { status: result ? 0 : 1, msg: result ? '插入 ' + page.name + ' 成功!' : '插入 ' + page.name + ' 失败!' };
}
@post('/:id')
async update(@requestParam('id') _id: string, @requestBody() page: Page) {
let result = this.client.updateById(_id, page)
return { status: result ? 0 : 1, msg: result ? '更新 ' + page.name + ' 成功!' : '更新 ' + page.name + ' 失败!' };
}
@httpDelete("/:id")
async delete(@requestParam('id') _id: string) {
let result = this.client.deleteById(_id)
return { status: result ? 0 : 1, msg: result ? '删除成功!' : '删除失败!' };
}
}

View File

@ -0,0 +1,11 @@
import { controller, get, post } from "@cc-server/binding";
@controller('/page/service')
class PageServiceController {
@get('')
index() {
return {
}
}
}

View File

@ -1,19 +0,0 @@
import { controller, httpGet, requestParam } from 'inversify-express-utils';
import * as docker from '@dayu/docker-api'
@controller('/service')
class ServiceController {
@httpGet('/list')
public async list() {
let services = await docker.service.list();
return services.map(s => ({
id: s.ID,
name: s.Spec.Name
}));
}
@httpGet('/:id')
public async details(@requestParam('id') id: string) {
return await docker.service.inspect(id)
}
}

View File

@ -1,22 +1,53 @@
import { CcServerBoot } from '@cc-server/core' import { CcServerBoot } from '@cc-server/core'
import { getContainer } from '@cc-server/ioc'
import { DBClient } from '@cc-server/db'
import { MongoCollection } from '@cc-server/db-mongo';
import { MongoClient, Db, Logger } from 'mongodb'
import * as fs from 'fs' import * as fs from 'fs'
import * as path from 'path' import * as path from 'path'
//process.env.DOCKER_HOST = 'https://ndcli.yumc.pw' //process.env.DOCKER_HOST = 'https://ndcli.yumc.pw'
// process.env.DOCKER_HOST = '/var/run/docker.sock' // process.env.DOCKER_HOST = '/var/run/docker.sock'
process.env.DOCKER_HOST = 'https://dscli.miaowoo.cc' // process.env.DOCKER_HOST = 'https://dscli.miaowoo.cc'
// process.env.DOCKER_HOST = 'http://172.20.0.90:2378'
// process.env.DOCKER_HOST = 'https://dcli.yumc.pw'
process.env.DOCKER_HOST = 'http://172.16.200.12:8376'
let CC_MONGO_URL = process.env.CC_MONGO_URL
let CC_MONGO_DB = process.env.CC_MONGO_DB
// if (process.env.local) {
console.log("RUN AT LOCAL DOCKER!!!!!")
CC_MONGO_URL = "mongodb://192.168.2.5:27017"
CC_MONGO_DB = "dayu"
// }
let server = new CcServerBoot(); const container = getContainer()
const server = new CcServerBoot(container);
let modulesDir = path.join(__dirname, 'controller') function injectDBClient(db: Db, table: string) {
server.container.bind(DBClient).toConstantValue(new MongoCollection(db.collection(table))).whenTargetNamed(table)
}
function requireDir(modulesDir: string) {
let list = fs.readdirSync(modulesDir); let list = fs.readdirSync(modulesDir);
for (let file of list) { for (let file of list) {
let moduleDir = path.join(modulesDir, file) let moduleDir = path.join(modulesDir, file)
let stat = fs.statSync(moduleDir); let stat = fs.statSync(moduleDir);
if (stat.isFile() && file.endsWith('.js')) { if (stat.isDirectory()) {
requireDir(moduleDir)
} else if (stat.isFile() && file.endsWith('.js')) {
require(moduleDir); require(moduleDir);
} }
} }
}
server.static().build().start(81); requireDir(path.join(__dirname, 'controller'))
MongoClient.connect(CC_MONGO_URL, { useNewUrlParser: true, connectTimeoutMS: 1000 }, (error, client) => {
if (error) {
console.log(error)
} else {
let db = client.db(CC_MONGO_DB);
// Logger.setLevel('debug');
injectDBClient(db, "page");
server.start(81)
}
})

View File

@ -181,6 +181,13 @@ export declare namespace service {
RollbackConfig: RollbackConfig; RollbackConfig: RollbackConfig;
} }
export interface UpdateStatus {
State: string;
StartedAt: string;
CompletedAt: string;
Message: string;
}
export interface Service { export interface Service {
ID: string; ID: string;
Version: Version; Version: Version;
@ -189,7 +196,7 @@ export declare namespace service {
Spec: Spec; Spec: Spec;
Endpoint: Endpoint; Endpoint: Endpoint;
PreviousSpec: PreviousSpec; PreviousSpec: PreviousSpec;
} UpdateStatus: UpdateStatus;
}
} }

View File

@ -10,6 +10,9 @@ export namespace service {
} }
export async function inspect(id: string, query: { insertDefaults: boolean } = { insertDefaults: false }) { export async function inspect(id: string, query: { insertDefaults: boolean } = { insertDefaults: false }) {
return await api.get<any>(`/services/${id}`, query); return await api.get<types.service.Service>(`/services/${id}`, query);
}
export async function update(id: string, query: { version: number, registryAuthFrom?: string, rollback?: string }, data: any) {
return await api.post<any>(api.getUri(`/services/${id}/update`, query), data)
} }
} }

View File

@ -19,6 +19,13 @@ export async function stream<T = http.ServerResponse>(path: string, data?: objec
return await handle<T>("GET", path, { params: data, responseType: "stream" }); return await handle<T>("GET", path, { params: data, responseType: "stream" });
} }
export function getUri(path: string, data?: object) {
return api.getUri({
url: path,
params: data
})
}
async function handle<T>(method: Method, path: string, reqConfig?: AxiosRequestConfig): Promise<T> { async function handle<T>(method: Method, path: string, reqConfig?: AxiosRequestConfig): Promise<T> {
let config: AxiosRequestConfig = { let config: AxiosRequestConfig = {
method, method,

View File

@ -11,7 +11,8 @@
"Other" "Other"
], ],
"activationEvents": [ "activationEvents": [
"*" "onView:docker-explorer",
"onView:openfaas-explorer"
], ],
"main": "./dist/extension.js", "main": "./dist/extension.js",
"contributes": { "contributes": {
@ -60,8 +61,8 @@
"vscode:prepublish": "yarn run compile", "vscode:prepublish": "yarn run compile",
"compile": "tsc -p ./", "compile": "tsc -p ./",
"watch": "tsc -watch -p ./", "watch": "tsc -watch -p ./",
"postinstall": "node ../../../node_modules/vscode/bin/install", "postinstall": "node ../../node_modules/vscode/bin/install",
"test": "yarn run compile && node ../../../node_modules/vscode/bin/test" "test": "yarn run compile && node ../../node_modules/vscode/bin/test"
}, },
"dependencies": { "dependencies": {
"@dayu/docker-api": "^0.0.1", "@dayu/docker-api": "^0.0.1",

View File

@ -6,6 +6,7 @@
"module": "commonjs", "module": "commonjs",
"sourceMap": true, "sourceMap": true,
"declaration": true, "declaration": true,
"declarationMap": true,
"noImplicitAny": true, "noImplicitAny": true,
"allowUnreachableCode": true, "allowUnreachableCode": true,
"experimentalDecorators": true, "experimentalDecorators": true,