record.hostName }
124 | />
125 |
126 | );
127 | }
128 | };
129 |
130 | export default Instance;
131 |
--------------------------------------------------------------------------------
/sofa-dashboard-front/src/pages/Instance/monitor.less:
--------------------------------------------------------------------------------
1 | @import '~antd/lib/style/themes/default.less';
2 |
3 | .text-right {
4 | float: right;
5 | }
6 |
7 | .item-info {
8 | padding: 6px 12px 6px 12px;
9 | border-bottom: 1px dashed #ebedf0;
10 | align-content: baseline;
11 | }
12 |
13 | .item-info:hover {
14 | background: #f0f0f0;
15 | }
16 |
17 | .item-info-span-title {
18 | display: inline-block;
19 | font-size: 0.8em;
20 | }
21 |
22 | .item-info-span-content {
23 | font-size: 1.1em;
24 | margin-left: 1em;
25 | color: limegreen;
26 | }
27 |
28 | .item-info .tooltips {
29 | // visibility: hidden;
30 | background-color: black;
31 | color: #fff;
32 | text-align: center;
33 | border-radius: 6px;
34 | padding: 6px;
35 |
36 | position: fixed;
37 | z-index: 10;
38 | left: 63%;
39 | top: 5%;
40 | }
41 |
42 | // .item-info:hover .tooltips {
43 | // visibility: visible;
44 | // }
45 |
--------------------------------------------------------------------------------
/sofa-dashboard-front/src/pages/Instance/tab/environment.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "dva";
3 | import PropsMonitor from '../component/PropsMonitor';
4 | import PropertyDetail from "../component/PropertyDetail";
5 |
6 | /**
7 | * 应用信息展示控制台
8 | */
9 | @connect(({ monitor }) => ({
10 | monitor: monitor,
11 | }))
12 | class Environment extends React.Component {
13 |
14 | componentDidMount() {
15 | this.load('fetchEnv');
16 |
17 | const intervalId = setInterval(() => {
18 | this.load('fetchEnv');
19 |
20 | }, 5000);
21 | this.setState({ intervalId: intervalId })
22 | }
23 |
24 | componentWillUnmount() {
25 | // use intervalId from the state to clear the interval
26 | clearInterval(this.state.intervalId);
27 | }
28 |
29 | render() {
30 | return (
31 |
42 | )
43 | };
44 |
45 | load = (type) => {
46 | const { dispatch } = this.props;
47 | dispatch({
48 | type: 'monitor/' + type,
49 | payload: {
50 | instanceId: this.props.id
51 | }
52 | })
53 | };
54 | }
55 |
56 |
57 | export default Environment;
58 |
--------------------------------------------------------------------------------
/sofa-dashboard-front/src/pages/Instance/tab/loggers.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {connect} from "dva";
3 | import PropsMonitor from '../component/PropsMonitor';
4 | import PropertyDetail from "../component/PropertyDetail";
5 |
6 | /**
7 | * 应用信息展示控制台
8 | */
9 | @connect(({monitor}) => ({
10 | monitor: monitor,
11 | }))
12 | class Loggers extends React.Component {
13 |
14 | componentDidMount() {
15 | this.load('fetchLoggers');
16 |
17 | const intervalId = setInterval(() => {
18 | this.load('fetchLoggers');
19 |
20 | }, 5000);
21 | this.setState({intervalId: intervalId})
22 | }
23 |
24 | componentWillUnmount() {
25 | // use intervalId from the state to clear the interval
26 | clearInterval(this.state.intervalId);
27 | }
28 |
29 | render() {
30 | return (
31 |
36 | )
37 | };
38 |
39 | load = (type) => {
40 | const {dispatch} = this.props;
41 | dispatch({
42 | type: 'monitor/' + type,
43 | payload: {
44 | instanceId: this.props.id
45 | }
46 | })
47 | };
48 | }
49 |
50 |
51 | export default Loggers;
52 |
--------------------------------------------------------------------------------
/sofa-dashboard-front/src/pages/Instance/tab/mappings.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "dva";
3 | import PropsMonitor from '../component/PropsMonitor';
4 | import PropertyDetail from "../component/PropertyDetail";
5 |
6 | /**
7 | * 应用信息展示控制台
8 | */
9 | @connect(({ monitor }) => ({
10 | monitor: monitor,
11 | }))
12 | class Mappings extends React.Component {
13 |
14 | componentDidMount() {
15 | this.load('fetchMappings');
16 |
17 | const intervalId = setInterval(() => {
18 | this.load('fetchMappings');
19 |
20 | }, 5000);
21 | this.setState({ intervalId: intervalId })
22 | }
23 |
24 | componentWillUnmount() {
25 | // use intervalId from the state to clear the interval
26 | clearInterval(this.state.intervalId);
27 | }
28 |
29 | render() {
30 | return (
31 |
36 | )
37 | };
38 |
39 | load = (type) => {
40 | const { dispatch } = this.props;
41 | dispatch({
42 | type: 'monitor/' + type,
43 | payload: {
44 | instanceId: this.props.id
45 | }
46 | })
47 | };
48 | }
49 |
50 |
51 | export default Mappings;
52 |
--------------------------------------------------------------------------------
/sofa-dashboard-front/src/pages/Login/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Redirect from 'umi/redirect';
3 | import { Form, Icon, Input, Button } from 'antd';
4 | import { connect } from 'dva';
5 | import Styles from './index.less';
6 |
7 | class NormalLoginForm extends React.Component {
8 | handleSubmit = e => {
9 | e.preventDefault();
10 | this.props.form.validateFields((err, values) => {
11 | if (!err) {
12 | // 发送用户信息,这里的 values 现在并没有使用
13 | this.props.dispatch({
14 | type: 'user/fetchUser',
15 | payload: values,
16 | });
17 | }
18 | });
19 | };
20 |
21 | render() {
22 | const { form, user } = this.props;
23 | const { getFieldDecorator } = form;
24 |
25 | if (user.userid) {
26 | return ;
27 | }
28 |
29 | return (
30 |
31 |
33 | {getFieldDecorator('username', {
34 | rules: [{ required: true, message: 'Please input your username!' }],
35 | })(
36 | }
38 | placeholder="Username"
39 | />,
40 | )}
41 |
42 |
43 | {getFieldDecorator('password', {
44 | rules: [{ required: true, message: 'Please input your Password!' }],
45 | })(
46 | }
48 | type="password"
49 | placeholder="Password"
50 | />,
51 | )}
52 |
53 |
56 |
57 |
58 | );
59 | }
60 | }
61 |
62 | const WrappedNormalLoginForm = Form.create({ name: 'normal_login' })(NormalLoginForm);
63 |
64 | export default connect(({ user }) => ({
65 | user,
66 | }))(WrappedNormalLoginForm);
67 |
--------------------------------------------------------------------------------
/sofa-dashboard-front/src/pages/Login/index.less:
--------------------------------------------------------------------------------
1 | .login {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | width: 100vw;
6 | height: 100vh;
7 |
8 | :global {
9 | .login-form {
10 | width: 300px;
11 | }
12 | .login-form-forgot {
13 | float: right;
14 | }
15 | .login-form-button {
16 | width: 100%;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/sofa-dashboard-front/src/pages/Overview/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Card, Table, Divider, Input } from 'antd';
3 | import { connect } from 'dva';
4 | const { Search } = Input;
5 | @connect(({ application }) => ({
6 | list: application.list || [],
7 | }))
8 | class Overview extends React.Component {
9 |
10 | getData = (keyWord) => {
11 | const { dispatch } = this.props;
12 | dispatch({
13 | type: 'application/fetch',
14 | payload: {
15 | "applicationName": keyWord
16 | }
17 | });
18 | }
19 |
20 | componentDidMount() {
21 | this.getData("");
22 | }
23 |
24 | handleSearch = (value) => {
25 | this.getData(value);
26 | }
27 | render() {
28 | const columns = [
29 | {
30 | title: '应用名',
31 | dataIndex: 'applicationName',
32 | key: 'applicationName',
33 | },
34 | {
35 | title: '应用实例数',
36 | dataIndex: 'applicationCount',
37 | key: 'applicationCount',
38 | },
39 | {
40 | title: '操作',
41 | key: 'action',
42 | render: (text, record) => (
43 |
44 | 实例列表
45 |
46 | 配置
47 |
48 | ),
49 | },
50 | ];
51 |
52 | return (
53 |
54 | this.handleSearch(value)} enterButton style={{ width: 500, marginBottom: 20 }} />
55 |
56 | {this.props.list && (
57 | record.applicationName}
61 | />
62 | )}
63 |
64 | );
65 | }
66 | };
67 |
68 | export default Overview;
--------------------------------------------------------------------------------
/sofa-dashboard-front/src/services/application.js:
--------------------------------------------------------------------------------
1 | import { stringify } from 'qs';
2 | import request from '../utils/request';
3 |
4 | export async function queryApplicationByKeyword (keyword) {
5 | return request(`/api/application?keyword=${keyword}`);
6 | }
7 |
8 | export async function queryInstance (params) {
9 | return request(`/api/instance?${ stringify(params) }`);
10 | }
11 |
12 | export async function queryInstanceEnv (instanceId) {
13 | return request(`/api/instance/${ instanceId }/env`);
14 | }
15 |
16 | export async function queryInstanceHealth (instanceId) {
17 | return request(`/api/instance/${ instanceId }/health`);
18 | }
19 |
20 | export async function queryInstanceLoggers (instanceId) {
21 | return request(`/api/instance/${ instanceId }/loggers`);
22 | }
23 |
24 | export async function queryInstanceMappings (instanceId) {
25 | return request(`/api/instance/${ instanceId }/mappings`);
26 | }
27 |
28 | export async function queryInstanceInfo (instanceId) {
29 | return request(`/api/instance/${ instanceId }/info`);
30 | }
31 |
32 | export async function queryThreadInfo (instanceId) {
33 | return request(`/api/instance/${ instanceId }/thread`)
34 | }
35 |
36 | export async function queryMemoryInfo (instanceId) {
37 | return request(`/api/instance/${ instanceId }/memory`)
38 | }
39 |
--------------------------------------------------------------------------------
/sofa-dashboard-front/src/services/ark.js:
--------------------------------------------------------------------------------
1 | import { stringify } from 'qs';
2 | import request from '../utils/request';
3 |
4 | export async function queryAll(params) {
5 | return request(`/api/ark/plugin-list?${stringify(params)}`);
6 | }
7 |
8 | export async function searchPlugins(params) {
9 | return request(`/api/ark/search-plugin?${stringify(params)}`);
10 | }
11 |
12 | export async function registerPlugin(params) {
13 | return request('/api/ark/register', {
14 | method: 'POST',
15 | // 制定下 content-type ,否则会出现 415
16 | headers: { 'content-type': 'application/json' },
17 | body: JSON.stringify(params),
18 | });
19 | }
20 |
21 | export async function updatePlugin(params) {
22 | return request('/api/ark/update-plugin', {
23 | method: 'POST',
24 | // 制定下 content-type ,否则会出现 415
25 | headers: { 'content-type': 'application/json' },
26 | body: JSON.stringify(params),
27 | });
28 | }
29 |
30 | export async function deletePlugin(params) {
31 | return request(`/api/ark/delete-plugin?${stringify(params)}`);
32 | }
33 |
34 | export async function relatedApp(params) {
35 | return request(`/api/ark/related-app?${stringify(params)}`);
36 | }
37 |
38 | export async function cancelRelatedApp(params) {
39 | return request(`/api/ark/cancel-related-app?${stringify(params)}`);
40 | }
41 |
42 | export async function submitVersion(params) {
43 | return request('/api/ark/register-new-version', {
44 | method: 'POST',
45 | // 制定下 content-type ,否则会出现 415
46 | headers: { 'content-type': 'application/json' },
47 | body: JSON.stringify(params),
48 | });
49 | }
50 | export async function deleteVersion(params) {
51 | return request(`/api/ark/delete-version?${stringify(params)}`);
52 | }
53 |
54 |
55 |
--------------------------------------------------------------------------------
/sofa-dashboard-front/src/services/arkapp.js:
--------------------------------------------------------------------------------
1 | import { stringify } from 'qs';
2 | import request from '../utils/request';
3 |
4 | export async function queryArkAppList(params) {
5 | return request(`/api/arkapp/ark-app?${stringify(params)}`);
6 | }
7 |
8 | export async function queryBizState(params) {
9 | return request(`/api/arkapp/biz-state-detail?${stringify(params)}`);
10 | }
11 |
12 | export async function getBizState(params) {
13 | return request(`/api/arkapp/biz-state?${stringify(params)}`);
14 | }
15 |
16 | // 推送命令
17 | export async function pushCommand(params) {
18 | return request('/api/arkapp/command', {
19 | method: 'POST',
20 | headers: { 'content-type': 'application/json' },
21 | body: JSON.stringify(params),
22 | });
23 | }
--------------------------------------------------------------------------------
/sofa-dashboard-front/src/services/governance.js:
--------------------------------------------------------------------------------
1 | import { stringify } from 'qs';
2 | import request from '../utils/request';
3 |
4 | export async function queryAll(params) {
5 | if (params.queryType === 'app') {
6 | return request(`/api/service/all-app?query=` + params.queryKey);
7 | } else {
8 | return request(`/api/service/all-service?query=` + params.queryKey);
9 | }
10 | }
11 |
12 | export async function queryServiceByAppName(params) {
13 | return request(`/api/service/service-app?${stringify(params)}`);
14 | }
15 |
16 | // 获取服务发布详情
17 | export async function queryProviderDetails(params) {
18 | return request(`/api/service/query/providers?${stringify(params)}`);
19 | }
20 |
21 | // 获取服务消费详情
22 | export async function queryConsumerDetails(params) {
23 | return request(`/api/service/query/consumers?${stringify(params)}`);
24 | }
--------------------------------------------------------------------------------
/sofa-dashboard-front/src/services/user.js:
--------------------------------------------------------------------------------
1 | import request from '../utils/request';
2 |
3 | export async function queryUser() {
4 | return request('/api/user');
5 | }
6 |
--------------------------------------------------------------------------------
/sofa-dashboard-front/src/utils/request.js:
--------------------------------------------------------------------------------
1 | /**
2 | * request 网络请求工具
3 | * 更详细的 api 文档: https://github.com/umijs/umi-request
4 | */
5 | import { extend } from 'umi-request';
6 | import { notification } from 'antd';
7 |
8 | const codeMessage = {
9 | 200: '服务器成功返回请求的数据。',
10 | 201: '新建或修改数据成功。',
11 | 202: '一个请求已经进入后台排队(异步任务)。',
12 | 204: '删除数据成功。',
13 | 400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
14 | 401: '用户没有权限(令牌、用户名、密码错误)。',
15 | 403: '用户得到授权,但是访问是被禁止的。',
16 | 404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
17 | 406: '请求的格式不可得。',
18 | 410: '请求的资源被永久删除,且不会再得到的。',
19 | 422: '当创建一个对象时,发生一个验证错误。',
20 | 500: '服务器发生错误,请检查服务器。',
21 | 502: '网关错误。',
22 | 503: '服务不可用,服务器暂时过载或维护。',
23 | 504: '网关超时。',
24 | };
25 | /**
26 | * 异常处理程序
27 | */
28 |
29 | const errorHandler = error => {
30 | const { response } = error;
31 |
32 | if (response && response.status) {
33 | const errorText = codeMessage[response.status] || response.statusText;
34 | const { status, url } = response;
35 | notification.error({
36 | message: `请求错误 ${status}: ${url}`,
37 | description: errorText,
38 | });
39 | }
40 | };
41 | /**
42 | * 配置request请求时的默认参数
43 | */
44 |
45 | const request = extend({
46 | errorHandler,
47 | // 默认错误处理
48 | credentials: 'include', // 默认请求是否带上cookie
49 | });
50 | export default request;
51 |
--------------------------------------------------------------------------------
/sql/SofaDashboardDB.sql:
--------------------------------------------------------------------------------
1 | -- ----------------------------
2 | -- Create Database
3 | -- ----------------------------
4 | CREATE DATABASE IF NOT EXISTS SofaDashboardDB DEFAULT CHARACTER SET = utf8mb4;
5 | Use SofaDashboardDB;
6 |
7 | -- ----------------------------
8 | -- Table structure for ark_module_app
9 | -- ----------------------------
10 | DROP TABLE IF EXISTS `ark_module_app`;
11 | CREATE TABLE `ark_module_app` (
12 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '模块应用主键',
13 | `module_id` int(11) NOT NULL COMMENT '模块id',
14 | `app_name` varchar(128) NOT NULL COMMENT '应用名',
15 | `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '模块创建时间',
16 | PRIMARY KEY (`id`)
17 | ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COMMENT '模块-应用关联表';
18 |
19 | -- ----------------------------
20 | -- Table structure for ark_module_info
21 | -- ----------------------------
22 | DROP TABLE IF EXISTS `ark_module_info`;
23 | CREATE TABLE `ark_module_info` (
24 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '模块主键',
25 | `plugin_name` varchar(128) NOT NULL COMMENT '模块名',
26 | `description` varchar(128) DEFAULT '' COMMENT '模块描述',
27 | `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '模块创建时间',
28 | PRIMARY KEY (`id`)
29 | ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COMMENT '模块信息表';
30 |
31 | -- ----------------------------
32 | -- Table structure for ark_module_user
33 | -- ----------------------------
34 | DROP TABLE IF EXISTS `ark_module_user`;
35 | CREATE TABLE `ark_module_user` (
36 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '模块负责人表主键',
37 | `module_id` int(11) NOT NULL COMMENT '插件模块id',
38 | `user_id` int(11) DEFAULT NULL COMMENT '插件ownerId',
39 | `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
40 | PRIMARY KEY (`id`)
41 | ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COMMENT '模块-负责人表';
42 |
43 | -- ----------------------------
44 | -- Table structure for ark_module_version
45 | -- ----------------------------
46 | DROP TABLE IF EXISTS `ark_module_version`;
47 | CREATE TABLE `ark_module_version` (
48 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '模块版本表主键',
49 | `module_id` int(11) NOT NULL COMMENT '模块id',
50 | `module_version` varchar(64) NOT NULL COMMENT '模块版本',
51 | `source_path` varchar(1024) DEFAULT '' COMMENT '资源路径',
52 | `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
53 | `is_release` tinyint(4) DEFAULT 0 COMMENT '是否发布',
54 | PRIMARY KEY (`id`)
55 | ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COMMENT '模块-版本表';
56 |
57 | -- ----------------------------
58 | -- Table structure for sofa_dashboard_user
59 | -- ----------------------------
60 | DROP TABLE IF EXISTS `sofa_dashboard_user`;
61 | CREATE TABLE `sofa_dashboard_user` (
62 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户表id',
63 | `nick_name` varchar(64) DEFAULT '' COMMENT '昵称',
64 | `email` varchar(64) DEFAULT '' COMMENT '用户邮箱',
65 | `phone` varchar(12) DEFAULT '' COMMENT '用户电话',
66 | `password` varchar(32) DEFAULT '' COMMENT '用户密码',
67 | `header_img_url` varchar(64) DEFAULT '' COMMENT '头像地址链接',
68 | PRIMARY KEY (`id`)
69 | ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COMMENT '用户表';
70 |
71 |
--------------------------------------------------------------------------------
/tools/change_version.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | shellDir=$(cd "$(dirname "$0")"; pwd)
3 |
4 | shopt -s expand_aliases
5 | if [ ! -n "$1" ] ;then
6 | echo "Please enter a version"
7 | exit 1
8 | else
9 | echo "The version is $1 !"
10 | fi
11 |
12 | if [ `uname` == "Darwin" ] ;then
13 | echo "This is OS X"
14 | alias sed='sed -i ""'
15 | else
16 | echo "This is Linux"
17 | alias sed='sed -i'
18 | fi
19 |
20 | cd $shellDir/..
21 | echo "Change version in registry-parent ===>"
22 | sed "// s/[^\$].*<\/version>/$1<\/version>/" ./pom.xml
23 |
24 | echo "Change version in registry-client-all ===>"
25 | sed "/[^\$].*<\/version>/$1<\/version>/" ./client/all/pom.xml
26 |
27 | echo "Change version in subproject pom ===>"
28 | for filename in `find . -name "pom.xml" -maxdepth 4`;do
29 | if [ $filename == "./client/all/pom.xml" ]; then
30 | continue
31 | fi
32 | echo "Deal with $filename"
33 | sed "//,/<\/parent>/ s/[^\$].*<\/version>/$1<\/version>/" $filename
34 | done
35 |
36 | #TODO
37 | #echo "Change version in server shell ===>"
--------------------------------------------------------------------------------
/tools/check_format.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | BASEDIR=$(dirname $0)
4 |
5 | cd ${BASEDIR}
6 |
7 | # make sure git has no un commit files
8 | if [ -n "$(git status --untracked-files=no --porcelain)" ]; then
9 | echo "Please commit your change before run this shell, un commit files:"
10 | git status --untracked-files=no --porcelain
11 | exit 1
12 | fi
--------------------------------------------------------------------------------
/tools/codestyle/HEADER:
--------------------------------------------------------------------------------
1 | Licensed to the Apache Software Foundation (ASF) under one or more
2 | contributor license agreements. See the NOTICE file distributed with
3 | this work for additional information regarding copyright ownership.
4 | The ASF licenses this file to You under the Apache License, Version 2.0
5 | (the "License"); you may not use this file except in compliance with
6 | the License. You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
--------------------------------------------------------------------------------