diff --git a/packages/core/package.json b/packages/core/package.json
index 14e5b4f..a7bde51 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -25,6 +25,7 @@
},
"devDependencies": {
"@types/express": "^4.17.0",
+ "@types/mongodb": "^3.3.13",
"@types/socket.io": "^2.1.2",
"nodemon": "^1.19.1",
"rimraf": "^2.6.3",
@@ -32,7 +33,7 @@
"typescript": "^3.5.2"
},
"nodemonConfig": {
- "verbose": true,
+ "verbose": false,
"ignore": [
".git",
"public"
diff --git a/packages/core/public/editor/index.html b/packages/core/public/editor/index.html
new file mode 100644
index 0000000..1746454
--- /dev/null
+++ b/packages/core/public/editor/index.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+ 编辑器
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/core/public/editor/index.js b/packages/core/public/editor/index.js
new file mode 100644
index 0000000..a4c2d3c
--- /dev/null
+++ b/packages/core/public/editor/index.js
@@ -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"
+ }
+ });
+})();
diff --git a/packages/core/public/event/index.html b/packages/core/public/event/index.html
new file mode 100644
index 0000000..3f9b35d
--- /dev/null
+++ b/packages/core/public/event/index.html
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/core/public/js/index.js b/packages/core/public/event/js/index.js
similarity index 100%
rename from packages/core/public/js/index.js
rename to packages/core/public/event/js/index.js
diff --git a/packages/core/public/index.html b/packages/core/public/index.html
index 3f9b35d..4aafc03 100644
--- a/packages/core/public/index.html
+++ b/packages/core/public/index.html
@@ -1,28 +1,30 @@
-
+
+
-
-
-
-
-
-
-
-
+
+ 四喜服务部署平台
+
+
+
+
-
-
+
+
+
\ No newline at end of file
diff --git a/packages/core/public/index.js b/packages/core/public/index.js
new file mode 100644
index 0000000..30cf929
--- /dev/null
+++ b/packages/core/public/index.js
@@ -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
+ });
+})();
diff --git a/packages/core/resources/docker.http b/packages/core/resources/docker.http
new file mode 100644
index 0000000..4730790
--- /dev/null
+++ b/packages/core/resources/docker.http
@@ -0,0 +1 @@
+POST https://dayu-api.miaowoo.cc/service/swirl_swirl/restart
diff --git a/packages/core/src/controller/container.ts b/packages/core/src/controller/docker/container.ts
similarity index 100%
rename from packages/core/src/controller/container.ts
rename to packages/core/src/controller/docker/container.ts
diff --git a/packages/core/src/controller/docker/node.ts b/packages/core/src/controller/docker/node.ts
new file mode 100644
index 0000000..f1091c1
--- /dev/null
+++ b/packages/core/src/controller/docker/node.ts
@@ -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)
+ }))
+ }
+ }
+}
diff --git a/packages/core/src/controller/docker/service.ts b/packages/core/src/controller/docker/service.ts
new file mode 100644
index 0000000..764e3da
--- /dev/null
+++ b/packages/core/src/controller/docker/service.ts
@@ -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: '服务自启动后从未重启
无法回滚!'
+ }
+ }
+ 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)
+ }
+ }
+ }
+}
diff --git a/packages/core/src/controller/stack.ts b/packages/core/src/controller/docker/stack.ts
similarity index 87%
rename from packages/core/src/controller/stack.ts
rename to packages/core/src/controller/docker/stack.ts
index a57f50a..23dc090 100644
--- a/packages/core/src/controller/stack.ts
+++ b/packages/core/src/controller/docker/stack.ts
@@ -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'
const STACK_LABEL = 'com.docker.stack.namespace';
@controller('/stack')
class StackController {
- @httpGet('/list')
+ @get('/list')
public async list(): Promise {
let stacks: { [key: string]: string[] } = {};
let result = [];
@@ -24,10 +24,13 @@ class StackController {
stack.push(service.Spec.Name);
}
}
- return result;
+ return {
+ status: 0,
+ data: result
+ };
}
- @httpGet('/:stack')
+ @get('/:stack')
public async details(@requestParam('stack') stack: string) {
let filter: any = {}
filter[`${STACK_LABEL}=${stack}`] = true
diff --git a/packages/core/src/controller/swarm.ts b/packages/core/src/controller/docker/swarm.ts
similarity index 100%
rename from packages/core/src/controller/swarm.ts
rename to packages/core/src/controller/docker/swarm.ts
diff --git a/packages/core/src/controller/system.ts b/packages/core/src/controller/docker/system.ts
similarity index 100%
rename from packages/core/src/controller/system.ts
rename to packages/core/src/controller/docker/system.ts
diff --git a/packages/core/src/controller/docker/task.ts b/packages/core/src/controller/docker/task.ts
new file mode 100644
index 0000000..2dc92dc
--- /dev/null
+++ b/packages/core/src/controller/docker/task.ts
@@ -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),
+ }))
+ };
+ }
+}
diff --git a/packages/core/src/controller/ext/group.ts b/packages/core/src/controller/ext/group.ts
new file mode 100644
index 0000000..0668985
--- /dev/null
+++ b/packages/core/src/controller/ext/group.ts
@@ -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 {
+ 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),
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/core/src/controller/node.ts b/packages/core/src/controller/node.ts
deleted file mode 100644
index b311506..0000000
--- a/packages/core/src/controller/node.ts
+++ /dev/null
@@ -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();
- }
-}
diff --git a/packages/core/src/controller/page/dashboard.ts b/packages/core/src/controller/page/dashboard.ts
new file mode 100644
index 0000000..255ffc5
--- /dev/null
+++ b/packages/core/src/controller/page/dashboard.ts
@@ -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": "登录"
+ }]
+ }
+ }
+ }
+}
diff --git a/packages/core/src/controller/page/manager.ts b/packages/core/src/controller/page/manager.ts
new file mode 100644
index 0000000..93af844
--- /dev/null
+++ b/packages/core/src/controller/page/manager.ts
@@ -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
+
+ @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 ? '删除成功!' : '删除失败!' };
+ }
+}
\ No newline at end of file
diff --git a/packages/core/src/controller/page/service.ts b/packages/core/src/controller/page/service.ts
new file mode 100644
index 0000000..c956f15
--- /dev/null
+++ b/packages/core/src/controller/page/service.ts
@@ -0,0 +1,11 @@
+import { controller, get, post } from "@cc-server/binding";
+
+@controller('/page/service')
+class PageServiceController {
+ @get('')
+ index() {
+ return {
+
+ }
+ }
+}
diff --git a/packages/core/src/controller/service.ts b/packages/core/src/controller/service.ts
deleted file mode 100644
index 5d902bc..0000000
--- a/packages/core/src/controller/service.ts
+++ /dev/null
@@ -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)
- }
-}
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index fcc3538..225bd32 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -1,22 +1,53 @@
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 path from 'path'
//process.env.DOCKER_HOST = 'https://ndcli.yumc.pw'
// 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')
-let list = fs.readdirSync(modulesDir);
-
-for (let file of list) {
- let moduleDir = path.join(modulesDir, file)
- let stat = fs.statSync(moduleDir);
- if (stat.isFile() && file.endsWith('.js')) {
- require(moduleDir);
+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);
+ for (let file of list) {
+ let moduleDir = path.join(modulesDir, file)
+ let stat = fs.statSync(moduleDir);
+ if (stat.isDirectory()) {
+ requireDir(moduleDir)
+ } else if (stat.isFile() && file.endsWith('.js')) {
+ 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)
+ }
+})
diff --git a/packages/docker-api/src/api/types/service.ts b/packages/docker-api/src/api/types/service.ts
index aa72ccf..5e23728 100644
--- a/packages/docker-api/src/api/types/service.ts
+++ b/packages/docker-api/src/api/types/service.ts
@@ -181,6 +181,13 @@ export declare namespace service {
RollbackConfig: RollbackConfig;
}
+ export interface UpdateStatus {
+ State: string;
+ StartedAt: string;
+ CompletedAt: string;
+ Message: string;
+ }
+
export interface Service {
ID: string;
Version: Version;
@@ -189,7 +196,7 @@ export declare namespace service {
Spec: Spec;
Endpoint: Endpoint;
PreviousSpec: PreviousSpec;
+ UpdateStatus: UpdateStatus;
}
-
}
diff --git a/packages/docker-api/src/client/service.ts b/packages/docker-api/src/client/service.ts
index fb728ff..a4d4d9d 100644
--- a/packages/docker-api/src/client/service.ts
+++ b/packages/docker-api/src/client/service.ts
@@ -10,6 +10,9 @@ export namespace service {
}
export async function inspect(id: string, query: { insertDefaults: boolean } = { insertDefaults: false }) {
- return await api.get(`/services/${id}`, query);
+ return await api.get(`/services/${id}`, query);
+ }
+ export async function update(id: string, query: { version: number, registryAuthFrom?: string, rollback?: string }, data: any) {
+ return await api.post(api.getUri(`/services/${id}/update`, query), data)
}
}
diff --git a/packages/docker-api/src/utils/api.ts b/packages/docker-api/src/utils/api.ts
index 700ae4b..ee3870d 100644
--- a/packages/docker-api/src/utils/api.ts
+++ b/packages/docker-api/src/utils/api.ts
@@ -19,6 +19,13 @@ export async function stream(path: string, data?: objec
return await handle("GET", path, { params: data, responseType: "stream" });
}
+export function getUri(path: string, data?: object) {
+ return api.getUri({
+ url: path,
+ params: data
+ })
+}
+
async function handle(method: Method, path: string, reqConfig?: AxiosRequestConfig): Promise {
let config: AxiosRequestConfig = {
method,
diff --git a/packages/vscode/package.json b/packages/vscode/package.json
index 1bd7017..68ebcac 100644
--- a/packages/vscode/package.json
+++ b/packages/vscode/package.json
@@ -11,7 +11,8 @@
"Other"
],
"activationEvents": [
- "*"
+ "onView:docker-explorer",
+ "onView:openfaas-explorer"
],
"main": "./dist/extension.js",
"contributes": {
@@ -60,8 +61,8 @@
"vscode:prepublish": "yarn run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
- "postinstall": "node ../../../node_modules/vscode/bin/install",
- "test": "yarn run compile && node ../../../node_modules/vscode/bin/test"
+ "postinstall": "node ../../node_modules/vscode/bin/install",
+ "test": "yarn run compile && node ../../node_modules/vscode/bin/test"
},
"dependencies": {
"@dayu/docker-api": "^0.0.1",
diff --git a/tsconfig.json b/tsconfig.json
index f754065..c3d49b0 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,6 +6,7 @@
"module": "commonjs",
"sourceMap": true,
"declaration": true,
+ "declarationMap": true,
"noImplicitAny": true,
"allowUnreachableCode": true,
"experimentalDecorators": true,