├── .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 | 3 | 4 | 5 | 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 | 3 | 4 | 5 | 6 | 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 | 3 | 4 | 5 | 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 = 'xApi Managerhttp://www.smaty.netxapirestfullfenglong专业实用的开源接口管理平台'; 19 | $("#import").val(vkbeautify.xml(xmlStr,4)) 20 | }); 21 | //xml转json 22 | $(".xmltrans").on("click", function () { 23 | var xmlText = $("#import").val(); 24 | var xotree = new XML.ObjTree(); 25 | var jsonData = xotree.parseXML(xmlText); 26 | if(jsonData.html){ 27 | swal("处理出错", "xml格式错误", "error"); 28 | } 29 | $("#export").val(JSON.stringify(jsonData, null, 4)) 30 | }); 31 | //json转xml 32 | $(".jsontrans").on("click", function () { 33 | 34 | var xotree = new XML.ObjTree(); 35 | var dataStr = $("#import").val(); 36 | try { 37 | var jsonData = JSON.parse(dataStr); 38 | var xml = xotree.writeXML(jsonData); 39 | var xmlText = vkbeautify.xml(xml,4); 40 | $("#export").val(xmlText) 41 | } 42 | catch(e) { 43 | swal("处理出错", "请核对输入json字符串是否正确", "error"); 44 | return 45 | } 46 | 47 | }); 48 | //json 格式化 49 | $(".JsonFormat").on("click", function(){ 50 | var jsonstr = $("#format-import").val(); 51 | try { 52 | var jsonData = JSON.parse(jsonstr); 53 | $("#format-export").JSONView(jsonData); 54 | } 55 | catch(e) { 56 | swal("处理出错", "请核对输入json字符串是否正确", "error"); 57 | return 58 | } 59 | }); 60 | //json 压缩 61 | $(".JsonCompress").on("click", function(){ 62 | var jsonstr = $("#format-import").val(); 63 | try { 64 | var jsonData = JSON.parse(jsonstr); 65 | $("#format-export").html(JSON.stringify(jsonData, null, 0)) 66 | } 67 | catch(e) { 68 | swal("处理出错", "请核对输入json字符串是否正确", "error"); 69 | return 70 | } 71 | }); -------------------------------------------------------------------------------- /resources/assets/js/content.js: -------------------------------------------------------------------------------- 1 | 2 | //判断当前页面是否在iframe中 3 | if (top == this) { 4 | var gohome = '
'; 5 | $('body').append(gohome); 6 | } 7 | -------------------------------------------------------------------------------- /resources/assets/js/format/json.js: -------------------------------------------------------------------------------- 1 | var formatJson = function (json, options) { 2 | var reg = null, 3 | formatted = '', 4 | pad = 0, 5 | PADDING = ' '; 6 | options = options || {}; 7 | options.newlineAfterColonIfBeforeBraceOrBracket = (options.newlineAfterColonIfBeforeBraceOrBracket === true) ? true : false; 8 | options.spaceAfterColon = (options.spaceAfterColon === false) ? false : true; 9 | if (typeof json !== 'string') { 10 | json = JSON.stringify(json); 11 | } else { 12 | json = JSON.parse(json); 13 | json = JSON.stringify(json); 14 | } 15 | reg = /([\{\}])/g; 16 | json = json.replace(reg, '\r\n$1\r\n'); 17 | reg = /([\[\]])/g; 18 | json = json.replace(reg, '\r\n$1\r\n'); 19 | reg = /(\,)/g; 20 | json = json.replace(reg, '$1\r\n'); 21 | reg = /(\r\n\r\n)/g; 22 | json = json.replace(reg, '\r\n'); 23 | reg = /\r\n\,/g; 24 | json = json.replace(reg, ','); 25 | if (!options.newlineAfterColonIfBeforeBraceOrBracket) { 26 | reg = /\:\r\n\{/g; 27 | json = json.replace(reg, ':{'); 28 | reg = /\:\r\n\[/g; 29 | json = json.replace(reg, ':['); 30 | } 31 | if (options.spaceAfterColon) { 32 | reg = /\:/g; 33 | json = json.replace(reg, ':'); 34 | } 35 | (json.split('\r\n')).forEach(function (node, index) { 36 | var i = 0, 37 | indent = 0, 38 | padding = ''; 39 | 40 | if (node.match(/\{$/) || node.match(/\[$/)) { 41 | indent = 1; 42 | } else if (node.match(/\}/) || node.match(/\]/)) { 43 | if (pad !== 0) { 44 | pad -= 1; 45 | } 46 | } else { 47 | indent = 0; 48 | } 49 | 50 | for (i = 0; i < pad; i++) { 51 | padding += PADDING; 52 | } 53 | 54 | formatted += padding + node + '\r\n'; 55 | pad += indent; 56 | } 57 | ); 58 | return formatted; 59 | }; -------------------------------------------------------------------------------- /resources/assets/js/format/jsonp.js: -------------------------------------------------------------------------------- 1 | var formatJsonp = function (jsonp, options) { 2 | 3 | var pattern = /(.*)\(([^)]*)\)/; 4 | if(pattern.test(jsonp)){ 5 | var info = jsonp.match(pattern); 6 | var callback = info[1]; 7 | var json = info[2]; 8 | }else{ 9 | return jsonp; 10 | } 11 | var reg = null, 12 | formatted = '', 13 | pad = 0, 14 | PADDING = ' ' 15 | result = null; 16 | options = options || {}; 17 | options.newlineAfterColonIfBeforeBraceOrBracket = (options.newlineAfterColonIfBeforeBraceOrBracket === true) ? true : false; 18 | options.spaceAfterColon = (options.spaceAfterColon === false) ? false : true; 19 | if (typeof json !== 'string') { 20 | json = JSON.stringify(json); 21 | } else { 22 | json = JSON.parse(json); 23 | json = JSON.stringify(json); 24 | } 25 | reg = /([\{\}])/g; 26 | json = json.replace(reg, '\r\n$1\r\n'); 27 | reg = /([\[\]])/g; 28 | json = json.replace(reg, '\r\n$1\r\n'); 29 | reg = /(\,)/g; 30 | json = json.replace(reg, '$1\r\n'); 31 | reg = /(\r\n\r\n)/g; 32 | json = json.replace(reg, '\r\n'); 33 | reg = /\r\n\,/g; 34 | json = json.replace(reg, ','); 35 | if (!options.newlineAfterColonIfBeforeBraceOrBracket) { 36 | reg = /\:\r\n\{/g; 37 | json = json.replace(reg, ':{'); 38 | reg = /\:\r\n\[/g; 39 | json = json.replace(reg, ':['); 40 | } 41 | if (options.spaceAfterColon) { 42 | reg = /\:/g; 43 | json = json.replace(reg, ':'); 44 | } 45 | (json.split('\r\n')).forEach(function (node, index) { 46 | var i = 0, 47 | indent = 0, 48 | padding = ''; 49 | 50 | if (node.match(/\{$/) || node.match(/\[$/)) { 51 | indent = 1; 52 | } else if (node.match(/\}/) || node.match(/\]/)) { 53 | if (pad !== 0) { 54 | pad -= 1; 55 | } 56 | } else { 57 | indent = 0; 58 | } 59 | 60 | for (i = 0; i < pad; i++) { 61 | padding += PADDING; 62 | } 63 | 64 | formatted += padding + node + '\r\n'; 65 | pad += indent; 66 | } 67 | ); 68 | result = callback+"(\r\n"+formatted+"\r\n)"; 69 | return result; 70 | }; -------------------------------------------------------------------------------- /resources/assets/js/format/xml.js: -------------------------------------------------------------------------------- 1 | String.prototype.removeLineEnd = function () { 2 | return this.replace(/(<.+?\s+?)(?:\n\s*?(.+?=".*?"))/g, '$1 $2') 3 | } 4 | function formatXml(text) { 5 | text = '\n' + text.replace(/(<\w+)(\s.*?>)/g, function ($0, name, props) { 6 | return name + ' ' + props.replace(/\s+(\w+=)/g, " $1"); 7 | }).replace(/>\s*?\n<"); 8 | 9 | text = text.replace(/\n/g, '\r').replace(//g, function ($0, text) { 10 | var ret = ''; 11 | return ret; 12 | }).replace(/\r/g, '\n'); 13 | 14 | var rgx = /\n(<(([^\?]).+?)(?:\s|\s*?>|\s*?(\/)>)(?:.*?(?:(?:(\/)>)|(?:<(\/)\2>)))?)/mg; 15 | var nodeStack = []; 16 | var output = text.replace(rgx, function ($0, all, name, isBegin, isCloseFull1, isCloseFull2, isFull1, isFull2) { 17 | var isClosed = (isCloseFull1 == '/') || (isCloseFull2 == '/' ) || (isFull1 == '/') || (isFull2 == '/'); 18 | var prefix = ''; 19 | if (isBegin == '!') { 20 | prefix = getPrefix(nodeStack.length); 21 | } 22 | else { 23 | if (isBegin != '/') { 24 | prefix = getPrefix(nodeStack.length); 25 | if (!isClosed) { 26 | nodeStack.push(name); 27 | } 28 | } 29 | else { 30 | nodeStack.pop(); 31 | prefix = getPrefix(nodeStack.length); 32 | } 33 | 34 | } 35 | var ret = '\n' + prefix + all; 36 | return ret; 37 | }); 38 | 39 | var prefixSpace = -1; 40 | var outputText = output.substring(1); 41 | 42 | outputText = outputText.replace(/\n/g, '\r').replace(/(\s*)/g, function ($0, prefix, text) { 43 | if (prefix.charAt(0) == '\r') 44 | prefix = prefix.substring(1); 45 | text = unescape(text).replace(/\r/g, '\n'); 46 | var ret = '\n' + prefix + ''; 47 | return ret; 48 | }); 49 | 50 | return outputText.replace(/\s+$/g, '').replace(/\r/g, '\r\n'); 51 | } 52 | function getPrefix(prefixIndex) { 53 | var span = ' '; 54 | var output = []; 55 | for (var i = 0; i < prefixIndex; ++i) { 56 | output.push(span); 57 | } 58 | 59 | return output.join(''); 60 | } -------------------------------------------------------------------------------- /resources/assets/js/plugins/footable/fonts/footable.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/js/plugins/footable/fonts/footable.eot -------------------------------------------------------------------------------- /resources/assets/js/plugins/footable/fonts/footable.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/js/plugins/footable/fonts/footable.ttf -------------------------------------------------------------------------------- /resources/assets/js/plugins/footable/fonts/footable.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/resources/assets/js/plugins/footable/fonts/footable.woff -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/char_counter.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-box .fr-counter { 24 | position: absolute; 25 | bottom: 0px; 26 | padding: 5px; 27 | right: 0px; 28 | color: #cccccc; 29 | content: attr(data-chars); 30 | font-size: 15px; 31 | font-family: "Times New Roman", Georgia, Serif; 32 | z-index: 1; 33 | background: #ffffff; 34 | border-top: solid 1px #ebebeb; 35 | border-left: solid 1px #ebebeb; 36 | border-radius: 2px 0 0 0; 37 | -moz-border-radius: 2px 0 0 0; 38 | -webkit-border-radius: 2px 0 0 0; 39 | -moz-background-clip: padding; 40 | -webkit-background-clip: padding-box; 41 | background-clip: padding-box; 42 | } 43 | .fr-box.fr-rtl .fr-counter { 44 | left: 0px; 45 | right: auto; 46 | border-left: none; 47 | border-right: solid 1px #ebebeb; 48 | border-radius: 0 2px 0 0; 49 | -moz-border-radius: 0 2px 0 0; 50 | -webkit-border-radius: 0 2px 0 0; 51 | -moz-background-clip: padding; 52 | -webkit-background-clip: padding-box; 53 | background-clip: padding-box; 54 | } 55 | .fr-box.fr-code-view .fr-counter { 56 | display: none; 57 | } 58 | -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/char_counter.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-box .fr-counter{position:absolute;bottom:0;padding:5px;right:0;color:#ccc;content:attr(data-chars);font-size:15px;font-family:"Times New Roman",Georgia,Serif;z-index:1;background:#fff;border-top:solid 1px #ebebeb;border-left:solid 1px #ebebeb;border-radius:2px 0 0;-moz-border-radius:2px 0 0;-webkit-border-radius:2px 0 0;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box}.fr-box.fr-rtl .fr-counter{left:0;right:auto;border-left:0;border-right:solid 1px #ebebeb;border-radius:0 2px 0 0;-moz-border-radius:0 2px 0 0;-webkit-border-radius:0 2px 0 0;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box}.fr-box.fr-code-view .fr-counter{display:none} -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/code_view.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}textarea.fr-code{display:none;width:100%;resize:none;-moz-resize:none;-webkit-resize:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border:0;padding:10px;margin:0;font-family:"Courier New",monospace;font-size:14px;background:#fff;color:#000;outline:0}.fr-box.fr-rtl textarea.fr-code{direction:rtl}.fr-box .CodeMirror{display:none}.fr-box.fr-code-view textarea.fr-code{display:block}.fr-box.fr-code-view.fr-inline{-webkit-box-shadow:0 1px 3px rgba(0,0,0,.12),0 1px 1px 1px rgba(0,0,0,.16);-moz-box-shadow:0 1px 3px rgba(0,0,0,.12),0 1px 1px 1px rgba(0,0,0,.16);box-shadow:0 1px 3px rgba(0,0,0,.12),0 1px 1px 1px rgba(0,0,0,.16)}.fr-box.fr-code-view .fr-element,.fr-box.fr-code-view .fr-placeholder,.fr-box.fr-code-view .fr-iframe{display:none}.fr-box.fr-code-view .CodeMirror{display:block}.fr-box.fr-inline.fr-code-view .fr-command.fr-btn.html-switch{display:block}.fr-box.fr-inline .fr-command.fr-btn.html-switch{position:absolute;top:0;right:0;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.12),0 1px 1px 1px rgba(0,0,0,.16);-moz-box-shadow:0 1px 3px rgba(0,0,0,.12),0 1px 1px 1px rgba(0,0,0,.16);box-shadow:0 1px 3px rgba(0,0,0,.12),0 1px 1px 1px rgba(0,0,0,.16);display:none;background:#fff;color:#222;-moz-outline:0;outline:0;border:0;line-height:1;cursor:pointer;text-align:left;padding:12px;-webkit-transition:background .2s ease 0s;-moz-transition:background .2s ease 0s;-ms-transition:background .2s ease 0s;-o-transition:background .2s ease 0s;border-radius:0;-moz-border-radius:0;-webkit-border-radius:0;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;z-index:2;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;text-decoration:none;user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none}.fr-box.fr-inline .fr-command.fr-btn.html-switch i{font-size:14px;width:14px;text-align:center}.fr-box.fr-inline .fr-command.fr-btn.html-switch.fr-desktop:hover{background:#ebebeb} -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/draggable.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-drag-helper { 24 | background: #1e88e5; 25 | height: 2px; 26 | margin-top: -1px; 27 | -webkit-opacity: 0.2; 28 | -moz-opacity: 0.2; 29 | opacity: 0.2; 30 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; 31 | position: absolute; 32 | z-index: 2147483640; 33 | display: none; 34 | } 35 | .fr-drag-helper.fr-visible { 36 | display: block; 37 | } 38 | .fr-dragging { 39 | -webkit-opacity: 0.4; 40 | -moz-opacity: 0.4; 41 | opacity: 0.4; 42 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; 43 | } 44 | -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/draggable.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-drag-helper{background:#1e88e5;height:2px;margin-top:-1px;-webkit-opacity:.2;-moz-opacity:.2;opacity:.2;-ms-filter:"alpha(Opacity=0)";position:absolute;z-index:2147483640;display:none}.fr-drag-helper.fr-visible{display:block}.fr-dragging{-webkit-opacity:.4;-moz-opacity:.4;opacity:.4;-ms-filter:"alpha(Opacity=0)"} -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/emoticons.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-popup .fr-emoticon { 24 | display: inline-block; 25 | font-size: 20px; 26 | width: 20px; 27 | padding: 5px; 28 | line-height: 1; 29 | cursor: default; 30 | font-weight: normal; 31 | font-family: "Apple Color Emoji", "Segoe UI Emoji", "NotoColorEmoji", "Segoe UI Symbol", "Android Emoji", "EmojiSymbols"; 32 | -webkit-box-sizing: content-box; 33 | -moz-box-sizing: content-box; 34 | box-sizing: content-box; 35 | } 36 | .fr-popup .fr-emoticon img { 37 | height: 20px; 38 | } 39 | .fr-popup .fr-link:focus { 40 | outline: 0; 41 | background: #ebebeb; 42 | } 43 | -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/emoticons.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-popup .fr-emoticon{display:inline-block;font-size:20px;width:20px;padding:5px;line-height:1;cursor:default;font-weight:400;font-family:"Apple Color Emoji","Segoe UI Emoji",NotoColorEmoji,"Segoe UI Symbol","Android Emoji",EmojiSymbols;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fr-popup .fr-emoticon img{height:20px}.fr-popup .fr-link:focus{outline:0;background:#ebebeb} -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/file.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-popup .fr-file-upload-layer{border:dashed 2px #bdbdbd;padding:25px 0;position:relative;font-size:14px;letter-spacing:1px;line-height:140%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;text-align:center}.fr-popup .fr-file-upload-layer:hover{background:#ebebeb}.fr-popup .fr-file-upload-layer.fr-drop{background:#ebebeb;border-color:#1e88e5}.fr-popup .fr-file-upload-layer .fr-form{-webkit-opacity:0;-moz-opacity:0;opacity:0;-ms-filter:"alpha(Opacity=0)";position:absolute;top:0;bottom:0;left:0;right:0;z-index:2147483640;overflow:hidden;margin:0!important;padding:0!important;width:100%!important}.fr-popup .fr-file-upload-layer .fr-form input{cursor:pointer;position:absolute;right:0;top:0;bottom:0;width:500%;height:100%;margin:0;font-size:400px}.fr-popup .fr-file-progress-bar-layer{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fr-popup .fr-file-progress-bar-layer>h3{font-size:16px;margin:10px 0;font-weight:400}.fr-popup .fr-file-progress-bar-layer>div.fr-action-buttons{display:none}.fr-popup .fr-file-progress-bar-layer>div.fr-loader{background:#bcdbf7;height:10px;width:100%;margin-top:20px;overflow:hidden;position:relative}.fr-popup .fr-file-progress-bar-layer>div.fr-loader span{display:block;height:100%;width:0;background:#1e88e5;-webkit-transition:width .2s ease 0s;-moz-transition:width .2s ease 0s;-ms-transition:width .2s ease 0s;-o-transition:width .2s ease 0s}.fr-popup .fr-file-progress-bar-layer>div.fr-loader.fr-indeterminate span{width:30%!important;position:absolute;top:0;-webkit-animation:loading 2s linear infinite;-moz-animation:loading 2s linear infinite;-o-animation:loading 2s linear infinite;animation:loading 2s linear infinite}.fr-popup .fr-file-progress-bar-layer.fr-error>div.fr-loader{display:none}.fr-popup .fr-file-progress-bar-layer.fr-error>div.fr-action-buttons{display:block}@keyframes loading{from{left:-25%}to{left:100%}}@-webkit-keyframes loading{from{left:-25%}to{left:100%}}@-moz-keyframes loading{from{left:-25%}to{left:100%}}@-o-keyframes loading{from{left:-25%}to{left:100%}} -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/fullscreen.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | body.fr-fullscreen { 8 | overflow: hidden; 9 | height: 100%; 10 | width: 100%; 11 | position: fixed; 12 | } 13 | .fr-box.fr-fullscreen { 14 | margin: 0 !important; 15 | position: fixed; 16 | top: 0; 17 | left: 0; 18 | bottom: 0; 19 | right: 0; 20 | z-index: 2147483630 !important; 21 | width: auto !important; 22 | } 23 | .fr-box.fr-fullscreen .fr-toolbar.fr-top { 24 | top: 0 !important; 25 | } 26 | .fr-box.fr-fullscreen .fr-toolbar.fr-bottom { 27 | bottom: 0 !important; 28 | } 29 | -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/fullscreen.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | body.fr-fullscreen{overflow:hidden;height:100%;width:100%;position:fixed}.fr-box.fr-fullscreen{margin:0!important;position:fixed;top:0;left:0;bottom:0;right:0;z-index:2147483630!important;width:auto!important}.fr-box.fr-fullscreen .fr-toolbar.fr-top{top:0!important}.fr-box.fr-fullscreen .fr-toolbar.fr-bottom{bottom:0!important} -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/help.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal { 24 | text-align: left; 25 | padding: 20px 20px 10px; 26 | } 27 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table { 28 | border-collapse: collapse; 29 | font-size: 14px; 30 | line-height: 1.5; 31 | width: 100%; 32 | } 33 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table + table { 34 | margin-top: 20px; 35 | } 36 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table tr { 37 | border: 0; 38 | } 39 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table th, 40 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table td { 41 | padding: 6px 0 4px; 42 | } 43 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table tbody tr { 44 | border-bottom: solid 1px #ebebeb; 45 | } 46 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table tbody td:first-child { 47 | width: 60%; 48 | color: #646464; 49 | } 50 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table tbody td:nth-child(n+2) { 51 | letter-spacing: 0.5px; 52 | } 53 | -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/help.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal{text-align:left;padding:20px 20px 10px}.fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table{border-collapse:collapse;font-size:14px;line-height:1.5;width:100%}.fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table+table{margin-top:20px}.fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table tr{border:0}.fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table th,.fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table td{padding:6px 0 4px}.fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table tbody tr{border-bottom:solid 1px #ebebeb}.fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table tbody td:first-child{width:60%;color:#646464}.fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table tbody td:nth-child(n+2){letter-spacing:.5px} -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/line_breaker.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-line-breaker { 24 | cursor: text; 25 | border-top: 1px solid #1e88e5; 26 | position: fixed; 27 | z-index: 2; 28 | display: none; 29 | } 30 | .fr-line-breaker.fr-visible { 31 | display: block; 32 | } 33 | .fr-line-breaker a.fr-floating-btn { 34 | position: absolute; 35 | left: calc(50% - (32px / 2)); 36 | top: -16px; 37 | } 38 | -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/line_breaker.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-line-breaker{cursor:text;border-top:1px solid #1e88e5;position:fixed;z-index:2;display:none}.fr-line-breaker.fr-visible{display:block}.fr-line-breaker a.fr-floating-btn{position:absolute;left:calc(50% - (32px / 2));top:-16px} -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/quick_insert.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-quick-insert { 24 | position: absolute; 25 | z-index: 2147483639; 26 | white-space: nowrap; 27 | padding-right: 5px; 28 | margin-left: -5px; 29 | -webkit-box-sizing: content-box; 30 | -moz-box-sizing: content-box; 31 | box-sizing: content-box; 32 | } 33 | .fr-quick-insert.fr-on a.fr-floating-btn svg { 34 | -webkit-transform: rotate(135deg); 35 | -moz-transform: rotate(135deg); 36 | -ms-transform: rotate(135deg); 37 | -o-transform: rotate(135deg); 38 | } 39 | .fr-quick-insert.fr-hidden { 40 | display: none; 41 | } 42 | .fr-qi-helper { 43 | position: absolute; 44 | z-index: 3; 45 | padding-left: 16px; 46 | white-space: nowrap; 47 | } 48 | .fr-qi-helper a.fr-btn.fr-floating-btn { 49 | text-align: center; 50 | display: inline-block; 51 | color: #222222; 52 | -webkit-opacity: 0; 53 | -moz-opacity: 0; 54 | opacity: 0; 55 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; 56 | -webkit-transform: scale(0); 57 | -moz-transform: scale(0); 58 | -ms-transform: scale(0); 59 | -o-transform: scale(0); 60 | } 61 | .fr-qi-helper a.fr-btn.fr-floating-btn.fr-size-1 { 62 | -webkit-opacity: 1; 63 | -moz-opacity: 1; 64 | opacity: 1; 65 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; 66 | -webkit-transform: scale(1); 67 | -moz-transform: scale(1); 68 | -ms-transform: scale(1); 69 | -o-transform: scale(1); 70 | } 71 | -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/quick_insert.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-quick-insert{position:absolute;z-index:2147483639;white-space:nowrap;padding-right:5px;margin-left:-5px;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fr-quick-insert.fr-on a.fr-floating-btn svg{-webkit-transform:rotate(135deg);-moz-transform:rotate(135deg);-ms-transform:rotate(135deg);-o-transform:rotate(135deg)}.fr-quick-insert.fr-hidden{display:none}.fr-qi-helper{position:absolute;z-index:3;padding-left:16px;white-space:nowrap}.fr-qi-helper a.fr-btn.fr-floating-btn{text-align:center;display:inline-block;color:#222;-webkit-opacity:0;-moz-opacity:0;opacity:0;-ms-filter:"alpha(Opacity=0)";-webkit-transform:scale(0);-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0)}.fr-qi-helper a.fr-btn.fr-floating-btn.fr-size-1{-webkit-opacity:1;-moz-opacity:1;opacity:1;-ms-filter:"alpha(Opacity=0)";-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1)} -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/special_characters.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-special-characters-modal { 24 | text-align: left; 25 | padding: 20px 20px 10px; 26 | } 27 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-special-characters-modal .fr-special-characters-list { 28 | margin-bottom: 20px; 29 | } 30 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-special-characters-modal .fr-special-characters-title { 31 | font-weight: bold; 32 | font-size: 14px; 33 | padding: 6px 0 4px; 34 | margin: 0 0 5px; 35 | } 36 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-special-characters-modal .fr-special-character { 37 | display: inline-block; 38 | font-size: 16px; 39 | width: 20px; 40 | height: 20px; 41 | padding: 5px; 42 | line-height: 20px; 43 | cursor: default; 44 | font-weight: normal; 45 | -webkit-box-sizing: content-box; 46 | -moz-box-sizing: content-box; 47 | box-sizing: content-box; 48 | text-align: center; 49 | border: 1px solid #cccccc; 50 | margin: -1px 0 0 -1px; 51 | } 52 | -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/plugins/special_characters.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-modal .fr-modal-wrapper .fr-modal-body .fr-special-characters-modal{text-align:left;padding:20px 20px 10px}.fr-modal .fr-modal-wrapper .fr-modal-body .fr-special-characters-modal .fr-special-characters-list{margin-bottom:20px}.fr-modal .fr-modal-wrapper .fr-modal-body .fr-special-characters-modal .fr-special-characters-title{font-weight:700;font-size:14px;padding:6px 0 4px;margin:0 0 5px}.fr-modal .fr-modal-wrapper .fr-modal-body .fr-special-characters-modal .fr-special-character{display:inline-block;font-size:16px;width:20px;height:20px;padding:5px;line-height:20px;cursor:default;font-weight:400;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;text-align:center;border:1px solid #ccc;margin:-1px 0 0 -1px} -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/third_party/embedly.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-element .fr-embedly { 24 | user-select: none; 25 | -o-user-select: none; 26 | -moz-user-select: none; 27 | -khtml-user-select: none; 28 | -webkit-user-select: none; 29 | -ms-user-select: none; 30 | position: relative; 31 | } 32 | .fr-element .fr-embedly::after { 33 | position: absolute; 34 | content: ''; 35 | z-index: 1; 36 | top: 0; 37 | left: 0; 38 | right: 0; 39 | bottom: 0; 40 | cursor: pointer; 41 | display: block; 42 | background: rgba(0, 0, 0, 0); 43 | } 44 | .fr-element .fr-embedly > * { 45 | -webkit-box-sizing: content-box; 46 | -moz-box-sizing: content-box; 47 | box-sizing: content-box; 48 | max-width: 100%; 49 | border: none; 50 | } 51 | .fr-box .fr-embedly-resizer { 52 | position: absolute; 53 | border: solid 1px #1e88e5; 54 | display: none; 55 | user-select: none; 56 | -o-user-select: none; 57 | -moz-user-select: none; 58 | -khtml-user-select: none; 59 | -webkit-user-select: none; 60 | -ms-user-select: none; 61 | } 62 | .fr-box .fr-embedly-resizer.fr-active { 63 | display: block; 64 | } 65 | -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/third_party/embedly.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-element .fr-embedly{user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none;position:relative}.fr-element .fr-embedly::after{position:absolute;content:'';z-index:1;top:0;left:0;right:0;bottom:0;cursor:pointer;display:block;background:rgba(0,0,0,0)}.fr-element .fr-embedly>*{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;max-width:100%;border:0}.fr-box .fr-embedly-resizer{position:absolute;border:solid 1px #1e88e5;display:none;user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none}.fr-box .fr-embedly-resizer.fr-active{display:block} -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/third_party/font_awesome.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-font-awesome-modal { 24 | text-align: left; 25 | padding: 20px 20px 10px; 26 | } 27 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-font-awesome-modal .fr-font-awesome-list { 28 | margin-bottom: 20px; 29 | } 30 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-font-awesome-modal .fr-font-awesome-title { 31 | font-size: 20px; 32 | padding: 6px 0 4px; 33 | margin: 15px 0 5px; 34 | border-bottom: solid 1px #f0f0f0; 35 | } 36 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-font-awesome-modal .fr-font-awesome { 37 | display: inline-block; 38 | font-size: 16px; 39 | width: 20px; 40 | height: 20px; 41 | padding: 16px; 42 | line-height: 20px; 43 | cursor: default; 44 | font-weight: normal; 45 | -webkit-box-sizing: content-box; 46 | -moz-box-sizing: content-box; 47 | box-sizing: content-box; 48 | text-align: center; 49 | margin: -1px 0 0 -1px; 50 | } 51 | -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/third_party/font_awesome.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-modal .fr-modal-wrapper .fr-modal-body .fr-font-awesome-modal{text-align:left;padding:20px 20px 10px}.fr-modal .fr-modal-wrapper .fr-modal-body .fr-font-awesome-modal .fr-font-awesome-list{margin-bottom:20px}.fr-modal .fr-modal-wrapper .fr-modal-body .fr-font-awesome-modal .fr-font-awesome-title{font-size:20px;padding:6px 0 4px;margin:15px 0 5px;border-bottom:solid 1px #f0f0f0}.fr-modal .fr-modal-wrapper .fr-modal-body .fr-font-awesome-modal .fr-font-awesome{display:inline-block;font-size:16px;width:20px;height:20px;padding:16px;line-height:20px;cursor:default;font-weight:400;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;text-align:center;margin:-1px 0 0 -1px} -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/third_party/image_tui.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .tui-image-editor-container { 24 | position: fixed; 25 | top: 0; 26 | left: 0; 27 | bottom: 0; 28 | right: 0; 29 | height: 100%; 30 | width: 100%; 31 | z-index: 10; 32 | } 33 | .tui-editor-cancel-btn { 34 | background-color: #ffffff; 35 | border: 1px solid #cccccc; 36 | color: #222; 37 | } 38 | .tui-editor-save-btn { 39 | background-color: #fdba3b; 40 | border: 1px solid #fdba3b; 41 | color: #ffffff; 42 | } 43 | -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/third_party/image_tui.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.tui-image-editor-container{position:fixed;top:0;left:0;bottom:0;right:0;height:100%;width:100%;z-index:10}.tui-editor-cancel-btn{background-color:#fff;border:1px solid #ccc;color:#222}.tui-editor-save-btn{background-color:#fdba3b;border:1px solid #fdba3b;color:#fff} -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/third_party/spell_checker.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .examples-variante > a { 24 | font-size: 14px; 25 | font-family: Arial, Helvetica, sans-serif; 26 | } 27 | .sc-cm-holder > .sc-cm { 28 | border-top: 5px solid #222222 !important; 29 | padding: 0px !important; 30 | line-height: 200% !important; 31 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16); 32 | -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16); 33 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16); 34 | } 35 | .sc-cm .sc-cm__item.examples-variante.sc-cm__item_active > a > i { 36 | display: none !important; 37 | } 38 | .sc-cm .sc-cm__item.examples-variante > a > i { 39 | display: none !important; 40 | } 41 | .sc-cm__item_dropdown .i-icon { 42 | display: none !important; 43 | } 44 | .sc-cm__item_dropdown .i-icon::before { 45 | display: none !important; 46 | } 47 | .sc-cm::before { 48 | display: none !important; 49 | } 50 | div.sc-cm-holder.sc-cm_show > ul > li.sc-cm__item.sc-cm__item_dropdown.sc-cm__item_arrow > div > ul { 51 | border-style: none !important; 52 | padding: 0px !important; 53 | } 54 | .sc-cm__item_dropdown:hover > a, 55 | .sc-cm a:hover { 56 | background-color: #ebebeb !important; 57 | } 58 | .sc-cm__item_active > a, 59 | .sc-cm__item_active > a:hover, 60 | .sc-cm a:active, 61 | .sc-cm a:focus { 62 | background-color: #d6d6d6 !important; 63 | } 64 | .sc-cm__item > a { 65 | line-height: 200% !important; 66 | } 67 | .sc-cm-holder > .sc-cm:before { 68 | background-color: #ebebeb !important; 69 | } 70 | .sc-cm-holder { 71 | display: none; 72 | } 73 | -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/css/third_party/spell_checker.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.examples-variante>a{font-size:14px;font-family:Arial,Helvetica,sans-serif}.sc-cm-holder>.sc-cm{border-top:5px solid #222!important;padding:0!important;line-height:200%!important;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.12),0 1px 1px 1px rgba(0,0,0,.16);-moz-box-shadow:0 1px 3px rgba(0,0,0,.12),0 1px 1px 1px rgba(0,0,0,.16);box-shadow:0 1px 3px rgba(0,0,0,.12),0 1px 1px 1px rgba(0,0,0,.16)}.sc-cm .sc-cm__item.examples-variante.sc-cm__item_active>a>i{display:none!important}.sc-cm .sc-cm__item.examples-variante>a>i{display:none!important}.sc-cm__item_dropdown .i-icon{display:none!important}.sc-cm__item_dropdown .i-icon::before{display:none!important}.sc-cm::before{display:none!important}div.sc-cm-holder.sc-cm_show>ul>li.sc-cm__item.sc-cm__item_dropdown.sc-cm__item_arrow>div>ul{border-style:none!important;padding:0!important}.sc-cm__item_dropdown:hover>a,.sc-cm a:hover{background-color:#ebebeb!important}.sc-cm__item_active>a,.sc-cm__item_active>a:hover,.sc-cm a:active,.sc-cm a:focus{background-color:#d6d6d6!important}.sc-cm__item>a{line-height:200%!important}.sc-cm-holder>.sc-cm:before{background-color:#ebebeb!important}.sc-cm-holder{display:none} -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/js/plugins/char_counter.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | !function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return t===undefined&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t)}:n(window.jQuery)}(function(a){a.extend(a.FE.DEFAULTS,{charCounterMax:-1,charCounterCount:!0}),a.FE.PLUGINS.charCounter=function(n){var r;function o(){return(n.el.textContent||"").replace(/\u200B/g,"").length}function e(e){if(n.opts.charCounterMax<0)return!0;if(o()").html(e).text().length+o()<=n.opts.charCounterMax?e:(n.events.trigger("charCounter.exceeded"),"")}function u(){if(n.opts.charCounterCount){var e=o()+(0')).css("bottom",n.$wp.css("border-bottom-width")),n.$box.append(r),n.events.on("keydown",e,!0),n.events.on("paste.afterCleanup",t),n.events.on("keyup contentChanged input",function(){n.events.trigger("charCounter.update")}),n.events.on("charCounter.update",u),n.events.trigger("charCounter.update"),void n.events.on("destroy",function(){a(n.o_win).off("resize.char"+n.id),r.removeData().remove(),r=null}))},count:o}}}); -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/js/plugins/entities.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof module&&module.exports?module.exports=function(e,r){return r===undefined&&(r="undefined"!=typeof window?require("jquery"):require("jquery")(e)),a(r)}:a(window.jQuery)}(function(c){c.extend(c.FE.DEFAULTS,{entities:""'¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿŒœŠšŸƒˆ˜ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρςστυφχψωϑϒϖ   ‌‍‎‏–—‘’‚“”„†‡•…‰′″‹›‾⁄€ℑ℘ℜ™ℵ←↑→↓↔↵⇐⇑⇒⇓⇔∀∂∃∅∇∈∉∋∏∑−∗√∝∞∠∧∨∩∪∫∴∼≅≈≠≡≤≥⊂⊃⊄⊆⊇⊕⊗⊥⋅⌈⌉⌊⌋⟨⟩◊♠♣♥♦"}),c.FE.PLUGINS.entities=function(t){var n,u;function i(e){var r=e.textContent;if(r.match(n)){for(var a="",i=0;i").html(t.opts.entities).text(),r=t.opts.entities.split(";");u={},n="";for(var a=0;a span").text(o.opts.fontFamily[f()]||t[0]||o.language.translate(o.opts.fontFamilyDefaultSelection))}}}},l.FE.RegisterCommand("fontFamily",{type:"dropdown",displaySelection:function(e){return e.opts.fontFamilySelection},defaultSelection:function(e){return e.opts.fontFamilyDefaultSelection},displaySelectionWidth:120,html:function(){var e='"},title:"Font Family",callback:function(e,t){this.fontFamily.apply(t)},refresh:function(e){this.fontFamily.refresh(e)},refreshOnShow:function(e,t){this.fontFamily.refreshOnShow(e,t)},plugin:"fontFamily"}),l.FE.DefineIcon("fontFamily",{NAME:"font"})}); -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/js/plugins/font_size.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | !function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return t===undefined&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t)}:n(window.jQuery)}(function(f){f.extend(f.FE.DEFAULTS,{fontSize:["8","9","10","11","12","14","18","24","30","36","48","60","72","96"],fontSizeSelection:!1,fontSizeDefaultSelection:"12",fontSizeUnit:"px"}),f.FE.PLUGINS.fontSize=function(r){return{apply:function(e){r.format.applyStyle("font-size",e)},refreshOnShow:function(e,t){var n=f(r.selection.element()).css("font-size");"pt"===r.opts.fontSizeUnit&&(n=Math.round(72*parseFloat(n,10)/96)+"pt"),t.find(".fr-command.fr-active").removeClass("fr-active").attr("aria-selected",!1),t.find('.fr-command[data-param1="'+n+'"]').addClass("fr-active").attr("aria-selected",!0);var o=t.find(".fr-dropdown-list"),i=t.find(".fr-active").parent();i.length?o.parent().scrollTop(i.offset().top-o.offset().top-(o.parent().outerHeight()/2-i.outerHeight()/2)):o.parent().scrollTop(0)},refresh:function(e){if(r.opts.fontSizeSelection){var t=r.helpers.getPX(f(r.selection.element()).css("font-size"));"pt"===r.opts.fontSizeUnit&&(t=Math.round(72*parseFloat(t,10)/96)+"pt"),e.find("> span").text(t)}}}},f.FE.RegisterCommand("fontSize",{type:"dropdown",title:"Font Size",displaySelection:function(e){return e.opts.fontSizeSelection},displaySelectionWidth:30,defaultSelection:function(e){return e.opts.fontSizeDefaultSelection},html:function(){for(var e='"},callback:function(e,t){this.fontSize.apply(t)},refresh:function(e){this.fontSize.refresh(e)},refreshOnShow:function(e,t){this.fontSize.refreshOnShow(e,t)},plugin:"fontSize"}),f.FE.DefineIcon("fontSize",{NAME:"text-height"})}); -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/js/plugins/help.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | !function(l){"function"==typeof define&&define.amd?define(["jquery"],l):"object"==typeof module&&module.exports?module.exports=function(e,t){return t===undefined&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),l(t)}:l(window.jQuery)}(function(n){n.extend(n.FE.DEFAULTS,{helpSets:[{title:"Inline Editor",commands:[{val:"OSkeyE",desc:"Show the editor"}]},{title:"Common actions",commands:[{val:"OSkeyC",desc:"Copy"},{val:"OSkeyX",desc:"Cut"},{val:"OSkeyV",desc:"Paste"},{val:"OSkeyZ",desc:"Undo"},{val:"OSkeyShift+Z",desc:"Redo"},{val:"OSkeyK",desc:"Insert Link"},{val:"OSkeyP",desc:"Insert Image"}]},{title:"Basic Formatting",commands:[{val:"OSkeyA",desc:"Select All"},{val:"OSkeyB",desc:"Bold"},{val:"OSkeyI",desc:"Italic"},{val:"OSkeyU",desc:"Underline"},{val:"OSkeyS",desc:"Strikethrough"},{val:"OSkey]",desc:"Increase Indent"},{val:"OSkey[",desc:"Decrease Indent"}]},{title:"Quote",commands:[{val:"OSkey'",desc:"Increase quote level"},{val:"OSkeyShift+'",desc:"Decrease quote level"}]},{title:"Image / Video",commands:[{val:"OSkey+",desc:"Resize larger"},{val:"OSkey-",desc:"Resize smaller"}]},{title:"Table",commands:[{val:"Alt+Space",desc:"Select table cell"},{val:"Shift+Left/Right arrow",desc:"Extend selection one cell"},{val:"Shift+Up/Down arrow",desc:"Extend selection one row"}]},{title:"Navigation",commands:[{val:"OSkey/",desc:"Shortcuts"},{val:"Alt+F10",desc:"Focus popup / toolbar"},{val:"Esc",desc:"Return focus to previous position"}]}]}),n.FE.PLUGINS.help=function(s){var o,a="help";return{_init:function(){},show:function(){if(!o){var e="

"+s.language.translate("Shortcuts")+"

",t=function(){for(var e='
',t=0;t";o+=""+s.language.translate(l.title)+"",o+="";for(var a=0;a",o+=""+s.language.translate(n.desc)+"",o+=""+n.val.replace("OSkey",s.helpers.isMac()?"⌘":"Ctrl+")+"",o+=""}e+=o+=""}return e+="
"}(),l=s.modals.create(a,e,t);o=l.$modal,l.$head,l.$body,s.events.$on(n(s.o_win),"resize",function(){s.modals.resize(a)})}s.modals.show(a),s.modals.resize(a)},hide:function(){s.modals.hide(a)}}},n.FroalaEditor.DefineIcon("help",{NAME:"question"}),n.FE.RegisterShortcut(n.FE.KEYCODE.SLASH,"help",null,"/"),n.FE.RegisterCommand("help",{title:"Help",icon:"help",undo:!1,focus:!1,modal:!0,callback:function(){this.help.show()},plugin:"help",showOnMobile:!1})}); -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/js/plugins/inline_class.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | !function(i){"function"==typeof define&&define.amd?define(["jquery"],i):"object"==typeof module&&module.exports?module.exports=function(n,e){return e===undefined&&(e="undefined"!=typeof window?require("jquery"):require("jquery")(n)),i(e)}:i(window.jQuery)}(function(s){s.extend(s.FE.DEFAULTS,{inlineClasses:{"fr-class-code":"Code","fr-class-highlighted":"Highlighted","fr-class-transparency":"Transparent"}}),s.FE.PLUGINS.inlineClass=function(i){return{apply:function(n){i.format.toggle("span",{"class":n})},refreshOnShow:function(n,e){e.find(".fr-command").each(function(){var n=s(this).data("param1"),e=i.format.is("span",{"class":n});s(this).toggleClass("fr-active",e).attr("aria-selected",e)})}}},s.FE.RegisterCommand("inlineClass",{type:"dropdown",title:"Inline Class",html:function(){var n='"},callback:function(n,e){this.inlineClass.apply(e)},refreshOnShow:function(n,e){this.inlineClass.refreshOnShow(n,e)},plugin:"inlineClass"}),s.FE.DefineIcon("inlineClass",{NAME:"tag"})}); -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/js/plugins/inline_style.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | !function(t){"function"==typeof define&&define.amd?define(["jquery"],t):"object"==typeof module&&module.exports?module.exports=function(e,n){return n===undefined&&(n="undefined"!=typeof window?require("jquery"):require("jquery")(e)),t(n)}:t(window.jQuery)}(function(o){o.extend(o.FE.DEFAULTS,{inlineStyles:{"Big Red":"font-size: 20px; color: red;","Small Blue":"font-size: 14px; color: blue;"}}),o.FE.PLUGINS.inlineStyle=function(l){return{apply:function(e){if(""!==l.selection.text())for(var n=e.split(";"),t=0;t'+o.FE.INVISIBLE_SPACE+o.FE.MARKERS+"")}}},o.FE.RegisterCommand("inlineStyle",{type:"dropdown",html:function(){var e='"},title:"Inline Style",callback:function(e,n){this.inlineStyle.apply(n)},plugin:"inlineStyle"}),o.FE.DefineIcon("inlineStyle",{NAME:"paint-brush"})}); -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/js/plugins/line_height.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | !function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return t===undefined&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t)}:n(window.jQuery)}(function(o){o.extend(o.FE.DEFAULTS,{lineHeights:{Default:"",Single:"1",1.15:"1.15",1.5:"1.5",Double:"2"}}),o.FE.PLUGINS.lineHeight=function(r){return{_init:function(){},apply:function(e){r.selection.save(),r.html.wrap(!0,!0,!0,!0),r.selection.restore();var t=r.selection.blocks();r.selection.save();for(var n=0;n'+this.language.translate(n)+"");return e+=""},title:"Line Height",callback:function(e,t){this.lineHeight.apply(t)},refreshOnShow:function(e,t){this.lineHeight.refreshOnShow(e,t)},plugin:"lineHeight"}),o.FE.DefineIcon("lineHeight",{NAME:"arrows-v",FA5NAME:"arrows-alt-v"})}); -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/js/plugins/paragraph_style.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | !function(t){"function"==typeof define&&define.amd?define(["jquery"],t):"object"==typeof module&&module.exports?module.exports=function(e,a){return a===undefined&&(a="undefined"!=typeof window?require("jquery"):require("jquery")(e)),t(a)}:t(window.jQuery)}(function(i){i.extend(i.FE.DEFAULTS,{paragraphStyles:{"fr-text-gray":"Gray","fr-text-bordered":"Bordered","fr-text-spaced":"Spaced","fr-text-uppercase":"Uppercase"},paragraphMultipleStyles:!0}),i.FE.PLUGINS.paragraphStyle=function(o){return{_init:function(){},apply:function(e,a,t){void 0===a&&(a=o.opts.paragraphStyles),void 0===t&&(t=o.opts.paragraphMultipleStyles);var r="";t||((r=Object.keys(a)).splice(r.indexOf(e),1),r=r.join(" ")),o.selection.save(),o.html.wrap(!0,!0,!0,!0),o.selection.restore();var n=o.selection.blocks();o.selection.save();for(var s=i(n[0]).hasClass(e),l=0;l'+this.language.translate(a[t])+"");return e+=""},title:"Paragraph Style",callback:function(e,a){this.paragraphStyle.apply(a)},refreshOnShow:function(e,a){this.paragraphStyle.refreshOnShow(e,a)},plugin:"paragraphStyle"}),i.FE.DefineIcon("paragraphStyle",{NAME:"magic"})}); -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/js/plugins/print.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | !function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return t===undefined&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t)}:n(window.jQuery)}(function(e){e.extend(e.FE.DEFAULTS,{html2pdf:window.html2pdf}),e.FE.PLUGINS.print=function(l){return{run:function(){!function(e){var t=l.$el.html(),n=null;l.shared.print_iframe?n=l.shared.print_iframe:((n=document.createElement("iframe")).name="fr-print",n.style.position="fixed",n.style.top="0",n.style.left="-9999px",n.style.height="100%",n.style.width="0",n.style.overflow="hidden",n.style["z-index"]="2147483647",n.style.tabIndex="-1",l.events.on("shared.destroy",function(){n.remove()}),l.shared.print_iframe=n);try{document.body.removeChild(n)}catch(d){}document.body.appendChild(n);var i=function(){e(),n.removeEventListener("load",i)};n.addEventListener("load",i);var o=n.contentWindow;o.document.open(),o.document.write(""+document.title+""),Array.prototype.forEach.call(document.querySelectorAll("style"),function(e){e=e.cloneNode(!0),o.document.write(e.outerHTML)});var r=document.querySelectorAll("link[rel=stylesheet]");Array.prototype.forEach.call(r,function(e){var t=document.createElement("link");t.rel=e.rel,t.href=e.href,t.media="print",t.type="text/css",t.media="all",o.document.write(t.outerHTML)}),o.document.write('
'),o.document.write(t),o.document.write("
"),o.document.close()}(function(){setTimeout(function(){l.events.disableBlur(),window.frames["fr-print"].focus(),window.frames["fr-print"].print(),l.$win.get(0).focus(),l.events.disableBlur(),l.events.focus()},0)})},toPDF:function(){l.opts.html2pdf&&(l.$el.css("text-align","left"),l.opts.html2pdf().set({margin:[10,20],html2canvas:{useCORS:!0}}).from(l.el).save(),setTimeout(function(){l.$el.css("text-align","")},100))}}},e.FE.DefineIcon("print",{NAME:"print"}),e.FE.RegisterCommand("print",{title:"Print",undo:!1,focus:!1,plugin:"print",callback:function(){this.print.run()}}),e.FE.DefineIcon("getPDF",{NAME:"file-pdf-o",FA5NAME:"file-pdf"}),e.FE.RegisterCommand("getPDF",{title:"Download PDF",type:"button",focus:!1,undo:!1,callback:function(){this.print.toPDF()}})}); -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/js/plugins/quote.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | !function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return t===undefined&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t)}:n(window.jQuery)}(function(a){a.FE.PLUGINS.quote=function(r){function o(e){for(;e.parentNode&&e.parentNode!=r.el;)e=e.parentNode;return e}return{apply:function(e){r.selection.save(),r.html.wrap(!0,!0,!0,!0),r.selection.restore(),"increase"==e?function(){var e,t=r.selection.blocks();for(e=0;e");for(n.insertBefore(t[0]),e=0;e'+this.language.translate(t[n])+(r?''+r+"":"")+""}return e+=""},callback:function(e,t){this.quote.apply(t)},plugin:"quote"}),a.FE.DefineIcon("quote",{NAME:"quote-left"})}); -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/js/plugins/save.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | !function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return t===undefined&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t)}:n(window.jQuery)}(function(l){l.extend(l.FE.DEFAULTS,{saveInterval:1e4,saveURL:null,saveParams:{},saveParam:"body",saveMethod:"POST"}),l.FE.PLUGINS.save=function(i){var e=null,u=null,t=!1,v=1,f=2,n={};function d(e,t){i.events.trigger("save.error",[{code:e,message:n[e]},t])}function s(e){void 0===e&&(e=i.html.get());var t=e,n=i.events.trigger("save.before",[e]);if(!1===n)return!1;if("string"==typeof n&&(e=n),i.opts.saveURL){var s={};for(var o in i.opts.saveParams)if(i.opts.saveParams.hasOwnProperty(o)){var a=i.opts.saveParams[o];s[o]="function"==typeof a?a.call(this):a}var r={};r[i.opts.saveParam]=e,l.ajax({type:i.opts.saveMethod,url:i.opts.saveURL,data:l.extend(r,s),crossDomain:i.opts.requestWithCORS,xhrFields:{withCredentials:i.opts.requestWithCredentials},headers:i.opts.requestHeaders}).done(function(e){u=t,i.events.trigger("save.after",[e])}).fail(function(e){d(f,e.response||e.responseText)})}else d(v)}function o(){clearTimeout(e),e=setTimeout(function(){var e=i.html.get();(u!=e||t)&&(t=!1,s(u=e))},i.opts.saveInterval)}return n[v]="Missing saveURL option.",n[f]="Something went wrong during save.",{_init:function(){i.opts.saveInterval&&(u=i.html.get(),i.events.on("contentChanged",o),i.events.on("keydown destroy",function(){clearTimeout(e)}))},save:s,reset:function(){o(),t=!1},force:function(){t=!0}}},l.FE.DefineIcon("save",{NAME:"floppy-o",FA5NAME:"save"}),l.FE.RegisterCommand("save",{title:"Save",undo:!1,focus:!1,refreshAfterCallback:!1,callback:function(){this.save.save()},plugin:"save"})}); -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/js/plugins/url.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | !function(t){"function"==typeof define&&define.amd?define(["jquery"],t):"object"==typeof module&&module.exports?module.exports=function(e,n){return n===undefined&&(n="undefined"!=typeof window?require("jquery"):require("jquery")(e)),t(n)}:t(window.jQuery)}(function(f){f.FE.URLRegEx="(^| |\\u00A0)("+f.FE.LinkRegEx+"|([a-z0-9+-_.]{1,}@[a-z0-9+-_.]{1,}\\.[a-z0-9+-_]{1,}))$",f.FE.PLUGINS.url=function(i){var l=null;function n(e,n,t){for(var r="";t.length&&"."==t[t.length-1];)r+=".",t=t.substring(0,t.length-1);var o=t;if(i.opts.linkConvertEmailAddress)i.helpers.isEmail(o)&&!/^mailto:.*/i.test(o)&&(o="mailto:"+o);else if(i.helpers.isEmail(o))return n+t;return/^((http|https|ftp|ftps|mailto|tel|sms|notes|data)\:)/i.test(o)||(o="//"+o),(n||"")+"'+t.replace(/&/g,"&").replace(/&/g,"&").replace(//g,">")+""+r}function a(){return new RegExp(f.FE.URLRegEx,"gi")}function s(e){return i.opts.linkAlwaysNoFollow&&(l="nofollow"),i.opts.linkAlwaysBlank&&(i.opts.linkNoOpener&&(l?l+=" noopener":l="noopener"),i.opts.linkNoReferrer&&(l?l+=" noreferrer":l="noreferrer")),e.replace(a(),n)}function p(e){var n=e.split(" ");return n[n.length-1]}function t(){var n=i.selection.ranges(0),t=n.startContainer;if(!t||t.nodeType!==Node.TEXT_NODE||n.startOffset!==(t.textContent||"").length)return!1;if(function e(n){return!!n&&("A"===n.tagName||!(!n.parentNode||n.parentNode==i.el)&&e(n.parentNode))}(t))return!1;if(a().test(p(t.textContent))){f(t).before(s(t.textContent));var r=f(t.parentNode).find("a[data-fr-linked]");r.removeAttr("data-fr-linked"),t.parentNode.removeChild(t),i.events.trigger("url.linked",[r.get(0)])}else if(t.textContent.split(" ").length<=2&&t.previousSibling&&"A"===t.previousSibling.tagName){var o=t.previousSibling.innerText+t.textContent;a().test(p(o))&&(f(t.previousSibling).replaceWith(s(o)),t.parentNode.removeChild(t))}}return{_init:function(){i.events.on("keypress",function(e){!i.selection.isCollapsed()||"."!=e.key&&")"!=e.key&&"("!=e.key||t()},!0),i.events.on("keydown",function(e){var n=e.which;!i.selection.isCollapsed()||n!=f.FE.KEYCODE.ENTER&&n!=f.FE.KEYCODE.SPACE||t()},!0),i.events.on("paste.beforeCleanup",function(e){if(i.helpers.isURL(e)){var n=null;return i.opts.linkAlwaysBlank&&(i.opts.linkNoOpener&&(n?n+=" noopener":n="noopener"),i.opts.linkNoReferrer&&(n?n+=" noreferrer":n="noreferrer")),"'+e+""}})}}}}); -------------------------------------------------------------------------------- /resources/assets/js/plugins/froala/js/third_party/image_aviary.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2019 Froala Labs 5 | */ 6 | 7 | !function(r){"function"==typeof define&&define.amd?define(["jquery"],r):"object"==typeof module&&module.exports?module.exports=function(e,t){return t===undefined&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),r(t)}:r(window.jQuery)}(function(s){if(s.extend(s.FE.DEFAULTS,{aviaryKey:"542e1ff5d5144b9b81cef846574ba6cf",aviaryScriptURL:"https://dme0ih8comzn4.cloudfront.net/imaging/v3/editor.js",aviaryOptions:{displayImageSize:!0,theme:"minimum"}}),s.FE.PLUGINS.imageAviary=function(d){function i(){d.shared.feather_editor=new Aviary.Feather(s.extend({apiKey:d.opts.aviaryKey,onSave:function(e,t){var r=new Image;r.crossOrigin="Anonymous",r.onload=function(){var e=document.createElement("CANVAS"),t=e.getContext("2d");e.height=this.height,e.width=this.width,t.drawImage(this,0,0);for(var r=e.toDataURL("image/png"),i=atob(r.split(",")[1]),a=[],n=0;nCancel '),s(".tui-editor-cancel-btn").click(function(e){c(i)}),s(".tui-editor-save-btn").click(function(e){!function(e,t,i){for(var n=e.toDataURL(),o=atob(n.split(",")[1]),d=[],a=0;a0){ 4 | var validator = $("#myForm").validate({ 5 | submitHandler: function(form) { 6 | $(".btn-info-submit").attr('disabled',true); 7 | var oid = $(".btn-info-submit").attr("oid"); 8 | $.ajax({ 9 | cache: false, 10 | type: "POST", 11 | url:"/website/info", 12 | data:$('#myForm').serialize(), 13 | headers: { 14 | 'X-CSRF-TOKEN': "" 15 | }, 16 | dataType: 'json', 17 | success: function(res) { 18 | $(".btn-info-submit").attr('disabled',false); 19 | if(res.status==200){ 20 | swal("保存成功", "","success") 21 | }else{ 22 | swal("请求出错", res.message, "error") 23 | } 24 | }, 25 | error: function(request) { 26 | $(".btn-info-submit").attr('disabled',false); 27 | swal("网络错误", "请稍后重试!","error") 28 | } 29 | }); 30 | }, 31 | rules:{ 32 | sitename:{ 33 | required:true, 34 | minlength:2, 35 | }, 36 | title:{ 37 | required:true, 38 | } 39 | }, 40 | messages:{ 41 | sitename:{ 42 | required:"至少为两个字符", 43 | }, 44 | title:{ 45 | required:"网站标题不能为空", 46 | } 47 | }, 48 | errorElement: 'custom', 49 | errorClass:'error', 50 | errorPlacement: function(error, element) { 51 | error.appendTo(element.next("span")) 52 | } 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /resources/templates/404.html: -------------------------------------------------------------------------------- 1 | {{template "header.html" .}} 2 |
3 |

{{.status}}

4 |

{{.message}}

5 | 6 |
7 |
您可以返回主页看看 8 |
主页 9 |
10 |
11 | -------------------------------------------------------------------------------- /resources/templates/apps.html: -------------------------------------------------------------------------------- 1 | {{template "header.html" .}} 2 | 3 |
4 |
5 | 6 |
7 | 16 |
17 |
JSON格式化、JSON压缩
18 |
19 | 查看 20 |
21 |
22 |
23 |
24 | 33 |
34 |

JSON/XML互转

35 |
36 | 查看 37 |
38 |
39 |
40 |
41 | 50 |
51 |

UNIX时间戳普通时间相互转换

52 |
53 | 查看 54 |
55 |
56 |
57 |
58 |
59 | 60 | -------------------------------------------------------------------------------- /resources/templates/apps_json.html: -------------------------------------------------------------------------------- 1 | {{template "header.html" .}} 2 | 3 | 4 | 5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | JSON/XML互转 13 |
14 | 15 |
16 |
17 |
18 |

JSON格式化、压缩:JSON样例

19 | 20 |
21 | 22 | 23 |
24 |
25 |
26 |

输出数据:

27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /resources/templates/apps_transform.html: -------------------------------------------------------------------------------- 1 | {{template "header.html" .}} 2 | 3 | 4 |
5 |
6 |
7 |
8 |
9 | 15 |
16 |

JSON/XML互转、JSON转XML、XML转JSON

17 |
18 |
19 |

输入数据(JSON或XML):JSON样例XML样例

20 | 21 |
22 | 23 | 24 |
25 |
26 |
27 |

输出数据:

28 | 29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /resources/templates/header.html: -------------------------------------------------------------------------------- 1 | {{define "header.html"}} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {{ .website.sitename }} 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | {{/**/}} 24 | 25 | 26 | 27 | 36 | 37 | 38 | {{end}} -------------------------------------------------------------------------------- /resources/templates/help_detail.html: -------------------------------------------------------------------------------- 1 | {{template "header.html" .}} 2 | 3 | 4 | 5 | 6 | 7 |
8 |
9 |
10 |
11 |
12 |
13 | 编辑 14 | 删除 15 |
16 |
17 |

18 | {{.data.title}} 19 |

20 |

{{.data.author}}  {{.data.ctime}}

21 |
22 |
23 | {{.data.content}} 24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /resources/templates/help_list.html: -------------------------------------------------------------------------------- 1 | {{template "header.html" .}} 2 | 3 | 4 | 5 | 6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /resources/templates/left_side.html: -------------------------------------------------------------------------------- 1 | {{define "left_side.html"}} 2 | 51 | {{end}} -------------------------------------------------------------------------------- /resources/templates/left_site_api.html: -------------------------------------------------------------------------------- 1 | {{define "left_side_api.html"}} 2 | 51 | {{end}} -------------------------------------------------------------------------------- /resources/templates/manager_category_detail.html: -------------------------------------------------------------------------------- 1 | {{template "header.html" .}} 2 | 3 | 4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |

分类列表 / 分类介绍

12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | 21 |
22 |
{{.classify.Classifyname}}
23 |
24 |
25 |
26 |
27 | 28 |
29 |
30 | {{.desc}} 31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /resources/templates/message_detail.html: -------------------------------------------------------------------------------- 1 | {{template "header.html" .}} 2 | 3 | 4 | 5 | 6 |
7 |
8 |
9 |
10 |

11 | 消息列表 / 消息内容 12 |

13 |
14 |

15 | 标题: {{.data.subject}} 16 |

17 |
18 | {{.data.sendtime}} 19 | 发件人: {{.data.sender}} 20 |
21 |
22 |
23 |
24 |
25 | {{.data.content}} 26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /resources/templates/operate_log.html: -------------------------------------------------------------------------------- 1 | {{template "header.html" .}} 2 | 3 | 4 | 5 |
6 | 7 |
8 |
9 |
10 | 11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 40 | 41 | 42 |
操作人操作对象类型时间
38 |
39 |
43 | 44 |
45 |
46 |
47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 61 | -------------------------------------------------------------------------------- /resources/templates/project.html: -------------------------------------------------------------------------------- 1 | {{template "header.html" .}} 2 | 3 | 4 |
5 |
6 | {{$oid := .organize}} 7 | {{range .project}} 8 |
9 | 12 |
13 |
14 |

15 | 16 |

17 |
18 |
19 |

{{.Desc}}

20 |
21 | {{if eq .Organize $oid}} 22 | 项目设置 23 | 环境设置 24 | {{else}} 25 | 26 | {{end}} 27 |
28 |
29 |
30 | {{end}} 31 |
32 | 33 | 40 |
41 |
42 |
43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /routes/initRouter.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "github.com/gin-contrib/sessions" 5 | "github.com/gin-contrib/sessions/cookie" 6 | "github.com/gin-gonic/gin" 7 | "html/template" 8 | "net/http" 9 | "time" 10 | "xapimanager/application/common" 11 | "xapimanager/application/middleware" 12 | "xapimanager/config" 13 | ) 14 | 15 | func InitRouter() *gin.Engine { 16 | 17 | router := gin.New() 18 | 19 | router.StaticFS("/assets", http.Dir(config.GetGlobal().ASSETS_PATH)) 20 | router.StaticFS("/upload", http.Dir(config.GetGlobal().UPLOAD_PATH)) 21 | 22 | router.SetFuncMap(template.FuncMap{ 23 | "inArray": common.InArray, 24 | "FormatTime": common.FormatTime, 25 | }) 26 | router.LoadHTMLGlob(config.GetGlobal().TEMPLATE_PATH + "/*") // html模板 27 | 28 | store := cookie.NewStore([]byte("secret")) 29 | store.Options(sessions.Options{ 30 | MaxAge: int(24 * time.Hour), 31 | Path: "/", 32 | }) 33 | router.Use(sessions.Sessions("xapi", store)) 34 | 35 | router.Use(middleware.Handler()) 36 | router.Use(gin.Logger()) 37 | 38 | router.NoRoute(func(c *gin.Context) { 39 | c.HTML(http.StatusOK, "404.html", gin.H{ 40 | "status": 404, 41 | "message": "找不到你要的内容", 42 | }) 43 | return 44 | }) 45 | 46 | router.NoMethod(func(c *gin.Context) { 47 | c.JSON(http.StatusNotFound, gin.H{ 48 | "code": 404, 49 | "msg": "找不到该方法", 50 | }) 51 | return 52 | }) 53 | 54 | //加载路由文件 55 | webRouter(router) 56 | projectRouter(router) 57 | 58 | return router 59 | } 60 | -------------------------------------------------------------------------------- /screenshot/apilist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/screenshot/apilist.png -------------------------------------------------------------------------------- /screenshot/apisearch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/screenshot/apisearch.gif -------------------------------------------------------------------------------- /screenshot/apps.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/screenshot/apps.jpg -------------------------------------------------------------------------------- /screenshot/mock.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/screenshot/mock.gif -------------------------------------------------------------------------------- /screenshot/plugin.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/screenshot/plugin.gif -------------------------------------------------------------------------------- /screenshot/team.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/screenshot/team.gif -------------------------------------------------------------------------------- /screenshot/testing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/screenshot/testing.gif -------------------------------------------------------------------------------- /storage/logs/index.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duolatech/xapimanager/2ec033d46adb7c0e1f90fec7482967b8fc29cf41/storage/logs/index.log --------------------------------------------------------------------------------