Repository: liuquanhao/moyu Branch: main Commit: 880e71a214fe Files: 89 Total size: 125.3 KB Directory structure: gitextract_ebjt9uy3/ ├── .github/ │ └── workflows/ │ └── release.yml ├── .gitignore ├── Makefile ├── ManagerDockerfile ├── PageDockerfile ├── README.md ├── manager/ │ ├── backend/ │ │ ├── .gitignore │ │ ├── controller/ │ │ │ ├── page_data_controller/ │ │ │ │ └── page_data_controller.go │ │ │ ├── page_url_controller/ │ │ │ │ └── page_url_controller.go │ │ │ └── user_controller/ │ │ │ └── user_controller.go │ │ ├── database/ │ │ │ └── init.sql │ │ ├── go.mod │ │ ├── go.sum │ │ ├── main.go │ │ ├── middleware/ │ │ │ ├── logged.go │ │ │ └── ws_options.go │ │ └── model/ │ │ ├── db.go │ │ ├── remote_url_model/ │ │ │ └── remote_url_model.go │ │ ├── session.go │ │ └── user_model/ │ │ └── user_model.go │ └── frontend/ │ ├── .babelrc │ ├── .editorconfig │ ├── .gitignore │ ├── .postcssrc.js │ ├── README.md │ ├── build/ │ │ ├── build.js │ │ ├── check-versions.js │ │ ├── utils.js │ │ ├── vue-loader.conf.js │ │ ├── webpack.base.conf.js │ │ ├── webpack.dev.conf.js │ │ └── webpack.prod.conf.js │ ├── config/ │ │ ├── dev.env.js │ │ ├── index.js │ │ └── prod.env.js │ ├── index.html │ ├── package.json │ ├── src/ │ │ ├── App.vue │ │ ├── components/ │ │ │ ├── Login.vue │ │ │ ├── MoyuFooter.vue │ │ │ └── MoyuNav.vue │ │ ├── layouts/ │ │ │ └── PageLayout.vue │ │ ├── main.js │ │ ├── pages/ │ │ │ ├── AddUrlPage.vue │ │ │ ├── LoginPage.vue │ │ │ └── MainPage.vue │ │ └── router/ │ │ └── index.js │ └── static/ │ └── .gitkeep └── page/ ├── backend/ │ ├── .gitignore │ ├── controller/ │ │ └── system.go │ ├── go.mod │ ├── go.sum │ ├── main.go │ ├── middleware/ │ │ └── ws_options.go │ └── service/ │ ├── cpu.go │ ├── disk.go │ ├── host.go │ ├── memory.go │ ├── network.go │ ├── page_data.go │ └── system.go └── frontend/ ├── .babelrc ├── .editorconfig ├── .gitignore ├── .postcssrc.js ├── README.md ├── build/ │ ├── build.js │ ├── check-versions.js │ ├── utils.js │ ├── vue-loader.conf.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ └── webpack.prod.conf.js ├── config/ │ ├── dev.env.js │ ├── index.js │ └── prod.env.js ├── index.html ├── package.json ├── src/ │ ├── App.vue │ ├── common/ │ │ └── filters.js │ ├── components/ │ │ ├── Cpu.vue │ │ ├── Disk.vue │ │ ├── Host.vue │ │ └── Mem.vue │ ├── layouts/ │ │ └── Main.vue │ ├── main.js │ ├── pages/ │ │ └── SystemPage.vue │ └── router/ │ └── index.js └── static/ └── .gitkeep ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/release.yml ================================================ name: 编译并发布项目 on: push: tags: - 'v*' jobs: release: name: "编译发布release程序" runs-on: ubuntu-latest strategy: matrix: node-version: [18.x] go-version: [1.19.x] steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - name: Use Go ${{ matrix.go-version }} uses: actions/setup-go@v3 with: go-version: ${{ matrix.go-version }} - name: Build moyu-manager frontend working-directory: ./manager/frontend run: | npm install npm run build - name: Build moyu-manager release binary working-directory: ./manager/backend run: | go get . GOOS=linux GOARCH=amd64 go build --ldflags="-w -s" -o moyu-manager-linux-amd64 . GOOS=linux GOARCH=arm64 go build --ldflags="-w -s" -o moyu-manager-linux-arm64 . zip -r ../../moyu-manager-linux-amd64.zip moyu-manager-linux-amd64 zip -r ../../moyu-manager-linux-arm64.zip moyu-manager-linux-arm64 zip -r ../../init.sql.zip database/init.sql - name: Build moyu-page frontend working-directory: ./page/frontend run: | npm install npm run build - name: Build moyu-page release binary working-directory: ./page/backend run: | go get . GOOS=linux GOARCH=amd64 go build --ldflags="-w -s" -o moyu-page-linux-amd64 . GOOS=linux GOARCH=arm64 go build --ldflags="-w -s" -o moyu-page-linux-arm64 . zip -r ../../moyu-page-linux-amd64.zip moyu-page-linux-amd64 zip -r ../../moyu-page-linux-arm64.zip moyu-page-linux-arm64 - uses: "marvinpinto/action-automatic-releases@latest" with: repo_token: "${{ secrets.GITHUB_TOKEN }}" prerelease: false files: | *.zip ================================================ FILE: .gitignore ================================================ target/ ================================================ FILE: Makefile ================================================ all: manager page mkdir -p target/bin && mkdir -p target/db && mv manager/backend/moyu-manager target/bin/ && mv page/backend/moyu-page target/bin/ && cat manager/backend/database/init.sql | sqlite3 target/db/moyu_manager.db manager: manager-frontend cd manager/backend && go build -o moyu-manager --ldflags="-w -s" . manager-frontend: cd manager/frontend && npm install && npm run build page: page-frontend cd page/backend && go build -o moyu-page --ldflags="-w -s" . page-frontend: cd page/frontend && npm install && npm run build clean: -rm -rf page/backend/dist page/backend/moyu-page manager/backend/dist manager/backend/moyu-manager target .PHONY: page-frontend page manager-frontend manager clean ================================================ FILE: ManagerDockerfile ================================================ FROM alpine:3.17.1 as builder COPY . . RUN apk add go nodejs npm RUN cd manager/frontend && npm install && npm run build && \ cd ../backend && go get . && go build -o /tmp/moyu-manager --ldflags="-w -s" . FROM alpine:3.17.1 as deploy EXPOSE 8080 RUN mkdir -p /moyu-manager/bin && mkdir -p /moyu-manager/db COPY --from=builder /tmp/moyu-manager /moyu-manager/bin/ ENTRYPOINT /moyu-manager/bin/moyu-manager ================================================ FILE: PageDockerfile ================================================ FROM alpine:3.17.1 as builder COPY . . RUN apk add go nodejs npm RUN cd page/frontend && npm install && npm run build && \ cd ../backend && go get . && go build -o moyu-page --ldflags="-w -s" . FROM alpine:3.17.1 as deploy EXPOSE 8081 COPY --from=builder backend/moyu-page . ENTRYPOINT /moyu-page ================================================ FILE: README.md ================================================ # 墨鱼探针 ## 简介 墨鱼探针为主从模式,主为`墨鱼manager`,从为`墨鱼page`。`墨鱼manager`可获取展示多个墨鱼page的简要状态信息,登录后可点击title进入墨鱼page页面。`墨鱼page`独立部署,可独立使用。 技术栈:go(fiber) + vue2 + element-ui + nes.css。 最终将前端和后端全部编译到单个二进制程序中,分为`moyu-manager`和`moyu-page`2个二进制程序。 ## 展示 ### 墨鱼page ![moyu_page](./imgs/moyu_page.png) ### 墨鱼manager 未登录主页: ![moyu_manager_main_unlogin](./imgs/moyu_manager_main_unlogin.png) 已登录主页: ![moyu_manager_main_login](./imgs/moyu_manager_main_login.png) 登录页: ![moyu_manager_login](./imgs/moyu_manager_login.png) 添加墨鱼page页面: ![moyu_manager_add_page](./imgs/moyu_manager_add_page.png) ## 直接使用release文件 ### moyu-page 1. 直接运行 ```bash PORT=8081 ./moyu-page ``` ### moyu-manager 1. 初始化数据库(第一次运行) ```bash cat init.sql | sqlite3 db/moyu_manager.db ``` 2. 目录结构 ```bash ├── bin │ └── moyu-manager └── db └── moyu_manager.db ``` 3. 运行 ```bash PORT=8080 ./bin/moyu-manager ``` 4. 添加墨鱼page。如本项目在线demo:`https://moyu-manager.linux.plus/`,直接复制墨鱼page主页`https://moyu-page.linux.plus/`到`添加墨鱼page页面`即可。在墨鱼manager中,如果已登录,可以点击title访问墨鱼page。 ## 编译使用 ### 依赖: make: ^4.0 nodejs: ^18.0 go: ^1.19.0 sqlite: ^3.0 ### 一键编译 1. 进入项目目录。 ```bash cd moyu ``` 2. 一键编译。 ```bash make ``` 3. 运行墨鱼manager和墨鱼page。 ```bash PORT=8081 target/bin/moyu-page PORT=8080 target/bin/moyu-manager ``` 4. (可选)清理项目,删除编译的墨鱼探针二进制等文件。 ```bash make clean ``` ### 手动编译 #### 编译墨鱼page 1. 进入项目目录。 ```bash cd moyu/page ``` 2. 编译前端资源。 ```bash cd frontend npm run build ``` 3. 编译后端项目。 ```bash cd ../backend go build -o moyu-page --ldflags="-w -s" . ``` #### 编译墨鱼manager 1. 进入项目目录。 ```bash cd moyu/manager ``` 2. 编译前端资源。 ```bash cd frontend npm run build ``` 3. 编译后端项目。 ```bash cd ../backend go build -o moyu-manager --ldflags="-w -s" . ``` ## Docker运行 ### 前提说明 #### 墨鱼page 墨鱼page直接运行docker无法获取宿主机信息,需要添加一些运行参数: 1. 由于需要获取宿主机网络接口流量,所以需要以host方式运行docker。 2. 获取磁盘分区信息需要`/proc/N/mountinfo`,所以需要将宿主机的某个进程的文件挂载到docker中,然后设置`HOST_PROC_MOUNTINFO`并运行项目。 3. 项目`PORT`变量默认`8081`,可自行指定其他端口。 #### 墨鱼manager 1. 墨鱼manager依赖`sqlite3`,需要使用`moyu/manager/backend/database/init.sql`创建和初始化用户表数据。 2. 请使用一下目录格式存放`moyu-manager`程序和数据库,容器挂载时需要注意目录位置,默认编译的项目根目录为`/moyu-manager`。 ```bash root@liuxu:/moyu-manager# tree . ├── bin │ └── moyu-manager └── db └── moyu_manager.db ``` ### 运行容器 #### 墨鱼page 1. 编译page镜像。 ```bash docker build -t moyu-page -f PageDockerfile . ``` 2. 单磁盘挂载情况下运行,其中`--network=host`指定使用宿主机网络,`--mount`挂载`dockerd`的进程`mountinfo`文件到docker中,并设置`HOST_PROC_MOUNTINFO`为挂载的文件路径。 ```bash docker run --network=host -e PORT=8081 --mount type=bind,source="/proc/$(pidof dockerd)/mountinfo",target=/root/mountinfo -e HOST_PROC_MOUNTINFO=/root/mountinfo moyu-page ``` 3. (可选)如果还有其他分区,如我的`/boot/efi`挂载到了独立分区,想获取到这个分区信息,需要把这个目录挂载到docker中。 ```bash docker run --network=host -e PORT=8081 -v /boot/efi:/boot/efi:ro --mount type=bind,source="/proc/$(pidof dockerd)/mountinfo",target=/root/mountinfo -e HOST_PROC_MOUNTINFO=/root/mountinfo moyu-page ``` #### 墨鱼manager 1. 编译manager镜像。 ```bash docker build -t moyu-manager -f ManagerDockerfile . ``` 2. 创建数据库。 ```bash mkdir db cat manager/backend/database/init.sql | sqlite3 db/moyu_manager.db ``` 3. 挂载数据库运行。 ```bash docker run -e PORT=8080 -p 8080:8080 -v ./db:/moyu-manager/db moyu-manager ``` ## FAQ 1. 我使用nginx做反代,设置了`location /moyu{}`该怎么办 答:运行墨鱼page或墨鱼manager时,添加`BASEURL=/moyu`环境变量运行。 2. 运行墨鱼manager,程序报找不到`moyu_manager.db`怎么办 答:确认自己的目录结构是不是如下: ```bash ├── bin │ └── moyu-manager └── db └── moyu_manager.db ``` 然后进入`bin/`目录下运行`moyu-manager` 3. 我想修改账号或者token 答:运行sqlite3,通过sql修改。 ```bash sqlite3 moyu_manager.db sqlite> UPDATE users SET token='token123' WHERE user='user' ``` 4. 我想创建新的账户和token 答:运行sqlite3,通过sql添加。 ```bash sqlite3 moyu_manager.db sqlite> INSERT INTO users(user, token) VALUES('user1', 'token1'); ``` ================================================ FILE: manager/backend/.gitignore ================================================ dist/ moyu-manager ================================================ FILE: manager/backend/controller/page_data_controller/page_data_controller.go ================================================ package page_data_controller import ( "encoding/json" "net/http" "time" "github.com/gofiber/websocket/v2" "github.com/liuquanhao/moyu/model" "github.com/liuquanhao/moyu/model/remote_url_model" ) // define: page/backend/service/page_data.go type PageData struct { Uptime uint64 `json:"uptime"` CPUPercent float64 `json:"cpu_percent"` MemoryPercent float64 `json:"memory_percent"` DiskPercent float64 `json:"disk_percent"` NetSendTotal uint64 `json:"net_send"` NetRecvTotal uint64 `json:"net_recv"` Timestamp int64 `json:"timestamp"` } type PageInfo struct { PageId uint32 `json:"page_id"` PageData *PageData `json:"page_data"` } func getPageData(myClient *http.Client, url string, target interface{}) error { resp, err := http.Get(url + "api/page_data") if err != nil { return err } if resp.StatusCode != 200 { return err } defer resp.Body.Close() return json.NewDecoder(resp.Body).Decode(target) } func loopPullPageData(pageId uint32, url string, ch chan *PageInfo) { var myClient = &http.Client{Timeout: 5 * time.Second} pageData := new(PageData) for { start := time.Now() err := getPageData(myClient, url, pageData) if err != nil { time.Sleep(3 * time.Second) } else { ch <- &PageInfo{ PageId: pageId, PageData: pageData, } } end := time.Now() useTime := end.Sub(start) if useTime < 1*time.Second { time.Sleep(1000*time.Millisecond - useTime) } } } func PushPageData(c *websocket.Conn) { db := model.DBConn var pageUrls []remote_url_model.PageUrl db.Find(&pageUrls) ch := make(chan *PageInfo, len(pageUrls)) defer close(ch) for _, pageUrl := range pageUrls { go loopPullPageData(pageUrl.Id, pageUrl.PageUrl, ch) } for pageInfo := range ch { c.WriteJSON(pageInfo) } } ================================================ FILE: manager/backend/controller/page_url_controller/page_url_controller.go ================================================ package page_url_controller import ( "github.com/gofiber/fiber/v2" "github.com/liuquanhao/moyu/model" "github.com/liuquanhao/moyu/model/remote_url_model" "github.com/go-playground/validator/v10" ) var validate = validator.New() func Add(c *fiber.Ctx) error { db := model.DBConn pageUrl := new(remote_url_model.PageUrl) if err := c.BodyParser(pageUrl); err != nil { return c.Status(fiber.StatusBadRequest).SendString(err.Error()) } if err := validate.Struct(pageUrl); err != nil { return c.Status(fiber.StatusBadRequest).SendString(err.Error()) } db.Create(&pageUrl) return c.JSON(pageUrl) } func Delete(c *fiber.Ctx) error { id := c.Params("id") db := model.DBConn var pageUrl remote_url_model.PageUrl db.Take(&pageUrl, id) if pageUrl.Title == "" { return c.Status(fiber.StatusNotFound).SendString("Not Found") } db.Delete(&pageUrl) return c.SendString("Delete success") } func List(c *fiber.Ctx) error { db := model.DBConn var pageUrls []remote_url_model.PageUrl db.Find(&pageUrls) s, _ := model.SessionStore.Get(c) if !s.Fresh() { return c.JSON(pageUrls) } // unlogin for _, pageUrl := range pageUrls { pageUrl.PageUrl = "" } return c.JSON(pageUrls) } ================================================ FILE: manager/backend/controller/user_controller/user_controller.go ================================================ package user_controller import ( "strconv" "time" "github.com/go-playground/validator/v10" "github.com/gofiber/fiber/v2" "github.com/liuquanhao/moyu/model" "github.com/liuquanhao/moyu/model/user_model" ) var validate = validator.New() func Login(c *fiber.Ctx) error { db := model.DBConn user := new(user_model.User) if err := c.BodyParser(user); err != nil { return c.Status(fiber.StatusBadRequest).SendString(err.Error()) } if err := validate.Struct(user); err != nil { return c.Status(fiber.StatusBadRequest).SendString(err.Error()) } db.Where(&user).Take(&user) if user.Id == 0 { return c.Status(fiber.StatusUnauthorized).SendString("Not Found User") } s, _ := model.SessionStore.Get(c) s.Set("uid", user.Id) s.Set("sid", s.ID()) s.Set("ip", c.Context().RemoteIP().String()) s.Set("login", time.Unix(time.Now().Unix(), 0).UTC().String()) s.Set("ua", string(c.Request().Header.UserAgent())) err := s.Save() if err != nil { return c.Status(fiber.StatusServiceUnavailable).SendString(err.Error()) } cookie := new(fiber.Cookie) cookie.Name = "uid" cookie.Value = strconv.FormatUint(uint64(user.Id), 10) cookie.Expires = time.Now().Add(24 * time.Hour) c.Cookie(cookie) c.Status(fiber.StatusNoContent) return nil } func Account(c *fiber.Ctx) error { s, _ := model.SessionStore.Get(c) if s.Fresh() { c.Status(fiber.StatusNotFound) return nil } return c.JSON(fiber.Map{ "sid": s.ID(), "uid": s.Get("uid"), "ip": s.Get("ip"), "login": s.Get("login"), "ua": s.Get("ua"), }) } func Logout(c *fiber.Ctx) error { s, _ := model.SessionStore.Get(c) if err := s.Destroy(); err != nil { return c.Status(fiber.StatusServiceUnavailable).SendString(err.Error()) } c.Status(fiber.StatusNoContent) return nil } ================================================ FILE: manager/backend/database/init.sql ================================================ CREATE TABLE IF NOT EXISTS sessions ( k VARCHAR(64) PRIMARY KEY NOT NULL DEFAULT '', v BLOB NOT NULL, e BIGINT NOT NULL DEFAULT '0', u TEXT ); CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, user VARCHAR(16) UNIQUE NOT NULL, token VARCHAR(32) NOT NULL ); CREATE TABLE IF NOT EXISTS page_urls ( id INTEGER PRIMARY KEY AUTOINCREMENT, title VARCHAR(32) NOT NULL, page_url VARCHAR(512) NOT NULL ); CREATE UNIQUE INDEX user_uiq on users (user); INSERT INTO users(user, token) VALUES ("user", "token123"); ================================================ FILE: manager/backend/go.mod ================================================ module github.com/liuquanhao/moyu go 1.19 require ( github.com/go-playground/validator/v10 v10.11.2 github.com/gofiber/fiber/v2 v2.42.0 github.com/gofiber/storage/sqlite3 v0.0.0-20230206084615-41a84b36b572 github.com/gofiber/websocket/v2 v2.1.4 gorm.io/driver/sqlite v1.4.4 gorm.io/gorm v1.24.5 ) require ( github.com/andybalholm/brotli v1.0.4 // indirect github.com/fasthttp/websocket v1.5.1 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/google/uuid v1.3.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/klauspost/compress v1.15.9 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mattn/go-sqlite3 v1.14.16 // indirect github.com/philhofer/fwd v1.1.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect github.com/tinylib/msgp v1.1.6 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.44.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect golang.org/x/crypto v0.5.0 // indirect golang.org/x/sys v0.4.0 // indirect golang.org/x/text v0.6.0 // indirect ) ================================================ FILE: manager/backend/go.sum ================================================ github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/fasthttp/websocket v1.5.1 h1:iZsMv5OtZ1E52hhCnlOm/feLCrPhutlrZgvEGcZa1FM= github.com/fasthttp/websocket v1.5.1/go.mod h1:s+gJkEn38QXLkNfOe/n75Yb8we+VEho1vYqeUYheomw= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= github.com/gofiber/fiber/v2 v2.42.0 h1:Fnp7ybWvS+sjNQsFvkhf4G8OhXswvB6Vee8hM/LyS+8= github.com/gofiber/fiber/v2 v2.42.0/go.mod h1:3+SGNjqMh5VQH5Vz2Wdi43zTIV16ktlFd3x3R6O1Zlc= github.com/gofiber/storage/sqlite3 v0.0.0-20230206084615-41a84b36b572 h1:FHnNfM4q4Z+WtuYoQ5vz2z+JWmsY/sV49IS1nekEko4= github.com/gofiber/storage/sqlite3 v0.0.0-20230206084615-41a84b36b572/go.mod h1:TXYMAnzU/KAYE7RrM5tH9FrFC9Sr0CgMySWpMedmJR8= github.com/gofiber/utils v1.0.1 h1:knct4cXwBipWQqFrOy1Pv6UcgPM+EXo9jDgc66V1Qio= github.com/gofiber/utils v1.0.1/go.mod h1:pacRFtghAE3UoknMOUiXh2Io/nLWSUHtQCi/3QASsOc= github.com/gofiber/websocket/v2 v2.1.4 h1:Ki6L7auleAwgi7iRmtUiWKltlbmtkCJ0COtK1nt8L3g= github.com/gofiber/websocket/v2 v2.1.4/go.mod h1:IC4ZUejlk0kJSaphJ1gjqgKfK9fhw8eoAr3/UdbOzEA= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4= github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8= github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d h1:Q+gqLBOPkFGHyCJxXMRqtUgUbTjI8/Ze8vu8GGyNFwo= github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw= github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.44.0 h1:R+gLUhldIsfg1HokMuQjdQ5bh9nuXHPIfvkYUu9eR5Q= github.com/valyala/fasthttp v1.44.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc= gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= gorm.io/gorm v1.24.5 h1:g6OPREKqqlWq4kh/3MCQbZKImeB9e6Xgc4zD+JgNZGE= gorm.io/gorm v1.24.5/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= ================================================ FILE: manager/backend/main.go ================================================ package main import ( "embed" "io/fs" "log" "net/http" "os" "path/filepath" "time" "github.com/liuquanhao/moyu/controller/page_data_controller" "github.com/liuquanhao/moyu/controller/page_url_controller" "github.com/liuquanhao/moyu/controller/user_controller" "github.com/liuquanhao/moyu/middleware" "github.com/liuquanhao/moyu/model" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" "github.com/gofiber/fiber/v2/middleware/filesystem" "github.com/gofiber/fiber/v2/middleware/session" "github.com/gofiber/storage/sqlite3" "github.com/gofiber/websocket/v2" "gorm.io/driver/sqlite" "gorm.io/gorm" ) func getCurrentPwd() string { exePath, _ := os.Executable() return filepath.Dir(exePath) } func initDatabase() { var err error model.DBConn, err = gorm.Open(sqlite.Open(getCurrentPwd() + "/../db/moyu_manager.db")) if err != nil { log.Fatal("failed to connect database: {}", err) } } func initSession() { storage := sqlite3.New(sqlite3.Config{ Database: getCurrentPwd() + "/../db/moyu_manager.db", Table: "sessions", Reset: false, GCInterval: 10 * time.Second, MaxOpenConns: 100, MaxIdleConns: 100, ConnMaxLifetime: 1 * time.Second, }) model.SessionStore = session.New(session.Config{ Storage: storage, }) } func setupRoutes(app *fiber.App) { app.Use(cors.New(cors.Config{ AllowOrigins: "*", AllowCredentials: true, })) base := app.Group(os.Getenv("BASEURL")) ws := base.Group("/ws") ws.Use("/*", middleware.UpgradeOptions) ws.Get("/page_data", websocket.New(page_data_controller.PushPageData)) api := base.Group("/api") api.Post("/login", user_controller.Login) api.Get("/page_url", page_url_controller.List) logged := app.Group("/api", middleware.Logged) logged.Post("/logout", user_controller.Logout) logged.Get("/account", user_controller.Account) logged.Post("/page_url", page_url_controller.Add) logged.Delete("/page_url/:id", page_url_controller.Delete) stripped, err := fs.Sub(frontend, "dist") if err != nil { log.Fatal(err) } base.Use("/", filesystem.New(filesystem.Config{ Root: http.FS(stripped), Browse: true, })) } //go:embed dist var frontend embed.FS func main() { app := fiber.New() initDatabase() initSession() setupRoutes(app) host := os.Getenv("HOST") port := os.Getenv("PORT") log.Println(host + ":" + port) log.Fatal(app.Listen(host + ":" + port)) } ================================================ FILE: manager/backend/middleware/logged.go ================================================ package middleware import ( "github.com/gofiber/fiber/v2" "github.com/liuquanhao/moyu/model" ) func Logged(c *fiber.Ctx) error { s, _ := model.SessionStore.Get(c) if s.Fresh() { c.Status(fiber.StatusUnauthorized) return nil } return c.Next() } ================================================ FILE: manager/backend/middleware/ws_options.go ================================================ package middleware import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/websocket/v2" ) func UpgradeOptions(c *fiber.Ctx) error { if websocket.IsWebSocketUpgrade(c) { return c.Next() } return fiber.ErrUpgradeRequired } ================================================ FILE: manager/backend/model/db.go ================================================ package model import "gorm.io/gorm" var ( DBConn *gorm.DB ) ================================================ FILE: manager/backend/model/remote_url_model/remote_url_model.go ================================================ package remote_url_model type PageUrl struct { Id uint32 `json:"id"` Title string `json:"title" validate:"required,max=32"` PageUrl string `json:"page_url" validate:"required,max=512"` } ================================================ FILE: manager/backend/model/session.go ================================================ package model import "github.com/gofiber/fiber/v2/middleware/session" var ( SessionStore *session.Store ) ================================================ FILE: manager/backend/model/user_model/user_model.go ================================================ package user_model type User struct { Id uint32 `json:"id"` User string `json:"user" validate:"required,max=16"` Token string `json:"token" validate:"required,max=32"` } ================================================ FILE: manager/frontend/.babelrc ================================================ { "presets": [ ["env", { "modules": false, "targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] } }], "stage-2" ], "plugins": ["transform-vue-jsx", "transform-runtime"] } ================================================ FILE: manager/frontend/.editorconfig ================================================ root = true [*] charset = utf-8 indent_style = space indent_size = 2 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true ================================================ FILE: manager/frontend/.gitignore ================================================ .DS_Store node_modules/ /dist/ npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln ================================================ FILE: manager/frontend/.postcssrc.js ================================================ // https://github.com/michael-ciniawsky/postcss-load-config module.exports = { "plugins": { "postcss-import": {}, "postcss-url": {}, // to edit target browsers: use "browserslist" field in package.json "autoprefixer": {} } } ================================================ FILE: manager/frontend/README.md ================================================ # frontend > moyu manager ## Build Setup ``` bash # install dependencies npm install # serve with hot reload at localhost:8080 npm run dev # build for production with minification npm run build # build for production and view the bundle analyzer report npm run build --report ``` For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). ================================================ FILE: manager/frontend/build/build.js ================================================ 'use strict' require('./check-versions')() process.env.NODE_ENV = 'production' const ora = require('ora') const rm = require('rimraf') const path = require('path') const chalk = require('chalk') const webpack = require('webpack') const config = require('../config') const webpackConfig = require('./webpack.prod.conf') const spinner = ora('building for production...') spinner.start() rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { if (err) throw err webpack(webpackConfig, (err, stats) => { spinner.stop() if (err) throw err process.stdout.write(stats.toString({ colors: true, modules: false, children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. chunks: false, chunkModules: false }) + '\n\n') if (stats.hasErrors()) { console.log(chalk.red(' Build failed with errors.\n')) process.exit(1) } console.log(chalk.cyan(' Build complete.\n')) console.log(chalk.yellow( ' Tip: built files are meant to be served over an HTTP server.\n' + ' Opening index.html over file:// won\'t work.\n' )) }) }) ================================================ FILE: manager/frontend/build/check-versions.js ================================================ 'use strict' const chalk = require('chalk') const semver = require('semver') const packageConfig = require('../package.json') const shell = require('shelljs') function exec (cmd) { return require('child_process').execSync(cmd).toString().trim() } const versionRequirements = [ { name: 'node', currentVersion: semver.clean(process.version), versionRequirement: packageConfig.engines.node } ] if (shell.which('npm')) { versionRequirements.push({ name: 'npm', currentVersion: exec('npm --version'), versionRequirement: packageConfig.engines.npm }) } module.exports = function () { const warnings = [] for (let i = 0; i < versionRequirements.length; i++) { const mod = versionRequirements[i] if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { warnings.push(mod.name + ': ' + chalk.red(mod.currentVersion) + ' should be ' + chalk.green(mod.versionRequirement) ) } } if (warnings.length) { console.log('') console.log(chalk.yellow('To use this template, you must update following to modules:')) console.log() for (let i = 0; i < warnings.length; i++) { const warning = warnings[i] console.log(' ' + warning) } console.log() process.exit(1) } } ================================================ FILE: manager/frontend/build/utils.js ================================================ 'use strict' const path = require('path') const config = require('../config') const ExtractTextPlugin = require('extract-text-webpack-plugin') const packageConfig = require('../package.json') exports.assetsPath = function (_path) { const assetsSubDirectory = process.env.NODE_ENV === 'production' ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory return path.posix.join(assetsSubDirectory, _path) } exports.cssLoaders = function (options) { options = options || {} const cssLoader = { loader: 'css-loader', options: { sourceMap: options.sourceMap } } const postcssLoader = { loader: 'postcss-loader', options: { sourceMap: options.sourceMap } } // generate loader string to be used with extract text plugin function generateLoaders (loader, loaderOptions) { const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] if (loader) { loaders.push({ loader: loader + '-loader', options: Object.assign({}, loaderOptions, { sourceMap: options.sourceMap }) }) } // Extract CSS when that option is specified // (which is the case during production build) if (options.extract) { return ExtractTextPlugin.extract({ use: loaders, fallback: 'vue-style-loader' }) } else { return ['vue-style-loader'].concat(loaders) } } // https://vue-loader.vuejs.org/en/configurations/extract-css.html return { css: generateLoaders(), postcss: generateLoaders(), less: generateLoaders('less'), sass: generateLoaders('sass', { indentedSyntax: true }), scss: generateLoaders('sass'), stylus: generateLoaders('stylus'), styl: generateLoaders('stylus') } } // Generate loaders for standalone style files (outside of .vue) exports.styleLoaders = function (options) { const output = [] const loaders = exports.cssLoaders(options) for (const extension in loaders) { const loader = loaders[extension] output.push({ test: new RegExp('\\.' + extension + '$'), use: loader }) } return output } exports.createNotifierCallback = () => { const notifier = require('node-notifier') return (severity, errors) => { if (severity !== 'error') return const error = errors[0] const filename = error.file && error.file.split('!').pop() notifier.notify({ title: packageConfig.name, message: severity + ': ' + error.name, subtitle: filename || '', icon: path.join(__dirname, 'logo.png') }) } } ================================================ FILE: manager/frontend/build/vue-loader.conf.js ================================================ 'use strict' const utils = require('./utils') const config = require('../config') const isProduction = process.env.NODE_ENV === 'production' const sourceMapEnabled = isProduction ? config.build.productionSourceMap : config.dev.cssSourceMap module.exports = { loaders: utils.cssLoaders({ sourceMap: sourceMapEnabled, extract: isProduction }), cssSourceMap: sourceMapEnabled, cacheBusting: config.dev.cacheBusting, transformToRequire: { video: ['src', 'poster'], source: 'src', img: 'src', image: 'xlink:href' } } ================================================ FILE: manager/frontend/build/webpack.base.conf.js ================================================ 'use strict' const path = require('path') const utils = require('./utils') const config = require('../config') const vueLoaderConfig = require('./vue-loader.conf') function resolve (dir) { return path.join(__dirname, '..', dir) } module.exports = { context: path.resolve(__dirname, '../'), entry: { app: './src/main.js' }, output: { path: config.build.assetsRoot, filename: '[name].js', publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath }, resolve: { extensions: ['.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), } }, module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: vueLoaderConfig }, { test: /\.js$/, loader: 'babel-loader', include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: utils.assetsPath('img/[name].[hash:7].[ext]') } }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: utils.assetsPath('media/[name].[hash:7].[ext]') } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: utils.assetsPath('fonts/[name].[hash:7].[ext]') } } ] }, node: { // prevent webpack from injecting useless setImmediate polyfill because Vue // source contains it (although only uses it if it's native). setImmediate: false, // prevent webpack from injecting mocks to Node native modules // that does not make sense for the client dgram: 'empty', fs: 'empty', net: 'empty', tls: 'empty', child_process: 'empty' } } ================================================ FILE: manager/frontend/build/webpack.dev.conf.js ================================================ 'use strict' const utils = require('./utils') const webpack = require('webpack') const config = require('../config') const merge = require('webpack-merge') const path = require('path') const baseWebpackConfig = require('./webpack.base.conf') const CopyWebpackPlugin = require('copy-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') const portfinder = require('portfinder') const HOST = process.env.HOST const PORT = process.env.PORT && Number(process.env.PORT) const devWebpackConfig = merge(baseWebpackConfig, { module: { rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) }, // cheap-module-eval-source-map is faster for development devtool: config.dev.devtool, // these devServer options should be customized in /config/index.js devServer: { clientLogLevel: 'warning', historyApiFallback: { rewrites: [ { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, ], }, hot: true, contentBase: false, // since we use CopyWebpackPlugin. compress: true, host: HOST || config.dev.host, port: PORT || config.dev.port, open: config.dev.autoOpenBrowser, overlay: config.dev.errorOverlay ? { warnings: false, errors: true } : false, publicPath: config.dev.assetsPublicPath, proxy: config.dev.proxyTable, quiet: true, // necessary for FriendlyErrorsPlugin watchOptions: { poll: config.dev.poll, } }, plugins: [ new webpack.DefinePlugin({ 'process.env': require('../config/dev.env') }), new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. new webpack.NoEmitOnErrorsPlugin(), // https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.html', inject: true }), // copy custom static assets new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../static'), to: config.dev.assetsSubDirectory, ignore: ['.*'] } ]) ] }) module.exports = new Promise((resolve, reject) => { portfinder.basePort = process.env.PORT || config.dev.port portfinder.getPort((err, port) => { if (err) { reject(err) } else { // publish the new Port, necessary for e2e tests process.env.PORT = port // add port to devServer config devWebpackConfig.devServer.port = port // Add FriendlyErrorsPlugin devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ compilationSuccessInfo: { messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], }, onErrors: config.dev.notifyOnErrors ? utils.createNotifierCallback() : undefined })) resolve(devWebpackConfig) } }) }) ================================================ FILE: manager/frontend/build/webpack.prod.conf.js ================================================ 'use strict' const path = require('path') const utils = require('./utils') const webpack = require('webpack') const config = require('../config') const merge = require('webpack-merge') const baseWebpackConfig = require('./webpack.base.conf') const CopyWebpackPlugin = require('copy-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const ExtractTextPlugin = require('extract-text-webpack-plugin') const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') const UglifyJsPlugin = require('uglifyjs-webpack-plugin') const env = require('../config/prod.env') const webpackConfig = merge(baseWebpackConfig, { module: { rules: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true, usePostCSS: true }) }, devtool: config.build.productionSourceMap ? config.build.devtool : false, output: { path: config.build.assetsRoot, filename: utils.assetsPath('js/[name].[chunkhash].js'), chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') }, plugins: [ // http://vuejs.github.io/vue-loader/en/workflow/production.html new webpack.DefinePlugin({ 'process.env': env }), new UglifyJsPlugin({ uglifyOptions: { compress: { warnings: false } }, sourceMap: config.build.productionSourceMap, parallel: true }), // extract css into its own file new ExtractTextPlugin({ filename: utils.assetsPath('css/[name].[contenthash].css'), // Setting the following option to `false` will not extract CSS from codesplit chunks. // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 allChunks: true, }), // Compress extracted CSS. We are using this plugin so that possible // duplicated CSS from different components can be deduped. new OptimizeCSSPlugin({ cssProcessorOptions: config.build.productionSourceMap ? { safe: true, map: { inline: false } } : { safe: true } }), // generate dist index.html with correct asset hash for caching. // you can customize output by editing /index.html // see https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ filename: config.build.index, template: 'index.html', inject: true, minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true // more options: // https://github.com/kangax/html-minifier#options-quick-reference }, // necessary to consistently work with multiple chunks via CommonsChunkPlugin chunksSortMode: 'dependency' }), // keep module.id stable when vendor modules does not change new webpack.HashedModuleIdsPlugin(), // enable scope hoisting new webpack.optimize.ModuleConcatenationPlugin(), // split vendor js into its own file new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks (module) { // any required modules inside node_modules are extracted to vendor return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 ) } }), // extract webpack runtime and module manifest to its own file in order to // prevent vendor hash from being updated whenever app bundle is updated new webpack.optimize.CommonsChunkPlugin({ name: 'manifest', minChunks: Infinity }), // This instance extracts shared chunks from code splitted chunks and bundles them // in a separate chunk, similar to the vendor chunk // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk new webpack.optimize.CommonsChunkPlugin({ name: 'app', async: 'vendor-async', children: true, minChunks: 3 }), // copy custom static assets new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../static'), to: config.build.assetsSubDirectory, ignore: ['.*'] } ]) ] }) if (config.build.productionGzip) { const CompressionWebpackPlugin = require('compression-webpack-plugin') webpackConfig.plugins.push( new CompressionWebpackPlugin({ asset: '[path].gz[query]', algorithm: 'gzip', test: new RegExp( '\\.(' + config.build.productionGzipExtensions.join('|') + ')$' ), threshold: 10240, minRatio: 0.8 }) ) } if (config.build.bundleAnalyzerReport) { const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin webpackConfig.plugins.push(new BundleAnalyzerPlugin()) } module.exports = webpackConfig ================================================ FILE: manager/frontend/config/dev.env.js ================================================ 'use strict' const merge = require('webpack-merge') const prodEnv = require('./prod.env') module.exports = merge(prodEnv, { NODE_ENV: '"development"' }) ================================================ FILE: manager/frontend/config/index.js ================================================ 'use strict' // Template version: 1.3.1 // see http://vuejs-templates.github.io/webpack for documentation. const path = require('path') module.exports = { dev: { // Paths assetsSubDirectory: 'static', assetsPublicPath: './', proxyTable: {}, // Various Dev Server settings host: 'localhost', // can be overwritten by process.env.HOST port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined autoOpenBrowser: false, errorOverlay: true, notifyOnErrors: true, poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- /** * Source Maps */ // https://webpack.js.org/configuration/devtool/#development devtool: 'cheap-module-eval-source-map', // If you have problems debugging vue-files in devtools, // set this to false - it *may* help // https://vue-loader.vuejs.org/en/options.html#cachebusting cacheBusting: true, cssSourceMap: true }, build: { // Template for index.html index: path.resolve(__dirname, '../dist/index.html'), // Paths assetsRoot: path.resolve(__dirname, '../dist'), assetsSubDirectory: 'static', assetsPublicPath: './', /** * Source Maps */ productionSourceMap: true, // https://webpack.js.org/configuration/devtool/#production devtool: '#source-map', // Gzip off by default as many popular static hosts such as // Surge or Netlify already gzip all static assets for you. // Before setting to `true`, make sure to: // npm install --save-dev compression-webpack-plugin productionGzip: false, productionGzipExtensions: ['js', 'css'], // Run the build command with an extra argument to // View the bundle analyzer report after build finishes: // `npm run build --report` // Set to `true` or `false` to always turn it on or off bundleAnalyzerReport: process.env.npm_config_report } } ================================================ FILE: manager/frontend/config/prod.env.js ================================================ 'use strict' module.exports = { NODE_ENV: '"production"' } ================================================ FILE: manager/frontend/index.html ================================================ Moyu Manager
================================================ FILE: manager/frontend/package.json ================================================ { "name": "frontend", "version": "1.0.0", "description": "moyu manager", "author": "liuxu ", "private": true, "scripts": { "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "start": "npm run dev", "build": "node build/build.js", "postbuild": "rm -rf ../backend/dist && mv dist ../backend/" }, "dependencies": { "axios": "^1.3.2", "element-ui": "^2.15.12", "modern-normalize": "^1.1.0", "nes.css": "^2.3.0", "vue": "^2.5.2", "vue-cookies": "^1.8.2", "vue-router": "^3.0.1" }, "devDependencies": { "autoprefixer": "^7.1.2", "babel-core": "^6.22.1", "babel-helper-vue-jsx-merge-props": "^2.0.3", "babel-loader": "^7.1.1", "babel-plugin-syntax-jsx": "^6.18.0", "babel-plugin-transform-runtime": "^6.22.0", "babel-plugin-transform-vue-jsx": "^3.5.0", "babel-preset-env": "^1.3.2", "babel-preset-stage-2": "^6.22.0", "chalk": "^2.0.1", "copy-webpack-plugin": "^4.0.1", "css-loader": "^0.28.0", "extract-text-webpack-plugin": "^3.0.0", "file-loader": "^1.1.4", "friendly-errors-webpack-plugin": "^1.6.1", "html-webpack-plugin": "^2.30.1", "node-notifier": "^5.1.2", "optimize-css-assets-webpack-plugin": "^3.2.0", "ora": "^1.2.0", "portfinder": "^1.0.13", "postcss-import": "^11.0.0", "postcss-loader": "^2.0.8", "postcss-url": "^7.2.1", "rimraf": "^2.6.0", "semver": "^5.3.0", "shelljs": "^0.7.6", "uglifyjs-webpack-plugin": "^1.1.1", "url-loader": "^0.5.8", "vue-loader": "^13.3.0", "vue-style-loader": "^3.0.1", "vue-template-compiler": "^2.5.2", "webpack": "^3.6.0", "webpack-bundle-analyzer": "^2.9.0", "webpack-dev-server": "^2.9.1", "webpack-merge": "^4.1.0" }, "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" }, "browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ] } ================================================ FILE: manager/frontend/src/App.vue ================================================ ================================================ FILE: manager/frontend/src/components/Login.vue ================================================ ================================================ FILE: manager/frontend/src/components/MoyuFooter.vue ================================================ ================================================ FILE: manager/frontend/src/components/MoyuNav.vue ================================================ ================================================ FILE: manager/frontend/src/layouts/PageLayout.vue ================================================ ================================================ FILE: manager/frontend/src/main.js ================================================ // The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import router from './router' import VueCookies from 'vue-cookies' import "modern-normalize/modern-normalize.css" import { Row, Col, Container, Header, Main, Footer, Table, TableColumn, } from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import axios from 'axios' import "nes.css/css/nes.min.css" Vue.config.productionTip = false Vue.prototype.axios = axios Vue.component(Row.name, Row) Vue.component(Col.name, Col) Vue.component(Container.name, Container) Vue.component(Header.name, Header) Vue.component(Main.name, Main) Vue.component(Footer.name, Footer) Vue.component(Table.name, Table) Vue.component(TableColumn.name, TableColumn) Vue.use(VueCookies) /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '' }) ================================================ FILE: manager/frontend/src/pages/AddUrlPage.vue ================================================ ================================================ FILE: manager/frontend/src/pages/LoginPage.vue ================================================ ================================================ FILE: manager/frontend/src/pages/MainPage.vue ================================================ ================================================ FILE: manager/frontend/src/router/index.js ================================================ import Vue from 'vue' import Router from 'vue-router' import LoginPage from '@/pages/LoginPage' import MainPage from '@/pages/MainPage' import AddUrlPage from '@/pages/AddUrlPage' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'MainPage', component: MainPage }, { path: '/login', name: 'LoginPage', component: LoginPage }, { path: '/add_url', name: 'AddUrlPage', component: AddUrlPage } ] }) ================================================ FILE: manager/frontend/static/.gitkeep ================================================ ================================================ FILE: page/backend/.gitignore ================================================ dist/ moyu-page ================================================ FILE: page/backend/controller/system.go ================================================ package controller import ( "time" "github.com/gofiber/fiber/v2" "github.com/gofiber/websocket/v2" "github.com/liuquanhao/moyu/service" ) func GetSysInfo(c *fiber.Ctx) error { return c.JSON(service.GetSystemInfo()) } func PushSysStatus(c *websocket.Conn) { for { c.WriteJSON(service.GetSystemStatus()) time.Sleep(1 * time.Second) } } func GetPageData(c *fiber.Ctx) error { return c.JSON(service.GetPageData()) } ================================================ FILE: page/backend/go.mod ================================================ module github.com/liuquanhao/moyu go 1.19 require ( github.com/bytedance/sonic v1.5.0 github.com/gofiber/fiber/v2 v2.40.1 github.com/gofiber/websocket/v2 v2.1.2 github.com/shirou/gopsutil/v3 v3.22.10 ) require ( github.com/andybalholm/brotli v1.0.4 // indirect github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06 // indirect github.com/fasthttp/websocket v1.5.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/klauspost/compress v1.15.9 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.16 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/savsgio/gotils v0.0.0-20211223103454-d0aaa54c5899 // indirect github.com/tklauser/go-sysconf v0.3.11 // indirect github.com/tklauser/numcpus v0.6.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.41.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect golang.org/x/sys v0.2.0 // indirect ) ================================================ FILE: page/backend/go.sum ================================================ github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/bytedance/sonic v1.5.0 h1:XWdTi8bwPgxIML+eNV1IwNuTROK6EUrQ65ey8yd6fRQ= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06 h1:1sDoSuDPWzhkdzNVxCxtIaKiAe96ESVPv8coGwc1gZ4= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fasthttp/websocket v1.5.0 h1:B4zbe3xXyvIdnqjOZrafVFklCUq5ZLo/TqCt5JA1wLE= github.com/fasthttp/websocket v1.5.0/go.mod h1:n0BlOQvJdPbTuBkZT0O5+jk/sp/1/VCzquR1BehI2F4= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/gofiber/fiber/v2 v2.40.1 h1:pc7n9VVpGIqNsvg9IPLQhyFEMJL8gCs1kneH5D1pIl4= github.com/gofiber/fiber/v2 v2.40.1/go.mod h1:Gko04sLksnHbzLSRBFWPFdzM9Ws9pRxvvIaohJK1dsk= github.com/gofiber/websocket/v2 v2.1.2 h1:EulKyLB/fJgui5+6c8irwEnYQ9FRsrLZfkrq9OfTDGc= github.com/gofiber/websocket/v2 v2.1.2/go.mod h1:S+sKWo0xeC7Wnz5h4/8f6D/NxsrLFIdWDYB3SyVO9pE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/klauspost/compress v1.14.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/savsgio/gotils v0.0.0-20211223103454-d0aaa54c5899 h1:Orn7s+r1raRTBKLSc9DmbktTT04sL+vkzsbRD2Q8rOI= github.com/savsgio/gotils v0.0.0-20211223103454-d0aaa54c5899/go.mod h1:oejLrk1Y/5zOF+c/aHtXqn3TFlzzbAgPWg8zBiAHDas= github.com/shirou/gopsutil/v3 v3.22.10 h1:4KMHdfBRYXGF9skjDWiL4RA2N+E8dRdodU/bOZpPoVg= github.com/shirou/gopsutil/v3 v3.22.10/go.mod h1:QNza6r4YQoydyCfo6rH0blGfKahgibh4dQmV5xdFkQk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.33.0/go.mod h1:KJRK/MXx0J+yd0c5hlR+s1tIHD72sniU8ZJjl97LIw4= github.com/valyala/fasthttp v1.41.0 h1:zeR0Z1my1wDHTRiamBCXVglQdbUwgb9uWG3k1HQz6jY= github.com/valyala/fasthttp v1.41.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= ================================================ FILE: page/backend/main.go ================================================ package main import ( "embed" "io/fs" "log" "net/http" "os" "github.com/bytedance/sonic" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" "github.com/gofiber/fiber/v2/middleware/filesystem" "github.com/gofiber/websocket/v2" "github.com/liuquanhao/moyu/controller" "github.com/liuquanhao/moyu/middleware" ) //go:embed dist var frontend embed.FS func main() { app := fiber.New(fiber.Config{ JSONEncoder: sonic.Marshal, JSONDecoder: sonic.Unmarshal, }) app.Use(cors.New()) base := app.Group(os.Getenv("BASEURL")) ws := base.Group("/ws") ws.Use("/*", middleware.UpgradeOptions) ws.Get("/sys_status", websocket.New(controller.PushSysStatus)) api := base.Group("/api") api.Get("/sys_info", controller.GetSysInfo) api.Get("/page_data", controller.GetPageData) stripped, err := fs.Sub(frontend, "dist") if err != nil { log.Fatal(err) } base.Use("/", filesystem.New(filesystem.Config{ Root: http.FS(stripped), Browse: true, })) host := os.Getenv("HOST") port := os.Getenv("PORT") log.Println(host + ":" + port) log.Fatal(app.Listen(host + ":" + port)) } ================================================ FILE: page/backend/middleware/ws_options.go ================================================ package middleware import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/websocket/v2" ) func UpgradeOptions(c *fiber.Ctx) error { if websocket.IsWebSocketUpgrade(c) { return c.Next() } return fiber.ErrUpgradeRequired } ================================================ FILE: page/backend/service/cpu.go ================================================ package service import "github.com/shirou/gopsutil/v3/cpu" type Core struct { CPU int32 `json:"cpu"` CoreID string `json:"core_id"` ModelName string `json:"model"` Mhz float64 `json:"mhz"` Flags []string `json:"flags"` } type CPUInfo struct { Count int `json:"count"` Cores []*Core `json:"cores"` } func (cpuInfo *CPUInfo) AppendCore(core *Core) { cpuInfo.Cores = append(cpuInfo.Cores, core) } func GetCPUInfo() *CPUInfo { cpuInfoStats, _ := cpu.Info() cpuInfo := new(CPUInfo) cpuInfo.Count, _ = cpu.Counts(true) for _, infoStat := range cpuInfoStats { core := &Core{ CPU: infoStat.CPU, CoreID: infoStat.CoreID, ModelName: infoStat.ModelName, Mhz: infoStat.Mhz, Flags: infoStat.Flags, } cpuInfo.AppendCore(core) } return cpuInfo } ================================================ FILE: page/backend/service/disk.go ================================================ package service import ( "strings" "github.com/shirou/gopsutil/v3/disk" ) type Partition struct { Device string `json:"device"` MountPoint string `json:"mount_point"` Size uint64 `json:"size"` UsedPercent float64 `json:"used_percent"` } type DiskInfo struct { Partitions []*Partition `json:"partitions"` } func (diskInfo *DiskInfo) AppendPartition(partition *Partition) { diskInfo.Partitions = append(diskInfo.Partitions, partition) } func GetDiskInfo() *DiskInfo { diskInfo := new(DiskInfo) partitionStats, _ := disk.Partitions(false) for _, partitionStat := range partitionStats { if strings.HasPrefix(partitionStat.Mountpoint, "/snap") || strings.HasPrefix(partitionStat.Mountpoint, "/loop") { continue } usageStat, err := disk.Usage(partitionStat.Mountpoint) if err != nil { continue } partition := &Partition{ Device: partitionStat.Device, MountPoint: partitionStat.Mountpoint, Size: usageStat.Total, UsedPercent: usageStat.UsedPercent, } diskInfo.AppendPartition(partition) } return diskInfo } ================================================ FILE: page/backend/service/host.go ================================================ package service import ( "github.com/shirou/gopsutil/v3/host" ) type HostInfo struct { Hostname string `json:"hostname"` Distribution string `json:"distribution"` Arch string `json:"arch"` Kernel string `json:"kernel"` VirtualPlatform string `json:"virtual_platform"` Uptime uint64 `json:"uptime"` } func GetHostInfo() *HostInfo { hostInfo, _ := host.Info() return &HostInfo{ Hostname: hostInfo.Hostname, Distribution: hostInfo.Platform + " " + hostInfo.PlatformVersion, Arch: hostInfo.KernelArch, Kernel: hostInfo.KernelVersion, VirtualPlatform: hostInfo.VirtualizationSystem, Uptime: hostInfo.Uptime, } } func GetUptime() uint64 { hostInfo, _ := host.Info() return hostInfo.Uptime } ================================================ FILE: page/backend/service/memory.go ================================================ package service import ( "github.com/shirou/gopsutil/v3/mem" ) type MemoryInfo struct { Memory uint64 `json:"memory"` Swap uint64 `json:"swap"` } type MemoryStatus struct { MemoryPercent float64 `json:"memory_percent"` SwapPercent float64 `json:"swap_percent"` } func GetMemoryInfo() *MemoryInfo { vmStat, _ := mem.VirtualMemory() swapStat, _ := mem.SwapMemory() return &MemoryInfo{ Memory: vmStat.Total, Swap: swapStat.Total, } } func GetMemoryStatus() *MemoryStatus { memInfo, _ := mem.VirtualMemory() swapInfo, _ := mem.SwapMemory() return &MemoryStatus{ MemoryPercent: memInfo.UsedPercent, SwapPercent: swapInfo.UsedPercent, } } ================================================ FILE: page/backend/service/network.go ================================================ package service import ( "regexp" "github.com/shirou/gopsutil/v3/net" ) type Ifce struct { Name string `json:"name"` ByteSend uint64 `json:"send_byte"` ByteRecv uint64 `json:"recv_byte"` } type NetworkInfo struct { Ifces []*Ifce `json:"ifces"` } func (networkInfo *NetworkInfo) AppendIfce(ifce *Ifce) { networkInfo.Ifces = append(networkInfo.Ifces, ifce) } func GetNetworkInfo() *NetworkInfo { networkInfo := new(NetworkInfo) ioStats, _ := net.IOCounters(true) r, _ := regexp.Compile("^(eth|enp).*") for _, ioStat := range ioStats { if !r.MatchString(ioStat.Name) { continue } ifce := &Ifce{ Name: ioStat.Name, ByteSend: ioStat.BytesSent, ByteRecv: ioStat.BytesRecv, } networkInfo.AppendIfce(ifce) } return networkInfo } ================================================ FILE: page/backend/service/page_data.go ================================================ package service import ( "time" "github.com/shirou/gopsutil/v3/cpu" "github.com/shirou/gopsutil/v3/disk" "github.com/shirou/gopsutil/v3/host" "github.com/shirou/gopsutil/v3/mem" "github.com/shirou/gopsutil/v3/net" ) type PageData struct { Uptime uint64 `json:"uptime"` CPUPercent float64 `json:"cpu_percent"` MemoryPercent float64 `json:"memory_percent"` DiskPercent float64 `json:"disk_percent"` NetSendTotal uint64 `json:"net_send"` NetRecvTotal uint64 `json:"net_recv"` Timestamp int64 `json:"timestamp"` } func GetPageData() *PageData { hostInfo, _ := host.Info() cpuPercent, _ := cpu.Percent(time.Duration(1)*time.Second, false) memInfo, _ := mem.VirtualMemory() diskUsage, _ := disk.Usage("/") ioStats, _ := net.IOCounters(false) return &PageData{ Uptime: hostInfo.Uptime, CPUPercent: cpuPercent[0], MemoryPercent: memInfo.UsedPercent, DiskPercent: diskUsage.UsedPercent, NetSendTotal: ioStats[0].BytesSent, NetRecvTotal: ioStats[0].BytesRecv, Timestamp: time.Now().Unix(), } } ================================================ FILE: page/backend/service/system.go ================================================ package service import ( "time" "github.com/shirou/gopsutil/v3/cpu" "github.com/shirou/gopsutil/v3/load" ) type SystemInfo struct { HostInfo *HostInfo `json:"host_info"` CPUInfo *CPUInfo `json:"cpu_info"` MemoryInfo *MemoryInfo `json:"memory_info"` NetworkInfo *NetworkInfo `json:"network_info"` DiskInfo *DiskInfo `json:"disk_info"` SystemStatus *SystemStatus `json:"system_status"` } type SystemStatus struct { CPUPercent []float64 `json:"cpu_percent"` LoadAvg *load.AvgStat `json:"load_avg"` MemoryStatus *MemoryStatus `json:"memory_status"` DiskInfo *DiskInfo `json:"disk_info"` NetworkInfo *NetworkInfo `json:"network_info"` Uptime uint64 `json:"uptime"` Timestamp int64 `json:"timestamp"` } func GetSystemInfo() *SystemInfo { return &SystemInfo{ HostInfo: GetHostInfo(), CPUInfo: GetCPUInfo(), MemoryInfo: GetMemoryInfo(), DiskInfo: GetDiskInfo(), NetworkInfo: GetNetworkInfo(), SystemStatus: GetSystemStatus(), } } func GetSystemStatus() *SystemStatus { cpuPercent, _ := cpu.Percent(time.Duration(1)*time.Second, true) loadAvg, _ := load.Avg() return &SystemStatus{ CPUPercent: cpuPercent, LoadAvg: loadAvg, MemoryStatus: GetMemoryStatus(), DiskInfo: GetDiskInfo(), NetworkInfo: GetNetworkInfo(), Uptime: GetUptime(), Timestamp: time.Now().Unix(), } } ================================================ FILE: page/frontend/.babelrc ================================================ { "presets": [["es2015", { "modules": false }]], "plugins": [ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ] } ================================================ FILE: page/frontend/.editorconfig ================================================ root = true [*] charset = utf-8 indent_style = space indent_size = 2 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true ================================================ FILE: page/frontend/.gitignore ================================================ .DS_Store node_modules/ /dist/ npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln ================================================ FILE: page/frontend/.postcssrc.js ================================================ // https://github.com/michael-ciniawsky/postcss-load-config module.exports = { "plugins": { "postcss-import": {}, "postcss-url": {}, // to edit target browsers: use "browserslist" field in package.json "autoprefixer": {} } } ================================================ FILE: page/frontend/README.md ================================================ # frontend > A Vue.js project ## Build Setup ``` bash # install dependencies npm install # serve with hot reload at localhost:8080 npm run dev # build for production with minification npm run build # build for production and view the bundle analyzer report npm run build --report ``` For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). ================================================ FILE: page/frontend/build/build.js ================================================ 'use strict' require('./check-versions')() process.env.NODE_ENV = 'production' const ora = require('ora') const rm = require('rimraf') const path = require('path') const chalk = require('chalk') const webpack = require('webpack') const config = require('../config') const webpackConfig = require('./webpack.prod.conf') const spinner = ora('building for production...') spinner.start() rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { if (err) throw err webpack(webpackConfig, (err, stats) => { spinner.stop() if (err) throw err process.stdout.write(stats.toString({ colors: true, modules: false, children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. chunks: false, chunkModules: false }) + '\n\n') if (stats.hasErrors()) { console.log(chalk.red(' Build failed with errors.\n')) process.exit(1) } console.log(chalk.cyan(' Build complete.\n')) console.log(chalk.yellow( ' Tip: built files are meant to be served over an HTTP server.\n' + ' Opening index.html over file:// won\'t work.\n' )) }) }) ================================================ FILE: page/frontend/build/check-versions.js ================================================ 'use strict' const chalk = require('chalk') const semver = require('semver') const packageConfig = require('../package.json') const shell = require('shelljs') function exec (cmd) { return require('child_process').execSync(cmd).toString().trim() } const versionRequirements = [ { name: 'node', currentVersion: semver.clean(process.version), versionRequirement: packageConfig.engines.node } ] if (shell.which('npm')) { versionRequirements.push({ name: 'npm', currentVersion: exec('npm --version'), versionRequirement: packageConfig.engines.npm }) } module.exports = function () { const warnings = [] for (let i = 0; i < versionRequirements.length; i++) { const mod = versionRequirements[i] if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { warnings.push(mod.name + ': ' + chalk.red(mod.currentVersion) + ' should be ' + chalk.green(mod.versionRequirement) ) } } if (warnings.length) { console.log('') console.log(chalk.yellow('To use this template, you must update following to modules:')) console.log() for (let i = 0; i < warnings.length; i++) { const warning = warnings[i] console.log(' ' + warning) } console.log() process.exit(1) } } ================================================ FILE: page/frontend/build/utils.js ================================================ 'use strict' const path = require('path') const config = require('../config') const ExtractTextPlugin = require('extract-text-webpack-plugin') const packageConfig = require('../package.json') exports.assetsPath = function (_path) { const assetsSubDirectory = process.env.NODE_ENV === 'production' ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory return path.posix.join(assetsSubDirectory, _path) } exports.cssLoaders = function (options) { options = options || {} const cssLoader = { loader: 'css-loader', options: { sourceMap: options.sourceMap } } const postcssLoader = { loader: 'postcss-loader', options: { sourceMap: options.sourceMap } } // generate loader string to be used with extract text plugin function generateLoaders (loader, loaderOptions) { const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] if (loader) { loaders.push({ loader: loader + '-loader', options: Object.assign({}, loaderOptions, { sourceMap: options.sourceMap }) }) } // Extract CSS when that option is specified // (which is the case during production build) if (options.extract) { return ExtractTextPlugin.extract({ use: loaders, fallback: 'vue-style-loader' }) } else { return ['vue-style-loader'].concat(loaders) } } // https://vue-loader.vuejs.org/en/configurations/extract-css.html return { css: generateLoaders(), postcss: generateLoaders(), less: generateLoaders('less'), sass: generateLoaders('sass', { indentedSyntax: true }), scss: generateLoaders('sass'), stylus: generateLoaders('stylus'), styl: generateLoaders('stylus') } } // Generate loaders for standalone style files (outside of .vue) exports.styleLoaders = function (options) { const output = [] const loaders = exports.cssLoaders(options) for (const extension in loaders) { const loader = loaders[extension] output.push({ test: new RegExp('\\.' + extension + '$'), use: loader }) } return output } exports.createNotifierCallback = () => { const notifier = require('node-notifier') return (severity, errors) => { if (severity !== 'error') return const error = errors[0] const filename = error.file && error.file.split('!').pop() notifier.notify({ title: packageConfig.name, message: severity + ': ' + error.name, subtitle: filename || '', icon: path.join(__dirname, 'logo.png') }) } } ================================================ FILE: page/frontend/build/vue-loader.conf.js ================================================ 'use strict' const utils = require('./utils') const config = require('../config') const isProduction = process.env.NODE_ENV === 'production' const sourceMapEnabled = isProduction ? config.build.productionSourceMap : config.dev.cssSourceMap module.exports = { loaders: utils.cssLoaders({ sourceMap: sourceMapEnabled, extract: isProduction }), cssSourceMap: sourceMapEnabled, cacheBusting: config.dev.cacheBusting, transformToRequire: { video: ['src', 'poster'], source: 'src', img: 'src', image: 'xlink:href' } } ================================================ FILE: page/frontend/build/webpack.base.conf.js ================================================ 'use strict' const path = require('path') const utils = require('./utils') const config = require('../config') const vueLoaderConfig = require('./vue-loader.conf') function resolve (dir) { return path.join(__dirname, '..', dir) } module.exports = { context: path.resolve(__dirname, '../'), entry: { app: './src/main.js' }, output: { path: config.build.assetsRoot, filename: '[name].js', publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath }, resolve: { extensions: ['.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), } }, module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: vueLoaderConfig }, { test: /\.js$/, loader: 'babel-loader', include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: utils.assetsPath('img/[name].[hash:7].[ext]') } }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: utils.assetsPath('media/[name].[hash:7].[ext]') } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: utils.assetsPath('fonts/[name].[hash:7].[ext]') } } ] }, node: { // prevent webpack from injecting useless setImmediate polyfill because Vue // source contains it (although only uses it if it's native). setImmediate: false, // prevent webpack from injecting mocks to Node native modules // that does not make sense for the client dgram: 'empty', fs: 'empty', net: 'empty', tls: 'empty', child_process: 'empty' } } ================================================ FILE: page/frontend/build/webpack.dev.conf.js ================================================ 'use strict' const utils = require('./utils') const webpack = require('webpack') const config = require('../config') const merge = require('webpack-merge') const path = require('path') const baseWebpackConfig = require('./webpack.base.conf') const CopyWebpackPlugin = require('copy-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') const portfinder = require('portfinder') const HOST = process.env.HOST const PORT = process.env.PORT && Number(process.env.PORT) const devWebpackConfig = merge(baseWebpackConfig, { module: { rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) }, // cheap-module-eval-source-map is faster for development devtool: config.dev.devtool, // these devServer options should be customized in /config/index.js devServer: { clientLogLevel: 'warning', historyApiFallback: { rewrites: [ { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, ], }, hot: true, contentBase: false, // since we use CopyWebpackPlugin. compress: true, host: HOST || config.dev.host, port: PORT || config.dev.port, open: config.dev.autoOpenBrowser, overlay: config.dev.errorOverlay ? { warnings: false, errors: true } : false, publicPath: config.dev.assetsPublicPath, proxy: config.dev.proxyTable, quiet: true, // necessary for FriendlyErrorsPlugin watchOptions: { poll: config.dev.poll, } }, plugins: [ new webpack.DefinePlugin({ 'process.env': require('../config/dev.env') }), new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. new webpack.NoEmitOnErrorsPlugin(), // https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.html', inject: true }), // copy custom static assets new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../static'), to: config.dev.assetsSubDirectory, ignore: ['.*'] } ]) ] }) module.exports = new Promise((resolve, reject) => { portfinder.basePort = process.env.PORT || config.dev.port portfinder.getPort((err, port) => { if (err) { reject(err) } else { // publish the new Port, necessary for e2e tests process.env.PORT = port // add port to devServer config devWebpackConfig.devServer.port = port // Add FriendlyErrorsPlugin devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ compilationSuccessInfo: { messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], }, onErrors: config.dev.notifyOnErrors ? utils.createNotifierCallback() : undefined })) resolve(devWebpackConfig) } }) }) ================================================ FILE: page/frontend/build/webpack.prod.conf.js ================================================ 'use strict' const path = require('path') const utils = require('./utils') const webpack = require('webpack') const config = require('../config') const merge = require('webpack-merge') const baseWebpackConfig = require('./webpack.base.conf') const CopyWebpackPlugin = require('copy-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const ExtractTextPlugin = require('extract-text-webpack-plugin') const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') const UglifyJsPlugin = require('uglifyjs-webpack-plugin') const env = require('../config/prod.env') const webpackConfig = merge(baseWebpackConfig, { module: { rules: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true, usePostCSS: true }) }, devtool: config.build.productionSourceMap ? config.build.devtool : false, output: { path: config.build.assetsRoot, filename: utils.assetsPath('js/[name].[chunkhash].js'), chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') }, plugins: [ // http://vuejs.github.io/vue-loader/en/workflow/production.html new webpack.DefinePlugin({ 'process.env': env }), new UglifyJsPlugin({ uglifyOptions: { compress: { warnings: false } }, sourceMap: config.build.productionSourceMap, parallel: true }), // extract css into its own file new ExtractTextPlugin({ filename: utils.assetsPath('css/[name].[contenthash].css'), // Setting the following option to `false` will not extract CSS from codesplit chunks. // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 allChunks: true, }), // Compress extracted CSS. We are using this plugin so that possible // duplicated CSS from different components can be deduped. new OptimizeCSSPlugin({ cssProcessorOptions: config.build.productionSourceMap ? { safe: true, map: { inline: false } } : { safe: true } }), // generate dist index.html with correct asset hash for caching. // you can customize output by editing /index.html // see https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ filename: config.build.index, template: 'index.html', inject: true, minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true // more options: // https://github.com/kangax/html-minifier#options-quick-reference }, // necessary to consistently work with multiple chunks via CommonsChunkPlugin chunksSortMode: 'dependency' }), // keep module.id stable when vendor modules does not change new webpack.HashedModuleIdsPlugin(), // enable scope hoisting new webpack.optimize.ModuleConcatenationPlugin(), // split vendor js into its own file new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks (module) { // any required modules inside node_modules are extracted to vendor return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 ) } }), // extract webpack runtime and module manifest to its own file in order to // prevent vendor hash from being updated whenever app bundle is updated new webpack.optimize.CommonsChunkPlugin({ name: 'manifest', minChunks: Infinity }), // This instance extracts shared chunks from code splitted chunks and bundles them // in a separate chunk, similar to the vendor chunk // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk new webpack.optimize.CommonsChunkPlugin({ name: 'app', async: 'vendor-async', children: true, minChunks: 3 }), // copy custom static assets new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../static'), to: config.build.assetsSubDirectory, ignore: ['.*'] } ]) ] }) if (config.build.productionGzip) { const CompressionWebpackPlugin = require('compression-webpack-plugin') webpackConfig.plugins.push( new CompressionWebpackPlugin({ asset: '[path].gz[query]', algorithm: 'gzip', test: new RegExp( '\\.(' + config.build.productionGzipExtensions.join('|') + ')$' ), threshold: 10240, minRatio: 0.8 }) ) } if (config.build.bundleAnalyzerReport) { const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin webpackConfig.plugins.push(new BundleAnalyzerPlugin()) } module.exports = webpackConfig ================================================ FILE: page/frontend/config/dev.env.js ================================================ 'use strict' const merge = require('webpack-merge') const prodEnv = require('./prod.env') module.exports = merge(prodEnv, { NODE_ENV: '"development"', }) ================================================ FILE: page/frontend/config/index.js ================================================ 'use strict' // Template version: 1.3.1 // see http://vuejs-templates.github.io/webpack for documentation. const path = require('path') module.exports = { dev: { // Paths assetsSubDirectory: 'static', assetsPublicPath: './', proxyTable: {}, // Various Dev Server settings host: 'localhost', // can be overwritten by process.env.HOST port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined autoOpenBrowser: false, errorOverlay: true, notifyOnErrors: true, poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- /** * Source Maps */ // https://webpack.js.org/configuration/devtool/#development devtool: 'cheap-module-eval-source-map', // If you have problems debugging vue-files in devtools, // set this to false - it *may* help // https://vue-loader.vuejs.org/en/options.html#cachebusting cacheBusting: true, cssSourceMap: true }, build: { // Template for index.html index: path.resolve(__dirname, '../dist/index.html'), // Paths assetsRoot: path.resolve(__dirname, '../dist'), assetsSubDirectory: 'static', assetsPublicPath: './', /** * Source Maps */ productionSourceMap: true, // https://webpack.js.org/configuration/devtool/#production devtool: '#source-map', // Gzip off by default as many popular static hosts such as // Surge or Netlify already gzip all static assets for you. // Before setting to `true`, make sure to: // npm install --save-dev compression-webpack-plugin productionGzip: false, productionGzipExtensions: ['js', 'css'], // Run the build command with an extra argument to // View the bundle analyzer report after build finishes: // `npm run build --report` // Set to `true` or `false` to always turn it on or off bundleAnalyzerReport: process.env.npm_config_report } } ================================================ FILE: page/frontend/config/prod.env.js ================================================ 'use strict' module.exports = { NODE_ENV: '"production"' } ================================================ FILE: page/frontend/index.html ================================================ Moyu Page
================================================ FILE: page/frontend/package.json ================================================ { "name": "frontend", "version": "1.0.0", "description": "A Vue.js project", "author": "liuxu", "private": true, "scripts": { "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "start": "npm run dev", "build": "node build/build.js", "postbuild": "rm -rf ../backend/dist && mv dist ../backend/" }, "dependencies": { "axios": "^1.1.3", "element-ui": "^2.15.12", "modern-normalize": "^1.1.0", "nes.css": "^2.3.0", "vue": "^2.5.2", "vue-router": "^3.0.1" }, "devDependencies": { "autoprefixer": "^7.1.2", "babel-core": "^6.22.1", "babel-helper-vue-jsx-merge-props": "^2.0.3", "babel-loader": "^7.1.1", "babel-plugin-component": "^1.1.1", "babel-plugin-syntax-jsx": "^6.18.0", "babel-plugin-transform-runtime": "^6.22.0", "babel-plugin-transform-vue-jsx": "^3.5.0", "babel-preset-env": "^1.3.2", "babel-preset-es2015": "^6.24.1", "babel-preset-stage-2": "^6.22.0", "chalk": "^2.0.1", "copy-webpack-plugin": "^4.0.1", "extract-text-webpack-plugin": "^3.0.0", "file-loader": "^1.1.4", "friendly-errors-webpack-plugin": "^1.6.1", "html-webpack-plugin": "^2.30.1", "node-notifier": "^5.1.2", "optimize-css-assets-webpack-plugin": "^3.2.0", "ora": "^1.2.0", "portfinder": "^1.0.13", "postcss-import": "^11.0.0", "postcss-loader": "^2.0.8", "postcss-url": "^7.2.1", "rimraf": "^2.6.0", "semver": "^5.3.0", "shelljs": "^0.7.6", "uglifyjs-webpack-plugin": "^1.1.1", "url-loader": "^0.5.8", "vue-loader": "^13.3.0", "vue-template-compiler": "^2.5.2", "webpack": "^3.6.0", "webpack-bundle-analyzer": "^2.9.0", "webpack-dev-server": "^2.9.1", "webpack-merge": "^4.1.0" }, "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" }, "browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ] } ================================================ FILE: page/frontend/src/App.vue ================================================ ================================================ FILE: page/frontend/src/common/filters.js ================================================ /** * 转换成可读字节 */ export function humanByte(size) { if (size < 1024) { return size + " Byte" } else if (size < 1024 * 1024) { return Math.trunc(size / 1024) + " KB" } else if (size < 1024 * 1024 * 1024) { return (size / 1024 / 1024).toFixed(2) + " MB" } else { return (size / 1024 / 1024 / 1024).toFixed(2) + " GB" } } /** * 将秒数转换成可读日时分秒 * 例:3600秒->1小时 * 例:183秒->3分3秒 */ export function humanSec(sec) { if (sec < 60) { return Math.trunc(sec) + " seconds"; } else if (sec < 3600) { var sec = sec % 60 var min = Math.trunc(sec / 60); return min + " minutes " + sec + " seconds" } else if (sec < 86400) { var min = Math.trunc(sec % 3600 / 60); var hour = Math.trunc(sec / 3600); return hour + " hours " + min + " minutes" } else { var hour = Math.trunc(sec % 86400 / 3600); var day = Math.trunc(sec / 86400); return day + " days " + hour + " hours" } } /** * 转换为num位百分比 */ export function humanPerc(float, num) { if (!float || !num) { return 0 } return float.toFixed(num) } ================================================ FILE: page/frontend/src/components/Cpu.vue ================================================ ================================================ FILE: page/frontend/src/components/Disk.vue ================================================ ================================================ FILE: page/frontend/src/components/Host.vue ================================================ ================================================ FILE: page/frontend/src/components/Mem.vue ================================================ ================================================ FILE: page/frontend/src/layouts/Main.vue ================================================ ================================================ FILE: page/frontend/src/main.js ================================================ // The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import router from './router' import "modern-normalize/modern-normalize.css" import { Row, Col, Container, Header, Main, Footer, } from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import axios from 'axios' import "nes.css/css/nes.min.css" Vue.config.productionTip = false Vue.prototype.axios = axios Vue.component(Row.name, Row) Vue.component(Col.name, Col) Vue.component(Container.name, Container) Vue.component(Header.name, Header) Vue.component(Main.name, Main) Vue.component(Footer.name, Footer) /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '' }) ================================================ FILE: page/frontend/src/pages/SystemPage.vue ================================================ ================================================ FILE: page/frontend/src/router/index.js ================================================ import Vue from 'vue' import Router from 'vue-router' import SystemPage from '@/pages/SystemPage' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'SystemPage', component: SystemPage } ] }) ================================================ FILE: page/frontend/static/.gitkeep ================================================