├── .env.example
├── .gitattributes
├── .gitignore
├── .settings
└── org.eclipse.wst.validation.prefs
├── application
├── Services
│ ├── api.go
│ ├── basic.go
│ ├── encrypt.go
│ ├── project.go
│ ├── template.go
│ ├── userauth.go
│ └── users.go
├── common
│ ├── common.go
│ └── desktop.go
├── controllers
│ ├── AppsController.go
│ ├── CacheController.go
│ ├── GroupController.go
│ ├── HelpController.go
│ ├── IndexController.go
│ ├── MessageController.go
│ ├── OperateLogController.go
│ ├── OrganizeController.go
│ ├── ProjectController.go
│ ├── SysController.go
│ ├── UploadController.go
│ ├── UsersController.go
│ └── manager
│ │ ├── ApiController.go
│ │ ├── CategoryController.go
│ │ ├── CompanyController.go
│ │ └── DashController.go
├── middleware
│ ├── CheckApiAuthenticated.go
│ ├── CheckAuthenticated.go
│ ├── GlobalMiddleware.go
│ └── IsAuthenticated.go
├── models
│ ├── Basic.go
│ ├── Cache.go
│ ├── Classify.go
│ ├── Company.go
│ ├── DB.go
│ ├── Group.go
│ ├── Help.go
│ ├── ManagerApi.go
│ ├── Message.go
│ ├── OperateLog.go
│ ├── Organize.go
│ ├── Project.go
│ ├── Rules.go
│ ├── Statistics.go
│ ├── System.go
│ └── Users.go
├── requests
│ ├── Api.go
│ ├── Category.go
│ ├── Company.go
│ ├── Help.go
│ ├── Login.go
│ ├── Organize.go
│ ├── Project.go
│ ├── ProjectEnv.go
│ ├── Register.go
│ └── Reset.go
├── schedule
│ └── index.html
└── utils
│ ├── e.go
│ ├── jwt.go
│ └── logrus.go
├── config
├── config.json
├── fetch.go
└── global.go
├── data
└── qiyu.sql
├── go.mod
├── go.sum
├── main.go
├── readme.md
├── resources
├── assets
│ ├── css
│ │ ├── animate.css
│ │ ├── bootstrap-rtl.css
│ │ ├── bootstrap.min.css
│ │ ├── demo
│ │ │ └── webuploader-demo.css
│ │ ├── font-awesome.css
│ │ ├── font-awesome.min.css
│ │ ├── login.css
│ │ ├── patterns
│ │ │ ├── header-profile-skin-1.png
│ │ │ ├── header-profile-skin-3.png
│ │ │ ├── header-profile.png
│ │ │ └── shattered.png
│ │ ├── plugins
│ │ │ ├── awesome-bootstrap-checkbox
│ │ │ │ └── awesome-bootstrap-checkbox.css
│ │ │ ├── blueimp
│ │ │ │ ├── css
│ │ │ │ │ ├── blueimp-gallery-indicator.css
│ │ │ │ │ ├── blueimp-gallery-video.css
│ │ │ │ │ ├── blueimp-gallery.css
│ │ │ │ │ ├── blueimp-gallery.min.css
│ │ │ │ │ └── demo.css
│ │ │ │ └── img
│ │ │ │ │ ├── error.png
│ │ │ │ │ ├── error.svg
│ │ │ │ │ ├── loading.gif
│ │ │ │ │ ├── play-pause.png
│ │ │ │ │ ├── play-pause.svg
│ │ │ │ │ ├── video-play.png
│ │ │ │ │ └── video-play.svg
│ │ │ ├── bootstrap-table
│ │ │ │ └── bootstrap-table.min.css
│ │ │ ├── chosen
│ │ │ │ ├── chosen-sprite.png
│ │ │ │ ├── chosen-sprite@2x.png
│ │ │ │ └── chosen.css
│ │ │ ├── clockpicker
│ │ │ │ └── clockpicker.css
│ │ │ ├── codemirror
│ │ │ │ ├── ambiance.css
│ │ │ │ └── codemirror.css
│ │ │ ├── colorpicker
│ │ │ │ ├── css
│ │ │ │ │ └── bootstrap-colorpicker.min.css
│ │ │ │ └── img
│ │ │ │ │ └── bootstrap-colorpicker
│ │ │ │ │ ├── alpha-horizontal.png
│ │ │ │ │ ├── alpha.png
│ │ │ │ │ ├── hue-horizontal.png
│ │ │ │ │ ├── hue.png
│ │ │ │ │ └── saturation.png
│ │ │ ├── cropper
│ │ │ │ └── cropper.min.css
│ │ │ ├── dataTables
│ │ │ │ └── dataTables.bootstrap.css
│ │ │ ├── datapicker
│ │ │ │ └── datepicker3.css
│ │ │ ├── duallistbox
│ │ │ │ └── bootstrap-duallistbox.css
│ │ │ ├── fullcalendar
│ │ │ │ ├── fullcalendar.css
│ │ │ │ └── fullcalendar.print.css
│ │ │ ├── iCheck
│ │ │ │ ├── custom.css
│ │ │ │ ├── green.png
│ │ │ │ └── green@2x.png
│ │ │ ├── jQueryUI
│ │ │ │ ├── images
│ │ │ │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png
│ │ │ │ │ ├── ui-bg_flat_75_ffffff_40x100.png
│ │ │ │ │ ├── ui-icons_222222_256x240.png
│ │ │ │ │ ├── ui-icons_454545_256x240.png
│ │ │ │ │ └── ui-icons_888888_256x240.png
│ │ │ │ └── jquery-ui-1.10.4.custom.min.css
│ │ │ ├── jasny
│ │ │ │ └── jasny-bootstrap.min.css
│ │ │ ├── morris
│ │ │ │ └── morris-0.4.3.min.css
│ │ │ ├── multiselect
│ │ │ │ └── bootstrap-multiselect.css
│ │ │ ├── plyr
│ │ │ │ ├── plyr.css
│ │ │ │ └── sprite.svg
│ │ │ ├── steps
│ │ │ │ └── jquery.steps.css
│ │ │ ├── sweetalert
│ │ │ │ └── sweetalert.css
│ │ │ ├── switchery
│ │ │ │ └── switchery.css
│ │ │ └── toastr
│ │ │ │ └── toastr.min.css
│ │ ├── select.min.css
│ │ └── style.css
│ ├── favicon.ico
│ ├── fonts
│ │ ├── FontAwesome.otf
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.svg
│ │ ├── fontawesome-webfont.ttf
│ │ ├── fontawesome-webfont.woff
│ │ ├── fontawesome-webfont.woff2
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.svg
│ │ ├── glyphicons-halflings-regular.ttf
│ │ ├── glyphicons-halflings-regular.woff
│ │ └── glyphicons-halflings-regular.woff2
│ ├── img
│ │ ├── Qiyu.png
│ │ ├── avatar.png
│ │ ├── bg.png
│ │ ├── icons.png
│ │ ├── loading-upload.gif
│ │ ├── locked.png
│ │ ├── login-background.jpg
│ │ ├── p1.jpg
│ │ ├── p3.jpg
│ │ ├── p_big1.jpg
│ │ ├── p_big2.jpg
│ │ ├── p_big3.jpg
│ │ ├── profile_big.jpg
│ │ ├── progress.png
│ │ ├── sprite-skin-flat.png
│ │ ├── success.png
│ │ ├── team.png
│ │ ├── user.png
│ │ └── webuploader.png
│ ├── index.html
│ └── js
│ │ ├── apiInfo.js
│ │ ├── apiaudit.js
│ │ ├── apidetail.js
│ │ ├── apilist.js
│ │ ├── apisearch.js
│ │ ├── apps.js
│ │ ├── bootstrap.min.js
│ │ ├── category.js
│ │ ├── common.js
│ │ ├── company.js
│ │ ├── content.js
│ │ ├── format
│ │ ├── json.js
│ │ ├── jsonp.js
│ │ └── xml.js
│ │ ├── group.js
│ │ ├── help.js
│ │ ├── index.js
│ │ ├── jquery-ui-1.10.4.min.js
│ │ ├── jquery-ui.custom.min.js
│ │ ├── jquery.cookie.js
│ │ ├── jquery.form.js
│ │ ├── jquery.min.js
│ │ ├── jquery.min.map
│ │ ├── jquery.validate.js
│ │ ├── login.js
│ │ ├── manager.js
│ │ ├── md5.min.js
│ │ ├── message.js
│ │ ├── organize.js
│ │ ├── plugins
│ │ ├── ObjTree
│ │ │ ├── JSAN.js
│ │ │ └── lib
│ │ │ │ └── XML
│ │ │ │ └── ObjTree.js
│ │ ├── clipboard
│ │ │ └── clipboard.min.js
│ │ ├── echarts
│ │ │ ├── china.js
│ │ │ └── echarts.min.js
│ │ ├── footable
│ │ │ ├── fonts
│ │ │ │ ├── footable.all.min.js
│ │ │ │ ├── footable.eot
│ │ │ │ ├── footable.svg
│ │ │ │ ├── footable.ttf
│ │ │ │ └── footable.woff
│ │ │ ├── footable.all.min.js
│ │ │ └── footable.core.css
│ │ ├── froala
│ │ │ ├── css
│ │ │ │ ├── froala_editor.css
│ │ │ │ ├── froala_editor.min.css
│ │ │ │ ├── froala_editor.pkgd.css
│ │ │ │ ├── froala_editor.pkgd.min.css
│ │ │ │ ├── froala_style.css
│ │ │ │ ├── froala_style.min.css
│ │ │ │ ├── plugins
│ │ │ │ │ ├── char_counter.css
│ │ │ │ │ ├── char_counter.min.css
│ │ │ │ │ ├── code_view.css
│ │ │ │ │ ├── code_view.min.css
│ │ │ │ │ ├── colors.css
│ │ │ │ │ ├── colors.min.css
│ │ │ │ │ ├── draggable.css
│ │ │ │ │ ├── draggable.min.css
│ │ │ │ │ ├── emoticons.css
│ │ │ │ │ ├── emoticons.min.css
│ │ │ │ │ ├── file.css
│ │ │ │ │ ├── file.min.css
│ │ │ │ │ ├── fullscreen.css
│ │ │ │ │ ├── fullscreen.min.css
│ │ │ │ │ ├── help.css
│ │ │ │ │ ├── help.min.css
│ │ │ │ │ ├── image.css
│ │ │ │ │ ├── image.min.css
│ │ │ │ │ ├── image_manager.css
│ │ │ │ │ ├── image_manager.min.css
│ │ │ │ │ ├── line_breaker.css
│ │ │ │ │ ├── line_breaker.min.css
│ │ │ │ │ ├── quick_insert.css
│ │ │ │ │ ├── quick_insert.min.css
│ │ │ │ │ ├── special_characters.css
│ │ │ │ │ ├── special_characters.min.css
│ │ │ │ │ ├── table.css
│ │ │ │ │ ├── table.min.css
│ │ │ │ │ ├── video.css
│ │ │ │ │ └── video.min.css
│ │ │ │ ├── themes
│ │ │ │ │ ├── dark.css
│ │ │ │ │ ├── dark.min.css
│ │ │ │ │ ├── gray.css
│ │ │ │ │ ├── gray.min.css
│ │ │ │ │ ├── red.css
│ │ │ │ │ ├── red.min.css
│ │ │ │ │ ├── royal.css
│ │ │ │ │ └── royal.min.css
│ │ │ │ └── third_party
│ │ │ │ │ ├── embedly.css
│ │ │ │ │ ├── embedly.min.css
│ │ │ │ │ ├── font_awesome.css
│ │ │ │ │ ├── font_awesome.min.css
│ │ │ │ │ ├── image_tui.css
│ │ │ │ │ ├── image_tui.min.css
│ │ │ │ │ ├── spell_checker.css
│ │ │ │ │ └── spell_checker.min.css
│ │ │ └── js
│ │ │ │ ├── froala_editor.min.js
│ │ │ │ ├── froala_editor.pkgd.min.js
│ │ │ │ ├── languages
│ │ │ │ ├── ar.js
│ │ │ │ ├── bs.js
│ │ │ │ ├── cs.js
│ │ │ │ ├── da.js
│ │ │ │ ├── de.js
│ │ │ │ ├── el.js
│ │ │ │ ├── en_ca.js
│ │ │ │ ├── en_gb.js
│ │ │ │ ├── es.js
│ │ │ │ ├── et.js
│ │ │ │ ├── fa.js
│ │ │ │ ├── fi.js
│ │ │ │ ├── fr.js
│ │ │ │ ├── he.js
│ │ │ │ ├── hr.js
│ │ │ │ ├── hu.js
│ │ │ │ ├── id.js
│ │ │ │ ├── it.js
│ │ │ │ ├── ja.js
│ │ │ │ ├── ko.js
│ │ │ │ ├── ku.js
│ │ │ │ ├── me.js
│ │ │ │ ├── nb.js
│ │ │ │ ├── nl.js
│ │ │ │ ├── pl.js
│ │ │ │ ├── pt_br.js
│ │ │ │ ├── pt_pt.js
│ │ │ │ ├── ro.js
│ │ │ │ ├── ru.js
│ │ │ │ ├── sk.js
│ │ │ │ ├── sr.js
│ │ │ │ ├── sv.js
│ │ │ │ ├── th.js
│ │ │ │ ├── tr.js
│ │ │ │ ├── uk.js
│ │ │ │ ├── vi.js
│ │ │ │ ├── zh_cn.js
│ │ │ │ └── zh_tw.js
│ │ │ │ ├── plugins
│ │ │ │ ├── align.min.js
│ │ │ │ ├── char_counter.min.js
│ │ │ │ ├── code_beautifier.min.js
│ │ │ │ ├── code_view.min.js
│ │ │ │ ├── colors.min.js
│ │ │ │ ├── draggable.min.js
│ │ │ │ ├── emoticons.min.js
│ │ │ │ ├── entities.min.js
│ │ │ │ ├── file.min.js
│ │ │ │ ├── font_family.min.js
│ │ │ │ ├── font_size.min.js
│ │ │ │ ├── forms.min.js
│ │ │ │ ├── fullscreen.min.js
│ │ │ │ ├── help.min.js
│ │ │ │ ├── image.min.js
│ │ │ │ ├── image_manager.min.js
│ │ │ │ ├── inline_class.min.js
│ │ │ │ ├── inline_style.min.js
│ │ │ │ ├── line_breaker.min.js
│ │ │ │ ├── line_height.min.js
│ │ │ │ ├── link.min.js
│ │ │ │ ├── lists.min.js
│ │ │ │ ├── paragraph_format.min.js
│ │ │ │ ├── paragraph_style.min.js
│ │ │ │ ├── print.min.js
│ │ │ │ ├── quick_insert.min.js
│ │ │ │ ├── quote.min.js
│ │ │ │ ├── save.min.js
│ │ │ │ ├── special_characters.min.js
│ │ │ │ ├── table.min.js
│ │ │ │ ├── url.min.js
│ │ │ │ ├── video.min.js
│ │ │ │ └── word_paste.min.js
│ │ │ │ └── third_party
│ │ │ │ ├── embedly.min.js
│ │ │ │ ├── font_awesome.min.js
│ │ │ │ ├── image_aviary.min.js
│ │ │ │ ├── image_tui.min.js
│ │ │ │ └── spell_checker.min.js
│ │ ├── iCheck
│ │ │ └── icheck.min.js
│ │ ├── jsonview
│ │ │ ├── jquery.jsonview.min.css
│ │ │ └── jquery.jsonview.min.js
│ │ ├── metisMenu
│ │ │ └── jquery.metisMenu.js
│ │ ├── nestable
│ │ │ └── jquery.nestable.js
│ │ ├── pace
│ │ │ └── pace.min.js
│ │ ├── pagination
│ │ │ ├── jquery.pagination.js
│ │ │ ├── jquery.pagination.min.js
│ │ │ └── pagination.css
│ │ ├── slideunlock
│ │ │ └── jquery.slideunlock.js
│ │ ├── slimscroll
│ │ │ └── jquery.slimscroll.min.js
│ │ ├── sweetalert
│ │ │ └── sweetalert.min.js
│ │ ├── validate
│ │ │ ├── additional-methods.min.js
│ │ │ ├── jquery.validate.min.js
│ │ │ └── messages_zh.min.js
│ │ └── vkBeautify
│ │ │ └── vkbeautify.js
│ │ ├── project.js
│ │ ├── renderlayer.js
│ │ ├── statistics.js
│ │ ├── system.js
│ │ ├── users.js
│ │ └── util.js
└── templates
│ ├── 404.html
│ ├── apps.html
│ ├── apps_json.html
│ ├── apps_timestamp.html
│ ├── apps_transform.html
│ ├── group.html
│ ├── group_data.html
│ ├── group_feature.html
│ ├── group_info.html
│ ├── header.html
│ ├── help_detail.html
│ ├── help_info.html
│ ├── help_list.html
│ ├── index.html
│ ├── index_api.html
│ ├── left_side.html
│ ├── left_site_api.html
│ ├── login.html
│ ├── manager_apiaudit.html
│ ├── manager_apidetail.html
│ ├── manager_apiinfo.html
│ ├── manager_apiinfo_modify.html
│ ├── manager_apilist.html
│ ├── manager_apisearch.html
│ ├── manager_category.html
│ ├── manager_category_detail.html
│ ├── manager_category_info.html
│ ├── manager_category_infoSub.html
│ ├── manager_category_sub.html
│ ├── manager_company.html
│ ├── manager_company_info.html
│ ├── manager_dash.html
│ ├── message_detail.html
│ ├── message_list.html
│ ├── operate_log.html
│ ├── organize.html
│ ├── organize_detail.html
│ ├── project.html
│ ├── project_env.html
│ ├── project_info.html
│ ├── register.html
│ ├── users.html
│ ├── users_detail.html
│ ├── users_person.html
│ └── website.html
├── routes
├── initRouter.go
├── project.go
└── web.go
├── screenshot
├── apilist.png
├── apisearch.gif
├── apps.jpg
├── mock.gif
├── plugin.gif
├── team.gif
└── testing.gif
└── storage
└── logs
└── index.log
/.env.example:
--------------------------------------------------------------------------------
1 | APP_ENV=local
2 | APP_KEY=base64:z9ap8/tdcUGxmiEkBaN5evAI16Ky2bywIObmMeMafGU=
3 | APP_DEBUG=false;
4 | APP_LOG_LEVEL=debug
5 | APP_URL=http://localhost
6 | APP_VERSION=2.0.0
7 | APP_INSTALL=0
8 |
9 | DB_CONNECTION=mysql
10 | DB_HOST=127.0.0.1
11 | DB_PORT=3306
12 | DB_DATABASE=xapi
13 | DB_PREFIX=mx_
14 | DB_USERNAME=root
15 | DB_PASSWORD=root123
16 |
17 | BROADCAST_DRIVER=log
18 | CACHE_DRIVER=file
19 | SESSION_DRIVER=file
20 | QUEUE_DRIVER=sync
21 |
22 | REDIS_HOST=127.0.0.1
23 | REDIS_PASSWORD=null
24 | REDIS_PORT=6379
25 |
26 | MAIL_DRIVER=smtp
27 | MAIL_HOST=mailtrap.io
28 | MAIL_PORT=2525
29 | MAIL_USERNAME=null
30 | MAIL_PASSWORD=null
31 | MAIL_ENCRYPTION=null
32 |
33 | PUSHER_APP_ID=
34 | PUSHER_APP_KEY=
35 | PUSHER_APP_SECRET=
36 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.css linguist-vendored
3 | *.scss linguist-vendored
4 | *.js linguist-language=go
5 | *.css linguist-language=go
6 | *.html linguist-language=go
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /public/storage
3 | /public/hot
4 | /storage/*.key
5 | /.idea
6 | Homestead.json
7 | Homestead.yaml
8 | .env
9 | .project
10 | .buildpath
11 | .vscode
12 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.wst.validation.prefs:
--------------------------------------------------------------------------------
1 | disabled=06vendor
2 | eclipse.preferences.version=1
3 |
--------------------------------------------------------------------------------
/application/Services/api.go:
--------------------------------------------------------------------------------
1 | package Services
2 |
3 | import (
4 | "reflect"
5 | "strings"
6 | "time"
7 | "xapimanager/application/common"
8 | "xapimanager/application/models"
9 | )
10 |
11 | func SyncApiDetail(did int, proid int, envid int) bool {
12 |
13 | var id int
14 | //同步信息检查
15 | detail := models.GetApiInfo(did)
16 | con := map[string]interface{}{
17 | "listid": detail.Listid,
18 | "proid": proid,
19 | "envid": envid,
20 | "version": detail.Version,
21 | }
22 | apidetail := models.GetApiDetail(con)
23 | //获取api信息
24 | time := time.Now().Unix()
25 | var data = map[string]interface{}{
26 | "envid": envid,
27 | "ctime": int(time),
28 | "mtime": int(time),
29 | "status": 1,
30 | }
31 | if detail.Id > 0 {
32 | t := reflect.TypeOf(detail)
33 | v := reflect.ValueOf(detail)
34 | for k := 0; k < t.NumField(); k++ {
35 | key := strings.ToLower(t.Field(k).Name)
36 | if !common.CheckAuth(key, []string{"id", "envid", "mtime", "ctime", "status"}) {
37 | data[strings.ToLower(t.Field(k).Name)] = v.Field(k).Interface()
38 | }
39 | }
40 | }
41 | if apidetail.Id > 0 {
42 | id = apidetail.Id
43 | } else {
44 | id = 0
45 | }
46 | result := models.ApiDetailStore(id, proid, envid, data)
47 |
48 | return result
49 | }
50 |
--------------------------------------------------------------------------------
/application/Services/basic.go:
--------------------------------------------------------------------------------
1 | package Services
2 |
3 | import (
4 | "encoding/json"
5 | "time"
6 | "xapimanager/application/models"
7 | "xapimanager/config"
8 | )
9 |
10 | /**
11 | * 获得站点信息
12 | * @result 站点信息
13 | */
14 | func GetWebsite() (site map[string]string) {
15 |
16 | var data string
17 | var err error
18 | site = map[string]string{}
19 | Cache := models.CacheConnect()
20 | key := "qy_website"
21 | if Cache.Hander != nil {
22 | data, err = Cache.Hander.Get(key).Result()
23 | if err == nil {
24 | json.Unmarshal([]byte(data), &site)
25 | }
26 | }
27 | if Cache.Hander == nil || err != nil {
28 | site = models.GetWebsite()
29 | jsonStr, _ := json.Marshal(site)
30 | if Cache.Hander != nil {
31 | Cache.Hander.Set(key, jsonStr,
32 | time.Second*time.Duration(config.GetGlobal().Sys_Cache))
33 | }
34 |
35 | }
36 |
37 | return
38 | }
39 |
40 | /**
41 | * 清除指定key的缓存
42 | */
43 | func ClearCache(key string) {
44 | // 清除分类缓存
45 | Cache := models.CacheConnect()
46 | if Cache.Hander != nil {
47 | Cache.Hander.Del(key).Result()
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/application/Services/encrypt.go:
--------------------------------------------------------------------------------
1 | package Services
2 |
3 | import (
4 | "bytes"
5 | "crypto/aes"
6 | "crypto/cipher"
7 | "encoding/base64"
8 | )
9 |
10 | //AES 加密
11 | func AesEncrypt(orig string, key string) string {
12 | // 转成字节数组
13 | origData := []byte(orig)
14 | k := []byte(key)
15 | // 分组秘钥
16 | block, _ := aes.NewCipher(k)
17 | // 获取秘钥块的长度
18 | blockSize := block.BlockSize()
19 | // 补全码
20 | origData = PKCS7Padding(origData, blockSize)
21 | // 加密模式
22 | blockMode := cipher.NewCBCEncrypter(block, k[:blockSize])
23 | // 创建数组
24 | cryted := make([]byte, len(origData))
25 | // 加密
26 | blockMode.CryptBlocks(cryted, origData)
27 | return base64.StdEncoding.EncodeToString(cryted)
28 | }
29 |
30 | //AES 解密
31 | func AesDecrypt(cryted string, key string) string {
32 | // 转成字节数组
33 | crytedByte, _ := base64.StdEncoding.DecodeString(cryted)
34 | k := []byte(key)
35 | // 分组秘钥
36 | block, _ := aes.NewCipher(k)
37 | // 获取秘钥块的长度
38 | blockSize := block.BlockSize()
39 | // 加密模式
40 | blockMode := cipher.NewCBCDecrypter(block, k[:blockSize])
41 | // 创建数组
42 | orig := make([]byte, len(crytedByte))
43 | // 解密
44 | blockMode.CryptBlocks(orig, crytedByte)
45 | // 去补全码
46 | orig = PKCS7UnPadding(orig)
47 | return string(orig)
48 | }
49 |
50 | //补码
51 | func PKCS7Padding(ciphertext []byte, blocksize int) []byte {
52 | padding := blocksize - len(ciphertext)%blocksize
53 | padtext := bytes.Repeat([]byte{byte(padding)}, padding)
54 | return append(ciphertext, padtext...)
55 | }
56 |
57 | //去码
58 | func PKCS7UnPadding(origData []byte) []byte {
59 | length := len(origData)
60 | unpadding := int(origData[length-1])
61 | return origData[:(length - unpadding)]
62 | }
63 |
--------------------------------------------------------------------------------
/application/Services/project.go:
--------------------------------------------------------------------------------
1 | package Services
2 |
3 | import (
4 | "encoding/json"
5 | "strconv"
6 | "time"
7 | "xapimanager/application/models"
8 | "xapimanager/config"
9 | )
10 |
11 | //获取用户在指定项目的权限组
12 | func GetProjectGroup(uid int, proid int) (group models.AuthGroup) {
13 |
14 | var data string
15 | var err error
16 | group = models.AuthGroup{}
17 | Cache := models.CacheConnect()
18 | key := "qy_user_project_group#" + strconv.Itoa(uid)
19 |
20 | if Cache.Hander != nil {
21 | data, err = Cache.Hander.Get(key).Result()
22 | if err == nil {
23 | json.Unmarshal([]byte(data), &group)
24 | }
25 | }
26 | if Cache.Hander == nil || err != nil {
27 | group = models.GetProjectGroup(uid, proid)
28 | jsonStr, _ := json.Marshal(group)
29 | if Cache.Hander != nil {
30 | Cache.Hander.Set(key, jsonStr,
31 | time.Second*time.Duration(config.GetGlobal().User_Cache))
32 | }
33 |
34 | }
35 |
36 | return
37 | }
38 |
39 | //获取用户所有的项目
40 | func GetUserProject(uid int) (projects []models.QyProject) {
41 |
42 | //获取用户信息
43 | var data string
44 | var err error
45 |
46 | Cache := models.CacheConnect()
47 | key := "qy_user_project_list#" + strconv.Itoa(uid)
48 |
49 | if Cache.Hander != nil {
50 | data, err = Cache.Hander.Get(key).Result()
51 | if err == nil {
52 | json.Unmarshal([]byte(data), &projects)
53 | }
54 | }
55 | if Cache.Hander == nil || err != nil {
56 | organizeIds := models.GetOrganizeIds(uid)
57 | //查询用户组私有项目id
58 | groupIds := models.GetGroupIds(uid)
59 | proids := models.GetGroupProject(groupIds)
60 | //查询用户的项目
61 | projects = models.GetUserProject(organizeIds, proids)
62 | data, _ := json.Marshal(projects)
63 | if Cache.Hander != nil {
64 | Cache.Hander.Set(key, data,
65 | time.Second*time.Duration(config.GetGlobal().User_Cache))
66 | }
67 |
68 | }
69 | return
70 | }
71 |
--------------------------------------------------------------------------------
/application/Services/template.go:
--------------------------------------------------------------------------------
1 | package Services
2 |
3 | import (
4 | "bytes"
5 | "html/template"
6 | "io/ioutil"
7 | "strings"
8 | "xapimanager/config"
9 | )
10 |
11 | func GetFgetorTemplate(templatefile string, data map[string]interface{}) string {
12 |
13 | file := config.GetGlobal().TEMPLATE_PATH + "/" + templatefile
14 |
15 | buf := new(bytes.Buffer)
16 | if contents, err := ioutil.ReadFile(file); err == nil {
17 | //因为contents是[]byte类型,直接转换成string类型后会多一行空格,需要使用strings.Replace替换换行符
18 | newContents := strings.Replace(string(contents), "\n", "", 1)
19 | var tmpl = template.Must(template.New("").Parse(newContents))
20 | tmpl.Execute(buf, data)
21 | }
22 |
23 | return buf.String()
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/application/Services/userauth.go:
--------------------------------------------------------------------------------
1 | package Services
2 |
3 | import (
4 | "encoding/json"
5 | "strconv"
6 | "strings"
7 | "time"
8 | "xapimanager/application/models"
9 | "xapimanager/config"
10 | )
11 |
12 | //获取用户权限并保存在cache中
13 | func GetUserAuth(uid int, proid int) (userAuth map[string][]string) {
14 |
15 | var data string
16 | var err error
17 | Cache := models.CacheConnect()
18 |
19 | key := "qy_user_userAuth#" + strconv.Itoa(uid) + "#" + strconv.Itoa(proid)
20 |
21 | userAuth = make(map[string][]string)
22 | if Cache.Hander != nil {
23 | data, err = Cache.Hander.Get(key).Result()
24 | if err == nil {
25 | json.Unmarshal([]byte(data), &userAuth)
26 | }
27 | }
28 | if Cache.Hander == nil || err != nil {
29 | //获取用户在该项目中的权限组
30 | group := models.GetProjectGroup(uid, proid)
31 | //获取菜单权限
32 | rules := models.GetUserMenuAuth(strings.Split(group.Rules, ","))
33 | //获取操作节点权限
34 | operate := models.GetUserOperateAuth(strings.Split(group.Operate, ","))
35 | //获取用户数据权限(查询分类权限)
36 | auth := models.GetUserDataAuth(group.Id, []int{2})
37 |
38 | var temp []string
39 | temp = []string{}
40 | for _, v := range rules {
41 | if len(v.Path) > 0 {
42 | temp = append(temp, v.Path)
43 | }
44 | }
45 | userAuth["rules"] = temp
46 |
47 | temp = []string{}
48 | for _, v := range operate {
49 | if len(v.Identify) > 0 {
50 | temp = append(temp, v.Identify)
51 | }
52 | }
53 | userAuth["operate"] = temp
54 | if len(auth) > 0 {
55 | userAuth["dataAuth"] = strings.Split(auth[0].Record, ",")
56 | } else {
57 | userAuth["dataAuth"] = []string{}
58 | }
59 |
60 | data, _ := json.Marshal(userAuth)
61 | if Cache.Hander != nil {
62 | Cache.Hander.Set(key, data,
63 | time.Second*time.Duration(config.GetGlobal().User_Cache))
64 | }
65 |
66 | }
67 |
68 | return
69 | }
70 |
71 | //获取所有可用节点操作
72 | func GetNodeAuth() (nodeAuth map[string]string) {
73 |
74 | var data string
75 | var err error
76 | Cache := models.CacheConnect()
77 | key := "qy_sys_NodeAuth"
78 |
79 | nodeAuth = make(map[string]string)
80 | if Cache.Hander != nil {
81 | data, err = Cache.Hander.Get(key).Result()
82 | if err == nil {
83 | json.Unmarshal([]byte(data), &nodeAuth)
84 | }
85 | }
86 | if Cache.Hander == nil || err != nil {
87 | auth := models.GetUserOperateAuth([]string{})
88 | for _, v := range auth {
89 | nodeAuth[v.Path] = v.Identify
90 | }
91 | data, _ := json.Marshal(nodeAuth)
92 | if Cache.Hander != nil {
93 | Cache.Hander.Set(key, data,
94 | time.Second*time.Duration(config.GetGlobal().User_Cache))
95 | }
96 | }
97 |
98 | return
99 | }
100 |
--------------------------------------------------------------------------------
/application/Services/users.go:
--------------------------------------------------------------------------------
1 | package Services
2 |
3 | import (
4 | "strings"
5 | "xapimanager/application/common"
6 | "xapimanager/application/models"
7 | )
8 |
9 | type Sendlist struct {
10 | Name string
11 | Address string
12 | }
13 |
14 | /**
15 | * 批量获取用户姓名
16 | * param userids = 1,2,3
17 | */
18 | func GetBatchUserName(userids string) (data []string) {
19 |
20 | userIds := strings.Split(userids, ",")
21 | uids := []int{}
22 | for _, v := range userIds {
23 | uids = append(uids, common.StringToInt(v))
24 | }
25 | userInfo := models.BatchUsers(uids)
26 |
27 | for _, v := range userInfo {
28 | data = append(data, v.Username)
29 | }
30 | return
31 | }
32 |
33 | /**
34 | * 获取用户姓名
35 | * param uid 用户id
36 | */
37 | func GetUserName(uid int) (username string) {
38 |
39 | info := models.GetUserInfo(uid)
40 |
41 | return info.Username
42 | }
43 |
--------------------------------------------------------------------------------
/application/common/desktop.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "fmt"
5 | "os/exec"
6 | "runtime"
7 | )
8 |
9 | var commands = map[string]string{
10 | "windows": "cmd /c start",
11 | "darwin": "open",
12 | "linux": "xdg-open",
13 | }
14 |
15 | var Version = "0.1.0"
16 |
17 | // Open calls the OS default program for uri
18 | func Open(uri string) error {
19 | run, ok := commands[runtime.GOOS]
20 | if !ok {
21 | return fmt.Errorf("don't know how to open things on %s platform", runtime.GOOS)
22 | }
23 |
24 | cmd := exec.Command(run, uri)
25 | return cmd.Start()
26 | }
27 |
--------------------------------------------------------------------------------
/application/controllers/AppsController.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "xapimanager/application/Services"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | )
8 |
9 | //应用列表
10 | func AppsList(c *gin.Context) {
11 |
12 | c.HTML(http.StatusOK, "apps.html", gin.H{
13 | "website": Services.GetWebsite(),
14 | })
15 | }
16 |
17 | //json/xml转换
18 | func AppsTransform(c *gin.Context) {
19 | c.HTML(http.StatusOK, "apps_transform.html", gin.H{
20 | "website": Services.GetWebsite(),
21 | })
22 | }
23 |
24 | //json格式化、压缩
25 | func AppsJson(c *gin.Context) {
26 | c.HTML(http.StatusOK, "apps_json.html", gin.H{
27 | "website": Services.GetWebsite(),
28 | })
29 | }
30 |
31 | //时间戳转换
32 | func AppsTimestamp(c *gin.Context) {
33 | c.HTML(http.StatusOK, "apps_timestamp.html", gin.H{
34 | "website": Services.GetWebsite(),
35 | })
36 | }
37 |
--------------------------------------------------------------------------------
/application/controllers/CacheController.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "xapimanager/application/models"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "strconv"
8 | )
9 |
10 | /**
11 | * 清除用户缓存,不检查是否成功
12 | * 缓存分为两类,用户缓存(以qy_user_开头)、系统缓存(以qy_sys_开头)
13 | */
14 | func ClearCache(c *gin.Context) {
15 |
16 | //获取用户权限
17 | userInfo, _ := c.Get("user")
18 | uid := userInfo.(map[string]interface{})["uid"].(int)
19 |
20 | Cache := models.CacheConnect()
21 | CacheList := []string{
22 | "qy_user_userAuth#" + strconv.Itoa(uid),
23 | "qy_user_project_group#" + strconv.Itoa(uid),
24 | "qy_user_project_list#" + strconv.Itoa(uid),
25 | }
26 | for _, key := range CacheList {
27 | if Cache.Hander != nil {
28 | Cache.Hander.Del(key).Result()
29 | }
30 | }
31 |
32 | c.JSON(http.StatusOK, gin.H{
33 | "status": 200,
34 | "message": "缓存清理成功",
35 | })
36 | }
37 |
--------------------------------------------------------------------------------
/application/controllers/IndexController.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "xapimanager/application/Services"
5 | "xapimanager/application/models"
6 | _ "xapimanager/application/utils"
7 | "github.com/gin-contrib/sessions"
8 | "github.com/gin-gonic/gin"
9 | "net/http"
10 | "strconv"
11 | "strings"
12 | )
13 |
14 | func Index(c *gin.Context) {
15 |
16 | //日志记录示例
17 | //data := map[string]interface{}{
18 | // "filename": "routes",
19 | // "size": 10,
20 | // "username": c.PostForm("username"),
21 | // "passwd": c.PostForm("passwd"),
22 | // "token": c.Request.Header.Get("Authorization"),
23 | //}
24 | //token := c.Request.Header.Get("Authorization")
25 | //utils.Log.WithFields(data).Info("路由文件记录")
26 | //获取用户信息
27 | userInfo, _ := c.Get("user")
28 | //查询用户组及该组的功能权限
29 | uid := userInfo.(map[string]interface{})["uid"].(int)
30 | gid := models.GetUserGroup(uid)
31 |
32 | var menu []models.Allmenu
33 | if gid == 1 {
34 | menu = models.GetMenu(1, 0)
35 | } else {
36 | rules := []string{"1", "2", "3", "4", "5", "7", "8", "9", "10", "14", "15", "16", "17", "18"}
37 | menu = models.GetManagerMenu(1, 0, rules)
38 | }
39 |
40 | session := sessions.Default(c)
41 | c.HTML(http.StatusOK, "index.html", gin.H{
42 | "website": Services.GetWebsite(),
43 | "menu": menu,
44 | "userinfo": map[string]interface{}{
45 | "username": session.Get("username"),
46 | "avatar": session.Get("avatar"),
47 | },
48 | })
49 | }
50 |
51 | func Manager(c *gin.Context) {
52 |
53 | proid, _ := strconv.Atoi(c.Param("proid"))
54 | env := models.GetProjectValidEnv(proid, "asc")
55 |
56 | //获取用户信息
57 | userInfo, _ := c.Get("user")
58 | //查询用户组及该组的功能权限
59 | uid := userInfo.(map[string]interface{})["uid"]
60 | group := Services.GetProjectGroup(uid.(int), proid)
61 | rules := strings.Split(group.Rules, ",")
62 | menu := models.GetManagerMenu(2, 0, rules)
63 |
64 | //用户当前环境
65 | cenv := models.GetCurrentEnv(uid.(int), proid)
66 | currentEnv := map[int]string{
67 | 0: "请选择环境",
68 | }
69 | for _, v := range env {
70 | if cenv.Envid == v.Id {
71 | delete(currentEnv, 0)
72 | currentEnv[v.Id] = v.Envname
73 | }
74 | }
75 | session := sessions.Default(c)
76 |
77 | c.HTML(http.StatusOK, "index_api.html", gin.H{
78 | "website": Services.GetWebsite(),
79 | "apimenu": menu,
80 | "projectEnv": env,
81 | "proid": proid,
82 | "currentEnv": currentEnv,
83 | "userinfo": map[string]interface{}{
84 | "username": session.Get("username"),
85 | "avatar": session.Get("avatar"),
86 | },
87 | })
88 | }
89 |
--------------------------------------------------------------------------------
/application/controllers/OperateLogController.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "xapimanager/application/Services"
5 | "xapimanager/application/common"
6 | "xapimanager/application/models"
7 | "github.com/gin-gonic/gin"
8 | "net/http"
9 | "strconv"
10 | "time"
11 | )
12 |
13 | //操作日志页面
14 | func OperateLog(c *gin.Context) {
15 |
16 | c.HTML(http.StatusOK, "operate_log.html", gin.H{
17 | "website": Services.GetWebsite(),
18 | })
19 | }
20 |
21 | //操作日志数据
22 | func AjaxOperateLog(c *gin.Context) {
23 |
24 | var userIds []int
25 | var list []interface{}
26 | //获取用户信息
27 | userInfo, _ := c.Get("user")
28 | oid := userInfo.(map[string]interface{})["oid"].(int)
29 |
30 | page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
31 | limit := 20
32 | start := (page - 1) * limit
33 |
34 | log := models.GetOperateLog(oid, start, limit)
35 |
36 | for _, v := range log["list"].([]models.QyLog) {
37 | userIds = append(userIds, v.Operator)
38 | }
39 | ltype := map[int]string{
40 | 1: "增加",
41 | 2: "修改",
42 | 3: "删除",
43 | }
44 | if len(userIds) > 0 {
45 | userIds = common.RemoveRepByMap(userIds)
46 | users := models.BatchUsers(userIds)
47 | for _, v := range log["list"].([]models.QyLog) {
48 | temp := map[string]interface{}{
49 | "Organize": v.Organize,
50 | "Object": v.Object,
51 | "Logtype": ltype[v.Logtype],
52 | "Operator": users[v.Operator].Username,
53 | "Desc": v.Desc,
54 | "Addtime": time.Unix(int64(v.Addtime), 0).Format("2006-01-02"),
55 | }
56 | list = append(list, temp)
57 | }
58 | }
59 |
60 | c.JSON(http.StatusOK, gin.H{
61 | "status": 200,
62 | "message": "成功",
63 | "data": map[string]interface{}{
64 | "list": list,
65 | "totalCount": log["totalCount"],
66 | },
67 | })
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/application/controllers/SysController.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "xapimanager/application/Services"
5 | "xapimanager/application/models"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | )
9 |
10 | //获取网站设置页
11 | func Website(c *gin.Context) {
12 |
13 | c.HTML(http.StatusOK, "website.html", gin.H{
14 | "website": Services.GetWebsite(),
15 | "site": models.GetWebsite(),
16 | })
17 |
18 | }
19 |
20 | //保存网站信息
21 | func WebsiteInfo(c *gin.Context) {
22 |
23 | var data = []string{"sitename", "title", "keywords", "description", "copyright"}
24 | for _, v := range data {
25 | models.WebsiteSave(v, c.PostForm(v))
26 | }
27 | Services.ClearCache("qy_website")
28 | c.JSON(http.StatusOK, gin.H{
29 | "status": 200,
30 | "message": "保存成功",
31 | })
32 | }
33 |
--------------------------------------------------------------------------------
/application/controllers/UploadController.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "xapimanager/application/common"
5 | "xapimanager/config"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | "strconv"
9 | "strings"
10 | "time"
11 | )
12 |
13 | //图片上传
14 | func UploadImage(c *gin.Context) {
15 | file, _ := c.FormFile("file")
16 | // 保存文件
17 | year, month, _ := time.Now().Date()
18 | sysconfig := config.GetGlobal()
19 |
20 | suffix := common.GetFileSuffix(file.Filename)
21 | filename := strconv.Itoa(int(time.Now().UnixNano()/1e6)) + suffix
22 | dstdir := sysconfig.UPLOAD_PATH + "/images/tmp/" + strconv.Itoa(year) + "/" + month.String()
23 | common.CreateDir(dstdir)
24 | dstfile := dstdir + "/" + filename
25 | c.SaveUploadedFile(file, dstfile)
26 | dst := strings.Split(dstfile, "storage")
27 | c.JSON(http.StatusOK, gin.H{
28 | "link": dst[1],
29 | })
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/application/controllers/manager/DashController.go:
--------------------------------------------------------------------------------
1 | package manager
2 |
3 | import (
4 | "xapimanager/application/Services"
5 | "xapimanager/application/models"
6 | "github.com/gin-gonic/gin"
7 | "math/rand"
8 | "net/http"
9 | "strconv"
10 | )
11 |
12 | //控制台
13 | func Dashboard(c *gin.Context) {
14 |
15 | proid, _ := strconv.Atoi(c.Param("proid"))
16 |
17 | c.HTML(http.StatusOK, "manager_dash.html", gin.H{
18 | "website": Services.GetWebsite(),
19 | "proid": proid,
20 | })
21 | }
22 |
23 | //各地区api调用量
24 | func DashboardArea(c *gin.Context) {
25 |
26 | var result []map[string]interface{}
27 | data := models.GetAreaData()
28 |
29 | for _, v := range data {
30 | temp := map[string]interface{}{
31 | "area_id": v.AreaId,
32 | "name": v.AreaName,
33 | "value": rand.Intn(200),
34 | "longitude": v.Longitude,
35 | "latitude": v.Latitude,
36 | }
37 | result = append(result, temp)
38 | }
39 |
40 | c.JSON(http.StatusOK, gin.H{
41 | "status": 200,
42 | "message": "成功",
43 | "data": result,
44 | })
45 | }
46 |
--------------------------------------------------------------------------------
/application/middleware/CheckApiAuthenticated.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "xapimanager/application/Services"
5 | "xapimanager/application/common"
6 | "xapimanager/config"
7 | "github.com/gin-gonic/gin"
8 | "net/http"
9 | "regexp"
10 | "strconv"
11 | )
12 |
13 | /**
14 | * Api权限检查
15 | * 第一步检查是否有项目权限
16 | * 第二步检查Api权限
17 | * 第三步检查节点操作权限
18 | */
19 | func ApiAuthCheck() gin.HandlerFunc {
20 | return func(c *gin.Context) {
21 |
22 | //获取用户信息
23 | userInfo, _ := c.Get("user")
24 | uid := userInfo.(map[string]interface{})["uid"].(int)
25 | username := userInfo.(map[string]interface{})["username"].(string)
26 |
27 | //未登录用户直接跳转到登录页
28 | if uid == 0 || username == "" {
29 | c.Redirect(http.StatusFound, "/login")
30 | c.Abort()
31 | return
32 | }
33 |
34 | proid, _ := strconv.Atoi(c.Param("proid"))
35 | c.Set("user", map[string]interface{}{
36 | "uid": uid,
37 | "username": username,
38 | "oid": 1,
39 | "auth": Services.GetUserAuth(uid, proid),
40 | })
41 |
42 | //项目权限检查
43 | project := Services.GetUserProject(uid)
44 | flag := false
45 | for _, v := range project {
46 | if proid == v.Id {
47 | flag = true
48 | }
49 | }
50 | if !flag {
51 | c.JSON(http.StatusOK, gin.H{
52 | "status": 513,
53 | "message": "您没有权限访问该项目,请联系管理员",
54 | })
55 | //终止
56 | c.Abort()
57 | return
58 | }
59 |
60 | //用户权限检查
61 | auth := Services.GetUserAuth(uid, proid)
62 | //匹配当前路由
63 | uri := c.Request.RequestURI
64 | reg := regexp.MustCompile(`\/manager\/\d+(\/[0-9A-Za-z\/]+)`)
65 | match := reg.FindStringSubmatch(uri)
66 | authflag := false
67 | str := ""
68 | optstr := ""
69 | if len(match) > 0 {
70 | //功能权限检查
71 | fetch := config.GetApiFetch()
72 | str = fetch[match[1]]
73 | if len(str) > 0 {
74 | authflag = common.CheckAuth(str, auth["rules"])
75 | }
76 | if len(str) == 0 {
77 | c.JSON(http.StatusOK, gin.H{
78 | "status": 512,
79 | "message": "您请求的Api没有fetch对应关系",
80 | })
81 | c.Abort()
82 | return
83 | } else if !authflag {
84 | //api不在映射列表中
85 | c.JSON(http.StatusOK, gin.H{
86 | "status": 511,
87 | "message": "您没有权限访问该Api",
88 | })
89 | c.Abort()
90 | return
91 | }
92 | //数据权限检查
93 | authflag = true
94 | optfetch := Services.GetNodeAuth()
95 | optstr = optfetch[match[1]]
96 | if len(optstr) > 0 {
97 | authflag = common.CheckAuth(optstr, auth["operate"])
98 | }
99 | if !authflag {
100 | c.JSON(http.StatusOK, gin.H{
101 | "status": 514,
102 | "message": "您没有该操作权限,请联系管理员",
103 | })
104 | c.Abort()
105 | return
106 | }
107 |
108 | }
109 |
110 | c.Next()
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/application/middleware/CheckAuthenticated.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "regexp"
7 | "strconv"
8 | "xapimanager/application/Services"
9 | "xapimanager/application/common"
10 | )
11 |
12 | //需排除权限限制的页面
13 | var exclude = []string{
14 | "/users", //用户列表
15 | "/group", //权限管理
16 | "/category/detail", //分类详情
17 | "/category/sub", //子分类列表
18 | "/category/infoSub", //子分类添加页
19 | "/Api/detail", //Api 详情页
20 | }
21 |
22 | //页面权限检查, 检查用户是否用该页面的访问权限
23 | func AuthCheck() gin.HandlerFunc {
24 | return func(c *gin.Context) {
25 |
26 | //获取用户信息
27 | userInfo, _ := c.Get("user")
28 | uid := userInfo.(map[string]interface{})["uid"].(int)
29 | username := userInfo.(map[string]interface{})["username"].(string)
30 |
31 | //未登录用户直接跳转到登录页
32 | if uid == 0 || username == "" {
33 | c.Redirect(http.StatusFound, "/login")
34 | c.Abort()
35 | return
36 | }
37 |
38 | proid, _ := strconv.Atoi(c.Param("proid"))
39 | c.Set("user", map[string]interface{}{
40 | "uid": uid,
41 | "username": username,
42 | "oid": 1,
43 | "auth": Services.GetUserAuth(uid, proid),
44 | })
45 | //项目权限检查
46 | project := Services.GetUserProject(uid)
47 | flag := false
48 | for _, v := range project {
49 | if proid == v.Id {
50 | flag = true
51 | }
52 | }
53 | if !flag {
54 | c.HTML(http.StatusOK, "404.html", gin.H{
55 | "status": 510,
56 | "message": "您没有权限访问该项目,请联系管理员",
57 | })
58 | //终止
59 | c.Abort()
60 | }
61 | //用户权限检查
62 | auth := Services.GetUserAuth(uid, proid)
63 |
64 | //匹配当前路由
65 | uri := c.Request.RequestURI
66 | reg := regexp.MustCompile(`\/manager\/\d+(\/[0-9A-Za-z\/]+)`)
67 | match := reg.FindStringSubmatch(uri)
68 | authflag := false
69 |
70 | if len(match) > 0 && len(match[1]) > 0 {
71 | if common.CheckAuth(match[1], exclude) {
72 | authflag = true
73 | } else {
74 | authflag = common.CheckAuth(match[1], auth["rules"])
75 | }
76 | }
77 |
78 | if !authflag {
79 | c.HTML(http.StatusOK, "404.html", gin.H{
80 | "status": 513,
81 | "message": "您没有权限访问该页面,请联系管理员",
82 | })
83 | c.Abort()
84 | }
85 |
86 | c.Next()
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/application/middleware/GlobalMiddleware.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "github.com/gin-contrib/sessions"
5 | "github.com/gin-gonic/gin"
6 | )
7 |
8 | func Handler() gin.HandlerFunc {
9 | return func(c *gin.Context) {
10 |
11 | session := sessions.Default(c)
12 | //获取用户信息
13 | uid := session.Get("uid")
14 | username := session.Get("username")
15 | avatar := session.Get("avatar")
16 |
17 | //获取用户信息
18 | if uid == nil || username == nil {
19 | c.Set("user", map[string]interface{}{
20 | "uid": 0,
21 | "username": "",
22 | "avatar": "/assets/images/avatar.png",
23 | })
24 | } else {
25 | c.Set("user", map[string]interface{}{
26 | "uid": uid.(int),
27 | "username": username.(string),
28 | "avatar": avatar,
29 | })
30 | }
31 | c.Next()
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/application/middleware/IsAuthenticated.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | )
7 |
8 | func Auth() gin.HandlerFunc {
9 | return func(c *gin.Context) {
10 |
11 | //var code int
12 | //var data interface{}ls
13 | //
14 | //code = utils.SUCCESS
15 | //token := c.Query("token")
16 | //if token == "" {
17 | // code = utils.INVALID_PARAMS
18 | //} else {
19 | // claims, err := utils.ParseToken(token)
20 | // if err != nil {
21 | // code = utils.ERROR_AUTH_CHECK_TOKEN_FAIL
22 | // } else if time.Now().Unix() > claims.ExpiresAt {
23 | // code = utils.ERROR_AUTH_CHECK_TOKEN_TIMEOUT
24 | // }
25 | //}
26 | //
27 | //if code != utils.SUCCESS {
28 | // c.JSON(http.StatusUnauthorized, gin.H{
29 | // "code": code,
30 | // "msg": utils.GetMsg(code),
31 | // "data": data,
32 | // })
33 | //
34 | // c.Abort()
35 | // return
36 | //}
37 |
38 | //获取用户信息
39 | userInfo, _ := c.Get("user")
40 | uid := userInfo.(map[string]interface{})["uid"].(int)
41 | username := userInfo.(map[string]interface{})["username"].(string)
42 |
43 | //未登录用户直接跳转到登录页
44 | if uid == 0 || username == "" {
45 | c.Redirect(http.StatusFound, "/login")
46 | c.Abort()
47 | return
48 | } else {
49 | c.Set("user", map[string]interface{}{
50 | "uid": uid,
51 | "username": username,
52 | "oid": 1,
53 | })
54 | c.Next()
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/application/models/Basic.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | var Db *DB
4 |
5 | type Allmenu struct {
6 | QyAuthRule
7 | Child []Allmenu
8 | }
9 |
10 | /**
11 | * 递归获取菜单栏
12 | * @param identify 标识(1主控制台,2接口平台)
13 | * @param pid 父级id
14 | * @result result 所有菜单
15 | */
16 | func GetMenu(identify int, pid int) (menu []Allmenu) {
17 |
18 | defer Db.Close()
19 | Db = Connect()
20 | var authRule []QyAuthRule
21 | var temp Allmenu
22 |
23 | Db.Hander.Where("identify=? and pid = ? and status=1 and isdel=2", identify, pid).Order("sort asc").Find(&authRule)
24 |
25 | if len(authRule) > 0 {
26 | for _, value := range authRule {
27 | temp.QyAuthRule = value
28 | temp.Child = GetMenu(identify, value.Id)
29 | menu = append(menu, temp)
30 | }
31 | }
32 |
33 | return
34 |
35 | }
36 |
37 | /**
38 | * 递归获取api菜单栏
39 | * @param identify 标识(1主控制台,2接口平台)
40 | * @param pid 父级id
41 | * @param rules 路由切片
42 | * @result result 所有菜单
43 | */
44 | func GetManagerMenu(identify int, pid int, rules []string) (menu []Allmenu) {
45 |
46 | defer Db.Close()
47 | Db = Connect()
48 | var authRule []QyAuthRule
49 | var temp Allmenu
50 |
51 | obj := Db.Hander.Where("identify=? and pid = ? and status=1 and isdel=2", identify, pid)
52 |
53 | if pid > 0 {
54 | obj = obj.Where("id in (?)", rules)
55 | }
56 | obj.Order("sort asc").Find(&authRule)
57 |
58 | if len(authRule) > 0 {
59 | for _, value := range authRule {
60 | temp.QyAuthRule = value
61 | temp.Child = GetManagerMenu(identify, value.Id, rules)
62 | menu = append(menu, temp)
63 | }
64 | }
65 |
66 | return
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/application/models/Cache.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "github.com/go-redis/redis"
5 | "xapimanager/application/utils"
6 | "xapimanager/config"
7 | )
8 |
9 | type Cache struct {
10 | Hander *redis.Client
11 | Scheme int
12 | rediscfg
13 | }
14 | type rediscfg struct {
15 | host string
16 | port string
17 | pass string
18 | db int
19 | }
20 |
21 | //连接
22 | func CacheConnect() (Cc *Cache) {
23 |
24 | Cc = CacheSingleton()
25 | Cc.Open()
26 | return
27 | }
28 |
29 | //创建单例模式
30 | func CacheSingleton() *Cache {
31 |
32 | sysc := config.GetGlobal()
33 | return &Cache{
34 | rediscfg: rediscfg{
35 | sysc.REDIS_IP,
36 | sysc.REDIS_PORT,
37 | sysc.REDIS_PASSWORD,
38 | sysc.REDIS_DB,
39 | },
40 | }
41 | }
42 |
43 | //打开
44 | func (Cc *Cache) Open() error {
45 |
46 | var err error
47 | client := redis.NewClient(&redis.Options{
48 | Addr: Cc.host + ":" + Cc.port,
49 | Password: Cc.pass,
50 | DB: Cc.db,
51 | })
52 | //判断redis连接是否可用
53 | if _, err := client.Ping().Result(); err != nil {
54 | //日志记录示例
55 | data := map[string]interface{}{
56 | "filename": "redisconnet",
57 | "size": 10,
58 | }
59 | utils.Log.WithFields(data).Info(err)
60 | } else {
61 | Cc.Hander = client
62 | }
63 |
64 | return err
65 |
66 | }
67 |
68 | //关闭
69 | func (Cc *Cache) Close() {
70 |
71 | Cc.Hander.Close()
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/application/models/Company.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "time"
4 |
5 | type QySecret struct {
6 | Id int
7 | Proid int
8 | Company string
9 | Appid string
10 | Appsecret string
11 | Status int
12 | Ctime int
13 | }
14 |
15 | /**
16 | * 获取企业秘钥列表
17 | * param proid 项目id
18 | * param company 公司名
19 | * param start 开始位置
20 | * param limit 条数
21 | * return result 秘钥列表
22 | */
23 | func CompanyList(proid int, company string, start int, limit int) (result map[string]interface{}) {
24 |
25 | defer Db.Close()
26 | Db = Connect()
27 | var count int
28 | var secret []QySecret
29 |
30 | obj := Db.Hander.Table("qy_secret").Where("proid =? and status in (?)", proid, []int{1, 2})
31 | if len(company) > 0 {
32 | obj = obj.Where("company like ?", "%"+company+"%")
33 | }
34 | obj.Count(&count)
35 | obj.Offset(start).Limit(limit).Find(&secret)
36 |
37 | result = make(map[string]interface{})
38 | result["totalCount"] = count
39 | result["list"] = secret
40 |
41 | return
42 | }
43 |
44 | /**
45 | * 获取企业秘钥
46 | * param id 密钥id
47 | */
48 | func GetCompany(id int) (result QySecret) {
49 |
50 | defer Db.Close()
51 | Db = Connect()
52 | Db.Hander.Table("qy_secret").Where("status in (?)", []int{1, 2}).
53 | Where("id = ?", id).Find(&result)
54 | return
55 | }
56 |
57 | /**
58 | * 保存企业秘钥
59 | * param id 密钥id
60 | * param data 密钥数据
61 | */
62 | func CompanySave(id int, data map[string]interface{}) bool {
63 |
64 | defer Db.Close()
65 | Db = Connect()
66 | if id > 0 {
67 | err := Db.Hander.Table("qy_secret").
68 | Where("id = ? and proid=? ", id, data["proid"].(int)).
69 | Updates(data).Error
70 | if err != nil {
71 | return false
72 | }
73 | } else {
74 | time := time.Now().Unix()
75 | info := &QySecret{
76 | 0,
77 | data["proid"].(int),
78 | data["company"].(string),
79 | data["appid"].(string),
80 | data["appsecret"].(string),
81 | data["status"].(int),
82 | int(time),
83 | }
84 | err := Db.Hander.Table("qy_secret").Create(info).Error
85 | if err != nil {
86 | return false
87 | }
88 | }
89 | return true
90 | }
91 |
92 | /**
93 | * 删除企业秘钥
94 | * param id 密钥id
95 | * param proid 项目id
96 | */
97 | func CompanyOperate(id int, proid int) bool {
98 | defer Db.Close()
99 | Db = Connect()
100 | if id > 0 {
101 | err := Db.Hander.Table("qy_secret").
102 | Where("id = ? and proid = ?", id, proid).
103 | Update("status", 3).Error
104 | if err != nil {
105 | return false
106 | }
107 | return true
108 | }
109 | return false
110 | }
111 |
--------------------------------------------------------------------------------
/application/models/DB.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "fmt"
5 | "github.com/jinzhu/gorm"
6 | _ "github.com/jinzhu/gorm/dialects/mysql"
7 | "time"
8 | "xapimanager/application/utils"
9 | "xapimanager/config"
10 | )
11 |
12 | type DB struct {
13 | Hander *gorm.DB
14 | cfg
15 | }
16 | type cfg struct {
17 | host string
18 | port string
19 | user string
20 | pass string
21 | dbname string
22 | prefix string
23 | }
24 |
25 | //连接mysql
26 | func Connect() (Dbase *DB) {
27 | //创建连接
28 | Dbase = Singleton()
29 | if err := Dbase.Open(); err != nil {
30 | //日志记录示例
31 | data := map[string]interface{}{
32 | "filename": "database",
33 | "size": 10,
34 | }
35 | utils.Log.WithFields(data).Info(err)
36 | }
37 | return
38 | }
39 |
40 | //创建单例模式
41 | func Singleton() *DB {
42 |
43 | sysc := config.GetGlobal()
44 | var dbconfig = cfg{
45 | sysc.MYSQL_IP,
46 | sysc.MYSQL_PORT,
47 | sysc.MYSQL_USERNAME,
48 | sysc.MYSQL_PASSWORD,
49 | sysc.MYSQL_DBNAME,
50 | sysc.MYSQL_PREFIX,
51 | }
52 | return &DB{
53 | cfg: dbconfig,
54 | }
55 | }
56 |
57 | //mysql 连接
58 | func (db *DB) Open() error {
59 |
60 | sysc := config.GetGlobal()
61 | connect := fmt.Sprintf("%s:%s@(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local",
62 | db.user, db.pass, db.host, db.port, db.dbname)
63 | obj, err := gorm.Open("mysql", connect)
64 | if err != nil {
65 | return err
66 | }
67 | //转换名称时不加s
68 | obj.SingularTable(true)
69 | //打印详细日志 //TODO 调试完成后需删除
70 | obj.LogMode(sysc.DbLogMode)
71 | //设置连接池
72 | obj.DB().SetConnMaxLifetime(time.Second * time.Duration(sysc.DbConnMaxLifetime))
73 | obj.DB().SetMaxIdleConns(sysc.DbMaxIdleConns)
74 | obj.DB().SetMaxOpenConns(sysc.DbMaxOpenConns)
75 | db.Hander = obj
76 | return nil
77 | }
78 |
79 | //mysql 关闭连接
80 | func (db *DB) Close() {
81 | if db != nil && db.Hander != nil {
82 | db.Hander.Close()
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/application/models/Help.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | type QyHelp struct {
8 | Id int
9 | Organize int
10 | Author int
11 | Title string
12 | Content string
13 | Status int
14 | Ctime int
15 | }
16 | type UserHelp struct {
17 | QyHelp
18 | Username string
19 | }
20 |
21 | //获取帮助中心列表
22 | func GetHelpList(data map[string]interface{}, start int, limit int) (result map[string]interface{}) {
23 |
24 | defer Db.Close()
25 | Db = Connect()
26 | var count int
27 | var help []UserHelp
28 | obj := Db.Hander.Table("qy_help as h").
29 | Joins("join qy_user as u on u.uid = h.author").
30 | Where("h.organize = ? and h.status = ?", data["organize"], 1)
31 | obj.Count(&count)
32 | obj.Select("h.*, u.username").Offset(start).Limit(limit).
33 | Order("h.ctime desc").Find(&help)
34 |
35 | result = map[string]interface{}{
36 | "totalCount": count,
37 | "list": help,
38 | }
39 | return
40 | }
41 |
42 | //帮助详情
43 | func GetHelpDetail(organize int, hid int) (help UserHelp) {
44 |
45 | defer Db.Close()
46 | Db = Connect()
47 | Db.Hander.Table("qy_help as h").
48 | Joins("join qy_user as u on u.uid = h.author").
49 | Where("h.organize = ? and h.id = ? and h.status = ?", organize, hid, 1).
50 | Select("h.*, u.username").
51 | Find(&help)
52 |
53 | return
54 | }
55 |
56 | //删除帮助中心
57 | func DeleteHelp(organize int, hid int) bool {
58 | defer Db.Close()
59 | Db = Connect()
60 | err := Db.Hander.Table("qy_help").
61 | Where("organize = ? and id= ?", organize, hid).
62 | Update("status", 2).Error
63 | if err != nil {
64 | return false
65 | }
66 | return true
67 |
68 | }
69 |
70 | //帮助中心保存
71 | func HelpStore(organize int, hid int, data map[string]interface{}) bool {
72 | defer Db.Close()
73 | Db = Connect()
74 | if hid > 0 {
75 | delete(data, "author")
76 | err := Db.Hander.Table("qy_help").
77 | Where("organize = ? and id=? ", organize, hid).
78 | Updates(data).Error
79 | if err != nil {
80 | return false
81 | }
82 | return true
83 | } else {
84 | time := time.Now().Unix()
85 | info := &QyHelp{
86 | 0,
87 | organize,
88 | data["author"].(int),
89 | data["title"].(string),
90 | data["content"].(string),
91 | 1,
92 | int(time),
93 | }
94 | if err := Db.Hander.Table("qy_help").Create(info).Error; err != nil {
95 | return false
96 | }
97 | return true
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/application/models/Message.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "time"
4 |
5 | type QyMessage struct {
6 | Id int
7 | Sender int
8 | Receiver int
9 | Pid int
10 | Subject string
11 | Content string
12 | Sendtime int
13 | Isread int
14 | }
15 | type UserMessage struct {
16 | QyMessage
17 | Username string
18 | }
19 |
20 | //发送消息
21 | func SendMessage(data map[string]interface{}) bool {
22 |
23 | defer Db.Close()
24 | Db = Connect()
25 | time := time.Now().Unix()
26 | info := &QyMessage{
27 | 0,
28 | data["sender"].(int),
29 | data["recevier"].(int),
30 | data["pid"].(int),
31 | data["subject"].(string),
32 | data["content"].(string),
33 | int(time),
34 | 2,
35 | }
36 | err := Db.Hander.Table("qy_message").Create(info).Error
37 | if err != nil {
38 | return false
39 | }
40 | return true
41 | }
42 |
43 | //获取消息列表
44 | func GetMessageList(data map[string]interface{}, start int, limit int) (result map[string]interface{}) {
45 |
46 | defer Db.Close()
47 | Db = Connect()
48 | var count int
49 | var message []UserMessage
50 | obj := Db.Hander.Table("qy_message as m").
51 | Joins("join qy_user as u on u.uid = m.sender").
52 | Where("m.receiver = ?", data["receiver"])
53 | obj.Count(&count)
54 | obj.Select("m.*, u.username").Offset(start).Limit(limit).
55 | Order("m.sendtime desc").Find(&message)
56 |
57 | result = map[string]interface{}{
58 | "totalCount": count,
59 | "list": message,
60 | }
61 | return
62 | }
63 |
64 | //消息详情
65 | func GetMessageDetail(uid int, mid int) (message UserMessage) {
66 |
67 | defer Db.Close()
68 | Db = Connect()
69 | Db.Hander.Table("qy_message as m").
70 | Joins("join qy_user as u on u.uid = m.sender").
71 | Where("m.receiver = ? and m.id=?", uid, mid).
72 | Select("m.*, u.username").
73 | Find(&message)
74 |
75 | return
76 | }
77 |
78 | //更新为已读
79 | func ReadUpdate(receiver int, ids []string) bool {
80 |
81 | defer Db.Close()
82 | Db = Connect()
83 | err := Db.Hander.Table("qy_message").
84 | Where("receiver =? and id in (?)", receiver, ids).
85 | Update("isread", 1).Error
86 | if err != nil {
87 | return false
88 | }
89 | return true
90 | }
91 |
92 | //批量删除
93 | func DeleteMessage(receiver int, ids []string) bool {
94 |
95 | defer Db.Close()
96 | Db = Connect()
97 | err := Db.Hander.Table("qy_message").
98 | Where("receiver =? and id in (?)", receiver, ids).
99 | Delete(struct{}{}).Error
100 | if err != nil {
101 | return false
102 | }
103 | return true
104 | }
105 |
106 | //获取未读消息
107 | func GetUnreadMessage(receiver int) (count int) {
108 |
109 | defer Db.Close()
110 | Db = Connect()
111 | Db.Hander.Table("qy_message").
112 | Where("receiver =? and isread = ?", receiver, 2).
113 | Count(&count)
114 | return
115 | }
116 |
--------------------------------------------------------------------------------
/application/models/OperateLog.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/gin-gonic/gin"
6 | "time"
7 | )
8 |
9 | type QyLog struct {
10 | Organize int
11 | Object string
12 | Logtype int
13 | Operator int
14 | Desc string
15 | Addtime int
16 | }
17 |
18 | //保存操作日志
19 | func OperateLog(object string, longtype int, c *gin.Context) bool {
20 |
21 | defer Db.Close()
22 | Db = Connect()
23 | //获取用户信息
24 | req := c.Request
25 | req.ParseForm()
26 | userInfo, _ := c.Get("user")
27 | info, _ := json.Marshal(
28 | map[string]interface{}{
29 | "URI": req.URL.Path,
30 | "Method": req.Method,
31 | "Param": req.PostForm,
32 | })
33 |
34 | var log QyLog
35 | log = QyLog{
36 | userInfo.(map[string]interface{})["oid"].(int),
37 | object,
38 | longtype,
39 | userInfo.(map[string]interface{})["uid"].(int),
40 | string(info),
41 | int(time.Now().Unix()),
42 | }
43 | err := Db.Hander.Create(&log).Error
44 | if err != nil {
45 | return false
46 | }
47 | return true
48 | }
49 |
50 | //获取分页操作日志
51 | func GetOperateLog(oid int, start int, limit int) (result map[string]interface{}) {
52 |
53 | defer Db.Close()
54 | Db = Connect()
55 | var count int
56 | var log []QyLog
57 |
58 | info := Db.Hander.Table("qy_log").
59 | Where("organize=?", oid)
60 |
61 | //查询信息
62 | info.Count(&count)
63 | info.Offset(start).Limit(limit).Find(&log)
64 |
65 | result = map[string]interface{}{
66 | "totalCount": count,
67 | "list": log,
68 | }
69 |
70 | return
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/application/models/Statistics.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type QyArea struct {
4 | Id int
5 | AreaId int
6 | AreaName string
7 | Pid int
8 | Longitude float64
9 | Latitude float64
10 | Sort int
11 | }
12 |
13 | func GetAreaData() (area []QyArea) {
14 |
15 | defer Db.Close()
16 | Db = Connect()
17 | Db.Hander.Table("qy_area").
18 | Where("pid !=? ", 0).
19 | Find(&area)
20 |
21 | return
22 | }
23 |
--------------------------------------------------------------------------------
/application/models/System.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type QySite struct {
4 | Id int `gorm:"primary_key"`
5 | Key string
6 | Value string
7 | Type int
8 | Des string
9 | }
10 |
11 | /**
12 | * 获得站点信息
13 | * @result 站点信息
14 | */
15 | func GetWebsite() (result map[string]string) {
16 |
17 | defer Db.Close()
18 | Db = Connect()
19 | var site []QySite
20 | result = make(map[string]string)
21 | Db.Hander.Find(&site)
22 | for _, v := range site {
23 | result[v.Key] = v.Value
24 | }
25 |
26 | return
27 |
28 | }
29 |
30 | /**
31 | * 保存站点信息
32 | * @result bool
33 | */
34 | func WebsiteSave(key string, value string) bool {
35 |
36 | defer Db.Close()
37 | Db = Connect()
38 | if err := Db.Hander.Table("qy_site").Where("`key` =?", key).Update("value", value).Error; err != nil {
39 | return false
40 | }
41 | return true
42 | }
43 |
--------------------------------------------------------------------------------
/application/requests/Api.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "gopkg.in/go-playground/validator.v9"
6 | "net/http"
7 | "regexp"
8 | )
9 |
10 | type ApiRequest struct {
11 | Apiname string `form:"apiname" binding:"required" validate:"required,max=20,min=2"`
12 | Subclassify string `form:"subClassify" binding:"required" validate:"required,max=10,min=1"`
13 | Version string `form:"version" binding:"required" validate:"required,max=10,min=1"`
14 | Requesttype string `form:"requesttype" binding:"required" validate:"required,max=10,min=1"`
15 | Gateway string `form:"gateway" binding:"required" validate:"gatewayURl"`
16 | Local string `form:"local" binding:"required" validate:"localURL"`
17 | Network string `form:"network" binding:"required" validate:"required,max=2,min=1"`
18 | Authentication string `form:"authentication" binding:"required" validate:"required,max=2,min=1"`
19 | Description string `form:"description" binding:"required" validate:"required,min=1"`
20 | }
21 |
22 | func ApiVerify() gin.HandlerFunc {
23 | return func(c *gin.Context) {
24 |
25 | var data ApiRequest
26 | //绑定数据
27 | errA := c.ShouldBind(&data)
28 |
29 | //校验请求数据
30 | validate := validator.New()
31 | //自己定义tag标签以及与之对应的处理逻辑
32 | validate.RegisterValidation("gatewayURl", gatewayURl)
33 | validate.RegisterValidation("localURL", localURL)
34 | errB := validate.Struct(&data)
35 |
36 | if errA != nil || errB != nil {
37 | c.JSON(http.StatusInternalServerError, gin.H{
38 | "status": 5010,
39 | "message": "请求参数不合法",
40 | })
41 | //终止
42 | c.Abort()
43 | } else {
44 | //该句可以省略,写出来只是表明可以进行验证下一步中间件,不写,也是内置会继续访问下一个中间件的
45 | c.Next()
46 | }
47 |
48 | }
49 | }
50 |
51 | //gateway uri验证
52 | func gatewayURl(fl validator.FieldLevel) bool {
53 |
54 | domain := fl.Field().String()
55 | matched, _ := regexp.MatchString(`^\/[A-Za-z0-9\-]+[\/=\?%\-&_~@[\]\':+!]*([^<>\"\"])*$`, domain)
56 |
57 | return matched
58 | }
59 |
60 | //local url校验
61 | func localURL(fl validator.FieldLevel) bool {
62 |
63 | sort := fl.Field().String()
64 | matched, _ := regexp.MatchString(`^((https|http)?:\/\/)+[A-Za-z0-9\-]+\.[A-Za-z0-9\-]+[\/=\?%\-&_~@[\]\':+!]*([^<>\"\"])*`, sort)
65 |
66 | return matched
67 | }
68 |
--------------------------------------------------------------------------------
/application/requests/Category.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "gopkg.in/go-playground/validator.v9"
6 | "net/http"
7 | )
8 |
9 | type CategoryRequest struct {
10 | Classify string `form:"classify" binding:"required" validate:"required,max=60,min=2"`
11 | }
12 |
13 | func CategoryVerify() gin.HandlerFunc {
14 | return func(c *gin.Context) {
15 |
16 | var data CategoryRequest
17 | //绑定数据
18 | errA := c.ShouldBind(&data)
19 |
20 | //校验请求数据
21 | validate := validator.New()
22 | errB := validate.Struct(&data)
23 |
24 | if errA != nil || errB != nil {
25 | c.JSON(http.StatusInternalServerError, gin.H{
26 | "status": 5010,
27 | "message": "请求参数不合法",
28 | })
29 | //终止
30 | c.Abort()
31 | } else {
32 | //该句可以省略,写出来只是表明可以进行验证下一步中间件,不写,也是内置会继续访问下一个中间件的
33 | c.Next()
34 | }
35 |
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/application/requests/Company.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "gopkg.in/go-playground/validator.v9"
6 | "net/http"
7 | )
8 |
9 | type CompanyRequest struct {
10 | Company string `form:"company" binding:"required" validate:"required,max=60,min=2"`
11 | AppId string `form:"appId" binding:"required" validate:"required,max=48,min=2"`
12 | AppSecret string `form:"appSecret" binding:"required" validate:"required,max=48,min=6"`
13 | }
14 |
15 | func CompanyVerify() gin.HandlerFunc {
16 | return func(c *gin.Context) {
17 |
18 | var data CompanyRequest
19 | //绑定数据
20 | errA := c.ShouldBind(&data)
21 |
22 | //校验请求数据
23 | validate := validator.New()
24 | errB := validate.Struct(&data)
25 |
26 | if errA != nil || errB != nil {
27 | c.JSON(http.StatusInternalServerError, gin.H{
28 | "status": 5010,
29 | "message": "请求参数不合法",
30 | })
31 | //终止
32 | c.Abort()
33 | } else {
34 | //该句可以省略,写出来只是表明可以进行验证下一步中间件,不写,也是内置会继续访问下一个中间件的
35 | c.Next()
36 | }
37 |
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/application/requests/Help.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "gopkg.in/go-playground/validator.v9"
6 | "net/http"
7 | )
8 |
9 | type HelpRequest struct {
10 | Title string `form:"title" binding:"required" validate:"required,max=60,min=2"`
11 | Content string `form:"content" binding:"required" validate:"required"`
12 | }
13 |
14 | func HelpVerify() gin.HandlerFunc {
15 | return func(c *gin.Context) {
16 |
17 | var data HelpRequest
18 | //绑定数据
19 | errA := c.ShouldBind(&data)
20 |
21 | //校验请求数据
22 | validate := validator.New()
23 | errB := validate.Struct(&data)
24 |
25 | if errA != nil || errB != nil {
26 | c.JSON(http.StatusInternalServerError, gin.H{
27 | "status": 5010,
28 | "message": "请求参数不合法",
29 | })
30 | //终止
31 | c.Abort()
32 | } else {
33 | //该句可以省略,写出来只是表明可以进行验证下一步中间件,不写,也是内置会继续访问下一个中间件的
34 | c.Next()
35 | }
36 |
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/application/requests/Login.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "gopkg.in/go-playground/validator.v9"
6 | "net/http"
7 | )
8 |
9 | type LoginRequest struct {
10 | User string `form:"user" binding:"required" validate:"required,max=60,min=2"`
11 | Pass string `form:"pass" binding:"required" validate:"required,max=32,min=16"`
12 | }
13 |
14 | func LoginVerify() gin.HandlerFunc {
15 | return func(c *gin.Context) {
16 |
17 | var data LoginRequest
18 | //绑定数据
19 | errA := c.ShouldBind(&data)
20 |
21 | //校验请求数据
22 | validate := validator.New()
23 | errB := validate.Struct(&data)
24 |
25 | if errA != nil || errB != nil {
26 | c.JSON(http.StatusInternalServerError, gin.H{
27 | "status": 5010,
28 | "message": "请求参数不合法",
29 | })
30 | //终止
31 | c.Abort()
32 | } else {
33 | //该句可以省略,写出来只是表明可以进行验证下一步中间件,不写,也是内置会继续访问下一个中间件的
34 | c.Next()
35 | }
36 |
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/application/requests/Organize.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "gopkg.in/go-playground/validator.v9"
6 | "net/http"
7 | )
8 |
9 | type OrgRequest struct {
10 | OrganizeName string `form:"organize_name" binding:"required" validate:"required,max=20,min=2"`
11 | OrganizeDesc string `form:"organize_desc" binding:"required" validate:"required,max=100,min=2"`
12 | }
13 |
14 | func OrganizeVerify() gin.HandlerFunc {
15 | return func(c *gin.Context) {
16 |
17 | var orgR OrgRequest
18 | //绑定数据
19 | errA := c.ShouldBind(&orgR)
20 |
21 | //校验请求数据
22 | validate := validator.New()
23 | errB := validate.Struct(&orgR)
24 |
25 | if errA != nil || errB != nil {
26 | c.JSON(http.StatusInternalServerError, gin.H{
27 | "status": 5010,
28 | "message": "请求参数不合法",
29 | })
30 | //终止
31 | c.Abort()
32 | } else {
33 | //该句可以省略,写出来只是表明可以进行验证下一步中间件,不写,也是内置会继续访问下一个中间件的
34 | c.Next()
35 | }
36 |
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/application/requests/Project.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "gopkg.in/go-playground/validator.v9"
6 | "net/http"
7 | )
8 |
9 | type ProjectRequest struct {
10 | Project string `form:"project" binding:"required" validate:"required,max=20,min=2"`
11 | Desc string `form:"desc" binding:"required" validate:"required,max=300,min=2"`
12 | }
13 |
14 | func ProjectVerify() gin.HandlerFunc {
15 | return func(c *gin.Context) {
16 |
17 | var proR ProjectRequest
18 | //绑定数据
19 | errA := c.ShouldBind(&proR)
20 |
21 | //校验请求数据
22 | validate := validator.New()
23 | errB := validate.Struct(&proR)
24 |
25 | if errA != nil || errB != nil {
26 | c.JSON(http.StatusInternalServerError, gin.H{
27 | "status": 5010,
28 | "message": "请求参数不合法",
29 | })
30 | //终止
31 | c.Abort()
32 | } else {
33 | //该句可以省略,写出来只是表明可以进行验证下一步中间件,不写,也是内置会继续访问下一个中间件的
34 | c.Next()
35 | }
36 |
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/application/requests/ProjectEnv.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "gopkg.in/go-playground/validator.v9"
6 | "net/http"
7 | "regexp"
8 | )
9 |
10 | type ProjectEnvRequest struct {
11 | Envname string `form:"envname" binding:"required" validate:"required,max=20,min=2"`
12 | Domain string `form:"domain" binding:"required" validate:"envDomain"`
13 | Sort string `form:"sort" binding:"required" validate:"envSort"`
14 | }
15 |
16 | func ProjectEnv() gin.HandlerFunc {
17 | return func(c *gin.Context) {
18 |
19 | var proR ProjectEnvRequest
20 | //绑定数据
21 | errA := c.ShouldBind(&proR)
22 |
23 | //校验请求数据
24 | validate := validator.New()
25 | //自己定义tag标签以及与之对应的处理逻辑
26 | validate.RegisterValidation("envDomain", envDomain)
27 | validate.RegisterValidation("envSort", envSort)
28 | errB := validate.Struct(&proR)
29 |
30 | if errA != nil || errB != nil {
31 | c.JSON(http.StatusInternalServerError, gin.H{
32 | "status": 5010,
33 | "message": "请求参数不合法",
34 | })
35 | //终止
36 | c.Abort()
37 | } else {
38 | //该句可以省略,写出来只是表明可以进行验证下一步中间件,不写,也是内置会继续访问下一个中间件的
39 | c.Next()
40 | }
41 |
42 | }
43 | }
44 |
45 | //环境域名验证
46 | func envDomain(fl validator.FieldLevel) bool {
47 |
48 | domain := fl.Field().String()
49 | matched, _ := regexp.MatchString(`^((https|http)?:\/\/)+[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+((:)+[0-9]{1,5})?(\/)?$`, domain)
50 |
51 | return matched
52 | }
53 |
54 | //环境排序验证
55 | func envSort(fl validator.FieldLevel) bool {
56 |
57 | sort := fl.Field().String()
58 | matched, _ := regexp.MatchString(`^\d+?$`, sort)
59 |
60 | return matched
61 | }
62 |
--------------------------------------------------------------------------------
/application/requests/Register.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "gopkg.in/go-playground/validator.v9"
6 | "net/http"
7 | "regexp"
8 | )
9 |
10 | type RegisterRequest struct {
11 | Username string `form:"username" binding:"required" validate:"required,max=20,min=2"`
12 | Password string `form:"password" binding:"required" validate:"required,max=32,min=16"`
13 | Phone string `form:"phone" binding:"required" validate:"required,phonecheck"`
14 | Email string `form:"email" binding:"required" validate:"required,email"`
15 | }
16 |
17 | func RegisterVerify() gin.HandlerFunc {
18 | return func(c *gin.Context) {
19 |
20 | var data RegisterRequest
21 | //绑定数据
22 | errA := c.ShouldBind(&data)
23 |
24 | //校验请求数据
25 | validate := validator.New()
26 |
27 | //自己定义tag标签以及与之对应的处理逻辑
28 | validate.RegisterValidation("phonecheck", phonecheck)
29 |
30 | errB := validate.Struct(&data)
31 |
32 | if errA != nil || errB != nil {
33 | c.JSON(http.StatusInternalServerError, gin.H{
34 | "status": 5010,
35 | "message": "请求参数不合法",
36 | })
37 | //终止
38 | c.Abort()
39 | } else {
40 | //该句可以省略,写出来只是表明可以进行验证下一步中间件,不写,也是内置会继续访问下一个中间件的
41 | c.Next()
42 | }
43 |
44 | }
45 | }
46 |
47 | //手机号 验证
48 | func phonecheck(fl validator.FieldLevel) bool {
49 |
50 | phone := fl.Field().String()
51 | matched, _ := regexp.MatchString(`^1\d{10}$`, phone)
52 |
53 | return matched
54 | }
55 |
--------------------------------------------------------------------------------
/application/requests/Reset.go:
--------------------------------------------------------------------------------
1 | package requests
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "gopkg.in/go-playground/validator.v9"
6 | "net/http"
7 | )
8 |
9 | type ResetRequest struct {
10 | Pass string `form:"pass" binding:"required" validate:"required,max=32,min=16"`
11 | Repass string `form:"repass" binding:"required" validate:"required,max=32,min=16"`
12 | Resetinfo string `form:"resetinfo" binding:"required" validate:"required,max=100,min=6"`
13 | }
14 |
15 | func ResetVerify() gin.HandlerFunc {
16 | return func(c *gin.Context) {
17 |
18 | var data ResetRequest
19 | //绑定数据
20 | errA := c.ShouldBind(&data)
21 |
22 | //校验请求数据
23 | validate := validator.New()
24 | errB := validate.Struct(&data)
25 |
26 | if data.Pass != data.Repass || errA != nil || errB != nil {
27 | c.JSON(http.StatusInternalServerError, gin.H{
28 | "status": 5010,
29 | "message": "请求参数不合法",
30 | })
31 | //终止
32 | c.Abort()
33 | } else {
34 | //该句可以省略,写出来只是表明可以进行验证下一步中间件,不写,也是内置会继续访问下一个中间件的
35 | c.Next()
36 | }
37 |
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/application/schedule/index.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/application/schedule/index.html
--------------------------------------------------------------------------------
/application/utils/e.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | const (
4 | SUCCESS = 200
5 | ERROR = 500
6 | INVALID_PARAMS = 400
7 |
8 | ERROR_EXIST_TAG = 10001
9 | ERROR_EXIST_TAG_FAIL = 10002
10 | ERROR_NOT_EXIST_TAG = 10003
11 | ERROR_GET_TAGS_FAIL = 10004
12 | ERROR_COUNT_TAG_FAIL = 10005
13 | ERROR_ADD_TAG_FAIL = 10006
14 | ERROR_EDIT_TAG_FAIL = 10007
15 | ERROR_DELETE_TAG_FAIL = 10008
16 | ERROR_EXPORT_TAG_FAIL = 10009
17 | ERROR_IMPORT_TAG_FAIL = 10010
18 |
19 | ERROR_NOT_EXIST_ARTICLE = 10011
20 | ERROR_CHECK_EXIST_ARTICLE_FAIL = 10012
21 | ERROR_ADD_ARTICLE_FAIL = 10013
22 | ERROR_DELETE_ARTICLE_FAIL = 10014
23 | ERROR_EDIT_ARTICLE_FAIL = 10015
24 | ERROR_COUNT_ARTICLE_FAIL = 10016
25 | ERROR_GET_ARTICLES_FAIL = 10017
26 | ERROR_GET_ARTICLE_FAIL = 10018
27 | ERROR_GEN_ARTICLE_POSTER_FAIL = 10019
28 |
29 | ERROR_AUTH_CHECK_TOKEN_FAIL = 20001
30 | ERROR_AUTH_CHECK_TOKEN_TIMEOUT = 20002
31 | ERROR_AUTH_TOKEN = 20003
32 | ERROR_AUTH = 20004
33 |
34 | ERROR_UPLOAD_SAVE_IMAGE_FAIL = 30001
35 | ERROR_UPLOAD_CHECK_IMAGE_FAIL = 30002
36 | ERROR_UPLOAD_CHECK_IMAGE_FORMAT = 30003
37 | )
38 |
39 | var MsgFlags = map[int]string{
40 | SUCCESS: "ok",
41 | ERROR: "fail",
42 | INVALID_PARAMS: "请求参数错误",
43 | ERROR_EXIST_TAG: "已存在该标签名称",
44 | ERROR_EXIST_TAG_FAIL: "获取已存在标签失败",
45 | ERROR_NOT_EXIST_TAG: "该标签不存在",
46 | ERROR_GET_TAGS_FAIL: "获取所有标签失败",
47 | ERROR_COUNT_TAG_FAIL: "统计标签失败",
48 | ERROR_ADD_TAG_FAIL: "新增标签失败",
49 | ERROR_EDIT_TAG_FAIL: "修改标签失败",
50 | ERROR_DELETE_TAG_FAIL: "删除标签失败",
51 | ERROR_EXPORT_TAG_FAIL: "导出标签失败",
52 | ERROR_IMPORT_TAG_FAIL: "导入标签失败",
53 | ERROR_NOT_EXIST_ARTICLE: "该文章不存在",
54 | ERROR_ADD_ARTICLE_FAIL: "新增文章失败",
55 | ERROR_DELETE_ARTICLE_FAIL: "删除文章失败",
56 | ERROR_CHECK_EXIST_ARTICLE_FAIL: "检查文章是否存在失败",
57 | ERROR_EDIT_ARTICLE_FAIL: "修改文章失败",
58 | ERROR_COUNT_ARTICLE_FAIL: "统计文章失败",
59 | ERROR_GET_ARTICLES_FAIL: "获取多个文章失败",
60 | ERROR_GET_ARTICLE_FAIL: "获取单个文章失败",
61 | ERROR_GEN_ARTICLE_POSTER_FAIL: "生成文章海报失败",
62 | ERROR_AUTH_CHECK_TOKEN_FAIL: "Token鉴权失败",
63 | ERROR_AUTH_CHECK_TOKEN_TIMEOUT: "Token已超时",
64 | ERROR_AUTH_TOKEN: "Token生成失败",
65 | ERROR_AUTH: "Token错误",
66 | ERROR_UPLOAD_SAVE_IMAGE_FAIL: "保存图片失败",
67 | ERROR_UPLOAD_CHECK_IMAGE_FAIL: "检查图片失败",
68 | ERROR_UPLOAD_CHECK_IMAGE_FORMAT: "校验图片错误,图片格式或大小有问题",
69 | }
70 |
71 | func GetMsg(code int) string {
72 | msg, ok := MsgFlags[code]
73 | if ok {
74 | return msg
75 | }
76 | return MsgFlags[ERROR]
77 | }
78 |
--------------------------------------------------------------------------------
/application/utils/jwt.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/dgrijalva/jwt-go"
5 | "time"
6 | )
7 |
8 | var jwtSecret = []byte("EhBu9G3bGTUssukg")
9 |
10 | type Claims struct {
11 | Username string `json:"username"`
12 | Password string `json:"password"`
13 | jwt.StandardClaims
14 | }
15 |
16 | //生成token
17 | func GenerateToken(username, password string) (string, error) {
18 | nowTime := time.Now()
19 | expireTime := nowTime.Add(3 * time.Hour)
20 |
21 | claims := Claims{
22 | username,
23 | password,
24 | jwt.StandardClaims{
25 | ExpiresAt: expireTime.Unix(),
26 |
27 | Issuer: "xapimanager",
28 | },
29 | }
30 |
31 | tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
32 | token, err := tokenClaims.SignedString(jwtSecret)
33 |
34 | return token, err
35 | }
36 |
37 | //解析token
38 | func ParseToken(token string) (*Claims, error) {
39 | tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
40 | return jwtSecret, nil
41 | })
42 |
43 | if tokenClaims != nil {
44 | if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
45 | return claims, nil
46 | }
47 | }
48 |
49 | return nil, err
50 | }
51 |
--------------------------------------------------------------------------------
/application/utils/logrus.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "xapimanager/application/common"
5 | "xapimanager/config"
6 | "github.com/lestrrat/go-file-rotatelogs"
7 | "github.com/pkg/errors"
8 | "github.com/rifflock/lfshook"
9 | "github.com/sirupsen/logrus"
10 | "os"
11 | "path"
12 | "time"
13 | )
14 |
15 | var Log *logrus.Logger
16 |
17 | func init() {
18 | Log = logrus.New()
19 | //设置日志格式为json格式
20 | Log.SetFormatter(&logrus.JSONFormatter{})
21 | // 设置日志级别为info以上
22 | Log.SetLevel(logrus.InfoLevel)
23 | path, _ := os.Getwd()
24 | logPath := path + "/" + config.GetGlobal().LOG_PATH
25 | // 判断日志路径是否存在(19/08/29 - 刘鸣扬)
26 | res, _ := common.PathExists(logPath)
27 | if !res {
28 | // 若日志路径不存在则创建目录(19/08/29 - 刘鸣扬)
29 | _, _ = common.CreateDir(logPath)
30 | }
31 | ConfigLocalFilesystemLogger(logPath, "std.log", time.Hour*24*7, time.Hour*24)
32 | }
33 | func ConfigLocalFilesystemLogger(logPath string, logFileName string, maxAge time.Duration, rotationTime time.Duration) {
34 | baseLogPaht := path.Join(logPath, logFileName)
35 | writer, err := rotatelogs.New(
36 | baseLogPaht+".%Y%m%d",
37 | rotatelogs.WithLinkName(baseLogPaht), // 生成软链,指向最新日志文件
38 | rotatelogs.WithMaxAge(maxAge), // 文件最大保存时间
39 | rotatelogs.WithRotationTime(rotationTime), // 日志切割时间间隔
40 | )
41 | if err != nil {
42 | Log.Errorf("config local file system logger error. %+v", errors.WithStack(err))
43 | }
44 | lfHook := lfshook.NewHook(lfshook.WriterMap{
45 | logrus.DebugLevel: writer, // 为不同级别设置不同的输出目的
46 | logrus.InfoLevel: writer,
47 | logrus.WarnLevel: writer,
48 | logrus.ErrorLevel: writer,
49 | logrus.FatalLevel: writer,
50 | logrus.PanicLevel: writer,
51 | }, &logrus.TextFormatter{DisableColors: true, TimestampFormat: "2006-01-02 15:04:05.000"})
52 |
53 | Log.AddHook(lfHook)
54 | }
55 |
--------------------------------------------------------------------------------
/config/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "debug": true,
3 | "server_port": "3000",
4 | "server_website": "http://127.0.0.1:3000",
5 |
6 | "mysql_ip": "127.0.0.1",
7 | "mysql_port": "3306",
8 | "mysql_username": "root",
9 | "mysql_password": "root123",
10 | "mysql_dbname": "qiyu",
11 | "mysql_prefix": "qy_",
12 |
13 | "redis_ip": "127.0.0.1",
14 | "redis_port": "6379",
15 | "redis_password": "",
16 | "redis_db": 1,
17 |
18 | "log_path": "storage/logs",
19 | "template_pate": "resources/templates",
20 | "assets_path": "resources/assets",
21 | "upload_path": "storage/upload",
22 |
23 | "DbLogMode": true,
24 | "DbMaxIdleConns": 200,
25 | "DbMaxOpenConns": 2000,
26 | "DbConnMaxLifetime": 5,
27 |
28 | "user_cache": 600,
29 | "sys_cache": 5
30 | }
--------------------------------------------------------------------------------
/config/fetch.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | /*
4 | * 该变量用于记录页面与调用Api的映射关系
5 | * 在权限控制中如果页面不能访问的话,页面调用的接口也不能访问
6 | * 所谓页面调用的接口,其实是页面的ajax请求
7 | */
8 |
9 | var fetch = map[string][]string{
10 | "/dash": []string{ //控制台页面
11 | "/dash/area"},
12 | "/Api/audit": []string{ //待审核Api页面
13 | "/Api/audit"},
14 | "/Api/list": []string{ //Api 列表页面
15 | "/Api"},
16 | "/Api/info": []string{ //Api添加、编辑、发布、删除
17 | "/Api/store",
18 | "/Api/operate",
19 | "/Api/publish",
20 | "/Api/discard"},
21 | "/company": []string{ //企业密钥页
22 | "/company/list",
23 | "/company/operate"},
24 | "/company/info": []string{ //企业密钥创建/编辑页
25 | "/company/store"},
26 | "/category/info": []string{ //分类添加/编辑
27 | "/category/store",
28 | "/category/operate"},
29 | }
30 |
31 | func GetApiFetch() (Apifetch map[string]string) {
32 |
33 | Apifetch = map[string]string{}
34 | for ko, vol := range fetch {
35 | for _, v := range vol {
36 | Apifetch[v] = ko
37 | }
38 | }
39 | return
40 | }
41 |
--------------------------------------------------------------------------------
/config/global.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "encoding/json"
5 | "io/ioutil"
6 | )
7 |
8 | type Global struct {
9 | DEBUG bool `json:"debug"`
10 | SERVER_PORT string `json:"server_port"`
11 | SERVER_WEBSITE string `json:"server_website"`
12 |
13 | MYSQL_IP string `json:"mysql_ip"`
14 | MYSQL_PORT string `json:"mysql_port"`
15 | MYSQL_USERNAME string `json:"mysql_username"`
16 | MYSQL_PASSWORD string `json:"mysql_password"`
17 | MYSQL_DBNAME string `json:"mysql_dbname"`
18 | MYSQL_PREFIX string `json:"mysql_prefix"`
19 |
20 | REDIS_IP string `json:"redis_ip"`
21 | REDIS_PORT string `json:"redis_port"`
22 | REDIS_PASSWORD string `json:"redis_password"`
23 | REDIS_DB int `json:"redis_db"`
24 |
25 | LOG_PATH string `json:"log_path"`
26 | TEMPLATE_PATH string `json:"template_pate"`
27 | ASSETS_PATH string `json:"assets_path"`
28 | UPLOAD_PATH string `json:"upload_path"`
29 |
30 | DbLogMode bool `json:"DbLogMode"` //数据库日志模式,开启true, 关闭false
31 | DbMaxIdleConns int `json:"DbMaxIdleConns"` //最大空闲连接数
32 | DbMaxOpenConns int `json:"DbMaxOpenConns"` //最大连接数
33 | DbConnMaxLifetime int `json:"DbConnMaxLifetime"` //mysql超时时间
34 |
35 | User_Cache int `json:"user_cache"` //用户缓存,单位s
36 | Sys_Cache int `json:"sys_cache"` //系统缓存,单位s
37 |
38 | }
39 |
40 | func GetGlobal() *Global {
41 |
42 | conf := "./config/config.json"
43 | data, _ := ioutil.ReadFile(conf)
44 |
45 | global := &Global{}
46 | json.Unmarshal(data, &global)
47 |
48 | return global
49 | }
50 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module xapimanager
2 |
3 | go 1.13
4 |
5 | require (
6 | github.com/dgrijalva/jwt-go v3.2.0+incompatible
7 | github.com/gin-contrib/sessions v0.0.3
8 | github.com/gin-gonic/gin v1.6.2
9 | github.com/go-redis/redis v6.15.7+incompatible
10 | github.com/jinzhu/gorm v1.9.12
11 | github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f
12 | github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
13 | github.com/mozillazg/go-pinyin v0.17.0
14 | github.com/pkg/errors v0.9.1
15 | github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
16 | github.com/sirupsen/logrus v1.5.0
17 | gopkg.in/ffmt.v1 v1.5.6
18 | gopkg.in/go-playground/validator.v9 v9.31.0
19 | )
20 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "runtime"
6 | "xapimanager/application/common"
7 | "xapimanager/config"
8 | "xapimanager/routes"
9 | )
10 |
11 | func main() {
12 |
13 | runtime.GOMAXPROCS(runtime.NumCPU())
14 |
15 | sysconfig := config.GetGlobal()
16 |
17 | if sysconfig.DEBUG {
18 | gin.SetMode(gin.DebugMode)
19 | } else {
20 | gin.SetMode(gin.ReleaseMode)
21 | }
22 |
23 | router := routes.InitRouter()
24 |
25 | router.Run(":" + sysconfig.SERVER_PORT)
26 |
27 | common.Open(sysconfig.SERVER_WEBSITE)
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | 项目介绍
2 | ========
3 | ### xApi Manager-哆啦接口管理平台
4 | XAPI MANAGER -专业实用的开源接口管理平台,为程序开发者提供一个灵活,方便,快捷的API管理工具,让API管理变的更加清晰、明朗。欢迎大家加入QQ交流群(623709829),共同探讨一些技术问题、交流一些想法
5 |
6 | 功能说明
7 | 1. 支持多团队、多项目、多环境,更符合技术开发和使用场景
8 | 2. 支持Api场景组并发测试、性能测试
9 | 3. 通过功能权限、数据权限让您精准的控制用户的使用权限,使其尽在掌控之中
10 | 4. 动态生成API数据,让前后端更好的协同开发,降低等待时间,提高开发效率
11 |
12 | - 官方地址:https://xapimanager.opiping.com/
13 | - 项目部署及配置:https://shuka.opiping.com/t/xapimanager (安装和升级请参考手册)
14 | - Windows、Mac、Ubuntu、Deepin 客户端下载: https://www.opiping.com/
15 |
16 |
17 |
18 | ### v3源码地址
19 |
20 | * 码云地址:https://git.oschina.net/duolatech/xapimanager
21 | * github地址:https://github.com/duolatech/xapimanager
22 |
23 | Windows、Mac、Ubuntu、Deepin客户端
24 | ========
25 | **多团队管理**
26 |
27 | **Api快速查找**
28 |
29 | **Api自动化测试**
30 |
31 | **Api Mock测试**
32 |
33 | **常用插件**
34 |
35 |
36 | golang 版本特点
37 |
38 | 1. 支持多项目、多环境,带给你更多的方便
39 | 2. 支持完整的权限管理限)
40 | 3. 优化Api管理、request、response数据多层级导入及展示
41 | 4. 简化操作、增加易用性、让体验更优
42 |
43 | ========
44 | **Api列表**
45 |
46 |
47 |
48 | 最后
49 | ========
50 | 非常欢迎大家贡献代码,让这个项目成长的更好。
51 |
--------------------------------------------------------------------------------
/resources/assets/css/patterns/header-profile-skin-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/patterns/header-profile-skin-1.png
--------------------------------------------------------------------------------
/resources/assets/css/patterns/header-profile-skin-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/patterns/header-profile-skin-3.png
--------------------------------------------------------------------------------
/resources/assets/css/patterns/header-profile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/patterns/header-profile.png
--------------------------------------------------------------------------------
/resources/assets/css/patterns/shattered.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/patterns/shattered.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/blueimp/css/blueimp-gallery-indicator.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /*
3 | * blueimp Gallery Indicator CSS 1.1.0
4 | * https://github.com/blueimp/Gallery
5 | *
6 | * Copyright 2013, Sebastian Tschan
7 | * https://blueimp.net
8 | *
9 | * Licensed under the MIT license:
10 | * http://www.opensource.org/licenses/MIT
11 | */
12 |
13 | .blueimp-gallery > .indicator {
14 | position: absolute;
15 | top: auto;
16 | right: 15px;
17 | bottom: 15px;
18 | left: 15px;
19 | margin: 0 40px;
20 | padding: 0;
21 | list-style: none;
22 | text-align: center;
23 | line-height: 10px;
24 | display: none;
25 | }
26 | .blueimp-gallery > .indicator > li {
27 | display: inline-block;
28 | width: 9px;
29 | height: 9px;
30 | margin: 6px 3px 0 3px;
31 | -webkit-box-sizing: content-box;
32 | -moz-box-sizing: content-box;
33 | box-sizing: content-box;
34 | border: 1px solid transparent;
35 | background: #ccc;
36 | background: rgba(255, 255, 255, 0.25) center no-repeat;
37 | border-radius: 5px;
38 | box-shadow: 0 0 2px #000;
39 | opacity: 0.5;
40 | cursor: pointer;
41 | }
42 | .blueimp-gallery > .indicator > li:hover,
43 | .blueimp-gallery > .indicator > .active {
44 | background-color: #fff;
45 | border-color: #fff;
46 | opacity: 1;
47 | }
48 | .blueimp-gallery-controls > .indicator {
49 | display: block;
50 | /* Fix z-index issues (controls behind slide element) on Android: */
51 | -webkit-transform: translateZ(0);
52 | -moz-transform: translateZ(0);
53 | -ms-transform: translateZ(0);
54 | -o-transform: translateZ(0);
55 | transform: translateZ(0);
56 | }
57 | .blueimp-gallery-single > .indicator {
58 | display: none;
59 | }
60 | .blueimp-gallery > .indicator {
61 | -webkit-user-select: none;
62 | -khtml-user-select: none;
63 | -moz-user-select: none;
64 | -ms-user-select: none;
65 | user-select: none;
66 | }
67 |
68 | /* IE7 fixes */
69 | *+html .blueimp-gallery > .indicator > li {
70 | display: inline;
71 | }
72 |
--------------------------------------------------------------------------------
/resources/assets/css/plugins/blueimp/css/blueimp-gallery-video.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /*
3 | * blueimp Gallery Video Factory CSS 1.3.0
4 | * https://github.com/blueimp/Gallery
5 | *
6 | * Copyright 2013, Sebastian Tschan
7 | * https://blueimp.net
8 | *
9 | * Licensed under the MIT license:
10 | * http://www.opensource.org/licenses/MIT
11 | */
12 |
13 | .blueimp-gallery > .slides > .slide > .video-content > img {
14 | position: absolute;
15 | top: 0;
16 | right: 0;
17 | bottom: 0;
18 | left: 0;
19 | margin: auto;
20 | width: auto;
21 | height: auto;
22 | max-width: 100%;
23 | max-height: 100%;
24 | /* Prevent artifacts in Mozilla Firefox: */
25 | -moz-backface-visibility: hidden;
26 | }
27 | .blueimp-gallery > .slides > .slide > .video-content > video {
28 | position: absolute;
29 | top: 0;
30 | left: 0;
31 | width: 100%;
32 | height: 100%;
33 | }
34 | .blueimp-gallery > .slides > .slide > .video-content > iframe {
35 | position: absolute;
36 | top: 100%;
37 | left: 0;
38 | width: 100%;
39 | height: 100%;
40 | border: none;
41 | }
42 | .blueimp-gallery > .slides > .slide > .video-playing > iframe {
43 | top: 0;
44 | }
45 | .blueimp-gallery > .slides > .slide > .video-content > a {
46 | position: absolute;
47 | top: 50%;
48 | right: 0;
49 | left: 0;
50 | margin: -64px auto 0;
51 | width: 128px;
52 | height: 128px;
53 | background: url(../img/video-play.png) center no-repeat;
54 | opacity: 0.8;
55 | cursor: pointer;
56 | }
57 | .blueimp-gallery > .slides > .slide > .video-content > a:hover {
58 | opacity: 1;
59 | }
60 | .blueimp-gallery > .slides > .slide > .video-playing > a,
61 | .blueimp-gallery > .slides > .slide > .video-playing > img {
62 | display: none;
63 | }
64 | .blueimp-gallery > .slides > .slide > .video-content > video {
65 | display: none;
66 | }
67 | .blueimp-gallery > .slides > .slide > .video-playing > video {
68 | display: block;
69 | }
70 | .blueimp-gallery > .slides > .slide > .video-loading > a {
71 | background: url(../img/loading.gif) center no-repeat;
72 | background-size: 64px 64px;
73 | }
74 |
75 | /* Replace PNGs with SVGs for capable browsers (excluding IE<9) */
76 | body:last-child .blueimp-gallery > .slides > .slide > .video-content:not(.video-loading) > a {
77 | background-image: url(../img/video-play.svg);
78 | }
79 |
80 | /* IE7 fixes */
81 | *+html .blueimp-gallery > .slides > .slide > .video-content {
82 | height: 100%;
83 | }
84 | *+html .blueimp-gallery > .slides > .slide > .video-content > a {
85 | left: 50%;
86 | margin-left: -64px;
87 | }
88 |
--------------------------------------------------------------------------------
/resources/assets/css/plugins/blueimp/css/demo.css:
--------------------------------------------------------------------------------
1 | /*
2 | * blueimp Gallery Demo CSS 2.0.0
3 | * https://github.com/blueimp/Gallery
4 | *
5 | * Copyright 2013, Sebastian Tschan
6 | * https://blueimp.net
7 | *
8 | * Licensed under the MIT license:
9 | * http://www.opensource.org/licenses/MIT
10 | */
11 |
12 | body {
13 | max-width: 750px;
14 | margin: 0 auto;
15 | padding: 1em;
16 | font-family: 'Lucida Grande', 'Lucida Sans Unicode', Arial, sans-serif;
17 | font-size: 1em;
18 | line-height: 1.4em;
19 | background: #222;
20 | color: #fff;
21 | -webkit-text-size-adjust: 100%;
22 | -ms-text-size-adjust: 100%;
23 | }
24 | a {
25 | color: orange;
26 | text-decoration: none;
27 | }
28 | img {
29 | border: 0;
30 | vertical-align: middle;
31 | }
32 | h1 {
33 | line-height: 1em;
34 | }
35 | h2,
36 | .links {
37 | text-align: center;
38 | }
39 |
40 | @media (min-width: 481px) {
41 | .navigation {
42 | list-style: none;
43 | padding: 0;
44 | }
45 | .navigation li {
46 | display: inline-block;
47 | }
48 | .navigation li:not(:first-child):before {
49 | content: '| ';
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/resources/assets/css/plugins/blueimp/img/error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/blueimp/img/error.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/blueimp/img/error.svg:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/resources/assets/css/plugins/blueimp/img/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/blueimp/img/loading.gif
--------------------------------------------------------------------------------
/resources/assets/css/plugins/blueimp/img/play-pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/blueimp/img/play-pause.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/blueimp/img/play-pause.svg:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/resources/assets/css/plugins/blueimp/img/video-play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/blueimp/img/video-play.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/blueimp/img/video-play.svg:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/resources/assets/css/plugins/chosen/chosen-sprite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/chosen/chosen-sprite.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/chosen/chosen-sprite@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/chosen/chosen-sprite@2x.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/colorpicker/img/bootstrap-colorpicker/alpha-horizontal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/colorpicker/img/bootstrap-colorpicker/alpha-horizontal.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/colorpicker/img/bootstrap-colorpicker/alpha.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/colorpicker/img/bootstrap-colorpicker/alpha.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/colorpicker/img/bootstrap-colorpicker/hue-horizontal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/colorpicker/img/bootstrap-colorpicker/hue-horizontal.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/colorpicker/img/bootstrap-colorpicker/hue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/colorpicker/img/bootstrap-colorpicker/hue.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/colorpicker/img/bootstrap-colorpicker/saturation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/colorpicker/img/bootstrap-colorpicker/saturation.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/duallistbox/bootstrap-duallistbox.css:
--------------------------------------------------------------------------------
1 | .bootstrap-duallistbox-container .buttons {
2 | width:calc(100% + 1px);
3 | margin-bottom: -6px;
4 | box-sizing: border-box;
5 | }
6 |
7 | .bootstrap-duallistbox-container label {
8 | display: block;
9 | }
10 |
11 | .bootstrap-duallistbox-container .info {
12 | display: inline-block;
13 | margin-bottom: 5px;
14 | }
15 |
16 | .bootstrap-duallistbox-container .clear1,
17 | .bootstrap-duallistbox-container .clear2 {
18 | display: none;
19 | font-size: 10px;
20 | }
21 |
22 | .bootstrap-duallistbox-container .box1.filtered .clear1,
23 | .bootstrap-duallistbox-container .box2.filtered .clear2 {
24 | display: inline-block;
25 | }
26 |
27 | .bootstrap-duallistbox-container .move,
28 | .bootstrap-duallistbox-container .remove {
29 | width: 50%;box-sizing: border-box;
30 | }
31 |
32 | .bootstrap-duallistbox-container .btn-group .btn {
33 | border-bottom-left-radius: 0;
34 | border-bottom-right-radius: 0;
35 | }
36 | .bootstrap-duallistbox-container select {
37 | border-top-left-radius: 0;
38 | border-top-right-radius: 0;
39 | }
40 |
41 | .bootstrap-duallistbox-container .moveall,
42 | .bootstrap-duallistbox-container .removeall {
43 | width: 50%;box-sizing: border-box;
44 | }
45 |
46 | .bootstrap-duallistbox-container.bs2compatible .btn-group > .btn + .btn {
47 | margin-left: 0;
48 | }
49 |
50 | .bootstrap-duallistbox-container select {
51 | height: 300px;
52 | box-sizing: border-box;
53 | }
54 | .bootstrap-duallistbox-container select:focus{
55 | border-color: #e5e6e7!important;
56 | }
57 |
58 | .bootstrap-duallistbox-container .filter {
59 | display: inline-block;
60 | width: 100%;
61 | height: 31px;margin-bottom:-1px;
62 | -webkit-box-sizing: border-box;
63 | -moz-box-sizing: border-box;
64 | }
65 |
66 | .bootstrap-duallistbox-container .filter.placeholder {
67 | color: #aaa;
68 | }
69 |
70 | .bootstrap-duallistbox-container.moveonselect .move,
71 | .bootstrap-duallistbox-container.moveonselect .remove {
72 | display:none;
73 | }
74 |
75 | .bootstrap-duallistbox-container.moveonselect .moveall,
76 | .bootstrap-duallistbox-container.moveonselect .removeall {
77 | width: 100%;
78 | }
79 |
--------------------------------------------------------------------------------
/resources/assets/css/plugins/fullcalendar/fullcalendar.print.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * FullCalendar v1.6.4 Print Stylesheet
3 | * Docs & License: http://arshaw.com/fullcalendar/
4 | * (c) 2013 Adam Shaw
5 | */
6 |
7 | /*
8 | * Include this stylesheet on your page to get a more printer-friendly calendar.
9 | * When including this stylesheet, use the media='print' attribute of the tag.
10 | * Make sure to include this stylesheet IN ADDITION to the regular fullcalendar.css.
11 | */
12 |
13 |
14 | /* Events
15 | -----------------------------------------------------*/
16 |
17 | .fc-event {
18 | background: #fff !important;
19 | color: #000 !important;
20 | }
21 |
22 | /* for vertical events */
23 |
24 | .fc-event-bg {
25 | display: none !important;
26 | }
27 |
28 | .fc-event .ui-resizable-handle {
29 | display: none !important;
30 | }
31 |
32 |
33 |
--------------------------------------------------------------------------------
/resources/assets/css/plugins/iCheck/custom.css:
--------------------------------------------------------------------------------
1 | /* iCheck plugin Square skin, green
2 | ----------------------------------- */
3 | .icheckbox_square-green,
4 | .iradio_square-green {
5 | display: inline-block;
6 | *display: inline;
7 | vertical-align: middle;
8 | margin: 0;
9 | padding: 0;
10 | width: 22px;
11 | height: 22px;
12 | background: url(green.png) no-repeat;
13 | border: none;
14 | cursor: pointer;
15 | }
16 |
17 | .icheckbox_square-green {
18 | background-position: 0 0;
19 | }
20 | .icheckbox_square-green.hover {
21 | background-position: -24px 0;
22 | }
23 | .icheckbox_square-green.checked {
24 | background-position: -48px 0;
25 | }
26 | .icheckbox_square-green.disabled {
27 | background-position: -72px 0;
28 | cursor: default;
29 | }
30 | .icheckbox_square-green.checked.disabled {
31 | background-position: -96px 0;
32 | }
33 |
34 | .iradio_square-green {
35 | background-position: -120px 0;
36 | }
37 | .iradio_square-green.hover {
38 | background-position: -144px 0;
39 | }
40 | .iradio_square-green.checked {
41 | background-position: -168px 0;
42 | }
43 | .iradio_square-green.disabled {
44 | background-position: -192px 0;
45 | cursor: default;
46 | }
47 | .iradio_square-green.checked.disabled {
48 | background-position: -216px 0;
49 | }
50 |
51 | /* HiDPI support */
52 | @media (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {
53 | .icheckbox_square-green,
54 | .iradio_square-green {
55 | background-image: url(green@2x.png);
56 | -webkit-background-size: 240px 24px;
57 | background-size: 240px 24px;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/resources/assets/css/plugins/iCheck/green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/iCheck/green.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/iCheck/green@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/iCheck/green@2x.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/jQueryUI/images/ui-bg_flat_0_aaaaaa_40x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/jQueryUI/images/ui-bg_flat_0_aaaaaa_40x100.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/jQueryUI/images/ui-bg_flat_75_ffffff_40x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/jQueryUI/images/ui-bg_flat_75_ffffff_40x100.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/jQueryUI/images/ui-icons_222222_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/jQueryUI/images/ui-icons_222222_256x240.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/jQueryUI/images/ui-icons_454545_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/jQueryUI/images/ui-icons_454545_256x240.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/jQueryUI/images/ui-icons_888888_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/css/plugins/jQueryUI/images/ui-icons_888888_256x240.png
--------------------------------------------------------------------------------
/resources/assets/css/plugins/morris/morris-0.4.3.min.css:
--------------------------------------------------------------------------------
1 | .morris-hover{position:absolute;z-index:1000;}.morris-hover.morris-default-style{border-radius:10px;padding:6px;color:#666;background:rgba(255, 255, 255, 0.8);border:solid 2px rgba(230, 230, 230, 0.8);font-family:sans-serif;font-size:12px;text-align:center;}.morris-hover.morris-default-style .morris-hover-row-label{font-weight:bold;margin:0.25em 0;}
2 | .morris-hover.morris-default-style .morris-hover-point{white-space:nowrap;margin:0.1em 0;}
3 |
--------------------------------------------------------------------------------
/resources/assets/css/plugins/multiselect/bootstrap-multiselect.css:
--------------------------------------------------------------------------------
1 | .multiselect-container{position:absolute;list-style-type:none;margin:0;padding:0}.multiselect-container .input-group{margin:5px}.multiselect-container>li{padding:0}.multiselect-container>li>a.multiselect-all label{font-weight:700}.multiselect-container>li.multiselect-group label{margin:0;padding:3px 20px 3px 20px;height:100%;font-weight:700}.multiselect-container>li.multiselect-group-clickable label{cursor:pointer}.multiselect-container>li>a{padding:0}.multiselect-container>li>a>label{margin:0;height:100%;cursor:pointer;font-weight:400;padding:3px 20px 3px 40px}.multiselect-container>li>a>label.radio,.multiselect-container>li>a>label.checkbox{margin:0}.multiselect-container>li>a>label>input[type=checkbox]{margin-bottom:5px}.btn-group>.btn-group:nth-child(2)>.multiselect.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.form-inline .multiselect-container label.checkbox,.form-inline .multiselect-container label.radio{padding:3px 20px 3px 40px}.form-inline .multiselect-container li a label.checkbox input[type=checkbox],.form-inline .multiselect-container li a label.radio input[type=radio]{margin-left:-20px;margin-right:0}
2 |
--------------------------------------------------------------------------------
/resources/assets/css/plugins/switchery/switchery.css:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Main stylesheet for Switchery.
4 | * http://abpetkov.github.io/switchery/
5 | *
6 | */
7 |
8 | .switchery {
9 | background-color: #fff;
10 | border: 1px solid #dfdfdf;
11 | border-radius: 20px;
12 | cursor: pointer;
13 | display: inline-block;
14 | height: 30px;
15 | position: relative;
16 | vertical-align: middle;
17 | width: 50px;
18 |
19 | -webkit-box-sizing: content-box;
20 | -moz-box-sizing: content-box;
21 | box-sizing: content-box;
22 | }
23 |
24 | .switchery > small {
25 | background: #fff;
26 | border-radius: 100%;
27 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
28 | height: 30px;
29 | position: absolute;
30 | top: 0;
31 | width: 30px;
32 | }
33 |
--------------------------------------------------------------------------------
/resources/assets/css/select.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * ui-select
3 | * http://github.com/angular-ui/ui-select
4 | * Version: 0.8.3 - 2014-10-14T18:22:05.435Z
5 | * License: MIT
6 | */.ui-select-highlight{font-weight:700}.ui-select-offscreen{clip:rect(0 0 0 0)!important;width:1px!important;height:1px!important;border:0!important;margin:0!important;padding:0!important;overflow:hidden!important;position:absolute!important;outline:0!important;left:0!important;top:0!important}.ng-dirty.ng-invalid>a.select2-choice{border-color:#D44950}.select2-result-single{padding-left:0}.selectize-input.selectize-focus{border-color:#007FBB!important}.selectize-control>.selectize-dropdown,.selectize-control>.selectize-input>input{width:100%}.ng-dirty.ng-invalid>div.selectize-input{border-color:#D44950}.btn-default-focus{color:#333;background-color:#EBEBEB;border-color:#ADADAD;text-decoration:none;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.input-group>.ui-select-bootstrap.dropdown{position:static}.input-group>.ui-select-bootstrap>input.ui-select-search.form-control{border-radius:4px 0 0 4px}.ui-select-bootstrap>.ui-select-match{text-align:left}.ui-select-bootstrap>.ui-select-match>.caret{position:absolute;top:45%;right:15px}.ui-select-bootstrap>.ui-select-choices{width:100%;height:auto;max-height:200px;overflow-x:hidden}.ui-select-multiple.ui-select-bootstrap{height:auto;padding:.3em}.ui-select-multiple.ui-select-bootstrap input.ui-select-search{background-color:transparent!important;border:none;outline:0;height:1.666666em}.ui-select-multiple.ui-select-bootstrap .ui-select-match .close{font-size:1.6em;line-height:.75}.ui-select-multiple.ui-select-bootstrap .ui-select-match-item{outline:0}.ui-select-bootstrap .ui-select-choices-row>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.ui-select-bootstrap .ui-select-choices-row>a:focus,.ui-select-bootstrap .ui-select-choices-row>a:hover{text-decoration:none;color:#262626;background-color:#f5f5f5}.ui-select-bootstrap .ui-select-choices-row.active>a{color:#fff;text-decoration:none;outline:0;background-color:#428bca}.ui-select-bootstrap .ui-select-choices-row.active.disabled>a,.ui-select-bootstrap .ui-select-choices-row.disabled>a{color:#777;cursor:not-allowed;background-color:#fff}.ui-select-match.ng-hide-add,.ui-select-search.ng-hide-add{display:none!important}.ui-select-bootstrap.ng-dirty.ng-invalid>button.btn.ui-select-match{border-color:#D44950}
--------------------------------------------------------------------------------
/resources/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/favicon.ico
--------------------------------------------------------------------------------
/resources/assets/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/resources/assets/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/resources/assets/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/resources/assets/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/resources/assets/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/resources/assets/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/resources/assets/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/resources/assets/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/resources/assets/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/resources/assets/img/Qiyu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/Qiyu.png
--------------------------------------------------------------------------------
/resources/assets/img/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/avatar.png
--------------------------------------------------------------------------------
/resources/assets/img/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/bg.png
--------------------------------------------------------------------------------
/resources/assets/img/icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/icons.png
--------------------------------------------------------------------------------
/resources/assets/img/loading-upload.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/loading-upload.gif
--------------------------------------------------------------------------------
/resources/assets/img/locked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/locked.png
--------------------------------------------------------------------------------
/resources/assets/img/login-background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/login-background.jpg
--------------------------------------------------------------------------------
/resources/assets/img/p1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/p1.jpg
--------------------------------------------------------------------------------
/resources/assets/img/p3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/p3.jpg
--------------------------------------------------------------------------------
/resources/assets/img/p_big1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/p_big1.jpg
--------------------------------------------------------------------------------
/resources/assets/img/p_big2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/p_big2.jpg
--------------------------------------------------------------------------------
/resources/assets/img/p_big3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/p_big3.jpg
--------------------------------------------------------------------------------
/resources/assets/img/profile_big.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/profile_big.jpg
--------------------------------------------------------------------------------
/resources/assets/img/progress.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/progress.png
--------------------------------------------------------------------------------
/resources/assets/img/sprite-skin-flat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/sprite-skin-flat.png
--------------------------------------------------------------------------------
/resources/assets/img/success.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/success.png
--------------------------------------------------------------------------------
/resources/assets/img/team.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/team.png
--------------------------------------------------------------------------------
/resources/assets/img/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/user.png
--------------------------------------------------------------------------------
/resources/assets/img/webuploader.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/img/webuploader.png
--------------------------------------------------------------------------------
/resources/assets/index.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/index.html
--------------------------------------------------------------------------------
/resources/assets/js/apisearch.js:
--------------------------------------------------------------------------------
1 |
2 | //Api 搜索
3 | $(".btn-Api-search").on("click", function(){
4 |
5 | var apiname = $('input[name="apiname"]').val();
6 | var classify = $('select[name="classify"]').val();
7 | var subClassify = $('select[name="subClassify"]').val();
8 | var URI = $('input[name="gateway"]').val();
9 | var author = $('input[name="author"]').val();
10 | var proid = $(this).attr("proid");
11 |
12 | var url = "/manager/"+proid+"/Api/list?type=search&apiname="+apiname;
13 | if(classify!=0) url+="&classify="+classify;
14 | if(subClassify!=0) url+="&subClassify="+subClassify;
15 | url+="&URI="+URI;
16 | url+="&author="+author;
17 |
18 | window.location.href= url;
19 | });
--------------------------------------------------------------------------------
/resources/assets/js/apps.js:
--------------------------------------------------------------------------------
1 |
2 | //json 样例
3 | $(".jsonExample").on("click",function () {
4 | var jsonStr = '{"data":{"name":"xApi Manager","url":"http://www.smaty.net","keywords":["xapi","restfull"],"author":["feng","long"],"description":"专业实用的开源接口管理平台"}}';
5 | var jsonData = JSON.parse(jsonStr);
6 | //json/xml 互转
7 | if($("#import").length>0){
8 | $("#import").val(JSON.stringify(jsonData, null, 4))
9 | }
10 | //json 格式化
11 | if($("#format-import").length>0){
12 | $("#format-import").val(JSON.stringify(jsonData, null, 4))
13 | }
14 |
15 | });
16 | //xml 样例
17 | $(".xmlExample").on("click",function () {
18 | var xmlStr = '
操作人 | 24 |操作对象 | 25 |类型 | 26 |时间 | 27 | 30 ||
---|---|---|---|---|
38 | 39 | | 40 |