[WIP] login
parent
c1e9271609
commit
dcc3f30afa
@ -0,0 +1,23 @@
|
||||
FROM golang:1.17 as api-builder
|
||||
|
||||
WORKDIR /usr/local/bin
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o dashboard .
|
||||
|
||||
FROM node:17 as web-builder
|
||||
|
||||
WORKDIR /usr/local/bin
|
||||
|
||||
RUN cd frontend && npm install && ng build --output-path web
|
||||
|
||||
FROM alpine:latest
|
||||
|
||||
WORKDIR /usr/local/bin
|
||||
|
||||
COPY --from=api-builder /usr/local/bin/dashboard .
|
||||
|
||||
COPY --from=web-builder /usr/local/bin/web .
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD [ "/usr/local/bin/dashboard" ]
|
@ -1,2 +1,42 @@
|
||||
# go-micro-dashboard
|
||||
Dashboard for go-micro.
|
||||
# Go Micro Dashboard
|
||||
|
||||
Go micro dashboard is designed to make it as easy as possible for users to work with go-micro framework.
|
||||
|
||||
# Features
|
||||
|
||||
- [ ] Dashboard
|
||||
- [ ] Services list
|
||||
- [ ] Support request endpoints
|
||||
- [ ] Support dashboard authenticate
|
||||
- [ ] Support configure service
|
||||
|
||||
## Development
|
||||
|
||||
### Server
|
||||
|
||||
#### Swagger
|
||||
|
||||
```
|
||||
swagger generate spec -o docs/swagger.json -b ./docs
|
||||
swag init
|
||||
```
|
||||
|
||||
### Web UI
|
||||
|
||||
[Document](https://github.com/xpunch/go-micro-dashboard/tree/main/frontend)
|
||||
|
||||
## Docker
|
||||
|
||||
```
|
||||
docker run .
|
||||
```
|
||||
|
||||
## Docker Compose
|
||||
|
||||
```
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[Apache License 2.0](./LICENSE)
|
||||
|
@ -0,0 +1,11 @@
|
||||
server:
|
||||
env: "dev"
|
||||
address: ":4000"
|
||||
cors:
|
||||
enable: true
|
||||
origin: "http://localhost:4200"
|
||||
swagger:
|
||||
host: "localhost:4000"
|
||||
base: "/"
|
||||
web:
|
||||
path: "./frontend/dist"
|
@ -0,0 +1,58 @@
|
||||
package config
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
EnvDev = "dev"
|
||||
EnvProd = "prod"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Server ServerConfig
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
Env string
|
||||
Address string
|
||||
Auth AuthConfig
|
||||
CORS CORSConfig
|
||||
Swagger SwaggerConfig
|
||||
Web WebConfig
|
||||
}
|
||||
|
||||
type AuthConfig struct {
|
||||
Username string
|
||||
Password string
|
||||
TokenSecret string
|
||||
TokenExpiration time.Duration
|
||||
}
|
||||
|
||||
type CORSConfig struct {
|
||||
Enable bool `toml:"enable"`
|
||||
Origin string `toml:"origin"`
|
||||
}
|
||||
|
||||
type SwaggerConfig struct {
|
||||
Host string
|
||||
Base string
|
||||
}
|
||||
|
||||
type WebConfig struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
func GetConfig() Config {
|
||||
return *_cfg
|
||||
}
|
||||
|
||||
func GetServerConfig() ServerConfig {
|
||||
return _cfg.Server
|
||||
}
|
||||
|
||||
func GetAuthConfig() AuthConfig {
|
||||
return _cfg.Server.Auth
|
||||
}
|
||||
|
||||
func GetSwaggerConfig() SwaggerConfig {
|
||||
return _cfg.Server.Swagger
|
||||
}
|
@ -0,0 +1 @@
|
||||
package config
|
@ -0,0 +1,96 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/asim/go-micro/plugins/config/encoder/toml/v4"
|
||||
"github.com/asim/go-micro/plugins/config/encoder/yaml/v4"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/xpunch/go-micro-dashboard/util"
|
||||
"go-micro.dev/v4/config"
|
||||
"go-micro.dev/v4/config/reader"
|
||||
"go-micro.dev/v4/config/reader/json"
|
||||
"go-micro.dev/v4/config/source/env"
|
||||
"go-micro.dev/v4/config/source/file"
|
||||
"go-micro.dev/v4/logger"
|
||||
)
|
||||
|
||||
// internal instance of Config
|
||||
var _cfg *Config = &Config{
|
||||
Server: ServerConfig{
|
||||
Env: EnvProd,
|
||||
Address: ":4000",
|
||||
Auth: AuthConfig{
|
||||
Username: "admin",
|
||||
Password: "123456",
|
||||
TokenSecret: "modifyme",
|
||||
TokenExpiration: 24 * time.Hour,
|
||||
},
|
||||
Swagger: SwaggerConfig{
|
||||
Host: "localhost:4000",
|
||||
Base: "/",
|
||||
},
|
||||
Web: WebConfig{
|
||||
Path: "web",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Load will load configurations and update it when changed
|
||||
func Load() error {
|
||||
var configor config.Config
|
||||
var err error
|
||||
switch strings.ToLower(os.Getenv("CONFIG_TYPE")) {
|
||||
case "env":
|
||||
configor, err = config.NewConfig(
|
||||
config.WithSource(env.NewSource()),
|
||||
)
|
||||
case "toml":
|
||||
filename := "config.toml"
|
||||
if name := os.Getenv("CONFIG_FILE"); len(name) > 0 {
|
||||
filename = name
|
||||
}
|
||||
configor, err = config.NewConfig(
|
||||
config.WithSource(file.NewSource(file.WithPath(filename))),
|
||||
config.WithReader(json.NewReader(reader.WithEncoder(toml.NewEncoder()))),
|
||||
)
|
||||
default:
|
||||
filename := "config.yaml"
|
||||
if name := os.Getenv("CONFIG_FILE"); len(name) > 0 {
|
||||
filename = name
|
||||
}
|
||||
configor, err = config.NewConfig(
|
||||
config.WithSource(file.NewSource(file.WithPath(filename))),
|
||||
config.WithReader(json.NewReader(reader.WithEncoder(yaml.NewEncoder()))),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "configor.New")
|
||||
}
|
||||
if err := configor.Load(); err != nil {
|
||||
return errors.Wrap(err, "configor.Load")
|
||||
}
|
||||
if err := configor.Scan(_cfg); err != nil {
|
||||
return errors.Wrap(err, "configor.Scan")
|
||||
}
|
||||
w, err := configor.Watch()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "configor.Watch")
|
||||
}
|
||||
util.GoSafe(func() {
|
||||
for {
|
||||
v, err := w.Next()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
if err := v.Scan(_cfg); err != nil {
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
@ -1,90 +1,17 @@
|
||||
<p align="center">
|
||||
<a href="https://ng-alain.com">
|
||||
<img width="100" src="https://ng-alain.com/assets/img/logo-color.svg">
|
||||
</a>
|
||||
</p>
|
||||
# Go Micro Dashboard
|
||||
|
||||
<h1 align="center">NG-ALAIN</h1>
|
||||
## Development
|
||||
|
||||
<div align="center">
|
||||
Out-of-box UI solution for enterprise applications, Let developers focus on business.
|
||||
### NG-Alain
|
||||
|
||||
[![Build Status](https://dev.azure.com/ng-alain/ng-alain/_apis/build/status/ng-alain-CI?branchName=master)](https://dev.azure.com/ng-alain/ng-alain/_build/latest?definitionId=2&branchName=master)
|
||||
[![Dependency Status](https://david-dm.org/ng-alain/ng-alain/status.svg?style=flat-square)](https://david-dm.org/ng-alain/ng-alain)
|
||||
[![GitHub Release Date](https://img.shields.io/github/release-date/ng-alain/ng-alain.svg?style=flat-square)](https://github.com/ng-alain/ng-alain/releases)
|
||||
[![NPM version](https://img.shields.io/npm/v/ng-alain.svg?style=flat-square)](https://www.npmjs.com/package/ng-alain)
|
||||
[![prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://prettier.io/)
|
||||
[![GitHub license](https://img.shields.io/github/license/mashape/apistatus.svg?style=flat-square)](https://github.com/ng-alain/ng-alain/blob/master/LICENSE)
|
||||
[![Gitter](https://img.shields.io/gitter/room/ng-alain/ng-alain.svg?style=flat-square)](https://gitter.im/ng-alain/ng-alain)
|
||||
[![ng-zorro-vscode](https://img.shields.io/badge/ng--zorro-VSCODE-brightgreen.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-zorro-vscode)
|
||||
[![ng-alain-vscode](https://img.shields.io/badge/ng--alain-VSCODE-brightgreen.svg?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-vscode)
|
||||
```
|
||||
ng new go-micro-dashboard
|
||||
ng add ng-alain
|
||||
```
|
||||
|
||||
</div>
|
||||
### Swagger
|
||||
|
||||
English | [简体中文](README-zh_CN.md)
|
||||
|
||||
## Quickstart
|
||||
|
||||
- [Getting Started](https://ng-alain.com/docs/getting-started)
|
||||
|
||||
## Links
|
||||
|
||||
+ [Document](https://ng-alain.com) ([Surge Mirror](https://ng-alain-doc.surge.sh))
|
||||
+ [@delon Source](https://github.com/ng-alain/delon)
|
||||
+ [DEMO](https://ng-alain.surge.sh) ([国内镜像](https://ng-alain.gitee.io/))
|
||||
|
||||
## Features
|
||||
|
||||
+ `ng-zorro-antd` based
|
||||
+ Responsive Layout
|
||||
+ I18n
|
||||
+ [@delon](https://github.com/ng-alain/delon)
|
||||
+ Lazy load Assets
|
||||
+ UI Router States
|
||||
+ Customize Theme
|
||||
+ Less preprocessor
|
||||
+ RTL
|
||||
+ Well organized & commented code
|
||||
+ Simple upgrade
|
||||
+ Support Docker deploy
|
||||
|
||||
## Architecture
|
||||
|
||||
![Architecture](https://raw.githubusercontent.com/ng-alain/delon/master/_screenshot/architecture.png)
|
||||
|
||||
> [delon](https://github.com/ng-alain/delon) is a production-ready solution for admin business components packages, Built on the design principles developed by Ant Design.
|
||||
|
||||
## App Shots
|
||||
|
||||
![desktop](https://raw.githubusercontent.com/ng-alain/delon/master/_screenshot/desktop.png)
|
||||
![ipad](https://raw.githubusercontent.com/ng-alain/delon/master/_screenshot/ipad.png)
|
||||
![iphone](https://raw.githubusercontent.com/ng-alain/delon/master/_screenshot/iphone.png)
|
||||
|
||||
## Contributing
|
||||
|
||||
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/ng-alain/ng-alain/pulls)
|
||||
|
||||
We welcome all contributions. Please read our [CONTRIBUTING.md](https://github.com/ng-alain/ng-alain/blob/master/CONTRIBUTING.md) first. You can submit any ideas as [pull requests](https://github.com/ng-alain/ng-alain/pulls) or as [GitHub issues](https://github.com/ng-alain/ng-alain/issues).
|
||||
|
||||
> If you're new to posting issues, we ask that you read [*How To Ask Questions The Smart Way*](http://www.catb.org/~esr/faqs/smart-questions.html) (**This guide does not provide actual support services for this project!**), [How to Ask a Question in Open Source Community](https://github.com/seajs/seajs/issues/545) and [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) prior to posting. Well written bug reports help us help you!
|
||||
|
||||
## Donation
|
||||
|
||||
ng-alain is an MIT-licensed open source project. In order to achieve better and sustainable development of the project, we expect to gain more backers. You can support us in any of the following ways:
|
||||
|
||||
- [patreon](https://www.patreon.com/cipchk)
|
||||
- [opencollective](https://opencollective.com/ng-alain)
|
||||
- [paypal](https://www.paypal.me/cipchk)
|
||||
- [支付宝或微信](https://ng-alain.com/assets/donate.png)
|
||||
|
||||
Or purchasing our [business theme](https://e.ng-alain.com/).
|
||||
|
||||
## Backers
|
||||
|
||||
Thank you to all our backers! 🙏
|
||||
|
||||
<a href="https://opencollective.com/ng-alain#backers" target="_blank"><img src="https://opencollective.com/ng-alain/backers.svg?width=890"></a>
|
||||
|
||||
### License
|
||||
|
||||
The MIT License (see the [LICENSE](https://github.com/ng-alain/ng-alain/blob/master/LICENSE) file for the full text)
|
||||
```
|
||||
npm install
|
||||
node_modules/.bin/nswag run
|
||||
```
|
||||
|
@ -0,0 +1,76 @@
|
||||
{
|
||||
"runtime": "Default",
|
||||
"defaultVariables": null,
|
||||
"documentGenerator": {
|
||||
"fromDocument": {
|
||||
"url": "http://localhost:4000/swagger/doc.json",
|
||||
"output": null,
|
||||
"newLineBehavior": "Auto"
|
||||
}
|
||||
},
|
||||
"codeGenerators": {
|
||||
"openApiToTypeScriptClient": {
|
||||
"className": "{controller}ServiceProxy",
|
||||
"moduleName": "",
|
||||
"namespace": "",
|
||||
"typeScriptVersion": 2.7,
|
||||
"template": "Angular",
|
||||
"promiseType": "Promise",
|
||||
"httpClass": "HttpClient",
|
||||
"withCredentials": false,
|
||||
"useSingletonProvider": false,
|
||||
"injectionTokenType": "InjectionToken",
|
||||
"rxJsVersion": 6.0,
|
||||
"dateTimeType": "Date",
|
||||
"nullValue": "Undefined",
|
||||
"generateClientClasses": true,
|
||||
"generateClientInterfaces": false,
|
||||
"generateOptionalParameters": false,
|
||||
"exportTypes": true,
|
||||
"wrapDtoExceptions": false,
|
||||
"exceptionClass": "ApiException",
|
||||
"clientBaseClass": null,
|
||||
"wrapResponses": false,
|
||||
"wrapResponseMethods": [],
|
||||
"generateResponseClasses": true,
|
||||
"responseClass": "SwaggerResponse",
|
||||
"protectedMethods": [],
|
||||
"configurationClass": null,
|
||||
"useTransformOptionsMethod": false,
|
||||
"useTransformResultMethod": false,
|
||||
"generateDtoTypes": true,
|
||||
"operationGenerationMode": "MultipleClientsFromOperationId",
|
||||
"markOptionalProperties": true,
|
||||
"generateCloneMethod": false,
|
||||
"typeStyle": "Class",
|
||||
"enumStyle": "Enum",
|
||||
"useLeafType": false,
|
||||
"classTypes": [],
|
||||
"extendedClasses": [],
|
||||
"extensionCode": null,
|
||||
"generateDefaultValues": true,
|
||||
"excludedTypeNames": [],
|
||||
"excludedParameterNames": [],
|
||||
"handleReferences": false,
|
||||
"generateConstructorInterface": true,
|
||||
"convertConstructorInterfaceData": false,
|
||||
"importRequiredTypes": true,
|
||||
"useGetBaseUrlMethod": false,
|
||||
"baseUrlTokenName": "API_BASE_URL",
|
||||
"queryNullValue": "",
|
||||
"useAbortSignal": false,
|
||||
"inlineNamedDictionaries": false,
|
||||
"inlineNamedAny": false,
|
||||
"includeHttpContext": false,
|
||||
"templateDirectory": null,
|
||||
"typeNameGeneratorType": null,
|
||||
"propertyNameGeneratorType": null,
|
||||
"enumNameGeneratorType": null,
|
||||
"checksumCacheEnabled": false,
|
||||
"serviceHost": null,
|
||||
"serviceSchemes": null,
|
||||
"output": "src/app/shared/service-proxies/service-proxies.ts",
|
||||
"newLineBehavior": "Auto"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,815 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
//----------------------
|
||||
// <auto-generated>
|
||||
// Generated using the NSwag toolchain v13.14.0.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0)) (http://NSwag.org)
|
||||
// </auto-generated>
|
||||
//----------------------
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
import { mergeMap as _observableMergeMap, catchError as _observableCatch } from 'rxjs/operators';
|
||||
import { Observable, throwError as _observableThrow, of as _observableOf } from 'rxjs';
|
||||
import { Injectable, Inject, Optional, InjectionToken } from '@angular/core';
|
||||
import { HttpClient, HttpHeaders, HttpResponse, HttpResponseBase } from '@angular/common/http';
|
||||
|
||||
export const API_BASE_URL = new InjectionToken<string>('API_BASE_URL');
|
||||
|
||||
@Injectable()
|
||||
export class AccountServiceProxy {
|
||||
private http: HttpClient;
|
||||
private baseUrl: string;
|
||||
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
|
||||
|
||||
constructor(@Inject(HttpClient) http: HttpClient, @Optional() @Inject(API_BASE_URL) baseUrl?: string) {
|
||||
this.http = http;
|
||||
this.baseUrl = baseUrl !== undefined && baseUrl !== null ? baseUrl : "localhost:4000/";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param input request
|
||||
* @return success
|
||||
*/
|
||||
login(input: LoginRequest) : Observable<LoginResponse> {
|
||||
let url_ = this.baseUrl + "/api/login";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
const content_ = JSON.stringify(input);
|
||||
|
||||
let options_ : any = {
|
||||
body: content_,
|
||||
observe: "response",
|
||||
responseType: "blob",
|
||||
headers: new HttpHeaders({
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json"
|
||||
})
|
||||
};
|
||||
|
||||
return this.http.request("post", url_, options_).pipe(_observableMergeMap((response_ : any) => {
|
||||
return this.processLogin(response_);
|
||||
})).pipe(_observableCatch((response_: any) => {
|
||||
if (response_ instanceof HttpResponseBase) {
|
||||
try {
|
||||
return this.processLogin(<any>response_);
|
||||
} catch (e) {
|
||||
return <Observable<LoginResponse>><any>_observableThrow(e);
|
||||
}
|
||||
} else
|
||||
return <Observable<LoginResponse>><any>_observableThrow(response_);
|
||||
}));
|
||||
}
|
||||
|
||||
protected processLogin(response: HttpResponseBase): Observable<LoginResponse> {
|
||||
const status = response.status;
|
||||
const responseBlob =
|
||||
response instanceof HttpResponse ? response.body :
|
||||
(<any>response).error instanceof Blob ? (<any>response).error : undefined;
|
||||
|
||||
let _headers: any = {}; if (response.headers) { for (let key of response.headers.keys()) { _headers[key] = response.headers.get(key); }}
|
||||
if (status === 200) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
let result200: any = null;
|
||||
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result200 = LoginResponse.fromJS(resultData200);
|
||||
return _observableOf(result200);
|
||||
}));
|
||||
} else if (status === 400) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
let result400: any = null;
|
||||
let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result400 = resultData400 !== undefined ? resultData400 : <any>null;
|
||||
return throwException("Bad Request", status, _responseText, _headers, result400);
|
||||
}));
|
||||
} else if (status === 401) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
let result401: any = null;
|
||||
let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result401 = resultData401 !== undefined ? resultData401 : <any>null;
|
||||
return throwException("Unauthorized", status, _responseText, _headers, result401);
|
||||
}));
|
||||
} else if (status === 500) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
let result500: any = null;
|
||||
let resultData500 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result500 = resultData500 !== undefined ? resultData500 : <any>null;
|
||||
return throwException("Internal Server Error", status, _responseText, _headers, result500);
|
||||
}));
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
}));
|
||||
}
|
||||
return _observableOf<LoginResponse>(<any>null);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class RegistryServiceProxy {
|
||||
private http: HttpClient;
|
||||
private baseUrl: string;
|
||||
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
|
||||
|
||||
constructor(@Inject(HttpClient) http: HttpClient, @Optional() @Inject(API_BASE_URL) baseUrl?: string) {
|
||||
this.http = http;
|
||||
this.baseUrl = baseUrl !== undefined && baseUrl !== null ? baseUrl : "localhost:4000/";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OK
|
||||
*/
|
||||
getServices() : Observable<GetServiceListResponse> {
|
||||
let url_ = this.baseUrl + "/api/registry/services";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_ : any = {
|
||||
observe: "response",
|
||||
responseType: "blob",
|
||||
headers: new HttpHeaders({
|
||||
"Accept": "application/json"
|
||||
})
|
||||
};
|
||||
|
||||
return this.http.request("get", url_, options_).pipe(_observableMergeMap((response_ : any) => {
|
||||
return this.processGetServices(response_);
|
||||
})).pipe(_observableCatch((response_: any) => {
|
||||
if (response_ instanceof HttpResponseBase) {
|
||||
try {
|
||||
return this.processGetServices(<any>response_);
|
||||
} catch (e) {
|
||||
return <Observable<GetServiceListResponse>><any>_observableThrow(e);
|
||||
}
|
||||
} else
|
||||
return <Observable<GetServiceListResponse>><any>_observableThrow(response_);
|
||||
}));
|
||||
}
|
||||
|
||||
protected processGetServices(response: HttpResponseBase): Observable<GetServiceListResponse> {
|
||||
const status = response.status;
|
||||
const responseBlob =
|
||||
response instanceof HttpResponse ? response.body :
|
||||
(<any>response).error instanceof Blob ? (<any>response).error : undefined;
|
||||
|
||||
let _headers: any = {}; if (response.headers) { for (let key of response.headers.keys()) { _headers[key] = response.headers.get(key); }}
|
||||
if (status === 200) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
let result200: any = null;
|
||||
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result200 = GetServiceListResponse.fromJS(resultData200);
|
||||
return _observableOf(result200);
|
||||
}));
|
||||
} else if (status === 400) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
let result400: any = null;
|
||||
let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result400 = resultData400 !== undefined ? resultData400 : <any>null;
|
||||
return throwException("Bad Request", status, _responseText, _headers, result400);
|
||||
}));
|
||||
} else if (status === 401) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
let result401: any = null;
|
||||
let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result401 = resultData401 !== undefined ? resultData401 : <any>null;
|
||||
return throwException("Unauthorized", status, _responseText, _headers, result401);
|
||||
}));
|
||||
} else if (status === 500) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
let result500: any = null;
|
||||
let resultData500 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result500 = resultData500 !== undefined ? resultData500 : <any>null;
|
||||
return throwException("Internal Server Error", status, _responseText, _headers, result500);
|
||||
}));
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
}));
|
||||
}
|
||||
return _observableOf<GetServiceListResponse>(<any>null);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class StatisticsServiceProxy {
|
||||
private http: HttpClient;
|
||||
private baseUrl: string;
|
||||
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
|
||||
|
||||
constructor(@Inject(HttpClient) http: HttpClient, @Optional() @Inject(API_BASE_URL) baseUrl?: string) {
|
||||
this.http = http;
|
||||
this.baseUrl = baseUrl !== undefined && baseUrl !== null ? baseUrl : "localhost:4000/";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OK
|
||||
*/
|
||||
getSummary() : Observable<GetSummaryResponse> {
|
||||
let url_ = this.baseUrl + "/api/summary";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_ : any = {
|
||||
observe: "response",
|
||||
responseType: "blob",
|
||||
headers: new HttpHeaders({
|
||||
"Accept": "application/json"
|
||||
})
|
||||
};
|
||||
|
||||
return this.http.request("get", url_, options_).pipe(_observableMergeMap((response_ : any) => {
|
||||
return this.processGetSummary(response_);
|
||||
})).pipe(_observableCatch((response_: any) => {
|
||||
if (response_ instanceof HttpResponseBase) {
|
||||
try {
|
||||
return this.processGetSummary(<any>response_);
|
||||
} catch (e) {
|
||||
return <Observable<GetSummaryResponse>><any>_observableThrow(e);
|
||||
}
|
||||
} else
|
||||
return <Observable<GetSummaryResponse>><any>_observableThrow(response_);
|
||||
}));
|
||||
}
|
||||
|
||||
protected processGetSummary(response: HttpResponseBase): Observable<GetSummaryResponse> {
|
||||
const status = response.status;
|
||||
const responseBlob =
|
||||
response instanceof HttpResponse ? response.body :
|
||||
(<any>response).error instanceof Blob ? (<any>response).error : undefined;
|
||||
|
||||
let _headers: any = {}; if (response.headers) { for (let key of response.headers.keys()) { _headers[key] = response.headers.get(key); }}
|
||||
if (status === 200) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
let result200: any = null;
|
||||
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result200 = GetSummaryResponse.fromJS(resultData200);
|
||||
return _observableOf(result200);
|
||||
}));
|
||||
} else if (status === 400) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
let result400: any = null;
|
||||
let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result400 = resultData400 !== undefined ? resultData400 : <any>null;
|
||||
return throwException("Bad Request", status, _responseText, _headers, result400);
|
||||
}));
|
||||
} else if (status === 401) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
let result401: any = null;
|
||||
let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result401 = resultData401 !== undefined ? resultData401 : <any>null;
|
||||
return throwException("Unauthorized", status, _responseText, _headers, result401);
|
||||
}));
|
||||
} else if (status === 500) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
let result500: any = null;
|
||||
let resultData500 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result500 = resultData500 !== undefined ? resultData500 : <any>null;
|
||||
return throwException("Internal Server Error", status, _responseText, _headers, result500);
|
||||
}));
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
}));
|
||||
}
|
||||
return _observableOf<GetSummaryResponse>(<any>null);
|
||||
}
|
||||
}
|
||||
|
||||
export class LoginRequest implements ILoginRequest {
|
||||
password!: string;
|
||||
username!: string;
|
||||
|
||||
constructor(data?: ILoginRequest) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
if (_data) {
|
||||
this.password = _data["password"];
|
||||
this.username = _data["username"];
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): LoginRequest {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new LoginRequest();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["password"] = this.password;
|
||||
data["username"] = this.username;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ILoginRequest {
|
||||
password: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
export class LoginResponse implements ILoginResponse {
|
||||
token?: string | undefined;
|
||||
|
||||
constructor(data?: ILoginResponse) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
if (_data) {
|
||||
this.token = _data["token"];
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): LoginResponse {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new LoginResponse();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["token"] = this.token;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ILoginResponse {
|
||||
token?: string | undefined;
|
||||
}
|
||||
|
||||
export class GetServiceListResponse implements IGetServiceListResponse {
|
||||
services?: RegistryService[] | undefined;
|
||||
|
||||
constructor(data?: IGetServiceListResponse) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
if (_data) {
|
||||
if (Array.isArray(_data["services"])) {
|
||||
this.services = [] as any;
|
||||
for (let item of _data["services"])
|
||||
this.services!.push(RegistryService.fromJS(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): GetServiceListResponse {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new GetServiceListResponse();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
if (Array.isArray(this.services)) {
|
||||
data["services"] = [];
|
||||
for (let item of this.services)
|
||||
data["services"].push(item.toJSON());
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IGetServiceListResponse {
|
||||
services?: RegistryService[] | undefined;
|
||||
}
|
||||
|
||||
export class RegistryEndpoint implements IRegistryEndpoint {
|
||||
metadata?: { [key: string]: string; } | undefined;
|
||||
name?: string | undefined;
|
||||
request?: RegistryValue | undefined;
|
||||
response?: RegistryValue | undefined;
|
||||
|
||||
constructor(data?: IRegistryEndpoint) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
if (_data) {
|
||||
if (_data["metadata"]) {
|
||||
this.metadata = {} as any;
|
||||
for (let key in _data["metadata"]) {
|
||||
if (_data["metadata"].hasOwnProperty(key))
|
||||
(<any>this.metadata)![key] = _data["metadata"][key];
|
||||
}
|
||||
}
|
||||
this.name = _data["name"];
|
||||
this.request = _data["request"] ? RegistryValue.fromJS(_data["request"]) : <any>undefined;
|
||||
this.response = _data["response"] ? RegistryValue.fromJS(_data["response"]) : <any>undefined;
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): RegistryEndpoint {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new RegistryEndpoint();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
if (this.metadata) {
|
||||
data["metadata"] = {};
|
||||
for (let key in this.metadata) {
|
||||
if (this.metadata.hasOwnProperty(key))
|
||||
(<any>data["metadata"])[key] = this.metadata[key];
|
||||
}
|
||||
}
|
||||
data["name"] = this.name;
|
||||
data["request"] = this.request ? this.request.toJSON() : <any>undefined;
|
||||
data["response"] = this.response ? this.response.toJSON() : <any>undefined;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IRegistryEndpoint {
|
||||
metadata?: { [key: string]: string; } | undefined;
|
||||
name?: string | undefined;
|
||||
request?: RegistryValue | undefined;
|
||||
response?: RegistryValue | undefined;
|
||||
}
|
||||
|
||||
export class RegistryNode implements IRegistryNode {
|
||||
address?: string | undefined;
|
||||
id?: string | undefined;
|
||||
metadata?: { [key: string]: string; } | undefined;
|
||||
|
||||
constructor(data?: IRegistryNode) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
if (_data) {
|
||||
this.address = _data["address"];
|
||||
this.id = _data["id"];
|
||||
if (_data["metadata"]) {
|
||||
this.metadata = {} as any;
|
||||
for (let key in _data["metadata"]) {
|
||||
if (_data["metadata"].hasOwnProperty(key))
|
||||
(<any>this.metadata)![key] = _data["metadata"][key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): RegistryNode {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new RegistryNode();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["address"] = this.address;
|
||||
data["id"] = this.id;
|
||||
if (this.metadata) {
|
||||
data["metadata"] = {};
|
||||
for (let key in this.metadata) {
|
||||
if (this.metadata.hasOwnProperty(key))
|
||||
(<any>data["metadata"])[key] = this.metadata[key];
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IRegistryNode {
|
||||
address?: string | undefined;
|
||||
id?: string | undefined;
|
||||
metadata?: { [key: string]: string; } | undefined;
|
||||
}
|
||||
|
||||
export class RegistryService implements IRegistryService {
|
||||
endpoints?: RegistryEndpoint[] | undefined;
|
||||
metadata?: { [key: string]: string; } | undefined;
|
||||
name?: string | undefined;
|
||||
nodes?: RegistryNode[] | undefined;
|
||||
version?: string | undefined;
|
||||
|
||||
constructor(data?: IRegistryService) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
if (_data) {
|
||||
if (Array.isArray(_data["endpoints"])) {
|
||||
this.endpoints = [] as any;
|
||||
for (let item of _data["endpoints"])
|
||||
this.endpoints!.push(RegistryEndpoint.fromJS(item));
|
||||
}
|
||||
if (_data["metadata"]) {
|
||||
this.metadata = {} as any;
|
||||
for (let key in _data["metadata"]) {
|
||||
if (_data["metadata"].hasOwnProperty(key))
|
||||
(<any>this.metadata)![key] = _data["metadata"][key];
|
||||
}
|
||||
}
|
||||
this.name = _data["name"];
|
||||
if (Array.isArray(_data["nodes"])) {
|
||||
this.nodes = [] as any;
|
||||
for (let item of _data["nodes"])
|
||||
this.nodes!.push(RegistryNode.fromJS(item));
|
||||
}
|
||||
this.version = _data["version"];
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): RegistryService {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new RegistryService();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
if (Array.isArray(this.endpoints)) {
|
||||
data["endpoints"] = [];
|
||||
for (let item of this.endpoints)
|
||||
data["endpoints"].push(item.toJSON());
|
||||
}
|
||||
if (this.metadata) {
|
||||
data["metadata"] = {};
|
||||
for (let key in this.metadata) {
|
||||
if (this.metadata.hasOwnProperty(key))
|
||||
(<any>data["metadata"])[key] = this.metadata[key];
|
||||
}
|
||||
}
|
||||
data["name"] = this.name;
|
||||
if (Array.isArray(this.nodes)) {
|
||||
data["nodes"] = [];
|
||||
for (let item of this.nodes)
|
||||
data["nodes"].push(item.toJSON());
|
||||
}
|
||||
data["version"] = this.version;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IRegistryService {
|
||||
endpoints?: RegistryEndpoint[] | undefined;
|
||||
metadata?: { [key: string]: string; } | undefined;
|
||||
name?: string | undefined;
|
||||
nodes?: RegistryNode[] | undefined;
|
||||
version?: string | undefined;
|
||||
}
|
||||
|
||||
export class RegistryValue implements IRegistryValue {
|
||||
name?: string | undefined;
|
||||
type?: string | undefined;
|
||||
values?: RegistryValue[] | undefined;
|
||||
|
||||
constructor(data?: IRegistryValue) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
if (_data) {
|
||||
this.name = _data["name"];
|
||||
this.type = _data["type"];
|
||||
if (Array.isArray(_data["values"])) {
|
||||
this.values = [] as any;
|
||||
for (let item of _data["values"])
|
||||
this.values!.push(RegistryValue.fromJS(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): RegistryValue {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new RegistryValue();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["name"] = this.name;
|
||||
data["type"] = this.type;
|
||||
if (Array.isArray(this.values)) {
|
||||
data["values"] = [];
|
||||
for (let item of this.values)
|
||||
data["values"].push(item.toJSON());
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IRegistryValue {
|
||||
name?: string | undefined;
|
||||
type?: string | undefined;
|
||||
values?: RegistryValue[] | undefined;
|
||||
}
|
||||
|
||||
export class GetSummaryResponse implements IGetSummaryResponse {
|
||||
registry?: RegistrySummary | undefined;
|
||||
services?: ServicesSummary | undefined;
|
||||
|
||||
constructor(data?: IGetSummaryResponse) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
if (_data) {
|
||||
this.registry = _data["registry"] ? RegistrySummary.fromJS(_data["registry"]) : <any>undefined;
|
||||
this.services = _data["services"] ? ServicesSummary.fromJS(_data["services"]) : <any>undefined;
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): GetSummaryResponse {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new GetSummaryResponse();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["registry"] = this.registry ? this.registry.toJSON() : <any>undefined;
|
||||
data["services"] = this.services ? this.services.toJSON() : <any>undefined;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IGetSummaryResponse {
|
||||
registry?: RegistrySummary | undefined;
|
||||
services?: ServicesSummary | undefined;
|
||||
}
|
||||
|
||||
export class RegistrySummary implements IRegistrySummary {
|
||||
addrs?: string[] | undefined;
|
||||
type?: string | undefined;
|
||||
|
||||
constructor(data?: IRegistrySummary) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
if (_data) {
|
||||
if (Array.isArray(_data["addrs"])) {
|
||||
this.addrs = [] as any;
|
||||
for (let item of _data["addrs"])
|
||||
this.addrs!.push(item);
|
||||
}
|
||||
this.type = _data["type"];
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): RegistrySummary {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new RegistrySummary();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
if (Array.isArray(this.addrs)) {
|
||||
data["addrs"] = [];
|
||||
for (let item of this.addrs)
|
||||
data["addrs"].push(item);
|
||||
}
|
||||
data["type"] = this.type;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IRegistrySummary {
|
||||
addrs?: string[] | undefined;
|
||||
type?: string | undefined;
|
||||
}
|
||||
|
||||
export class ServicesSummary implements IServicesSummary {
|
||||
count?: number | undefined;
|
||||
nodes_count?: number | undefined;
|
||||
|
||||
constructor(data?: IServicesSummary) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
if (_data) {
|
||||
this.count = _data["count"];
|
||||
this.nodes_count = _data["nodes_count"];
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): ServicesSummary {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new ServicesSummary();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["count"] = this.count;
|
||||
data["nodes_count"] = this.nodes_count;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IServicesSummary {
|
||||
count?: number | undefined;
|
||||
nodes_count?: number | undefined;
|
||||
}
|
||||
|
||||
export class ApiException extends Error {
|
||||
message: string;
|
||||
status: number;
|
||||
response: string;
|
||||
headers: { [key: string]: any; };
|
||||
result: any;
|
||||
|
||||
constructor(message: string, status: number, response: string, headers: { [key: string]: any; }, result: any) {
|
||||
super();
|
||||
|
||||
this.message = message;
|
||||
this.status = status;
|
||||
this.response = response;
|
||||
this.headers = headers;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
protected isApiException = true;
|
||||
|
||||
static isApiException(obj: any): obj is ApiException {
|
||||
return obj.isApiException === true;
|
||||
}
|
||||
}
|
||||
|
||||
function throwException(message: string, status: number, response: string, headers: { [key: string]: any; }, result?: any): Observable<any> {
|
||||
if (result !== null && result !== undefined)
|
||||
return _observableThrow(result);
|
||||
else
|
||||
return _observableThrow(new ApiException(message, status, response, headers, null));
|
||||
}
|
||||
|
||||
function blobToText(blob: any): Observable<string> {
|
||||
return new Observable<string>((observer: any) => {
|
||||
if (!blob) {
|
||||
observer.next("");
|
||||
observer.complete();
|
||||
} else {
|
||||
let reader = new FileReader();
|
||||
reader.onload = event => {
|
||||
observer.next((<any>event.target).result);
|
||||
observer.complete();
|
||||
};
|
||||
reader.readAsText(blob);
|
||||
}
|
||||
});
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import * as ApiServiceProxies from './service-proxies';
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
ApiServiceProxies.AccountServiceProxy,
|
||||
ApiServiceProxies.RegistryServiceProxy,
|
||||
ApiServiceProxies.StatisticsServiceProxy,
|
||||
]
|
||||
})
|
||||
export class ServiceProxyModule {
|
||||
}
|
@ -1,14 +1,117 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>GoMicroDashboard</title>
|
||||
<title>Go Micro Dashboard</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<style type="text/css">.preloader{position:fixed;top:0;left:0;width:100%;height:100%;overflow:hidden;background:#49a9ee;z-index:9999;transition:opacity .65s}.preloader-hidden-add{opacity:1;display:block}.preloader-hidden-add-active{opacity:0}.preloader-hidden{display:none}.cs-loader{position:absolute;top:0;left:0;height:100%;width:100%}.cs-loader-inner{transform:translateY(-50%);top:50%;position:absolute;width:100%;color:#fff;text-align:center}.cs-loader-inner label{font-size:20px;opacity:0;display:inline-block}@keyframes lol{0%{opacity:0;transform:translateX(-300px)}33%{opacity:1;transform:translateX(0)}66%{opacity:1;transform:translateX(0)}100%{opacity:0;transform:translateX(300px)}}.cs-loader-inner label:nth-child(6){animation:lol 3s infinite ease-in-out}.cs-loader-inner label:nth-child(5){animation:lol 3s .1s infinite ease-in-out}.cs-loader-inner label:nth-child(4){animation:lol 3s .2s infinite ease-in-out}.cs-loader-inner label:nth-child(3){animation:lol 3s .3s infinite ease-in-out}.cs-loader-inner label:nth-child(2){animation:lol 3s .4s infinite ease-in-out}.cs-loader-inner label:nth-child(1){animation:lol 3s .5s infinite ease-in-out}</style></head>
|
||||
<style type="text/css">
|
||||
.preloader {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background: #49a9ee;
|
||||
z-index: 9999;
|
||||
transition: opacity .65s
|
||||
}
|
||||
|
||||
.preloader-hidden-add {
|
||||
opacity: 1;
|
||||
display: block
|
||||
}
|
||||
|
||||
.preloader-hidden-add-active {
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
.preloader-hidden {
|
||||
display: none
|
||||
}
|
||||
|
||||
.cs-loader {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.cs-loader-inner {
|
||||
transform: translateY(-50%);
|
||||
top: 50%;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
color: #fff;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.cs-loader-inner label {
|
||||
font-size: 20px;
|
||||
opacity: 0;
|
||||
display: inline-block
|
||||
}
|
||||
|
||||
@keyframes lol {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(-300px)
|
||||
}
|
||||
|
||||
33% {
|
||||
opacity: 1;
|
||||
transform: translateX(0)
|
||||
}
|
||||
|
||||
66% {
|
||||
opacity: 1;
|
||||
transform: translateX(0)
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateX(300px)
|
||||
}
|
||||
}
|
||||
|
||||
.cs-loader-inner label:nth-child(6) {
|
||||
animation: lol 3s infinite ease-in-out
|
||||
}
|
||||
|
||||
.cs-loader-inner label:nth-child(5) {
|
||||
animation: lol 3s .1s infinite ease-in-out
|
||||
}
|
||||
|
||||
.cs-loader-inner label:nth-child(4) {
|
||||
animation: lol 3s .2s infinite ease-in-out
|
||||
}
|
||||
|
||||
.cs-loader-inner label:nth-child(3) {
|
||||
animation: lol 3s .3s infinite ease-in-out
|
||||
}
|
||||
|
||||
.cs-loader-inner label:nth-child(2) {
|
||||
animation: lol 3s .4s infinite ease-in-out
|
||||
}
|
||||
|
||||
.cs-loader-inner label:nth-child(1) {
|
||||
animation: lol 3s .5s infinite ease-in-out
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
<div class="preloader"><div class="cs-loader"><div class="cs-loader-inner"><label> ●</label><label> ●</label><label> ●</label><label> ●</label><label> ●</label><label> ●</label></div></div></div>
|
||||
<div class="preloader">
|
||||
<div class="cs-loader">
|
||||
<div class="cs-loader-inner"><label> ●</label><label> ●</label><label> ●</label><label> ●</label><label>
|
||||
●</label><label> ●</label></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</html>
|
@ -0,0 +1,91 @@
|
||||
module github.com/xpunch/go-micro-dashboard
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
|
||||
github.com/asim/go-micro/plugins/client/grpc/v4 v4.0.0-20211103025805-c5be9f560cdb
|
||||
github.com/asim/go-micro/plugins/config/encoder/toml/v4 v4.0.0-20211103025805-c5be9f560cdb
|
||||
github.com/asim/go-micro/plugins/config/encoder/yaml/v4 v4.0.0-20211103025805-c5be9f560cdb
|
||||
github.com/asim/go-micro/plugins/registry/etcd/v4 v4.0.0-20211103025805-c5be9f560cdb
|
||||
github.com/asim/go-micro/plugins/registry/kubernetes/v4 v4.0.0-20211101090014-adaa98e6cffe
|
||||
github.com/asim/go-micro/plugins/server/http/v4 v4.0.0-20211103025805-c5be9f560cdb
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/gin-contrib/static v0.0.1
|
||||
github.com/gin-gonic/gin v1.7.4
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/swaggo/gin-swagger v1.3.3
|
||||
github.com/swaggo/swag v1.7.4
|
||||
go-micro.dev/v4 v4.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.0 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/bitly/go-simplejson v0.5.0 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.3.1 // indirect
|
||||
github.com/go-git/go-git/v5 v5.4.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.5 // indirect
|
||||
github.com/go-openapi/spec v0.20.3 // indirect
|
||||
github.com/go-openapi/swag v0.19.14 // indirect
|
||||
github.com/go-playground/locales v0.13.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.17.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.4.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/uuid v1.2.0 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.11 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/leodido/go-urn v1.2.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/miekg/dns v1.1.43 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/hashstructure v1.1.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/ugorji/go/codec v1.1.7 // indirect
|
||||
github.com/urfave/cli/v2 v2.3.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.0 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.17.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
golang.org/x/tools v0.1.2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
|
||||
google.golang.org/grpc v1.42.0 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
@ -0,0 +1,63 @@
|
||||
package account
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/render"
|
||||
"github.com/xpunch/go-micro-dashboard/config"
|
||||
"github.com/xpunch/go-micro-dashboard/handler/route"
|
||||
)
|
||||
|
||||
type service struct{}
|
||||
|
||||
func NewRouteRegistrar() route.Registrar {
|
||||
return service{}
|
||||
}
|
||||
|
||||
func (s service) RegisterRoute(router gin.IRoutes) {
|
||||
router.POST("/api/login", s.Login)
|
||||
}
|
||||
|
||||
type loginRequest struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
type loginResponse struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// @Tags Account
|
||||
// @ID account_login
|
||||
// @Param input body loginRequest true "request"
|
||||
// @Success 200 {object} loginResponse "success"
|
||||
// @Failure 400 {object} string
|
||||
// @Failure 401 {object} string
|
||||
// @Failure 500 {object} string
|
||||
// @Router /api/login [post]
|
||||
func (s *service) Login(ctx *gin.Context) {
|
||||
var req loginRequest
|
||||
if err := ctx.ShouldBindJSON(&req); nil != err {
|
||||
ctx.Render(400, render.String{Format: err.Error()})
|
||||
return
|
||||
}
|
||||
if req.Username != config.GetServerConfig().Auth.Username ||
|
||||
req.Password != config.GetServerConfig().Auth.Password {
|
||||
ctx.Render(400, render.String{Format: "incorrect username or password"})
|
||||
return
|
||||
}
|
||||
claims := jwt.StandardClaims{
|
||||
Subject: req.Username,
|
||||
IssuedAt: time.Now().Unix(),
|
||||
ExpiresAt: time.Now().Add(config.GetAuthConfig().TokenExpiration).Unix(),
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
signedToken, err := token.SignedString([]byte(config.GetAuthConfig().TokenSecret))
|
||||
if err != nil {
|
||||
ctx.Render(400, render.String{Format: err.Error()})
|
||||
return
|
||||
}
|
||||
ctx.JSON(200, loginResponse{Token: signedToken})
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gin-contrib/static"
|
||||
"github.com/gin-gonic/gin"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
"github.com/swaggo/gin-swagger/swaggerFiles"
|
||||
"github.com/xpunch/go-micro-dashboard/config"
|
||||
"github.com/xpunch/go-micro-dashboard/docs"
|
||||
"github.com/xpunch/go-micro-dashboard/handler/account"
|
||||
"github.com/xpunch/go-micro-dashboard/handler/registry"
|
||||
"github.com/xpunch/go-micro-dashboard/handler/route"
|
||||
"github.com/xpunch/go-micro-dashboard/handler/statistics"
|
||||
"go-micro.dev/v4/client"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Client client.Client
|
||||
Router *gin.Engine
|
||||
}
|
||||
|
||||
func Register(opts Options) error {
|
||||
router := opts.Router
|
||||
if cfg := config.GetServerConfig(); cfg.Env == config.EnvDev {
|
||||
docs.SwaggerInfo.Host = cfg.Swagger.Host
|
||||
docs.SwaggerInfo.BasePath = cfg.Swagger.Base
|
||||
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||
}
|
||||
router.Use(static.Serve("/", static.LocalFile(config.GetServerConfig().Web.Path, false)))
|
||||
router.NoRoute(func(c *gin.Context) {
|
||||
c.File(filepath.Join(config.GetServerConfig().Web.Path, "index.html"))
|
||||
})
|
||||
if cfg := config.GetServerConfig().CORS; cfg.Enable {
|
||||
router.Use(CorsHandler(cfg.Origin))
|
||||
}
|
||||
for _, r := range []route.Registrar{
|
||||
account.NewRouteRegistrar(),
|
||||
} {
|
||||
r.RegisterRoute(router)
|
||||
}
|
||||
authRouter := opts.Router.Use(AuthRequired())
|
||||
for _, r := range []route.Registrar{
|
||||
registry.NewRouteRegistrar(opts.Client.Options().Registry),
|
||||
statistics.NewRouteRegistrar(opts.Client.Options().Registry),
|
||||
} {
|
||||
r.RegisterRoute(authRouter)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/xpunch/go-micro-dashboard/config"
|
||||
)
|
||||
|
||||
func AuthRequired() gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
log.Println(ctx.Request)
|
||||
if ctx.Request.Method == "OPTIONS" {
|
||||
ctx.Next()
|
||||
return
|
||||
}
|
||||
tokenString := ctx.GetHeader("Authorization")
|
||||
if len(tokenString) == 0 || !strings.HasPrefix(tokenString, "Bearer ") {
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, "")
|
||||
return
|
||||
}
|
||||
tokenString = tokenString[7:]
|
||||
claims := jwt.StandardClaims{}
|
||||
token, err := jwt.ParseWithClaims(tokenString, &claims, func(t *jwt.Token) (interface{}, error) {
|
||||
return []byte(config.GetAuthConfig().TokenSecret), nil
|
||||
})
|
||||
if err != nil {
|
||||
ctx.AbortWithError(401, err)
|
||||
}
|
||||
if !token.Valid {
|
||||
ctx.AbortWithStatus(401)
|
||||
}
|
||||
ctx.Set("username", claims.Subject)
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func CorsHandler(allowOrigin string) gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
ctx.Header("Access-Control-Allow-Origin", allowOrigin)
|
||||
ctx.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||
ctx.Header("Access-Control-Allow-Methods", "POST, GET, DELETE, PUT, OPTIONS")
|
||||
ctx.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
|
||||
ctx.Header("Access-Control-Allow-Credentials", "true")
|
||||
if ctx.Request.Method == "OPTIONS" {
|
||||
ctx.AbortWithStatus(http.StatusNoContent)
|
||||
}
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package registry
|
||||
|
||||
type registryService struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
Endpoints []registryEndpoint `json:"endpoints,omitempty"`
|
||||
Nodes []registryNode `json:"nodes,omitempty"`
|
||||
}
|
||||
|
||||
type registryEndpoint struct {
|
||||
Name string `json:"name"`
|
||||
Request registryValue `json:"request"`
|
||||
Response registryValue `json:"response"`
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
}
|
||||
|
||||
type registryNode struct {
|
||||
Id string `json:"id"`
|
||||
Address string `json:"address"`
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
}
|
||||
|
||||
type registryValue struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Values []registryValue `json:"values"`
|
||||
}
|
||||
|
||||
type getServiceListResponse struct {
|
||||
Services []registryService `json:"services"`
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/xpunch/go-micro-dashboard/handler/route"
|
||||
"go-micro.dev/v4/registry"
|
||||
)
|
||||
|
||||
type service struct {
|
||||
registry registry.Registry
|
||||
}
|
||||
|
||||
func NewRouteRegistrar(registry registry.Registry) route.Registrar {
|
||||
return service{registry: registry}
|
||||
}
|
||||
|
||||
func (s service) RegisterRoute(router gin.IRoutes) {
|
||||
router.POST("/api/registry/services", s.GetServices)
|
||||
}
|
||||
|
||||
// @Tags Registry
|
||||
// @ID registry_getServices
|
||||
// @Success 200 {object} getServiceListResponse
|
||||
// @Failure 400 {object} string
|
||||
// @Failure 401 {object} string
|
||||
// @Failure 500 {object} string
|
||||
// @Router /api/registry/services [get]
|
||||
func (h *service) GetServices(ctx *gin.Context) {
|
||||
services, err := h.registry.ListServices()
|
||||
if err != nil {
|
||||
ctx.AbortWithStatusJSON(500, err)
|
||||
}
|
||||
var convertValue func(v *registry.Value) registryValue
|
||||
convertValue = func(v *registry.Value) registryValue {
|
||||
res := registryValue{
|
||||
Name: v.Name,
|
||||
Type: v.Type,
|
||||
Values: make([]registryValue, 0, len(v.Values)),
|
||||
}
|
||||
for _, vv := range v.Values {
|
||||
res.Values = append(res.Values, convertValue(vv))
|
||||
}
|
||||
return res
|
||||
}
|
||||
resp := getServiceListResponse{Services: make([]registryService, 0, len(services))}
|
||||
for _, s := range services {
|
||||
endpoints := make([]registryEndpoint, 0, len(s.Endpoints))
|
||||
for _, e := range s.Endpoints {
|
||||
endpoints = append(endpoints, registryEndpoint{
|
||||
Name: e.Name,
|
||||
Request: convertValue(e.Request),
|
||||
Response: convertValue(e.Response),
|
||||
Metadata: e.Metadata,
|
||||
})
|
||||
}
|
||||
nodes := make([]registryNode, 0, len(s.Nodes))
|
||||
for _, n := range s.Nodes {
|
||||
nodes = append(nodes, registryNode{
|
||||
Id: n.Id,
|
||||
Address: n.Address,
|
||||
Metadata: n.Metadata,
|
||||
})
|
||||
}
|
||||
resp.Services = append(resp.Services, registryService{
|
||||
Name: s.Name,
|
||||
Version: s.Version,
|
||||
Metadata: s.Metadata,
|
||||
Endpoints: endpoints,
|
||||
Nodes: nodes,
|
||||
})
|
||||
}
|
||||
ctx.JSON(200, services)
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package route
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
type Registrar interface {
|
||||
RegisterRoute(gin.IRoutes)
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package statistics
|
||||
|
||||
type getSummaryResponse struct {
|
||||
Registry registrySummary `json:"registry"`
|
||||
Services servicesSummary `json:"services"`
|
||||
}
|
||||
|
||||
type registrySummary struct {
|
||||
Type string `json:"type"`
|
||||
Addrs []string `json:"addrs"`
|
||||
}
|
||||
|
||||
type servicesSummary struct {
|
||||
Count int `json:"count"`
|
||||
NodesCount int `json:"nodes_count"`
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package statistics
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/xpunch/go-micro-dashboard/handler/route"
|
||||
"go-micro.dev/v4/registry"
|
||||
)
|
||||
|
||||
type service struct {
|
||||
registry registry.Registry
|
||||
}
|
||||
|
||||
func NewRouteRegistrar(registry registry.Registry) route.Registrar {
|
||||
return service{registry: registry}
|
||||
}
|
||||
|
||||
func (s service) RegisterRoute(router gin.IRoutes) {
|
||||
router.POST("/api/summary", s.GetSummary)
|
||||
}
|
||||
|
||||
// @Tags Statistics
|
||||
// @ID statistics_getSummary
|
||||
// @Success 200 {object} getSummaryResponse
|
||||
// @Failure 400 {object} string
|
||||
// @Failure 401 {object} string
|
||||
// @Failure 500 {object} string
|
||||
// @Router /api/summary [get]
|
||||
func (s *service) GetSummary(ctx *gin.Context) {
|
||||
services, err := s.registry.ListServices()
|
||||
if err != nil {
|
||||
ctx.AbortWithStatusJSON(500, err)
|
||||
}
|
||||
servicesByName := make(map[string]struct{})
|
||||
var servicesNodesCount int
|
||||
for _, s := range services {
|
||||
if _, ok := servicesByName[s.Name]; !ok {
|
||||
servicesByName[s.Name] = struct{}{}
|
||||
}
|
||||
servicesNodesCount += len(s.Nodes)
|
||||
}
|
||||
var resp = getSummaryResponse{
|
||||
Registry: registrySummary{
|
||||
Type: s.registry.String(),
|
||||
Addrs: s.registry.Options().Addrs,
|
||||
},
|
||||
Services: servicesSummary{
|
||||
Count: len(servicesByName),
|
||||
NodesCount: servicesNodesCount,
|
||||
},
|
||||
}
|
||||
ctx.JSON(200, resp)
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
mhttp "github.com/asim/go-micro/plugins/server/http/v4"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/xpunch/go-micro-dashboard/config"
|
||||
"github.com/xpunch/go-micro-dashboard/handler"
|
||||
"go-micro.dev/v4"
|
||||
"go-micro.dev/v4/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
Name = "go.micro.dashboard"
|
||||
Version = "1.0.0"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := config.Load(); err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
srv := micro.NewService(micro.Server(mhttp.NewServer()))
|
||||
opts := []micro.Option{
|
||||
micro.Name(Name),
|
||||
micro.Address(config.GetServerConfig().Address),
|
||||
micro.Version(Version),
|
||||
}
|
||||
srv.Init(opts...)
|
||||
if config.GetServerConfig().Env == config.EnvProd {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
router := gin.New()
|
||||
router.Use(gin.Recovery(), gin.Logger())
|
||||
if err := handler.Register(handler.Options{Client: srv.Client(), Router: router}); err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
if err := micro.RegisterHandler(srv.Server(), router); err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
if err := srv.Run(); err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "github.com/asim/go-micro/plugins/client/grpc/v4"
|
||||
_ "github.com/asim/go-micro/plugins/registry/etcd/v4"
|
||||
_ "github.com/asim/go-micro/plugins/registry/kubernetes/v4"
|
||||
)
|
@ -0,0 +1,7 @@
|
||||
package main
|
||||
|
||||
// @title Go Micro Dashboard API
|
||||
// @version 1.0.0
|
||||
// @description This is the go micro dashboard api server.
|
||||
// @termsOfService http://swagger.io/terms/
|
||||
// @BasePath /
|
@ -0,0 +1,22 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
|
||||
"go-micro.dev/v4/logger"
|
||||
)
|
||||
|
||||
// GoSafe will run func in goroutine safely, avoid crash from unexpected panic
|
||||
func GoSafe(fn func()) {
|
||||
if fn == nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
logger.Errorf("[panic]%v\n%s", e, debug.Stack())
|
||||
}
|
||||
}()
|
||||
fn()
|
||||
}()
|
||||
}
|
Loading…
Reference in New Issue