├── web
├── .env
├── .env.production
├── src
│ ├── vite-env.d.ts
│ ├── assets
│ │ └── logo.png
│ ├── constants
│ │ ├── style.ts
│ │ └── env.ts
│ ├── shims-vue.d.ts
│ ├── views
│ │ ├── Login.tsx
│ │ ├── Register.tsx
│ │ ├── configurations
│ │ │ ├── BlockIPs.tsx
│ │ │ ├── SignedKeys.tsx
│ │ │ ├── Emails.tsx
│ │ │ ├── MockTime.tsx
│ │ │ ├── Configs.tsx
│ │ │ ├── RequestConcurrencies.tsx
│ │ │ ├── HTTPServerInterceptors.tsx
│ │ │ └── SessionInterceptors.tsx
│ │ └── detectors
│ │ │ ├── Ping.tsx
│ │ │ └── TCP.tsx
│ ├── helpers
│ │ └── http-error.ts
│ ├── main.ts
│ ├── main.css
│ ├── states
│ │ └── admin.ts
│ ├── components
│ │ ├── ExFormInterface.tsx
│ │ ├── ExLoading.tsx
│ │ ├── ExDetectorTable.tsx
│ │ ├── ExFluxDetail.tsx
│ │ └── ExUserSelect.tsx
│ ├── routes
│ │ ├── router.ts
│ │ └── index.ts
│ ├── storages
│ │ └── local.ts
│ └── Root.tsx
├── public
│ └── favicon.ico
├── .eslintignore
├── .eslintrc.js
├── index.html
├── tsconfig.json
├── vite.config.ts
├── package.json
└── README.md
├── .dockerignore
├── hooks
└── pre-commit
├── asset
├── dist
│ ├── assets
│ │ ├── Detector-2eea4969.css
│ │ ├── DNSResult-469dd845.css
│ │ ├── ExConfigEditorList-d9dcbf54.css
│ │ ├── HTTPResult-1372e26e.css
│ │ ├── PingResult-bb2df28a.css
│ │ ├── TCPResult-d1b9c3a1.css
│ │ ├── DatabaseResult-91ba9c30.css
│ │ ├── Users-987e0845.css
│ │ ├── ExDetectorResultTable-7e6b265e.css
│ │ ├── Requests-b25ed006.css
│ │ ├── Database-054a89cc.css
│ │ ├── ExLoading-8e54e9e5.css
│ │ ├── ExTable-08f645c7.css
│ │ ├── ExFluxDetail-5d574f48.css
│ │ ├── ExLoginRegister-85c3f6b4.css
│ │ ├── Login-2fc5b58e.js
│ │ ├── Register-55c86676.js
│ │ ├── ExFormInterface-d830d527.js
│ │ ├── ExLoading-572e76eb.js
│ │ ├── index-fc3ae4d7.css
│ │ ├── BlockIPs-483ba5e4.js
│ │ ├── SignedKeys-f01bc2d6.js
│ │ ├── Emails-a0c0a272.js
│ │ ├── MockTime-72101ce2.js
│ │ ├── Ping-898e4802.js
│ │ ├── TCP-b7c9b1e1.js
│ │ ├── Configs-499aae23.js
│ │ ├── ExConfigEditorList-10d35f0e.js
│ │ ├── DNS-16d14de9.js
│ │ ├── ExConfigTable-74c02379.js
│ │ ├── RequestConcurrencies-160bdd88.js
│ │ ├── PingResult-e88b1dd9.js
│ │ ├── TCPResult-cc687576.js
│ │ ├── DatabaseResult-963f24cb.js
│ │ ├── Logins-188ecd5f.js
│ │ ├── DNSResult-8c28b47b.js
│ │ ├── HTTPServerInterceptors-873741cf.js
│ │ ├── SessionInterceptors-01600b82.js
│ │ ├── configs-5a9029c7.js
│ │ ├── Caches-62ddc5f6.js
│ │ ├── RouterMocks-02b867d7.js
│ │ ├── HTTPResult-a7bd9ce2.js
│ │ ├── HTTP-5b90fc3c.js
│ │ ├── Profile-b96e6843.js
│ │ ├── Home-0972bc05.js
│ │ ├── Database-729075d0.js
│ │ ├── HTTPErrors-446f87b9.js
│ │ ├── ExDetectorResultTable-2191f62a.js
│ │ └── ExLoginRegister-baddf736.js
│ ├── favicon.ico
│ └── index.html
├── http_server_interceptor.js
└── asset.go
├── images
├── profile.jpg
├── dns-setting.jpg
├── tcp-setting.jpg
├── http-setting.jpg
├── main-setting.jpg
├── ping-setting.jpg
├── database-setting.jpg
├── dns-detect-result.jpg
├── http-detect-result.jpg
├── ping-detect-result.jpg
├── tcp-detect-result.jpg
├── database-detect-result.jpg
├── dns-detect-result-detail.jpg
├── http-detect-result-detail.jpg
├── ping-detect-result-detail.jpg
├── tcp-detect-result-detail.jpg
└── database-detect-result-detail.jpg
├── entrypoint.sh
├── config
├── production.yml
├── test.yml
├── dev.yml
└── default.yml
├── controller
├── controller_doc.go
├── user_doc.go
├── admin.go
├── asset.go
└── asset_test.go
├── QUESTIONS.md
├── download-swagger.sh
├── ent
├── runtime
│ └── runtime.go
├── marshal.go
├── predicate
│ └── predicate.go
└── enttest
│ └── enttest.go
├── template
├── additional.tmpl
└── marshal.tmpl
├── router_mock
├── router_mock_test.go
└── router_mock.go
├── .golangci.yml
├── middleware
├── middleware.go
├── stats_test.go
├── ip_blocker.go
├── url_prefix.go
├── ip_blocker_test.go
├── entry_test.go
├── interceptor.go
├── error_test.go
├── router_mocker.go
└── entry.go
├── schema
├── tcpdetector.go
├── pingdetector.go
├── dnsdetector.go
├── httpdetector.go
├── tcpdetectorresult.go
├── dnsdetectorresult.go
├── databasedetector.go
├── databasedetectorresult.go
├── time.go
├── pingdetectorresult.go
├── userlogin.go
├── httpdetectorresult.go
└── status.go
├── location
├── urls.go
├── location_test.go
└── location.go
├── validate
├── file.go
├── flux.go
├── configuration.go
├── detector.go
├── user.go
└── common.go
├── .goreleaser.yml
├── service
├── service.go
├── ip_blocker_test.go
├── ip_blocker.go
├── performance_test.go
├── application_test.go
├── captcha_test.go
└── application.go
├── .gitignore
├── util
├── stack_test.go
├── env_test.go
├── env.go
├── number.go
├── map_test.go
├── number_test.go
├── map.go
├── context_test.go
├── stack.go
├── string_test.go
└── date_test.go
├── .github
└── workflows
│ └── build.yml
├── helper
├── snappy.go
├── redis_test.go
├── ent_test.go
├── influxdb_test.go
└── snappy_test.go
├── Dockerfile
├── router_concurrency
└── router_concurrency_test.go
├── cs
├── cs.go
└── action.go
├── detector
├── detector_test.go
└── database_test.go
├── router
└── router.go
├── Makefile
├── profiler
└── profiler.go
├── session
├── session_test.go
└── session.go
├── log
└── log_test.go
├── .air.toml
├── interceptor
└── session.go
└── request
└── http_instance.go
/web/.env:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/.env.production:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | web/node_modules
--------------------------------------------------------------------------------
/hooks/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | make lint && make lint-web
--------------------------------------------------------------------------------
/web/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/asset/dist/assets/Detector-2eea4969.css:
--------------------------------------------------------------------------------
1 | .a1r7hurf{width:100%;margin-top:20px}
2 |
--------------------------------------------------------------------------------
/asset/dist/assets/DNSResult-469dd845.css:
--------------------------------------------------------------------------------
1 | .p1iljsez{max-width:800px;white-space:nowrap}
2 |
--------------------------------------------------------------------------------
/asset/dist/assets/ExConfigEditorList-d9dcbf54.css:
--------------------------------------------------------------------------------
1 | .apayaz1{width:100%;margin-top:20px}
2 |
--------------------------------------------------------------------------------
/asset/dist/assets/HTTPResult-1372e26e.css:
--------------------------------------------------------------------------------
1 | .p1777rsr{max-width:980px;white-space:nowrap}
2 |
--------------------------------------------------------------------------------
/asset/dist/assets/PingResult-bb2df28a.css:
--------------------------------------------------------------------------------
1 | .p15nhdzn{max-width:800px;white-space:nowrap}
2 |
--------------------------------------------------------------------------------
/asset/dist/assets/TCPResult-d1b9c3a1.css:
--------------------------------------------------------------------------------
1 | .p1m3677v{max-width:800px;white-space:nowrap}
2 |
--------------------------------------------------------------------------------
/images/profile.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/profile.jpg
--------------------------------------------------------------------------------
/asset/dist/assets/DatabaseResult-91ba9c30.css:
--------------------------------------------------------------------------------
1 | .pux6hxu{max-width:800px;white-space:nowrap}
2 |
--------------------------------------------------------------------------------
/asset/dist/assets/Users-987e0845.css:
--------------------------------------------------------------------------------
1 | .ulmcati{margin:0;padding:0;list-style-position:insied}
2 |
--------------------------------------------------------------------------------
/asset/dist/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/asset/dist/favicon.ico
--------------------------------------------------------------------------------
/images/dns-setting.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/dns-setting.jpg
--------------------------------------------------------------------------------
/images/tcp-setting.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/tcp-setting.jpg
--------------------------------------------------------------------------------
/web/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/web/public/favicon.ico
--------------------------------------------------------------------------------
/asset/dist/assets/ExDetectorResultTable-7e6b265e.css:
--------------------------------------------------------------------------------
1 | .s97967t{padding:10px 15px;cursor:pointer}
2 |
--------------------------------------------------------------------------------
/asset/dist/assets/Requests-b25ed006.css:
--------------------------------------------------------------------------------
1 | .u1nflspg{margin:0;padding:0;list-style-position:inside}
2 |
--------------------------------------------------------------------------------
/images/http-setting.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/http-setting.jpg
--------------------------------------------------------------------------------
/images/main-setting.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/main-setting.jpg
--------------------------------------------------------------------------------
/images/ping-setting.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/ping-setting.jpg
--------------------------------------------------------------------------------
/web/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/web/src/assets/logo.png
--------------------------------------------------------------------------------
/asset/dist/assets/Database-054a89cc.css:
--------------------------------------------------------------------------------
1 | .cg46r4v{margin:0;padding:0;list-style:inside;line-height:2.5em}
2 |
--------------------------------------------------------------------------------
/images/database-setting.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/database-setting.jpg
--------------------------------------------------------------------------------
/images/dns-detect-result.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/dns-detect-result.jpg
--------------------------------------------------------------------------------
/images/http-detect-result.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/http-detect-result.jpg
--------------------------------------------------------------------------------
/images/ping-detect-result.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/ping-detect-result.jpg
--------------------------------------------------------------------------------
/images/tcp-detect-result.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/tcp-detect-result.jpg
--------------------------------------------------------------------------------
/asset/dist/assets/ExLoading-8e54e9e5.css:
--------------------------------------------------------------------------------
1 | .s5jcqi3{float:left;margin-right:10px}.l53tqur{margin:auto;width:200px}
2 |
--------------------------------------------------------------------------------
/images/database-detect-result.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/database-detect-result.jpg
--------------------------------------------------------------------------------
/asset/dist/assets/ExTable-08f645c7.css:
--------------------------------------------------------------------------------
1 | .pyx5alf{margin-top:10px;float:right}.u13tuy86{padding:0;margin:0;list-style:inside}
2 |
--------------------------------------------------------------------------------
/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | if [ "${1:0:1}" = '-' ]; then
5 | set -- cybertect "$@"
6 | fi
7 |
8 | exec "$@"
--------------------------------------------------------------------------------
/images/dns-detect-result-detail.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/dns-detect-result-detail.jpg
--------------------------------------------------------------------------------
/images/http-detect-result-detail.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/http-detect-result-detail.jpg
--------------------------------------------------------------------------------
/images/ping-detect-result-detail.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/ping-detect-result-detail.jpg
--------------------------------------------------------------------------------
/images/tcp-detect-result-detail.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/tcp-detect-result-detail.jpg
--------------------------------------------------------------------------------
/config/production.yml:
--------------------------------------------------------------------------------
1 | # 仅需要添加与default不相同的配置
2 | # 生产环境配置
3 |
4 | redis:
5 | uri: redis://miniredis/?slow=200ms&maxProcessing=1000
--------------------------------------------------------------------------------
/images/database-detect-result-detail.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicanso/cyber-tect/HEAD/images/database-detect-result-detail.jpg
--------------------------------------------------------------------------------
/web/src/constants/style.ts:
--------------------------------------------------------------------------------
1 | export const mainHeaderHeight = 60;
2 | export const padding = 10;
3 | export const mainNavigationWidth = 200;
4 |
--------------------------------------------------------------------------------
/asset/dist/assets/ExFluxDetail-5d574f48.css:
--------------------------------------------------------------------------------
1 | .i18la4si{margin:0;padding:0 20px;max-width:400px;word-wrap:break-word;word-break:break-all;white-space:normal;list-style-position:insied}
2 |
--------------------------------------------------------------------------------
/asset/dist/assets/ExLoginRegister-85c3f6b4.css:
--------------------------------------------------------------------------------
1 | .c1eq9ar{max-width:640px;margin:120px auto}.c1ppv7dc{text-align:center;height:40px;cursor:pointer}.sd1d3ho{width:100%}.cvblerb{height:100%}
2 |
--------------------------------------------------------------------------------
/config/test.yml:
--------------------------------------------------------------------------------
1 | # 仅需要添加与default不相同的配置
2 | # 测试环境配置
3 | redis:
4 | uri: redis://miniredis/?slow=200ms&maxProcessing=1000
5 |
6 | database:
7 | uri: postgres://postgres:postgres@127.0.0.1:5432/cybertect
8 |
--------------------------------------------------------------------------------
/controller/controller_doc.go:
--------------------------------------------------------------------------------
1 | // +build swagger
2 |
3 | package controller
4 |
5 | // 204无响应数据
6 | // swagger:response apiNoContentResponse
7 | type apiNoContentResponse struct {
8 | Body string
9 | }
10 |
--------------------------------------------------------------------------------
/web/src/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.vue" {
2 | import { DefineComponent } from "vue";
3 | // eslint-disable-next-line
4 | const component: DefineComponent<{}, {}, any>;
5 | export default component;
6 | }
7 |
--------------------------------------------------------------------------------
/web/.eslintignore:
--------------------------------------------------------------------------------
1 | # don't ever lint node_modules
2 | node_modules
3 | # don't lint build output (make sure it's set to your correct build folder name)
4 | dist
5 | # don't lint nyc coverage output
6 | coverage
7 |
8 | .eslintrc.js
9 |
--------------------------------------------------------------------------------
/QUESTIONS.md:
--------------------------------------------------------------------------------
1 | # 问题汇总
2 |
3 | ## Mini Redis
4 |
5 | 用户session等信息会使用redis保存,由于项目主要用于应用监控,登录用户数较少,为了减少对其它服务的依赖,因此使用[miniredis](https://github.com/alicebob/miniredis)替换真正的redis服务,miniredis是基于内存的,因此如果程序重启则sesion信息会清除。
6 |
7 | ## 超级用户
8 |
9 | 第一个注册的用户自动创建为超级用户。
--------------------------------------------------------------------------------
/config/dev.yml:
--------------------------------------------------------------------------------
1 | # 仅需要添加与default不相同的配置
2 | # 本地开发环境
3 | redis:
4 | uri: redis://127.0.0.1:6379/?slow=200ms&maxProcessing=1000
5 |
6 | database:
7 | uri: postgres://vicanso:A123456@127.0.0.1:5432/cybertect?maxIdleConns=5&maxIdleTime=30m&maxOpenConns=100
--------------------------------------------------------------------------------
/web/src/constants/env.ts:
--------------------------------------------------------------------------------
1 | const env = import.meta.env;
2 |
3 | // isDevelopment 是否开发环境
4 | export function isDevelopment(): boolean {
5 | return env.DEV;
6 | }
7 |
8 | // isProduction 是否生产环境
9 | export function isProduction(): boolean {
10 | return env.PROD;
11 | }
12 |
--------------------------------------------------------------------------------
/web/src/views/Login.tsx:
--------------------------------------------------------------------------------
1 | import { defineComponent } from "vue";
2 |
3 | import ExLoginRegister from "../components/ExLoginRegister";
4 |
5 | export default defineComponent({
6 | name: "LoginView",
7 | render() {
8 | return ;
9 | },
10 | });
11 |
--------------------------------------------------------------------------------
/asset/dist/assets/Login-2fc5b58e.js:
--------------------------------------------------------------------------------
1 | import{E as e}from"./ExLoginRegister-baddf736.js";import{d as o,N as r}from"./ui-32540f7e.js";import"./index-aeeb7070.js";import"./common-a7d1cc73.js";import"./naive-094f875f.js";const p=o({name:"LoginView",render(){return r(e,null,null)}});export{p as default};
2 |
--------------------------------------------------------------------------------
/web/src/views/Register.tsx:
--------------------------------------------------------------------------------
1 | import { defineComponent } from "vue";
2 | import ExLoginRegister from "../components/ExLoginRegister";
3 |
4 | export default defineComponent({
5 | name: "RegisterView",
6 | render() {
7 | return ;
8 | },
9 | });
10 |
--------------------------------------------------------------------------------
/asset/dist/assets/Register-55c86676.js:
--------------------------------------------------------------------------------
1 | import{E as e}from"./ExLoginRegister-baddf736.js";import{d as r,N as t}from"./ui-32540f7e.js";import"./index-aeeb7070.js";import"./common-a7d1cc73.js";import"./naive-094f875f.js";const p=r({name:"RegisterView",render(){return t(e,{type:"register"},null)}});export{p as default};
2 |
--------------------------------------------------------------------------------
/download-swagger.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | download_url=$(curl -s https://api.github.com/repos/go-swagger/go-swagger/releases/latest | \
4 | jq -r '.assets[] | select(.name | contains("'"$(uname | tr '[:upper:]' '[:lower:]')"'_amd64")) | .browser_download_url')
5 | curl -o /usr/local/bin/swagger -L'#' "$download_url"
6 | chmod +x /usr/local/bin/swagger
7 |
--------------------------------------------------------------------------------
/asset/dist/assets/ExFormInterface-d830d527.js:
--------------------------------------------------------------------------------
1 | let u=function(t){return t.Select="select",t.MultiSelect="multiSelect",t.DateTime="dateTime",t.DateRange="dateRange",t.InputNumber="inputNumber",t.InputNumberGroup="inputGroup",t.InputDuration="inputDuration",t.DynamicInput="dynamicInput",t.TextArea="textArea",t.Blank="blank",t.MultiUserSelect="multiUserSelect",t}({});export{u as F};
2 |
--------------------------------------------------------------------------------
/web/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: 'vue-eslint-parser',
4 | plugins: [
5 | '@typescript-eslint',
6 | ],
7 | parserOptions: {
8 | parser: '@typescript-eslint/parser',
9 | },
10 | extends: [
11 | 'eslint:recommended',
12 | 'plugin:@typescript-eslint/recommended',
13 | 'plugin:vue/vue3-recommended',
14 | ]
15 | };
--------------------------------------------------------------------------------
/asset/dist/assets/ExLoading-572e76eb.js:
--------------------------------------------------------------------------------
1 | import{d as t,N as s,c as o}from"./ui-32540f7e.js";import{s as n}from"./naive-094f875f.js";const a="s5jcqi3",r="l53tqur",u=t({name:"ExLoading",props:{style:{type:Object,default:()=>({marginTop:"60px"})}},render(){const{style:e}=this.$props;return s("div",{style:e,class:r},[s(n,{size:"small",class:a},null),o("正在加载中,请稍候...")])}});export{u as E};
2 |
--------------------------------------------------------------------------------
/ent/runtime/runtime.go:
--------------------------------------------------------------------------------
1 | // Code generated by ent, DO NOT EDIT.
2 |
3 | package runtime
4 |
5 | // The schema-stitching logic is generated in github.com/vicanso/cybertect/ent/runtime.go
6 |
7 | const (
8 | Version = "v0.12.3" // Version of ent codegen.
9 | Sum = "h1:N5lO2EOrHpCH5HYfiMOCHYbo+oh5M8GjT0/cx5x6xkk=" // Sum of ent codegen.
10 | )
11 |
--------------------------------------------------------------------------------
/template/additional.tmpl:
--------------------------------------------------------------------------------
1 | {{ define "model/fields/additional" }}
2 | {{/* 添加额外字段 */}}
3 | {{- range $i, $f := $.Fields }}
4 | {{- if eq $f.Name "status" }}
5 | // 状态描述
6 | StatusDesc string `json:"statusDesc,omitempty"`
7 | {{- end }}
8 |
9 | {{- if eq $f.Name "task" }}
10 | // 状态描述
11 | TaskName string `json:"taskName,omitempty"`
12 | {{- end }}
13 |
14 | {{- end }}
15 | {{ end }}
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | cybertect
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "moduleResolution": "node",
6 | "strict": true,
7 | "jsx": "preserve",
8 | "sourceMap": true,
9 | "resolveJsonModule": true,
10 | "esModuleInterop": true,
11 | "lib": ["esnext", "dom"]
12 | },
13 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
14 | }
15 |
--------------------------------------------------------------------------------
/router_mock/router_mock_test.go:
--------------------------------------------------------------------------------
1 | package routermock
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | )
8 |
9 | func TestRouterConfig(t *testing.T) {
10 | assert := assert.New(t)
11 | Update([]string{
12 | `{
13 | "route": "/",
14 | "method": "GET",
15 | "status": 400
16 | }`,
17 | })
18 | routeConfig := Get("GET", "/")
19 | assert.Equal(400, routeConfig.Status)
20 | }
21 |
--------------------------------------------------------------------------------
/.golangci.yml:
--------------------------------------------------------------------------------
1 | version: "2"
2 | run:
3 | build-tags:
4 | - codeanalysis
5 | linters:
6 | exclusions:
7 | generated: lax
8 | presets:
9 | - comments
10 | - common-false-positives
11 | - legacy
12 | - std-error-handling
13 | paths:
14 | - third_party$
15 | - builtin$
16 | - examples$
17 | - web$
18 | formatters:
19 | exclusions:
20 | generated: lax
21 | paths:
22 | - third_party$
23 | - builtin$
24 | - examples$
25 |
--------------------------------------------------------------------------------
/web/src/helpers/http-error.ts:
--------------------------------------------------------------------------------
1 | class HTTPError extends Error {
2 | // http状态码
3 | status: number;
4 | // 是否异常
5 | exception?: boolean;
6 | // 出错信息
7 | message: string;
8 | // 出错分类
9 | category?: string;
10 | // 出错代码,如果某个出错需要单独处理,可定义唯一的出错码
11 | code?: string;
12 | // 子错误
13 | errs?: HTTPError[];
14 | // 其它一些额外的信息
15 | extra?: Record;
16 | constructor(status: number, message: string) {
17 | super(message);
18 | this.status = status;
19 | this.message = message;
20 | }
21 | }
22 |
23 | export default HTTPError;
24 |
--------------------------------------------------------------------------------
/web/src/main.ts:
--------------------------------------------------------------------------------
1 | import { create } from "naive-ui";
2 | import { createApp } from "vue";
3 | import Root from "./Root";
4 | import router from "./routes/router";
5 | import { settingStorage } from "./storages/local";
6 |
7 | const naive = create();
8 | const app = createApp(Root);
9 | app.use(router).use(naive);
10 |
11 | const loadSetting = async () => {
12 | try {
13 | await settingStorage.load();
14 | } catch (err) {
15 | console.error(err);
16 | }
17 | };
18 |
19 | router
20 | .isReady()
21 | .then(loadSetting)
22 | .then(() => app.mount("#app"))
23 | .catch(console.error);
24 |
--------------------------------------------------------------------------------
/asset/dist/assets/index-fc3ae4d7.css:
--------------------------------------------------------------------------------
1 | .uuuzaf2{margin-right:5px}.h1xvxe3a{height:60px;line-height:60px;padding:0 30px}.lkvn6gz{float:left;cursor:pointer}.l18fhfgm{margin:50px auto;text-align:center}body{margin:0;font-size:14px;font-family:v-sans,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol;line-height:1.6;-webkit-text-size-adjust:100%}.clearfix:after{visibility:hidden;display:block;font-size:0;content:" ";clear:both;height:0}.widthFull{width:100%!important}.tac{text-align:center}.mright5{margin-right:5px}.mbottom15{margin-bottom:15px}.luwg8pj{top:60px!important}.c1llctjr{padding:20px}
2 |
--------------------------------------------------------------------------------
/template/marshal.tmpl:
--------------------------------------------------------------------------------
1 | {{/* gotype: entgo.io/ent/entc/gen.Graph */}}
2 |
3 | {{ define "marshal" }}
4 |
5 | {{ $pkg := base $.Config.Package }}
6 | {{ template "header" $ }}
7 |
8 | import "encoding/json"
9 |
10 | {{ range $n := $.Nodes }}
11 |
12 | {{- range $i, $f := $n.Fields }}
13 | {{/* 如果有status字段,则调整MarshalJSON */}}
14 | {{- if eq $f.Name "status" }}
15 | type Marshal{{ $n.Name }} {{ $n.Name }}
16 | func (t *{{ $n.Name }}) MarshalJSON() ([]byte, error) {
17 | tmp := (*Marshal{{ $n.Name }})(t)
18 | tmp.StatusDesc = tmp.Status.String()
19 | return json.Marshal(tmp)
20 | }
21 | {{- end }}
22 |
23 | {{- end }}
24 |
25 | {{ end }}
26 |
27 | {{ end }}
28 |
--------------------------------------------------------------------------------
/web/src/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-size: 14px;
4 | font-family: v-sans, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
5 | sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
6 | line-height: 1.6;
7 | -webkit-text-size-adjust: 100%;
8 | }
9 |
10 | .clearfix:after {
11 | visibility: hidden;
12 | display: block;
13 | font-size: 0;
14 | content: " ";
15 | clear: both;
16 | height: 0;
17 | }
18 |
19 | .widthFull {
20 | width: 100% !important;
21 | }
22 |
23 | .tac {
24 | text-align: center;
25 | }
26 |
27 | .mright5 {
28 | margin-right: 5px;
29 | }
30 | .mbottom15 {
31 | margin-bottom: 15px;
32 | }
--------------------------------------------------------------------------------
/web/src/states/admin.ts:
--------------------------------------------------------------------------------
1 | import request from "../helpers/request";
2 |
3 | import { ADMINS_CACHE_ID } from "../constants/url";
4 |
5 | interface CacheData {
6 | data: string;
7 | }
8 |
9 | // adminFindCacheByKey 查询缓存
10 | export async function adminFindCacheByKey(key: string): Promise {
11 | const url = ADMINS_CACHE_ID.replace(":key", key);
12 | const { data } = await request.get(url);
13 | return data;
14 | }
15 |
16 | // adminCleanCacheByKey 清除缓存
17 | export async function adminCleanCacheByKey(key: string): Promise {
18 | const url = ADMINS_CACHE_ID.replace(":key", key);
19 | await request.delete(url);
20 | return;
21 | }
22 |
--------------------------------------------------------------------------------
/middleware/middleware.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package middleware
16 |
--------------------------------------------------------------------------------
/schema/tcpdetector.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "entgo.io/ent"
5 | "entgo.io/ent/schema/field"
6 | )
7 |
8 | // TCPDetector holds the schema definition for the TCPDetector entity.
9 | type TCPDetector struct {
10 | ent.Schema
11 | }
12 |
13 | // Fields of the TCPDetector.
14 | func (TCPDetector) Fields() []ent.Field {
15 | return []ent.Field{
16 | field.Strings("addrs").
17 | Comment("检测地址列表"),
18 | }
19 | }
20 |
21 | // Mixin of the TCPDetector
22 | func (TCPDetector) Mixin() []ent.Mixin {
23 | return []ent.Mixin{
24 | TimeMixin{},
25 | StatusMixin{},
26 | DetectorMixin{},
27 | }
28 | }
29 |
30 | // Edges of the TCPDetector.
31 | func (TCPDetector) Edges() []ent.Edge {
32 | return nil
33 | }
34 |
--------------------------------------------------------------------------------
/asset/dist/assets/BlockIPs-483ba5e4.js:
--------------------------------------------------------------------------------
1 | import{E as t}from"./ExConfigEditorList-10d35f0e.js";import{C as r}from"./configs-5a9029c7.js";import{d as e,N as i}from"./ui-32540f7e.js";import"./ExConfigEditor-da704c67.js";import"./index-aeeb7070.js";import"./common-a7d1cc73.js";import"./naive-094f875f.js";import"./ExForm-cdb0faae.js";import"./detector-090ed643.js";import"./ExFormInterface-d830d527.js";import"./ExLoading-572e76eb.js";import"./ExConfigTable-74c02379.js";import"./ExTable-dcbd254b.js";const k=e({name:"BlockIPConfigs",render(){const o=[{name:"IP地址:",key:"data",placeholder:"请输入IP地址或网段"}];return i(t,{listTitle:"黑名单IP配置",editorTitle:"添加/更新黑名单配置",editorDescription:"用于拦截访问IP",category:r.BlockIP,extraFormItems:o},null)}});export{k as default};
2 |
--------------------------------------------------------------------------------
/schema/pingdetector.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "entgo.io/ent"
5 | "entgo.io/ent/schema/field"
6 | )
7 |
8 | // PingDetector holds the schema definition for the PingDetector entity.
9 | type PingDetector struct {
10 | ent.Schema
11 | }
12 |
13 | // Fields of the PingDetector.
14 | func (PingDetector) Fields() []ent.Field {
15 | return []ent.Field{
16 | field.Strings("ips").
17 | Comment("检测IP列表"),
18 | }
19 | }
20 |
21 | // Mixin of the TCPDetector
22 | func (PingDetector) Mixin() []ent.Mixin {
23 | return []ent.Mixin{
24 | TimeMixin{},
25 | StatusMixin{},
26 | DetectorMixin{},
27 | }
28 | }
29 |
30 | // Edges of the PingDetector.
31 | func (PingDetector) Edges() []ent.Edge {
32 | return nil
33 | }
34 |
--------------------------------------------------------------------------------
/asset/dist/assets/SignedKeys-f01bc2d6.js:
--------------------------------------------------------------------------------
1 | import{E as e}from"./ExConfigEditorList-10d35f0e.js";import{C as o}from"./configs-5a9029c7.js";import{d as r,N as i}from"./ui-32540f7e.js";import"./ExConfigEditor-da704c67.js";import"./index-aeeb7070.js";import"./common-a7d1cc73.js";import"./naive-094f875f.js";import"./ExForm-cdb0faae.js";import"./detector-090ed643.js";import"./ExFormInterface-d830d527.js";import"./ExLoading-572e76eb.js";import"./ExConfigTable-74c02379.js";import"./ExTable-dcbd254b.js";const u=r({name:"SignedKeys",render(){const t=[{name:"密钥:",key:"data",placeholder:"请输入签名使用的密钥,多个密钥以,分隔"}];return i(e,{listTitle:"密钥配置",editorTitle:"添加/更新密钥配置",editorDescription:"配置用于生成session加密的密钥",category:o.SignedKey,extraFormItems:t},null)}});export{u as default};
2 |
--------------------------------------------------------------------------------
/location/urls.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package location
16 |
17 | // 相关的URL
18 | const (
19 | locationURL = "/ip-locations/json/:ip"
20 | )
21 |
--------------------------------------------------------------------------------
/web/src/views/configurations/BlockIPs.tsx:
--------------------------------------------------------------------------------
1 | import { defineComponent } from "vue";
2 | import ExConfigEditorList from "../../components/ExConfigEditorList";
3 | import { ConfigCategory } from "../../states/configs";
4 |
5 | export default defineComponent({
6 | name: "BlockIPConfigs",
7 | render() {
8 | const extraFormItems = [
9 | {
10 | name: "IP地址:",
11 | key: "data",
12 | placeholder: "请输入IP地址或网段",
13 | },
14 | ];
15 | return (
16 |
23 | );
24 | },
25 | });
26 |
--------------------------------------------------------------------------------
/web/src/views/configurations/SignedKeys.tsx:
--------------------------------------------------------------------------------
1 | import { defineComponent } from "vue";
2 | import ExConfigEditorList from "../../components/ExConfigEditorList";
3 | import { ConfigCategory } from "../../states/configs";
4 |
5 | export default defineComponent({
6 | name: "SignedKeys",
7 | render() {
8 | const extraFormItems = [
9 | {
10 | name: "密钥:",
11 | key: "data",
12 | placeholder: "请输入签名使用的密钥,多个密钥以,分隔",
13 | },
14 | ];
15 | return (
16 |
23 | );
24 | },
25 | });
26 |
--------------------------------------------------------------------------------
/asset/dist/assets/Emails-a0c0a272.js:
--------------------------------------------------------------------------------
1 | import{E as o}from"./ExConfigEditorList-10d35f0e.js";import{C as r}from"./configs-5a9029c7.js";import{F as e}from"./ExFormInterface-d830d527.js";import{d as i,N as m}from"./ui-32540f7e.js";import"./ExConfigEditor-da704c67.js";import"./index-aeeb7070.js";import"./common-a7d1cc73.js";import"./naive-094f875f.js";import"./ExForm-cdb0faae.js";import"./detector-090ed643.js";import"./ExLoading-572e76eb.js";import"./ExConfigTable-74c02379.js";import"./ExTable-dcbd254b.js";const F=i({name:"EmailConfigs",render(){const t=[{name:"邮箱地址:",key:"data",type:e.TextArea,span:24,placeholder:"请输入邮箱地址,多个地址以,分隔"}];return m(o,{listTitle:"邮箱地址配置",editorTitle:"添加/更新邮箱地址配置",editorDescription:"配置各类邮件接收列表",category:r.Email,extraFormItems:t},null)}});export{F as default};
2 |
--------------------------------------------------------------------------------
/asset/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | cybertect
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/validate/file.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package validate
16 |
17 | func init() {
18 | buckets := []string{
19 | "files",
20 | }
21 | Add("xFileBucket", newIsInString(buckets))
22 | }
23 |
--------------------------------------------------------------------------------
/web/src/components/ExFormInterface.tsx:
--------------------------------------------------------------------------------
1 | export interface FormItem {
2 | name: string;
3 | key: string;
4 | type?: string;
5 | placeholder?: string;
6 | span?: number;
7 | defaultValue?: unknown;
8 | disabled?: boolean;
9 | // TODO 确认是否有其它方式表示
10 | // eslint-disable-next-line
11 | options?: any[];
12 | suffix?: string;
13 | min?: number;
14 | max?: number;
15 | }
16 |
17 | export enum FormItemTypes {
18 | Select = "select",
19 | MultiSelect = "multiSelect",
20 | DateTime = "dateTime",
21 | DateRange = "dateRange",
22 | InputNumber = "inputNumber",
23 | InputNumberGroup = "inputGroup",
24 | InputDuration = "inputDuration",
25 | DynamicInput = "dynamicInput",
26 | TextArea = "textArea",
27 | Blank = "blank",
28 | MultiUserSelect = "multiUserSelect",
29 | }
30 |
--------------------------------------------------------------------------------
/.goreleaser.yml:
--------------------------------------------------------------------------------
1 | # This is an example .goreleaser.yml file with some sane defaults.
2 | # Make sure to check the documentation at http://goreleaser.com
3 | before:
4 | hooks:
5 | # You may remove this if you don't use go modules.
6 | - go mod download
7 | builds:
8 | - env:
9 | - CGO_ENABLED=0
10 | goos:
11 | - linux
12 | - windows
13 | - darwin
14 | goarch:
15 | - amd64
16 | - arm64
17 | archives:
18 | - replacements:
19 | darwin: Darwin
20 | linux: Linux
21 | windows: Windows
22 | 386: i386
23 | amd64: x86_64
24 | checksum:
25 | name_template: 'checksums.txt'
26 | snapshot:
27 | name_template: "{{ .Tag }}-next"
28 | changelog:
29 | sort: asc
30 | filters:
31 | exclude:
32 | - '^docs:'
33 | - '^test:'
--------------------------------------------------------------------------------
/service/service.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package service
16 |
17 | import (
18 | "github.com/vicanso/cybertect/cache"
19 | )
20 |
21 | var (
22 | redisSrv = cache.GetRedisCache()
23 | )
24 |
--------------------------------------------------------------------------------
/web/src/components/ExLoading.tsx:
--------------------------------------------------------------------------------
1 | import { NSpin } from "naive-ui";
2 | import { defineComponent } from "vue";
3 | import { css } from "@linaria/core";
4 |
5 | const spinClass = css`
6 | float: left;
7 | margin-right: 10px;
8 | `;
9 | const loadingClass = css`
10 | margin: auto;
11 | width: 200px;
12 | `;
13 |
14 | export default defineComponent({
15 | name: "ExLoading",
16 | props: {
17 | style: {
18 | type: Object,
19 | default: () => {
20 | return {
21 | marginTop: "60px",
22 | };
23 | },
24 | },
25 | },
26 | render() {
27 | const { style } = this.$props;
28 | return (
29 |
30 |
31 | 正在加载中,请稍候...
32 |
33 | );
34 | },
35 | });
36 |
--------------------------------------------------------------------------------
/asset/http_server_interceptor.js:
--------------------------------------------------------------------------------
1 | // 从请求的query中获取key对应的值
2 | function getReqQuery(key) {
3 | return req.query[key];
4 | }
5 |
6 | // 修改请求头的query
7 | function setReqQuery(key, value) {
8 | req.modifiedQuery = true;
9 | req.query[key] = value;
10 | }
11 |
12 | // 从请求数据中获取body中key对应的值
13 | function getReqBody(key) {
14 | return req.body[key];
15 | }
16 |
17 | // 设置请求数据中的body的值
18 | function setReqBody(key, value) {
19 | req.modifiedBody = true;
20 | req.body[key] = value;
21 | }
22 |
23 | // 设置响应数据
24 | function setRespBody(key, value) {
25 | if (!resp.status) {
26 | resp.status = 200;
27 | }
28 | resp.body[key] = value;
29 | }
30 |
31 | // 设置响应状态码
32 | function setRespStatus(status) {
33 | resp.status = status;
34 | }
35 |
36 | // 设置响应HTTP头
37 | function setRespHeader(key, value) {
38 | resp.header[key] = value;
39 | }
--------------------------------------------------------------------------------
/validate/flux.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package validate
16 |
17 | func init() {
18 | // influxdb measurement
19 | AddAlias("xMeasurement", "ascii,min=1,max=20")
20 | // influxdb tag
21 | AddAlias("xTag", "ascii,min=1,max=200")
22 | }
23 |
--------------------------------------------------------------------------------
/schema/dnsdetector.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "entgo.io/ent"
5 | "entgo.io/ent/schema/field"
6 | )
7 |
8 | // DNSDetector holds the schema definition for the DNSDetector entity.
9 | type DNSDetector struct {
10 | ent.Schema
11 | }
12 |
13 | // Fields of the DNSDetector.
14 | func (DNSDetector) Fields() []ent.Field {
15 | return []ent.Field{
16 | field.String("host").
17 | NotEmpty().
18 | Comment("域名地址"),
19 | field.Strings("ips").
20 | Comment("域名配置的IP列表"),
21 | field.Strings("servers").
22 | Comment("DNS服务器列表"),
23 | }
24 | }
25 |
26 | // Mixin of the DNSDetector.
27 | func (DNSDetector) Mixin() []ent.Mixin {
28 | return []ent.Mixin{
29 | TimeMixin{},
30 | StatusMixin{},
31 | DetectorMixin{},
32 | }
33 | }
34 |
35 | // Edges of the DNSDetector.
36 | func (DNSDetector) Edges() []ent.Edge {
37 | return nil
38 | }
39 |
--------------------------------------------------------------------------------
/asset/asset.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | // 静态态文件打包
16 |
17 | package asset
18 |
19 | import (
20 | "embed"
21 | )
22 |
23 | //go:embed *
24 | var assetFS embed.FS
25 |
26 | // GetFS get asset embed fs
27 | func GetFS() embed.FS {
28 | return assetFS
29 | }
30 |
--------------------------------------------------------------------------------
/web/src/routes/router.ts:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHashHistory } from "vue-router";
2 | import { routes } from "./routes";
3 |
4 | type loadingEvent = () => void;
5 |
6 | const noop = function () {
7 | return;
8 | };
9 |
10 | let startEvent = noop;
11 | let finishEvent = noop;
12 |
13 | export function setLoadingEvent(
14 | start: loadingEvent,
15 | finish: loadingEvent
16 | ): void {
17 | startEvent = start;
18 | finishEvent = finish;
19 | }
20 |
21 | const router = createRouter({
22 | history: createWebHashHistory(),
23 | routes,
24 | });
25 |
26 | router.beforeEach(function (to, from, next) {
27 | if (!from || to.path !== from.path) {
28 | startEvent();
29 | }
30 | next();
31 | });
32 |
33 | router.afterEach(function (to, from) {
34 | if (!from || to.path !== from.path) {
35 | finishEvent();
36 | }
37 | });
38 | export default router;
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | *.pid
3 |
4 | vendor
5 | tmp
6 | tmp/*
7 | *.out
8 | *.log
9 | cybertect
10 | api.yml
11 |
12 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
13 |
14 | # dependencies
15 | web/node_modules
16 | web/.pnp
17 | .pnp.js
18 |
19 | # testing
20 | web/coverage
21 |
22 | # production
23 | web/build
24 | web/dist
25 |
26 | # misc
27 | .DS_Store
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | npm-debug.log*
34 | yarn-debug.log*
35 | yarn-error.log*
36 |
37 |
38 | .DS_Store
39 | node_modules
40 | /dist
41 |
42 | # local env files
43 | .env.local
44 | .env.*.local
45 |
46 | # Log files
47 | npm-debug.log*
48 | yarn-debug.log*
49 | yarn-error.log*
50 |
51 | # Editor directories and files
52 | .idea
53 | .vscode
54 | *.suo
55 | *.ntvs*
56 | *.njsproj
57 | *.sln
58 | *.sw?
59 |
--------------------------------------------------------------------------------
/util/stack_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package util
16 |
17 | import (
18 | "testing"
19 |
20 | "github.com/stretchr/testify/assert"
21 | )
22 |
23 | func TestGetStack(t *testing.T) {
24 | assert := assert.New(t)
25 | stacks := GetStack(5)
26 | assert.NotEmpty(stacks)
27 | }
28 |
--------------------------------------------------------------------------------
/validate/configuration.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package validate
16 |
17 | func init() {
18 | // 应用配置名称
19 | AddAlias("xConfigurationName", "min=2,max=20")
20 | AddAlias("xConfigurationCategory", "alphanum,min=2,max=30")
21 | AddAlias("xConfigurationData", "min=0,max=500")
22 | }
23 |
--------------------------------------------------------------------------------
/web/src/views/configurations/Emails.tsx:
--------------------------------------------------------------------------------
1 | import { defineComponent } from "vue";
2 | import ExConfigEditorList from "../../components/ExConfigEditorList";
3 | import { ConfigCategory } from "../../states/configs";
4 | import { FormItem, FormItemTypes } from "../../components/ExFormInterface";
5 |
6 | export default defineComponent({
7 | name: "EmailConfigs",
8 | render() {
9 | const extraFormItems: FormItem[] = [
10 | {
11 | name: "邮箱地址:",
12 | key: "data",
13 | type: FormItemTypes.TextArea,
14 | span: 24,
15 | placeholder: "请输入邮箱地址,多个地址以,分隔",
16 | },
17 | ];
18 | return (
19 |
26 | );
27 | },
28 | });
29 |
--------------------------------------------------------------------------------
/asset/dist/assets/MockTime-72101ce2.js:
--------------------------------------------------------------------------------
1 | import{g as m,E as c}from"./ExConfigEditor-da704c67.js";import{E as n}from"./ExLoading-572e76eb.js";import{s as f}from"./index-aeeb7070.js";import{C as i,c as p}from"./configs-5a9029c7.js";import{d,r as s,N as a}from"./ui-32540f7e.js";import{u as l}from"./naive-094f875f.js";import"./ExForm-cdb0faae.js";import"./detector-090ed643.js";import"./ExFormInterface-d830d527.js";import"./common-a7d1cc73.js";const x=d({name:"MockTime",setup(){const r=l(),o=s(0),e=s(!0);return(async()=>{e.value=!0;try{const t=await p();t.id&&(o.value=t.id)}catch(t){f(r,t)}finally{e.value=!1}})(),{id:o,processing:e}},render(){const{id:r,processing:o}=this;if(o)return a(n,null,null);const e=m({category:i.MockTime,name:i.MockTime});return e.push({name:"时间配置:",key:"data",type:"datetime",placeholder:"请选择要Mock的时间"}),a(c,{id:r,title:"添加/更新MockTime配置",description:"针对应用时间Mock,用于测试环境中调整应用时间",formItems:e},null)}});export{x as default};
2 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*.*.*'
7 | jobs:
8 |
9 | build:
10 | name: Build
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v4
14 |
15 | - uses: actions/setup-go@v5
16 | with:
17 | go-version-file: go.mod
18 |
19 | - name: Get dependencies
20 | run:
21 | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin latest
22 |
23 | - name: Install
24 | run: make install
25 |
26 | - name: Generate
27 | run: make generate
28 |
29 | - name: Tidy
30 | run: make tidy
31 | - name: Run GoReleaser
32 | uses: goreleaser/goreleaser-action@v4
33 | with:
34 | version: latest
35 | args: release --clean --skip-validate
36 | env:
37 | GITHUB_TOKEN: ${{ secrets.GH_PAT }}
--------------------------------------------------------------------------------
/asset/dist/assets/Ping-898e4802.js:
--------------------------------------------------------------------------------
1 | import{F as s}from"./ExFormInterface-d830d527.js";import{u as i,p,m as a,n as m,o as c}from"./detector-090ed643.js";import{c as u}from"./ExTable-dcbd254b.js";import{g as D,a as l,D as I}from"./Detector-ad8130de.js";import{d,N as f}from"./ui-32540f7e.js";import"./index-aeeb7070.js";import"./common-a7d1cc73.js";import"./naive-094f875f.js";import"./configs-5a9029c7.js";import"./ExForm-cdb0faae.js";import"./ExLoading-572e76eb.js";const N=d({name:"PingDetector",setup(){const t=i().pingDetectors;return{findByID:async e=>await p(e),pingDetectors:t}},render(){const{pingDetectors:t,findByID:r}=this,e=[u({key:"ips",title:"IP列表"})],o=[{type:s.DynamicInput,name:"IP列表:",key:"ips",span:12,placeholder:"请输入对应的IP地址",min:1}],n=D({ips:l("IP地址列表不能为空")});return f(I,{columns:e,fetch:a,findByID:r,updateByID:m,create:c,title:"Ping检测配置",description:"指定Ping检测IP列表,定时Ping该IP是否正常",formItems:o,data:t,rules:n},null)}});export{N as default};
2 |
--------------------------------------------------------------------------------
/asset/dist/assets/TCP-b7c9b1e1.js:
--------------------------------------------------------------------------------
1 | import{F as a}from"./ExFormInterface-d830d527.js";import{u as c,t as n,j as m,k as i,l as p}from"./detector-090ed643.js";import{c as d}from"./ExTable-dcbd254b.js";import{g as u,a as D,D as l}from"./Detector-ad8130de.js";import{d as f,N as y}from"./ui-32540f7e.js";import"./index-aeeb7070.js";import"./common-a7d1cc73.js";import"./naive-094f875f.js";import"./configs-5a9029c7.js";import"./ExForm-cdb0faae.js";import"./ExLoading-572e76eb.js";const g=f({name:"TCPDetector",setup(){const t=c().tcpDetectors;return{findByID:async e=>await n(e),tcpDetectors:t}},render(){const{tcpDetectors:t,findByID:r}=this,e=[d({key:"addrs",title:"检测地址"})],o=[{type:a.DynamicInput,name:"地址列表:",key:"addrs",span:12,placeholder:"请输入需要检测的地址,如(IP:Port)",min:1}],s=u({addrs:D("检测地址列表不能为空")});return y(l,{columns:e,fetch:m,findByID:r,updateByID:i,create:p,title:"TCP检测配置",description:"指定IP与端口,定时检测是否可用",formItems:o,data:t,rules:s},null)}});export{g as default};
2 |
--------------------------------------------------------------------------------
/asset/dist/assets/Configs-499aae23.js:
--------------------------------------------------------------------------------
1 | import{E as f}from"./ExConfigTable-74c02379.js";import{a as c}from"./configs-5a9029c7.js";import{s as d}from"./index-aeeb7070.js";import{E as m}from"./ExLoading-572e76eb.js";import{u as p,D as i,E as V}from"./naive-094f875f.js";import{d as g,r as o,N as e}from"./ui-32540f7e.js";import"./ExTable-dcbd254b.js";import"./ExFormInterface-d830d527.js";import"./common-a7d1cc73.js";const u="当前生效配置",x=g({name:"ConfigsView",setup(){const r=p(),n=o("所有配置"),t=o(""),a=o(!1);return{fetchValid:async()=>{if(!a.value){a.value=!0;try{const l=await c();t.value=JSON.stringify(l,null,2)}catch(l){d(r,l)}finally{a.value=!1}}},tab:n,currentValid:t,fetchingCurrentValid:a}},render(){const{tab:r,fetchValid:n,currentValid:t,fetchingCurrentValid:a}=this;return e(V,{defaultValue:r,onUpdateValue:s=>{s===u&&n()}},{default:()=>[e(i,{name:"所有配置"},{default:()=>[e(f,null,null)]}),e(i,{name:u},{default:()=>[a&&e(m,null,null),!a&&e("pre",null,[t])]})]})}});export{x as default};
2 |
--------------------------------------------------------------------------------
/helper/snappy.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package helper
16 |
17 | import "github.com/golang/snappy"
18 |
19 | func SnappyEncode(data []byte) []byte {
20 | dst := []byte{}
21 | dst = snappy.Encode(dst, data)
22 | return dst
23 | }
24 |
25 | func SnappyDecode(buf []byte) ([]byte, error) {
26 | var dst []byte
27 | return snappy.Decode(dst, buf)
28 | }
29 |
--------------------------------------------------------------------------------
/web/src/routes/index.ts:
--------------------------------------------------------------------------------
1 | import { names } from "./routes";
2 | import router from "./router";
3 | import { LocationQueryRaw } from "vue-router";
4 |
5 | export function goTo(
6 | name: string,
7 | params: {
8 | replace?: boolean;
9 | query?: LocationQueryRaw;
10 | }
11 | ): void {
12 | router.push({
13 | name,
14 | replace: params.replace,
15 | query: params.query,
16 | });
17 | }
18 |
19 | export function goToLogin(replace?: boolean): void {
20 | goTo(names.login, {
21 | replace: replace ?? false,
22 | });
23 | }
24 |
25 | export function goToRegister(replace?: boolean): void {
26 | goTo(names.register, {
27 | replace: replace ?? false,
28 | });
29 | }
30 |
31 | export function goToHome(replace?: boolean): void {
32 | goTo(names.home, {
33 | replace: replace ?? false,
34 | });
35 | }
36 |
37 | export function goToProfile(replace?: boolean): void {
38 | goTo(names.profile, {
39 | replace: replace ?? false,
40 | });
41 | }
42 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:18-alpine as webbuilder
2 |
3 | COPY . /cybertect
4 | RUN apk update \
5 | && apk add make \
6 | && cd /cybertect/web \
7 | && yarn \
8 | && yarn build \
9 | && rm -rf node_modules
10 |
11 | FROM golang:1.23-alpine as builder
12 |
13 | COPY --from=webbuilder /cybertect /cybertect
14 |
15 | RUN apk update \
16 | && apk add git make curl jq \
17 | && cd /cybertect \
18 | && rm -rf asset/dist \
19 | && cp -rf web/dist asset/ \
20 | && make install \
21 | && make generate \
22 | && make build
23 |
24 | FROM alpine
25 |
26 | EXPOSE 7001
27 |
28 | # tzdata 安装所有时区配置或可根据需要只添加所需时区
29 |
30 | RUN apk add --no-cache ca-certificates tzdata
31 |
32 | COPY --from=builder /cybertect/cybertect /usr/local/bin/cybertect
33 | COPY --from=builder /cybertect/entrypoint.sh /entrypoint.sh
34 |
35 | HEALTHCHECK --timeout=10s --interval=10s CMD [ "wget", "http://127.0.0.1:7001/ping", "-q", "-O", "-"]
36 |
37 | CMD ["cybertect"]
38 |
39 | ENTRYPOINT ["/entrypoint.sh"]
40 |
--------------------------------------------------------------------------------
/service/ip_blocker_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package service
16 |
17 | import (
18 | "testing"
19 |
20 | "github.com/stretchr/testify/assert"
21 | )
22 |
23 | func TestIPBlocker(t *testing.T) {
24 | assert := assert.New(t)
25 | assert.Nil(ResetIPBlocker([]string{
26 | "1.1.1.1",
27 | }))
28 | assert.True(IsBlockIP("1.1.1.1"))
29 | assert.False(IsBlockIP("1.1.1.2"))
30 | }
31 |
--------------------------------------------------------------------------------
/schema/httpdetector.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "entgo.io/ent"
5 | "entgo.io/ent/schema/field"
6 | )
7 |
8 | // HTTPDetector holds the schema definition for the HTTP entity.
9 | type HTTPDetector struct {
10 | ent.Schema
11 | }
12 |
13 | // Fields of the HTTP.
14 | func (HTTPDetector) Fields() []ent.Field {
15 | return []ent.Field{
16 | field.Strings("ips").
17 | Comment("IP列表"),
18 | field.String("url").
19 | NotEmpty().
20 | Comment("测试URL"),
21 | field.String("script").
22 | Optional().
23 | Comment("检测脚本"),
24 | field.Strings("proxies").
25 | Optional().
26 | Comment("代理列表"),
27 | field.Int8("randomQueryString").
28 | Optional().
29 | Comment("随机query string"),
30 | }
31 | }
32 |
33 | // Mixin http mixin
34 | func (HTTPDetector) Mixin() []ent.Mixin {
35 | return []ent.Mixin{
36 | TimeMixin{},
37 | StatusMixin{},
38 | DetectorMixin{},
39 | }
40 | }
41 |
42 | // Edges of the HTTP.
43 | func (HTTPDetector) Edges() []ent.Edge {
44 | return nil
45 | }
46 |
--------------------------------------------------------------------------------
/helper/redis_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package helper
16 |
17 | import (
18 | "testing"
19 |
20 | "github.com/stretchr/testify/assert"
21 | )
22 |
23 | func TestRedisStats(t *testing.T) {
24 | assert := assert.New(t)
25 | m := RedisStats()
26 | assert.NotNil(m)
27 | }
28 |
29 | func TestRedisPing(t *testing.T) {
30 | assert := assert.New(t)
31 | err := RedisPing()
32 | assert.Nil(err)
33 | }
34 |
--------------------------------------------------------------------------------
/router_concurrency/router_concurrency_test.go:
--------------------------------------------------------------------------------
1 | package routerconcurrency
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | "github.com/vicanso/elton"
8 | )
9 |
10 | func TestRouterLimiter(t *testing.T) {
11 | assert := assert.New(t)
12 |
13 | InitLimiter([]elton.RouterInfo{
14 | {
15 | Method: "GET",
16 | Route: "/",
17 | },
18 | {
19 | Method: "GET",
20 | Route: "/users/me",
21 | },
22 | })
23 | rc := GetLimiter()
24 | key := "GET /"
25 | count, max := rc.IncConcurrency(key)
26 | assert.Equal(uint32(1), count)
27 | assert.Equal(uint32(0), max)
28 |
29 | assert.Equal(uint32(1), rc.GetConcurrency(key))
30 |
31 | rc.DecConcurrency(key)
32 | assert.Equal(uint32(0), rc.GetConcurrency(key))
33 |
34 | // 重置路由并发配置
35 | Update([]string{
36 | `{
37 | "route": "/",
38 | "method": "GET",
39 | "max": 10,
40 | "rateLimit": "100/s"
41 | }`,
42 | })
43 | count, max = rc.IncConcurrency(key)
44 | assert.Equal(uint32(1), count)
45 | assert.Equal(uint32(10), max)
46 | }
47 |
--------------------------------------------------------------------------------
/util/env_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package util
16 |
17 | import (
18 | "testing"
19 |
20 | "github.com/stretchr/testify/assert"
21 | "github.com/vicanso/cybertect/config"
22 | )
23 |
24 | func TestENV(t *testing.T) {
25 | assert := assert.New(t)
26 |
27 | env := config.GetENV()
28 | assert.Equal(env == config.Dev, IsDevelopment())
29 | assert.Equal(env == config.Test, IsTest())
30 | assert.False(IsProduction())
31 | }
32 |
--------------------------------------------------------------------------------
/web/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import vue from "@vitejs/plugin-vue";
3 | import vueJsx from "@vitejs/plugin-vue-jsx";
4 | import VitePluginLinaria from 'vite-plugin-linaria';
5 |
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | base: process.env.STATIC || "/",
9 | plugins: [
10 | vue(),
11 | vueJsx(),
12 | VitePluginLinaria(),
13 | ],
14 | build: {
15 | rollupOptions: {
16 | output: {
17 | manualChunks: {
18 | common: [
19 | "axios",
20 | "dayjs",
21 | "localforage",
22 | "pako",
23 | ],
24 | ui: [
25 | "vue",
26 | "vue-router",
27 | ],
28 | naive: [
29 | "naive-ui",
30 | ]
31 | },
32 | },
33 | },
34 | },
35 | server: {
36 | proxy: {
37 | "/api": {
38 | target: "http://localhost:7001",
39 | rewrite: (path) => path.replace(/^\/api/, ""),
40 | },
41 | },
42 | },
43 | });
44 |
--------------------------------------------------------------------------------
/web/src/storages/local.ts:
--------------------------------------------------------------------------------
1 | import localforage from "localforage";
2 |
3 | const store = localforage.createInstance({
4 | name: "cybertect",
5 | });
6 |
7 | class LocalStorage {
8 | private key: string;
9 | private data: Record;
10 | constructor(key: string) {
11 | this.data = {};
12 | this.key = key;
13 | }
14 | getData(): Record {
15 | return Object.assign({}, this.data);
16 | }
17 | // load 加载数据
18 | async load(): Promise> {
19 | const data = await store.getItem(this.key);
20 | if (!data) {
21 | return {};
22 | }
23 | const str = data as string;
24 | this.data = JSON.parse(str || "{}");
25 | return this.getData();
26 | }
27 | // set 设置数据
28 | async set(key: string, value: string | number | boolean) {
29 | const data = this.data;
30 | data[key] = value;
31 | await store.setItem(this.key, JSON.stringify(data));
32 | return this.getData();
33 | }
34 | }
35 |
36 | export const settingStorage = new LocalStorage("settings");
37 |
--------------------------------------------------------------------------------
/controller/user_doc.go:
--------------------------------------------------------------------------------
1 | // +build swagger
2 | // 用户相关接口文档
3 |
4 | package controller
5 |
6 | import "github.com/vicanso/cybertect/ent"
7 |
8 | // 用户列表响应
9 | // swagger:response apiUserListResponse
10 | type apiUserListResponse struct {
11 | // in: body
12 | Body *userListResp
13 | }
14 |
15 | // 用户信息查询参数
16 | // swagger:parameters userList
17 | type apiUserListParams struct {
18 | userListParams
19 | }
20 |
21 | // 用户登录Token响应
22 | // swagger:response apiUserLoginTokenResponse
23 | type apiUserLoginTokenResponse struct {
24 | // in: body
25 | Body *userLoginTokenResp
26 | }
27 |
28 | // 用户信息
29 | // swagger:response apiUserInfoResponse
30 | type apiUserInfoResponse struct {
31 | // in: body
32 | Body *userInfoResp
33 | }
34 |
35 | // 用户登录与注册参数
36 | // swagger:parameters userRegister userLogin
37 | type apiUserRegisterLoginParams struct {
38 | // in: body
39 | Body *userRegisterLoginParams
40 | }
41 |
42 | // 用户注册响应
43 | // swagger:response apiUserRegisterResponse
44 | type apiUserRegisterResponse struct {
45 | // in: body
46 | Body *ent.User
47 | }
48 |
--------------------------------------------------------------------------------
/util/env.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package util
16 |
17 | import "github.com/vicanso/cybertect/config"
18 |
19 | // IsDevelopment 判断是否开发环境
20 | func IsDevelopment() bool {
21 | return config.GetENV() == config.Dev
22 | }
23 |
24 | // IsTest 判断是否测试环境
25 | func IsTest() bool {
26 | return config.GetENV() == config.Test
27 | }
28 |
29 | // IsProduction 判断是否生产环境
30 | func IsProduction() bool {
31 | return config.GetENV() == config.Production
32 | }
33 |
--------------------------------------------------------------------------------
/helper/ent_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package helper
16 |
17 | import (
18 | "testing"
19 |
20 | "github.com/stretchr/testify/assert"
21 | )
22 |
23 | func TestEnt(t *testing.T) {
24 | assert := assert.New(t)
25 |
26 | assert.NotNil(EntGetClient())
27 |
28 | assert.Nil(EntInitSchema())
29 |
30 | assert.Nil(EntPing())
31 | }
32 |
33 | func TestEntGetStats(t *testing.T) {
34 | assert := assert.New(t)
35 | stats := EntGetStats()
36 | assert.NotNil(stats)
37 | }
38 |
--------------------------------------------------------------------------------
/helper/influxdb_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package helper
16 |
17 | import (
18 | "testing"
19 | "time"
20 | )
21 |
22 | func TestInfluxWrite(t *testing.T) {
23 | GetInfluxDB().Write("test", map[string]string{
24 | "type": "vip",
25 | }, map[string]interface{}{
26 | "name": "test",
27 | "count": 1,
28 | })
29 | GetInfluxDB().Write("test", map[string]string{
30 | "type": "vip",
31 | }, map[string]interface{}{
32 | "name": "test",
33 | "count": 1,
34 | }, time.Now())
35 | }
36 |
--------------------------------------------------------------------------------
/web/src/Root.tsx:
--------------------------------------------------------------------------------
1 | import { defineComponent } from "vue";
2 | import {
3 | darkTheme,
4 | NConfigProvider,
5 | NDialogProvider,
6 | NGlobalStyle,
7 | NLoadingBarProvider,
8 | NMessageProvider,
9 | NNotificationProvider,
10 | zhCN,
11 | } from "naive-ui";
12 | import useCommonState from "./states/common";
13 |
14 | import App from "./App";
15 |
16 | export default defineComponent({
17 | name: "RootPage",
18 | setup() {
19 | const { settings } = useCommonState();
20 | return {
21 | settings,
22 | };
23 | },
24 | render() {
25 | const isDark = this.settings.theme === "dark";
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | );
40 | },
41 | });
42 |
--------------------------------------------------------------------------------
/validate/detector.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package validate
16 |
17 | func init() {
18 | AddAlias("xDetectorName", "min=1,max=30")
19 | AddAlias("xDetectorDesc", "min=1,max=300")
20 | AddAlias("xDetectorReceiver", "email")
21 | AddAlias("xDetectorTaskID", "number")
22 | // 检测结果
23 | AddAlias("xDetectorResult", "oneof=1 2")
24 | AddAlias("xDetectorCategories", "min=1,max=30")
25 |
26 | AddAlias("xDetectorDatabaseURI", "min=1,max=500")
27 | AddAlias("xDetectorTLSPemData", "ascii,min=1,max=10240")
28 | }
29 |
--------------------------------------------------------------------------------
/util/number.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package util
16 |
17 | import (
18 | "fmt"
19 | "math"
20 | "strconv"
21 | )
22 |
23 | // 可根据需求调整相差值的判断
24 | const tolerance = 1e-6
25 |
26 | // AlmostEqual returns true is abs(a - b) < 1e-6
27 | func AlmostEqual(a, b float64) bool {
28 | return math.Abs(a-b) <= tolerance
29 | }
30 |
31 | // ToFixed formats float64 to string
32 | func ToFixed(value float64, precision int) string {
33 | str := "%." + strconv.Itoa(precision) + "f"
34 | return fmt.Sprintf(str, value)
35 | }
36 |
--------------------------------------------------------------------------------
/middleware/stats_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package middleware
16 |
17 | import (
18 | "net/http/httptest"
19 | "testing"
20 |
21 | "github.com/stretchr/testify/assert"
22 | "github.com/vicanso/elton"
23 | )
24 |
25 | func TestNewStats(t *testing.T) {
26 | assert := assert.New(t)
27 | // 仅测试执行,不检查数据
28 | fn := NewStats()
29 | req := httptest.NewRequest("GET", "/", nil)
30 | c := elton.NewContext(nil, req)
31 | c.Next = func() error {
32 | return nil
33 | }
34 | err := fn(c)
35 | assert.Nil(err)
36 | }
37 |
--------------------------------------------------------------------------------
/cs/cs.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package cs
16 |
17 | import "regexp"
18 |
19 | const (
20 | // CID context id
21 | CID = "cid"
22 | // UserSession user session
23 | UserSession = "userSession"
24 | )
25 |
26 | const (
27 | // MagicalCaptcha magical captcha(for test only)
28 | MagicalCaptcha = "0145"
29 | )
30 |
31 | const (
32 | // ResultSuccess result success
33 | ResultSuccess = iota
34 | // ResultFail result fail
35 | ResultFail
36 | )
37 |
38 | // ***处理
39 | var MaskRegExp = regexp.MustCompile(`(?i)password`)
40 |
--------------------------------------------------------------------------------
/service/ip_blocker.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package service
16 |
17 | import (
18 | "github.com/vicanso/ips"
19 | )
20 |
21 | var blockIPS = ips.New()
22 |
23 | // ResetIPBlocker 重置IP拦截器的IP列表
24 | func ResetIPBlocker(ipList []string) error {
25 | return blockIPS.Replace(ipList...)
26 | }
27 |
28 | // IsBlockIP 判断该IP是否有需要拦截
29 | func IsBlockIP(ip string) bool {
30 | return blockIPS.Contains(ip)
31 | }
32 |
33 | // GetIPBlockList 获取block的ip地址列表
34 | func GetIPBlockList() []string {
35 | return blockIPS.Strings()
36 | }
37 |
--------------------------------------------------------------------------------
/schema/tcpdetectorresult.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "entgo.io/ent"
5 | "entgo.io/ent/schema/field"
6 | )
7 |
8 | // TCPDetectorResult holds the schema definition for the TCPDetectorResult entity.
9 | type TCPDetectorResult struct {
10 | ent.Schema
11 | }
12 |
13 | type TCPDetectorSubResult struct {
14 | Result DetectorResult `json:"result"`
15 | Addr string `json:"addr"`
16 | Duration int `json:"duration"`
17 | Message string `json:"message"`
18 | }
19 |
20 | type TCPDetectorSubResults []*TCPDetectorSubResult
21 |
22 | // Fields of the TCPDetectorResult.
23 | func (TCPDetectorResult) Fields() []ent.Field {
24 | return []ent.Field{
25 | field.Strings("addrs").
26 | Comment("检测地址"),
27 | field.JSON("results", TCPDetectorSubResults{}).
28 | Comment("检测结果列表"),
29 | }
30 | }
31 |
32 | // Mixin of the TCPDetectorResult.
33 | func (TCPDetectorResult) Mixin() []ent.Mixin {
34 | return []ent.Mixin{
35 | TimeMixin{},
36 | DetectorResultMixin{},
37 | }
38 | }
39 |
40 | // Edges of the TCPDetectorResult.
41 | func (TCPDetectorResult) Edges() []ent.Edge {
42 | return nil
43 | }
44 |
--------------------------------------------------------------------------------
/asset/dist/assets/ExConfigEditorList-10d35f0e.js:
--------------------------------------------------------------------------------
1 | import{g,E as f}from"./ExConfigEditor-da704c67.js";import{E}from"./ExConfigTable-74c02379.js";import{M as t}from"./index-aeeb7070.js";import{d as x,r as u,N as o,c as y}from"./ui-32540f7e.js";import{d as D,r as I}from"./naive-094f875f.js";const C="apayaz1",b=x({name:"ExConfigEditorList",props:{listTitle:{type:String,required:!0},editorTitle:{type:String,required:!0},editorDescription:{type:String,required:!0},category:{type:String,required:!0},extraFormItems:{type:Array,default:()=>[]}},setup(){const e=u(t.List);return{updatedID:u(0),toggle:s=>{e.value=s},mode:e}},render(){const{listTitle:e,editorTitle:d,category:i,editorDescription:s,extraFormItems:p}=this.$props,{mode:l,toggle:r,updatedID:m}=this;if(l===t.List)return o(I,{title:e},{default:()=>[o(E,{category:i,onUpdate:a=>{this.updatedID=a,r(t.Update)}},null),o(D,{size:"large",class:C,onClick:()=>{this.updatedID=0,r(t.Add)}},{default:()=>[y("增加配置")]})]});const n=g({category:i});return p.forEach(a=>{const c=Object.assign({},a);n.push(c)}),o(f,{title:d,description:s,id:m,formItems:n,onSubmitDone:()=>{r(t.List)},onBack:()=>{r(t.List)}},null)}});export{b as E};
2 |
--------------------------------------------------------------------------------
/detector/detector_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package detector
16 |
17 | import (
18 | "testing"
19 |
20 | "github.com/stretchr/testify/assert"
21 | )
22 |
23 | func TestIsMatchAlarmCount(t *testing.T) {
24 | assert := assert.New(t)
25 |
26 | assert.True(isMatchAlarmCount(1))
27 | assert.True(isMatchAlarmCount(2))
28 | assert.True(isMatchAlarmCount(3))
29 | assert.False(isMatchAlarmCount(4))
30 | assert.True(isMatchAlarmCount(10))
31 | assert.False(isMatchAlarmCount(61))
32 | assert.True(isMatchAlarmCount(120))
33 | }
34 |
--------------------------------------------------------------------------------
/util/map_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package util
16 |
17 | import (
18 | "testing"
19 |
20 | "github.com/stretchr/testify/assert"
21 | )
22 |
23 | func TestMergeMapStringInterface(t *testing.T) {
24 | assert := assert.New(t)
25 |
26 | target := MergeMapStringInterface(map[string]interface{}{
27 | "a": 1,
28 | }, map[string]interface{}{
29 | "b": true,
30 | }, map[string]interface{}{
31 | "c": "2",
32 | })
33 |
34 | assert.Equal(1, target["a"])
35 | assert.Equal(true, target["b"])
36 | assert.Equal("2", target["c"])
37 | }
38 |
--------------------------------------------------------------------------------
/router/router.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package router
16 |
17 | import (
18 | "github.com/vicanso/elton"
19 | )
20 |
21 | var (
22 | // groupList 路由组列表
23 | groupList = make([]*elton.Group, 0)
24 | )
25 |
26 | // NewGroup new router group
27 | func NewGroup(path string, handlerList ...elton.Handler) *elton.Group {
28 | // 如果配置文件中有配置路由
29 | g := elton.NewGroup(path, handlerList...)
30 | groupList = append(groupList, g)
31 | return g
32 | }
33 |
34 | // GetGroups get all groups
35 | func GetGroups() []*elton.Group {
36 | return groupList
37 | }
38 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: default test test-cover dev generate hooks lint-web doc
2 |
3 | # for dev
4 | dev:
5 | air -c .air.toml
6 | dev-debug:
7 | LOG_LEVEL=0 make dev
8 | doc:
9 | CGO_ENABLED=0 swagger generate spec -t swagger -o ./asset/api.yml && swagger validate ./asset/api.yml
10 |
11 | test:
12 | go test -race -cover ./...
13 |
14 | install:
15 | go get -d entgo.io/ent/cmd/entc@v0.14.1
16 |
17 | build-web:
18 | cd web && npm install && npm run build && rm -rf node_modules && cd .. && rm -rf asset/dist && cp -rf web/dist asset/
19 |
20 | generate:
21 | go run entgo.io/ent/cmd/ent generate --feature sql/modifier --feature privacy --feature intercept ./schema --template ./template --target ./ent
22 |
23 | describe:
24 | entc describe ./schema
25 |
26 | test-cover:
27 | go test -race -coverprofile=test.out ./... && go tool cover --html=test.out
28 |
29 | list-mod:
30 | go list -m -u all
31 |
32 | tidy:
33 | go mod tidy
34 |
35 | build:
36 | go build -ldflags "-X main.Version=0.0.1 -X 'main.BuildedAt=`date`'" -o cybertect
37 |
38 |
39 | lint:
40 | golangci-lint run
41 |
42 | lint-web:
43 | cd web && yarn lint
44 |
45 | hooks:
46 | cp hooks/* .git/hooks/
--------------------------------------------------------------------------------
/asset/dist/assets/DNS-16d14de9.js:
--------------------------------------------------------------------------------
1 | import{F as o}from"./ExFormInterface-d830d527.js";import{u as m,d as c,e as p,f as D,i as d}from"./detector-090ed643.js";import{c as n}from"./ExTable-dcbd254b.js";import{g as l,n as u,a,D as y}from"./Detector-ad8130de.js";import{d as I,N as f}from"./ui-32540f7e.js";import"./index-aeeb7070.js";import"./common-a7d1cc73.js";import"./naive-094f875f.js";import"./configs-5a9029c7.js";import"./ExForm-cdb0faae.js";import"./ExLoading-572e76eb.js";const L=I({name:"DNSDetector",setup(){const e=m().dnsDetectors;return{findByID:async t=>await c(t),dnsDetectors:e}},render(){const{dnsDetectors:e,findByID:s}=this,t=[{title:"域名",key:"host"},n({key:"ips",title:"IP列表"}),n({key:"servers",title:"DNS服务器"})],r=[{name:"域名",key:"host",placeholder:"请输入要检测的域名",span:8},{type:o.DynamicInput,name:"IP列表:",key:"ips",span:8,placeholder:"请输入对应的IP解析",min:1},{type:o.DynamicInput,name:"DNS服务器:",key:"servers",span:8,placeholder:"请输入DNS服务器",min:1}],i=l({host:u("域名不能为空"),servers:a("DNS服务器列表不能为空"),ips:a("域名解析IP地址列表不能为空")});return f(y,{columns:t,fetch:p,findByID:s,updateByID:D,create:d,title:"DNS检测",description:"指定DNS服务器检测解析IP是否正确",formItems:r,data:e,rules:i},null)}});export{L as default};
2 |
--------------------------------------------------------------------------------
/helper/snappy_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package helper
16 |
17 | import (
18 | "testing"
19 |
20 | "github.com/stretchr/testify/assert"
21 | )
22 |
23 | func TestSnappy(t *testing.T) {
24 | assert := assert.New(t)
25 | data := []byte("Snappy 是一个 C++ 的用来压缩和解压缩的开发包。其目标不是最大限度压缩或者兼容其他压缩格式,而是旨在提供高速压缩速度和合理的压缩率。Snappy 比 zlib 更快,但文件相对要大 20% 到 100%。在 64位模式的 Core i7 处理器上,可达每秒 250~500兆的压缩速度。")
26 | buf := SnappyEncode(data)
27 | assert.True(len(buf) < len(data))
28 |
29 | result, err := SnappyDecode(buf)
30 | assert.Nil(err)
31 | assert.Equal(data, result)
32 | }
33 |
--------------------------------------------------------------------------------
/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0",
3 | "scripts": {
4 | "format": "prettier --write src/*.ts src/*.tsx src/**/*.ts src/**/*.tsx src/**/**/*.ts src/**/**/*.tsx",
5 | "lint": "eslint . --ext .js,.tsx,.ts --fix && vue-tsc --noEmit",
6 | "dev": "vite",
7 | "build": "STATIC=/static vite build",
8 | "serve": "vite preview"
9 | },
10 | "dependencies": {
11 | "@linaria/core": "^3.0.0-beta.15",
12 | "@vicons/fa": "^0.12.0",
13 | "axios": "^1.4.0",
14 | "dayjs": "^1.11.8",
15 | "localforage": "^1.10.0",
16 | "lodash-es": "^4.17.21",
17 | "naive-ui": "^2.34.4",
18 | "pako": "^2.1.0",
19 | "vue": "^3.3.4",
20 | "vue-router": "^4.2.2"
21 | },
22 | "devDependencies": {
23 | "@types/pako": "^2.0.0",
24 | "@typescript-eslint/eslint-plugin": "^5.59.8",
25 | "@vitejs/plugin-vue": "^4.2.3",
26 | "@vitejs/plugin-vue-jsx": "^3.0.1",
27 | "@vue/compiler-sfc": "^3.3.4",
28 | "eslint": "^8.41.0",
29 | "eslint-plugin-vue": "^9.14.1",
30 | "prettier": "^2.8.8",
31 | "typescript": "^5.1.3",
32 | "vite": "^4.3.9",
33 | "vite-plugin-linaria": "1.0.0",
34 | "vue-tsc": "^1.6.5"
35 | }
36 | }
--------------------------------------------------------------------------------
/asset/dist/assets/ExConfigTable-74c02379.js:
--------------------------------------------------------------------------------
1 | import{E as f,n as l,b as u}from"./ExTable-dcbd254b.js";import{B as i}from"./index-aeeb7070.js";import{g as d,u as p,h as g,b as m}from"./configs-5a9029c7.js";import{d as y,E as C,N as b,i as h}from"./ui-32540f7e.js";function k(t){return typeof t=="function"||Object.prototype.toString.call(t)==="[object Object]"&&!h(t)}function E(){return[{title:"名称",key:"name"},u("data"),{title:"分类",key:"category"},{title:"状态",key:"status",render(t){return t.status===m.Enabled?"启用":"禁用"}},{title:"创建者",key:"owner"},{title:"配置生效时间",key:"startedAt",render(t){return i(t.startedAt)}},{title:"配置失效时间",key:"endedAt",render(t){return i(t.endedAt)}},{title:"配置描述",key:"description",width:100,ellipsis:{tooltip:!0}}]}function s(){}const N=y({name:"ConfigTable",props:{title:{type:String,default:""},category:{type:String,default:()=>""},onUpdate:{type:Function,default:s}},setup(t){const{configs:e}=p(),n=()=>g({category:t.category});return C(()=>{d()}),{fetchConfigs:n,configs:e}},render(){const{title:t,onUpdate:e}=this.$props,{configs:n,fetchConfigs:a,$slots:o}=this,r=E();return e!==s&&r.push(l(c=>{e(c.id)})),b(f,{title:t,columns:r,data:n,fetch:a},k(o)?o:{default:()=>[o]})}});export{N as E};
2 |
--------------------------------------------------------------------------------
/profiler/profiler.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package profiler
16 |
17 | import (
18 | "bytes"
19 | "time"
20 |
21 | "github.com/felixge/fgprof"
22 | "github.com/vicanso/hes"
23 | )
24 |
25 | func GetProf(d time.Duration) (*bytes.Buffer, error) {
26 | // 禁止拉取超过1分钟的prof
27 | if d > 1*time.Minute {
28 | return nil, hes.New("duration should be less than 1m")
29 | }
30 | result := &bytes.Buffer{}
31 | done := fgprof.Start(result, fgprof.FormatPprof)
32 | time.Sleep(d)
33 | err := done()
34 | if err != nil {
35 | return nil, err
36 | }
37 | return result, nil
38 | }
39 |
--------------------------------------------------------------------------------
/service/performance_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package service
16 |
17 | import (
18 | "context"
19 | "testing"
20 |
21 | "github.com/stretchr/testify/assert"
22 | )
23 |
24 | func TestPerformanceConcurrency(t *testing.T) {
25 | assert := assert.New(t)
26 | assert.Equal(int32(1), IncreaseConcurrency())
27 | assert.Equal(int32(1), GetConcurrency())
28 | assert.Equal(int32(0), DecreaseConcurrency())
29 | }
30 |
31 | func TestGetPerformance(t *testing.T) {
32 | assert := assert.New(t)
33 | p := GetPerformance(context.Background())
34 | assert.NotEmpty(p.MemSys)
35 | }
36 |
--------------------------------------------------------------------------------
/session/session_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package session
16 |
17 | import (
18 | "net/http/httptest"
19 | "testing"
20 |
21 | "github.com/stretchr/testify/assert"
22 | "github.com/vicanso/elton"
23 | se "github.com/vicanso/elton-session"
24 | )
25 |
26 | func TestNewSession(t *testing.T) {
27 | assert := assert.New(t)
28 |
29 | req := httptest.NewRequest("GET", "/", nil)
30 | c := elton.NewContext(nil, req)
31 | c.Next = func() error {
32 | return nil
33 | }
34 | fn := New()
35 | err := fn(c)
36 | assert.Nil(err)
37 | assert.NotNil(c.Get(se.Key))
38 | }
39 |
--------------------------------------------------------------------------------
/asset/dist/assets/RequestConcurrencies-160bdd88.js:
--------------------------------------------------------------------------------
1 | import{E as n}from"./ExConfigEditorList-10d35f0e.js";import{F as o}from"./ExFormInterface-d830d527.js";import{E as a}from"./ExLoading-572e76eb.js";import{G as m,s as i,E as p}from"./index-aeeb7070.js";import{C as c}from"./configs-5a9029c7.js";import{d as u,f as l,N as s}from"./ui-32540f7e.js";import{u as d}from"./naive-094f875f.js";import"./ExConfigEditor-da704c67.js";import"./ExForm-cdb0faae.js";import"./detector-090ed643.js";import"./ExConfigTable-74c02379.js";import"./ExTable-dcbd254b.js";import"./common-a7d1cc73.js";const H=u({name:"RequestCOncurrencies",setup(){const{requestInstances:e}=p(),r=d();return l(async()=>{try{await m()}catch(t){i(r,t)}}),{requestInstances:e}},render(){const{requestInstances:e}=this;if(e.processing)return s(a,null,null);const r=[{type:o.Blank,name:"",key:""},{name:"实例:",key:"data.name",type:o.Select,placeholder:"请选择限制并发数的实例",options:e.items.map(t=>({label:t.name,value:t.name}))},{name:"并发数:",key:"data.max",type:o.InputNumber,placeholder:"请输入并发限制"}];return s(n,{listTitle:"HTTP请求实例并发配置",editorTitle:"添加/更新HTTP请求实例并发限制",editorDescription:"设置各HTTP请求实例的并发请求数",category:c.RequestConcurrency,extraFormItems:r},null)}});export{H as default};
2 |
--------------------------------------------------------------------------------
/util/number_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package util
16 |
17 | import (
18 | "testing"
19 |
20 | "github.com/stretchr/testify/assert"
21 | )
22 |
23 | func TestAlmostEqual(t *testing.T) {
24 | assert := assert.New(t)
25 | assert.True(AlmostEqual(0.1+0.2+9e-7, 0.3))
26 | assert.False(AlmostEqual(0.2+1e-5, 0.2))
27 | }
28 |
29 | func TestToFixed(t *testing.T) {
30 | assert := assert.New(t)
31 |
32 | assert.Equal("1.12", ToFixed(1.1221132, 2))
33 | assert.Equal("1.13", ToFixed(1.129, 2))
34 | assert.Equal("1.10", ToFixed(1.1, 2))
35 | assert.Equal("1", ToFixed(1.1, 0))
36 | }
37 |
--------------------------------------------------------------------------------
/schema/dnsdetectorresult.go:
--------------------------------------------------------------------------------
1 | package schema
2 |
3 | import (
4 | "entgo.io/ent"
5 | "entgo.io/ent/schema/field"
6 | )
7 |
8 | // DNSDetectorResult holds the schema definition for the DNSDetectorResult entity.
9 | type DNSDetectorResult struct {
10 | ent.Schema
11 | }
12 |
13 | type DNSDetectorSubResult struct {
14 | Result DetectorResult `json:"result,omitempty"`
15 | IPS []string `json:"ips,omitempty"`
16 | Server string `json:"server,omitempty"`
17 | Duration int `json:"duration,omitempty"`
18 | Message string `json:"message,omitempty"`
19 | }
20 | type DNSDetectorSubResults []*DNSDetectorSubResult
21 |
22 | // Fields of the DNSDetectorResult.
23 | func (DNSDetectorResult) Fields() []ent.Field {
24 | return []ent.Field{
25 | field.String("host").
26 | Comment("检测Host"),
27 | field.JSON("results", DNSDetectorSubResults{}).
28 | Comment("检测结果列表"),
29 | }
30 | }
31 |
32 | // Mixin of the DNSDetectorResult
33 | func (DNSDetectorResult) Mixin() []ent.Mixin {
34 | return []ent.Mixin{
35 | TimeMixin{},
36 | DetectorResultMixin{},
37 | }
38 | }
39 |
40 | // Edges of the DNSDetectorResult.
41 | func (DNSDetectorResult) Edges() []ent.Edge {
42 | return nil
43 | }
44 |
--------------------------------------------------------------------------------
/asset/dist/assets/PingResult-e88b1dd9.js:
--------------------------------------------------------------------------------
1 | import{u as i,z as u}from"./detector-090ed643.js";import{d as o,c as l}from"./ExTable-dcbd254b.js";import{N as c}from"./index-aeeb7070.js";import{E as m,n as p}from"./ExDetectorResultTable-2191f62a.js";import{d,N as t}from"./ui-32540f7e.js";import{M as f,C as g,r as k}from"./naive-094f875f.js";import"./ExFormInterface-d830d527.js";import"./common-a7d1cc73.js";const y="p15nhdzn",I=d({name:"PingResult",setup(){const e=async s=>{await u(s)};return{pingDetectorResults:i().pingDetectorResults,fetch:e}},render(){const{pingDetectorResults:e,fetch:s}=this,a=[{title:"名称",key:"taskName"},o({title:"结果",key:"result.desc"}),l({title:"检测IP",key:"ips"}),{title:"最大耗时(ms)",key:"maxDuration"},l({title:"失败信息",key:"messages"}),{title:"更新于",key:"updatedAt",render(r){return c(r.updatedAt)}},{title:"更多",key:"",render(r){const n=[{title:"IP",key:"ip",fixed:"left",width:200},o({title:"结果",key:"result.desc"}),{title:"耗时(ms)",key:"duration"},{title:"失败信息",key:"message"}];return t(g,{placement:"left-end",trigger:"click"},{default:()=>[t("div",{class:y},[t(f,{columns:n,data:r.results},null)])],...{trigger:p}})}}];return t(k,{title:"Ping检测结果"},{default:()=>[t(m,{columns:a,data:e,fetch:s,category:"ping"},null)]})}});export{I as default};
2 |
--------------------------------------------------------------------------------
/asset/dist/assets/TCPResult-cc687576.js:
--------------------------------------------------------------------------------
1 | import{u as c,y as i}from"./detector-090ed643.js";import{d as o,c as a}from"./ExTable-dcbd254b.js";import{N as u}from"./index-aeeb7070.js";import{E as m,n as d}from"./ExDetectorResultTable-2191f62a.js";import{d as p,N as t}from"./ui-32540f7e.js";import{M as f,C as y,r as k}from"./naive-094f875f.js";import"./ExFormInterface-d830d527.js";import"./common-a7d1cc73.js";const C="p1m3677v",x=p({name:"TCPResult",setup(){const e=async s=>{await i(s)};return{tcpDetectorResults:c().tcpDetectorResults,fetch:e}},render(){const{tcpDetectorResults:e,fetch:s}=this,l=[{title:"名称",key:"taskName"},o({title:"结果",key:"result.desc"}),a({title:"检测地址",key:"addrs"}),{title:"最大耗时(ms)",key:"maxDuration"},a({title:"失败信息",key:"messages"}),{title:"更新于",key:"updatedAt",render(r){return u(r.updatedAt)}},{title:"更多",key:"",render(r){const n=[{title:"地址",key:"addr",fixed:"left",width:200},o({title:"结果",key:"result.desc"}),{title:"耗时(ms)",key:"duration"},{title:"失败信息",key:"message"}];return t(y,{placement:"left-end",trigger:"click"},{default:()=>[t("div",{class:C},[t(f,{columns:n,data:r.results},null)])],...{trigger:d}})}}];return t(k,{title:"TCP检测结果"},{default:()=>[t(m,{columns:l,data:e,fetch:s,category:"tcp"},null)]})}});export{x as default};
2 |
--------------------------------------------------------------------------------
/asset/dist/assets/DatabaseResult-963f24cb.js:
--------------------------------------------------------------------------------
1 | import{u as n,B as i}from"./detector-090ed643.js";import{d as r,c as l}from"./ExTable-dcbd254b.js";import{E as c,n as m}from"./ExDetectorResultTable-2191f62a.js";import{N as d}from"./index-aeeb7070.js";import{d as p,N as e}from"./ui-32540f7e.js";import{M as f,C as k,r as y}from"./naive-094f875f.js";import"./ExFormInterface-d830d527.js";import"./common-a7d1cc73.js";const D="pux6hxu",L=p({name:"DatabaseResult",setup(){const t=async a=>{await i(a)};return{databaseDetectorResults:n().databaseDetectorResults,fetch:t}},render(){const{databaseDetectorResults:t,fetch:a}=this,o=[{title:"名称",key:"taskName"},r({title:"结果",key:"result.desc"}),l({title:"连接串列表",key:"uris"}),{title:"最大耗时(ms)",key:"maxDuration"},l({title:"失败信息",key:"messages"}),{title:"更新于",key:"updatedAt",render(s){return d(s.updatedAt)}},{title:"更多",key:"",render(s){const u=[{title:"连接串",key:"uri"},r({title:"结果",key:"result.desc"}),{title:"耗时(ms)",key:"duration"},{title:"失败信息",key:"message"}];return e(k,{placement:"left-end",trigger:"click"},{default:()=>[e("div",{class:D},[e(f,{columns:u,data:s.results},null)])],...{trigger:m}})}}];return e(y,{title:"Database检测结果"},{default:()=>[e(c,{columns:o,data:t,fetch:a,category:"database"},null)]})}});export{L as default};
2 |
--------------------------------------------------------------------------------
/asset/dist/assets/Logins-188ecd5f.js:
--------------------------------------------------------------------------------
1 | import{E as o}from"./ExTable-dcbd254b.js";import{p as n,q as s,k as r,r as l}from"./index-aeeb7070.js";import{F as a}from"./ExFormInterface-d830d527.js";import{d,E as u,N as c}from"./ui-32540f7e.js";import"./naive-094f875f.js";import"./common-a7d1cc73.js";function p(){return[{title:"账户",key:"account",width:120,ellipsis:{tooltip:!0}},{title:"IP",key:"ip",width:120,ellipsis:{tooltip:!0}},{title:"定位",key:"location",width:150},{title:"运营商",key:"isp",width:80},{title:"Track ID",key:"trackID"},{title:"Session ID",key:"sessionID"},{title:"Forwarded For",key:"xForwardedFor",width:140,ellipsis:{tooltip:!0}},{title:"User Agent",key:"userAgent",width:120,ellipsis:{tooltip:!0}}]}function g(){return[{key:"account",name:"账户:",placeholder:"请输入要筛选的账号"},{key:"begin:end",name:"开始结束时间:",type:a.DateRange,span:12,placeholder:"请选择开始时间:请选择结束时间",defaultValue:[s().toISOString(),new Date().toISOString()]}]}const I=d({name:"LoginStats",setup(){const{logins:e}=r(),i=async t=>l({limit:t.limit,offset:t.offset,account:t.account||"",begin:t.begin||"",end:t.end||"",order:"-updatedAt"});return u(()=>{n()}),{logins:e,fetchLogins:i}},render(){const{logins:e,fetchLogins:i}=this;return c(o,{title:"登录查询",filters:g(),columns:p(),data:e,fetch:i},null)}});export{I as default};
2 |
--------------------------------------------------------------------------------
/asset/dist/assets/DNSResult-8c28b47b.js:
--------------------------------------------------------------------------------
1 | import{u as i,A as u}from"./detector-090ed643.js";import{d as l,c as o}from"./ExTable-dcbd254b.js";import{N as c}from"./index-aeeb7070.js";import{E as m,n as d}from"./ExDetectorResultTable-2191f62a.js";import{d as p,N as e}from"./ui-32540f7e.js";import{M as f,C as k,r as y}from"./naive-094f875f.js";import"./ExFormInterface-d830d527.js";import"./common-a7d1cc73.js";const D="p1iljsez",A=p({name:"DNSResult",setup(){const t=async s=>{await u(s)};return{dnsDetectorResults:i().dnsDetectorResults,fetch:t}},render(){const{dnsDetectorResults:t,fetch:s}=this,a=[{title:"名称",key:"taskName"},l({title:"结果",key:"result.desc"}),{title:"域名",key:"host"},{title:"最大耗时(ms)",key:"maxDuration"},o({title:"失败信息",key:"messages"}),{title:"更新于",key:"updatedAt",render(r){return c(r.updatedAt)}},{title:"更多",key:"",render(r){const n=[{title:"DNS服务器",key:"server",fixed:"left",width:200},l({title:"结果",key:"result.desc"}),o({title:"IP列表",key:"ips"}),{title:"耗时(ms)",key:"duration"},{title:"失败信息",key:"message"}];return e(k,{placement:"left-end",trigger:"click"},{default:()=>[e("div",{class:D},[e(f,{columns:n,data:r.results},null)])],...{trigger:d}})}}];return e(y,{title:"DNS检测结果"},{default:()=>[e(m,{columns:a,data:t,fetch:s,category:"dns"},null)]})}});export{A as default};
2 |
--------------------------------------------------------------------------------
/middleware/ip_blocker.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package middleware
16 |
17 | import (
18 | "net/http"
19 |
20 | "github.com/vicanso/elton"
21 | "github.com/vicanso/hes"
22 | )
23 |
24 | var (
25 | ErrIPNotAllow = &hes.Error{
26 | StatusCode: http.StatusBadRequest,
27 | Message: "request is forbidden",
28 | Category: "IB",
29 | }
30 | )
31 |
32 | type IPBlockFunc func(string) bool
33 |
34 | // NewIPBlocker create a new block ip middleware
35 | func NewIPBlocker(fn IPBlockFunc) elton.Handler {
36 | return func(c *elton.Context) error {
37 | if fn(c.RealIP()) {
38 | return ErrIPNotAllow
39 | }
40 | return c.Next()
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/middleware/url_prefix.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package middleware
16 |
17 | import (
18 | "fmt"
19 | "net/http"
20 | "regexp"
21 | "strings"
22 |
23 | "github.com/vicanso/elton"
24 | )
25 |
26 | // NewPrefixHandler create a new request url prefix handler
27 | func NewPrefixHandler(prefixes []string) elton.PreHandler {
28 | var prefixReg *regexp.Regexp
29 | if len(prefixes) != 0 {
30 | prefixReg = regexp.MustCompile(fmt.Sprintf(`^(%s)`, strings.Join(prefixes, "|")))
31 | }
32 | return func(req *http.Request) {
33 | if prefixReg == nil {
34 | return
35 | }
36 | req.URL.Path = prefixReg.ReplaceAllString(req.URL.Path, "")
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/asset/dist/assets/HTTPServerInterceptors-873741cf.js:
--------------------------------------------------------------------------------
1 | import{D as m,s as n,E as i}from"./index-aeeb7070.js";import{E as p}from"./ExLoading-572e76eb.js";import{F as o}from"./ExFormInterface-d830d527.js";import{E as c}from"./ExConfigEditorList-10d35f0e.js";import{C as l}from"./configs-5a9029c7.js";import{d as u,f as d,N as s}from"./ui-32540f7e.js";import{u as T}from"./naive-094f875f.js";import"./common-a7d1cc73.js";import"./ExConfigEditor-da704c67.js";import"./ExForm-cdb0faae.js";import"./detector-090ed643.js";import"./ExConfigTable-74c02379.js";import"./ExTable-dcbd254b.js";const k=u({name:"HTTPServerInterceptors",setup(){const e=T(),{routers:t}=i();return d(async()=>{try{await m()}catch(r){n(e,r)}}),{routers:t}},render(){const{routers:e}=this;if(e.processing)return s(p,null,null);const t=[{name:"路由:",key:"data.router",type:o.Select,placeholder:"请选择路由",options:e.items.map(r=>{const a=`${r.method} ${r.route}`;return{label:a,value:a}})},{name:"前置脚本:",key:"data.before",type:o.TextArea,span:24,placeholder:"请输入请求处理前的相关处理脚本"},{name:"后置脚本:",key:"data.after",type:o.TextArea,span:24,placeholder:"请输入请求处理后的相关处理脚本"}];return s(c,{listTitle:"HTTP服务拦截配置",editorTitle:"添加/更新HTTP服务拦截配置",editorDescription:"设置HTTP服务各路由的拦截配置",category:l.HTTPServerInterceptor,extraFormItems:t},null)}});export{k as default};
2 |
--------------------------------------------------------------------------------
/location/location_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package location
16 |
17 | import (
18 | "context"
19 | "testing"
20 |
21 | "github.com/stretchr/testify/assert"
22 | "github.com/vicanso/go-axios"
23 | )
24 |
25 | func TestGetLocationByIP(t *testing.T) {
26 | assert := assert.New(t)
27 |
28 | done := ins.Mock(&axios.Response{
29 | Status: 200,
30 | Data: []byte(`{
31 | "ip": "47.242.95.102",
32 | "country": "美国",
33 | "province": "加利福尼亚",
34 | "city": "圣克拉拉",
35 | "isp": "阿里巴巴"
36 | }`),
37 | })
38 | defer done()
39 | lo, err := GetByIP(context.Background(), "127.0.0.1")
40 | assert.Nil(err)
41 | assert.Equal("圣克拉拉", lo.City)
42 | }
43 |
--------------------------------------------------------------------------------
/log/log_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package log
16 |
17 | import (
18 | "net/url"
19 | "testing"
20 |
21 | "github.com/stretchr/testify/assert"
22 | )
23 |
24 | func TestLogStruct(t *testing.T) {
25 | assert := assert.New(t)
26 |
27 | type T struct {
28 | Name string
29 | }
30 |
31 | e := Struct(&T{
32 | Name: "abc",
33 | })
34 | assert.NotNil(e)
35 |
36 | e = Struct([]*T{
37 | {
38 | Name: "abc",
39 | }, {
40 | Name: "def",
41 | },
42 | })
43 | assert.NotNil(e)
44 |
45 | e = Struct(url.Values{
46 | "key": []string{
47 | "a",
48 | "b",
49 | },
50 | "name": []string{
51 | "a",
52 | },
53 | })
54 | assert.NotNil(e)
55 | }
56 |
--------------------------------------------------------------------------------
/service/application_test.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | )
8 |
9 | func TestApplicationStatus(t *testing.T) {
10 | assert := assert.New(t)
11 | originalApplicationStatus := applicationStatus.Load()
12 | defer SetApplicationStatus(originalApplicationStatus)
13 |
14 | assert.Equal(originalApplicationStatus, GetApplicationStatus())
15 |
16 | SetApplicationStatus(ApplicationStatusStopped)
17 | assert.False(ApplicationIsRunning())
18 | SetApplicationStatus(ApplicationStatusStopping)
19 | assert.False(ApplicationIsRunning())
20 | SetApplicationStatus(ApplicationStatusRunning)
21 | assert.True(ApplicationIsRunning())
22 | }
23 |
24 | func TestApplicationVersion(t *testing.T) {
25 | assert := assert.New(t)
26 | originalVersion := applicationVersion
27 | defer SetApplicationVersion(originalVersion)
28 |
29 | v := "2020"
30 | SetApplicationVersion(v)
31 | assert.Equal(v, GetApplicationVersion())
32 | }
33 |
34 | func TestApplicationBuildedAt(t *testing.T) {
35 | assert := assert.New(t)
36 | originalBuildedAt := applicationBuildedAt
37 | defer SetApplicationBuildedAt(originalBuildedAt)
38 |
39 | buildedAt := "2020"
40 | SetApplicationBuildedAt(buildedAt)
41 | assert.Equal(buildedAt, GetApplicationBuildedAt())
42 | }
43 |
--------------------------------------------------------------------------------
/asset/dist/assets/SessionInterceptors-01600b82.js:
--------------------------------------------------------------------------------
1 | import{E as n}from"./ExConfigEditorList-10d35f0e.js";import{F as s}from"./ExFormInterface-d830d527.js";import{E as i}from"./ExLoading-572e76eb.js";import{D as m,s as p,E as l}from"./index-aeeb7070.js";import{C as c}from"./configs-5a9029c7.js";import{d as u,f as d,N as a}from"./ui-32540f7e.js";import{u as f}from"./naive-094f875f.js";import"./ExConfigEditor-da704c67.js";import"./ExForm-cdb0faae.js";import"./detector-090ed643.js";import"./ExConfigTable-74c02379.js";import"./ExTable-dcbd254b.js";import"./common-a7d1cc73.js";const T=u({name:"SessionInterceptors",setup(){const t=f(),{routers:e}=l();return d(async()=>{try{await m()}catch(r){p(t,r)}}),{routers:e}},render(){const{routers:t}=this;if(t.processing)return a(i,null,null);const e=[];t.items.forEach(o=>{e.includes(o.route)||e.push(o.route)}),e.sort();const r=[{type:s.Blank,name:"",key:""},{name:"提示信息:",key:"data.message",placeholder:"请输入出错提示信息"},{name:"允许账号:",key:"data.allowAccount",placeholder:"请输入允许账号,多个账号以,分割"},{name:"允许路由:",key:"data.allowRoutes",type:s.MultiSelect,placeholder:"请输入允许路由,可以多选",options:e.map(o=>({label:o,value:o}))}];return a(n,{listTitle:"Session拦截配置",editorTitle:"添加/更新Session拦截配置",editorDescription:"设置Session拦截的相关配置",category:c.SessionInterceptor,extraFormItems:r},null)}});export{T as default};
2 |
--------------------------------------------------------------------------------
/util/map.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package util
16 |
17 | // MergeMapStringInterface 合并map[string]interface{}
18 | func MergeMapStringInterface(target map[string]interface{}, sources ...map[string]interface{}) map[string]interface{} {
19 | for _, source := range sources {
20 | for key, value := range source {
21 | if value != nil {
22 | target[key] = value
23 | }
24 | }
25 | }
26 | return target
27 | }
28 |
29 | func MergeMapString(target map[string]string, sources ...map[string]string) map[string]string {
30 | for _, source := range sources {
31 | for key, value := range source {
32 | if value != "" {
33 | target[key] = value
34 | }
35 | }
36 | }
37 | return target
38 | }
39 |
--------------------------------------------------------------------------------
/asset/dist/assets/configs-5a9029c7.js:
--------------------------------------------------------------------------------
1 | import{x as c,J as s,K as a,L as r}from"./index-aeeb7070.js";import{j as o,b as u}from"./ui-32540f7e.js";var l=(n=>(n.MockTime="mockTime",n.BlockIP="blockIP",n.SignedKey="signedKey",n.RouterConcurrency="routerConcurrency",n.SessionInterceptor="sessionInterceptor",n.RequestConcurrency="requestConcurrency",n.Router="router",n.Email="email",n.HTTPServerInterceptor="httpServerInterceptor",n))(l||{}),f=(n=>(n[n.Enabled=1]="Enabled",n[n.Disabled=2]="Disabled",n))(f||{});const e=o({processing:!1,items:[],count:-1});function d(n){n.key=`${n.id}`}async function y(n){const{data:t}=await c.post(s,n);return t}async function k(){const{data:n}=await c.get(s,{params:{category:"mockTime",name:"mockTime",limit:1}}),t=n.configurations||[];return t.length===0?{}:t[0]}async function T(n){const t=r.replace(":id",`${n}`),{data:i}=await c.get(t);return i}async function b(n){const t=r.replace(":id",`${n.id}`);await c.patch(t,n.data)}async function g(n){if(!e.processing){n.limit||(n.limit=50);try{e.processing=!0;const{data:t}=await c.get(s,{params:n}),i=t.count||0;i>=0&&(e.count=i),e.items=t.configurations||[],e.items.forEach(d)}finally{e.processing=!1}}}function w(){e.items=[],e.count=-1}async function D(){const{data:n}=await c.get(a);return n}const m={configs:u(e)};function h(){return m}export{l as C,D as a,f as b,k as c,b as d,y as e,T as f,w as g,g as h,h as u};
2 |
--------------------------------------------------------------------------------
/session/session.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package session
16 |
17 | import (
18 | "github.com/vicanso/elton"
19 | session "github.com/vicanso/elton-session"
20 | "github.com/vicanso/cybertect/cache"
21 | "github.com/vicanso/cybertect/config"
22 | "github.com/vicanso/cybertect/util"
23 | )
24 |
25 | var scf = config.MustGetSessionConfig()
26 |
27 | // New new session middleware
28 | func New() elton.Handler {
29 | store := cache.GetRedisSession()
30 | return session.NewByCookie(session.CookieConfig{
31 | Store: store,
32 | Signed: true,
33 | Expired: scf.TTL,
34 | GenID: func() string {
35 | return util.GenXID()
36 | },
37 | Name: scf.Key,
38 | Path: scf.CookiePath,
39 | MaxAge: int(scf.TTL.Seconds()),
40 | HttpOnly: true,
41 | })
42 | }
43 |
--------------------------------------------------------------------------------
/schema/databasedetector.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package schema
16 |
17 | import (
18 | "entgo.io/ent"
19 | "entgo.io/ent/schema/field"
20 | )
21 |
22 | type DatabaseDetector struct {
23 | ent.Schema
24 | }
25 |
26 | // Fields of the DatabaseDetector
27 | func (DatabaseDetector) Fields() []ent.Field {
28 | // TODO 是否要增加证书配置
29 | return []ent.Field{
30 | field.Strings("uris").
31 | Comment("redis连接串列表"),
32 | field.Text("certPem").
33 | Optional().
34 | Comment("cert pem block数据"),
35 | field.Text("keyPem").
36 | Optional().
37 | Comment("key pem block数据"),
38 | }
39 | }
40 |
41 | // Mixin of the DatabaseDetector
42 | func (DatabaseDetector) Mixin() []ent.Mixin {
43 | return []ent.Mixin{
44 | TimeMixin{},
45 | StatusMixin{},
46 | DetectorMixin{},
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/web/src/components/ExDetectorTable.tsx:
--------------------------------------------------------------------------------
1 | import { TableColumn } from "naive-ui/lib/data-table/src/interface";
2 | import { newListColumn } from "../components/ExTable";
3 | import { formatDate } from "../helpers/util";
4 |
5 | export function getDefaultColumns(): TableColumn[] {
6 | return [
7 | {
8 | title: "名称",
9 | key: "name",
10 | width: 120,
11 | fixed: "left",
12 | },
13 | {
14 | title: "状态",
15 | key: "status",
16 | render(row: Record) {
17 | return row.statusDesc as string;
18 | },
19 | width: 60,
20 | },
21 | {
22 | title: "超时设置",
23 | key: "timeout",
24 | width: 80,
25 | },
26 | {
27 | title: "检测间隔",
28 | key: "interval",
29 | width: 80,
30 | },
31 | newListColumn({
32 | key: "owners",
33 | title: "所有人",
34 | width: 120,
35 | }),
36 | newListColumn({
37 | key: "receivers",
38 | title: "告警接收人",
39 | width: 120,
40 | }),
41 | {
42 | title: "更新于",
43 | key: "updatedAt",
44 | width: 180,
45 | render(row: Record) {
46 | return formatDate(row.updatedAt as string);
47 | },
48 | },
49 | {
50 | title: "配置描述",
51 | key: "description",
52 | width: 100,
53 | ellipsis: {
54 | tooltip: true,
55 | },
56 | },
57 | ];
58 | }
59 |
--------------------------------------------------------------------------------
/validate/user.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package validate
16 |
17 | func init() {
18 | // 用户账号
19 | AddAlias("xUserAccount", "ascii,min=2,max=10")
20 | // 用户密码
21 | AddAlias("xUserPassword", "ascii,len=44")
22 | // 用户名称
23 | AddAlias("xUserName", "min=1,max=20")
24 | // 用户邮箱
25 | AddAlias("xUserEmail", "email")
26 | // 用户角色
27 | AddAlias("xUserRole", "ascii,min=1,max=10")
28 | // 用户分组
29 | AddAlias("xUserGroup", "ascii,min=1,max=10")
30 | // 用户接收告警地址
31 | // 因为更新时使用空格表示删,因此不限制为url
32 | AddAlias("xUserAlarmURL", "ascii,min=1,max=200")
33 | // 用户行为分类
34 | // TODO 是否调整为支持配置的方式
35 | Add("xUserActionCategory", newIsInString([]string{
36 | "click",
37 | "login",
38 | "register",
39 | "routeChange",
40 | "error",
41 | }))
42 | // 用户行为触发所在路由
43 | AddAlias("xUserActionRoute", "max=50")
44 | }
45 |
--------------------------------------------------------------------------------
/asset/dist/assets/Caches-62ddc5f6.js:
--------------------------------------------------------------------------------
1 | import{H as p,x as h,a as d,s as f,t as v}from"./index-aeeb7070.js";import{d as m,r as u,N as s,c}from"./ui-32540f7e.js";import{u as C,v as N,w as n,y as w,d as y,r as k}from"./naive-094f875f.js";import"./common-a7d1cc73.js";async function g(e){const a=p.replace(":key",e),{data:t}=await h.get(a);return t}async function F(e){const a=p.replace(":key",e);await h.delete(a)}const z=m({name:"CachesView",setup(){const e=C(),a=u(""),t=u(!1),l=u("");return{processing:t,key:a,fetch:async()=>{if(!a.value){d(e,"请输入要查询的key");return}if(!t.value){t.value=!0;try{l.value="";const r=await g(a.value);try{const i=JSON.parse(r.data);l.value=JSON.stringify(i,null,2)}catch{l.value=r.data}}catch(r){f(e,r)}finally{t.value=!1}}},del:async()=>{if(!a.value){d(e,"请输入要删除的key");return}if(!t.value)try{l.value="",await F(a.value),v(e,"已成功清除数据")}catch(r){f(e,r)}finally{t.value=!1}},cacheData:l}},render(){const e="large",{fetch:a,cacheData:t,del:l}=this;return s(k,{title:"缓存查询与清除"},{default:()=>[s("p",null,[c("session的缓存格式 ss:sessionID")]),s(N,{xGap:24},{default:()=>[s(n,{span:12},{default:()=>[s(w,{placeholder:"请输入缓存的key",size:e,clearable:!0,onUpdateValue:o=>{this.key=o}},null)]}),s(n,{span:6},{default:()=>[s(y,{class:"widthFull",size:e,onClick:()=>a()},{default:()=>[c("查询")]})]}),s(n,{span:6},{default:()=>[s(y,{class:"widthFull",size:e,onClick:()=>l()},{default:()=>[c("清除")]})]}),t&&s(n,{span:24},{default:()=>[s("pre",null,[t])]})]})]})}});export{z as default};
2 |
--------------------------------------------------------------------------------
/middleware/ip_blocker_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package middleware
16 |
17 | import (
18 | "net/http/httptest"
19 | "testing"
20 |
21 | "github.com/stretchr/testify/assert"
22 | "github.com/vicanso/elton"
23 | )
24 |
25 | func TestNewIPBlocker(t *testing.T) {
26 | assert := assert.New(t)
27 | blockFn := func(ip string) bool {
28 | return ip == "1.1.1.1"
29 | }
30 | fn := NewIPBlocker(blockFn)
31 |
32 | req := httptest.NewRequest("GET", "/", nil)
33 | req.Header.Set(elton.HeaderXForwardedFor, "1.1.1.1")
34 | c := elton.NewContext(nil, req)
35 | err := fn(c)
36 | assert.Equal(ErrIPNotAllow, err)
37 |
38 | req.Header.Del(elton.HeaderXForwardedFor)
39 | // 由于context的ip会缓存,因此重新创建
40 | c = elton.NewContext(nil, req)
41 | done := false
42 | c.Next = func() error {
43 | done = true
44 | return nil
45 | }
46 | err = fn(c)
47 | assert.Nil(err)
48 | assert.True(done)
49 | }
50 |
--------------------------------------------------------------------------------
/middleware/entry_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package middleware
16 |
17 | import (
18 | "net/http/httptest"
19 | "testing"
20 |
21 | "github.com/stretchr/testify/assert"
22 | "github.com/vicanso/elton"
23 | )
24 |
25 | func TestNewEntry(t *testing.T) {
26 | assert := assert.New(t)
27 | req := httptest.NewRequest("GET", "/", nil)
28 | c := elton.NewContext(httptest.NewRecorder(), req)
29 | c.ID = "abc"
30 | done := false
31 | c.Next = func() error {
32 | done = true
33 | return nil
34 | }
35 | doneEntry := false
36 | doneExit := false
37 |
38 | fn := NewEntry(func() int32 {
39 | doneEntry = true
40 | return 0
41 | }, func() int32 {
42 | doneExit = true
43 | return 0
44 | })
45 | err := fn(c)
46 | assert.Nil(err)
47 | assert.True(done)
48 | assert.True(doneEntry)
49 | assert.True(doneExit)
50 | assert.Equal("abc", c.GetHeader(xResponseID))
51 | assert.Equal("no-cache", c.GetHeader("Cache-Control"))
52 | }
53 |
--------------------------------------------------------------------------------
/web/src/components/ExFluxDetail.tsx:
--------------------------------------------------------------------------------
1 | import { InfoCircle } from "@vicons/fa";
2 | import { NIcon, NPopover } from "naive-ui";
3 | import { defineComponent, PropType } from "vue";
4 | import ExFluxDetailList from "./ExFluxDetailList";
5 |
6 | export default defineComponent({
7 | name: "ExFluxDetail",
8 | props: {
9 | measurement: {
10 | type: String,
11 | required: true,
12 | },
13 | data: {
14 | type: Object as PropType>,
15 | required: true,
16 | },
17 | tagKeys: {
18 | type: Array as PropType,
19 | required: true,
20 | },
21 | },
22 | render() {
23 | const { data, measurement, tagKeys } = this.$props;
24 | const slots = {
25 | trigger: () => (
26 |
27 |
28 |
29 | ),
30 | };
31 | const tags: Record = {};
32 | Object.keys(data).forEach((key) => {
33 | if (!tagKeys.includes(key)) {
34 | return;
35 | }
36 | const v = data[key];
37 | if (!v) {
38 | return;
39 | }
40 | tags[key] = v as string;
41 | });
42 | return (
43 |
50 |
55 |
56 | );
57 | },
58 | });
59 |
--------------------------------------------------------------------------------
/schema/databasedetectorresult.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package schema
16 |
17 | import (
18 | "entgo.io/ent"
19 | "entgo.io/ent/schema/field"
20 | )
21 |
22 | type DatabaseDetectorResult struct {
23 | ent.Schema
24 | }
25 |
26 | type DatabaseDetectorSubResult struct {
27 | Result DetectorResult `json:"result"`
28 | URI string `json:"uri"`
29 | Duration int `json:"duration"`
30 | Message string `json:"message"`
31 | }
32 |
33 | type DatabaseDetectorSubResults []*DatabaseDetectorSubResult
34 |
35 | // Fields of the DatabaseDetectorResult
36 | func (DatabaseDetectorResult) Fields() []ent.Field {
37 | return []ent.Field{
38 | field.Strings("uris").
39 | Comment("检测的redis连接地址"),
40 | field.JSON("results", DatabaseDetectorSubResults{}).
41 | Comment("检测结果列表"),
42 | }
43 | }
44 |
45 | func (DatabaseDetectorResult) Mixin() []ent.Mixin {
46 | return []ent.Mixin{
47 | TimeMixin{},
48 | DetectorResultMixin{},
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/.air.toml:
--------------------------------------------------------------------------------
1 | # Config file for [Air](https://github.com/cosmtrek/air) in TOML format
2 |
3 | # Working directory
4 | # . or absolute path, please note that the directories following must be under root.
5 | root = "."
6 | tmp_dir = "tmp"
7 |
8 | [build]
9 | # Just plain old shell command. You could use `make` as well.
10 | cmd = "go build -o ./tmp/main ."
11 | # Binary file yields from `cmd`.
12 | bin = "tmp/main"
13 | # Customize binary.
14 | full_bin = "GO_ENV=dev MAIL_USER=test@outlook.com SECRET=cybertect ./tmp/main"
15 | # Watch these filename extensions.
16 | include_ext = ["go", "yml"]
17 | # Ignore these filename extensions or directories.
18 | exclude_dir = ["assets", "tmp", "vendor", "web"]
19 | # Watch these directories if you specified.
20 | include_dir = []
21 | # Exclude files.
22 | exclude_file = []
23 | # Exclude unchanged files.
24 | exclude_unchanged = true
25 | # This log file places in your tmp_dir.
26 | log = "air.log"
27 | # It's not necessary to trigger build each time file changes if it's too frequent.
28 | delay = 1000 # ms
29 | # Stop running old binary when build errors occur.
30 | stop_on_error = true
31 | # Send Interrupt signal before killing process (windows does not support this feature)
32 | send_interrupt = true
33 | # Delay after sending Interrupt signal
34 | kill_delay = 500 # ms
35 |
36 | [log]
37 | # Show log time
38 | time = false
39 |
40 | [color]
41 | # Customize each part's color. If no color found, use the raw app log.
42 | main = "magenta"
43 | watcher = "cyan"
44 | build = "yellow"
45 | runner = "green"
46 |
47 | [misc]
48 | # Delete tmp directory on exit
49 | clean_on_exit = true
--------------------------------------------------------------------------------
/asset/dist/assets/RouterMocks-02b867d7.js:
--------------------------------------------------------------------------------
1 | import{E as n}from"./ExConfigEditorList-10d35f0e.js";import{C as m}from"./configs-5a9029c7.js";import{F as e}from"./ExFormInterface-d830d527.js";import{D as l,s as p,E as i}from"./index-aeeb7070.js";import{E as c}from"./ExLoading-572e76eb.js";import{d as u,f as d,N as s}from"./ui-32540f7e.js";import{u as y}from"./naive-094f875f.js";import"./ExConfigEditor-da704c67.js";import"./ExForm-cdb0faae.js";import"./detector-090ed643.js";import"./ExConfigTable-74c02379.js";import"./ExTable-dcbd254b.js";import"./common-a7d1cc73.js";const v=u({name:"RouterMocks",setup(){const{routers:t}=i(),r=y();return d(async()=>{try{await l()}catch(o){p(r,o)}}),{routers:t}},render(){const{routers:t}=this;if(t.processing)return s(c,null,null);const r=[{type:e.Blank,name:"",key:""},{name:"路由:",key:"data.router",type:e.Select,placeholder:"请选择路由",options:t.items.map(o=>{const a=`${o.method} ${o.route}`;return{label:a,value:a}})},{name:"状态码:",key:"data.status",type:e.InputNumber,placeholder:"请输入响应状态码"},{name:"响应数据类型:",type:e.Select,key:"data.contentType",placeholder:"请选择响应数据类型",options:[{label:"json",value:"application/json; charset=UTF-8"},{label:"plain",value:"text/plain; charset=UTF-8"},{label:"html",value:"text/html; charset=UTF-8"}]},{name:"延时响应:",key:"data.delaySeconds",type:e.InputNumber,placeholder:"请输入延时响应时长(秒)"},{name:"完整URL:",key:"data.url",placeholder:"请输入完整的url(可选)"},{name:"响应数据:",key:"data.response",type:e.TextArea,span:24,placeholder:"请输入响应数据"}];return s(n,{listTitle:"路由Mock配置",editorTitle:"添加/更新路由Mock配置",editorDescription:"设置各路由的Mock响应",category:m.Router,extraFormItems:r},null)}});export{v as default};
2 |
--------------------------------------------------------------------------------
/middleware/interceptor.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package middleware
16 |
17 | import (
18 | "github.com/vicanso/elton"
19 | "github.com/vicanso/cybertect/interceptor"
20 | )
21 |
22 | func NewInterceptor() elton.Handler {
23 | return func(c *elton.Context) error {
24 | inter, err := interceptor.NewHTTPServer(c)
25 | if err != nil {
26 | return err
27 | }
28 | // 如果返回空表示没有设置interceptor
29 | if inter == nil {
30 | return c.Next()
31 | }
32 | // 前置处理
33 | resp, err := inter.Before()
34 | if err != nil {
35 | return err
36 | }
37 | // 如果状态码不为0,则表示已设置响应数据
38 | if resp != nil && resp.Status != 0 {
39 | resp.SetResponse(c)
40 | return nil
41 | }
42 | err = c.Next()
43 | if err != nil {
44 | return err
45 | }
46 | // 后置处理
47 | resp, err = inter.After()
48 | if err != nil {
49 | return err
50 | }
51 | // 如果状态码不为0,则表示重设响应数据
52 | if resp != nil && resp.Status != 0 {
53 | resp.SetResponse(c)
54 | return nil
55 | }
56 | return nil
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/util/context_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 | package util
15 |
16 | import (
17 | "net/http"
18 | "net/http/httptest"
19 | "testing"
20 |
21 | "github.com/stretchr/testify/assert"
22 | "github.com/vicanso/elton"
23 | "github.com/vicanso/cybertect/config"
24 | )
25 |
26 | func TestGetTrackID(t *testing.T) {
27 | assert := assert.New(t)
28 | sessionConfig = config.MustGetSessionConfig()
29 | req := httptest.NewRequest("GET", "/", nil)
30 | cookie := http.Cookie{
31 | Name: sessionConfig.TrackKey,
32 | Value: "abcd",
33 | }
34 | req.AddCookie(&cookie)
35 | c := elton.NewContext(nil, req)
36 | assert.Equal(cookie.Value, GetTrackID(c))
37 | }
38 |
39 | func TestGetSessionID(t *testing.T) {
40 | assert := assert.New(t)
41 | sessionConfig = config.MustGetSessionConfig()
42 | req := httptest.NewRequest("GET", "/", nil)
43 | cookie := http.Cookie{
44 | Name: sessionConfig.Key,
45 | Value: "abcd",
46 | }
47 | req.AddCookie(&cookie)
48 | c := elton.NewContext(nil, req)
49 | assert.Equal(cookie.Value, GetSessionID(c))
50 | }
51 |
--------------------------------------------------------------------------------
/schema/time.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 tree xie
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package schema
16 |
17 | import (
18 | "time"
19 |
20 | "entgo.io/ent"
21 | "entgo.io/ent/schema/field"
22 | "entgo.io/ent/schema/index"
23 | "entgo.io/ent/schema/mixin"
24 | )
25 |
26 | // TimeMixin 公共的时间schema
27 | type TimeMixin struct {
28 | mixin.Schema
29 | }
30 |
31 | // Fields 公共时间schema的字段,包括创建于与更新于
32 | func (TimeMixin) Fields() []ent.Field {
33 | return []ent.Field{
34 | field.Time("created_at").
35 | // 对于多个单词组成的,如果需要使用select,则需要添加sql tag
36 | StructTag(`json:"createdAt" sql:"created_at"`).
37 | Immutable().
38 | Default(time.Now).
39 | Comment("创建时间,添加记录时由程序自动生成"),
40 | field.Time("updated_at").
41 | StructTag(`json:"updatedAt" sql:"updated_at"`).
42 | Default(time.Now).
43 | Immutable().
44 | UpdateDefault(time.Now).
45 | Comment("更新时间,更新记录时由程序自动生成"),
46 | }
47 | }
48 |
49 | // Indexes 公共时间字段索引
50 | func (TimeMixin) Indexes() []ent.Index {
51 | return []ent.Index{
52 | index.Fields("created_at"),
53 | index.Fields("updated_at"),
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/web/README.md:
--------------------------------------------------------------------------------
1 | # Vue 3 + Typescript + Vite
2 |
3 | This template should help get you started developing with Vue 3 and Typescript in Vite.
4 |
5 | ## Recommended IDE Setup
6 |
7 | [VSCode](https://code.visualstudio.com/) + [Vetur](https://marketplace.visualstudio.com/items?itemName=octref.vetur). Make sure to enable `vetur.experimental.templateInterpolationService` in settings!
8 |
9 | ### If Using `