Forráskód Böngészése

1、feat: 使用go代理实现桌面端应用

tuon 1 éve
szülő
commit
4246204624

+ 5 - 1
.gitignore

@@ -34,4 +34,8 @@ server/data/files
 nginx-ui.tar.gz
 
 server/conf/app.local.conf
-server/data/sessions
+server/data/sessions
+
+/data/sessions
+/conf/app.local.conf
+/build/

+ 4 - 1
README.md

@@ -9,5 +9,8 @@ wails dev
 
 ## 打包
 ```shell
-wails build
+## 生产版本
+wails build -webview2=embed
+## 带debug
+wails build -webview2=embed -debug
 ```

BIN
build/bin/nginx-ui.exe


+ 1 - 1
conf/app.conf

@@ -1,5 +1,5 @@
 appname = server
-httpport = 8080
+httpport = 38080
 runmode = dev
 copyrequestbody = true
 

BIN
data/db/sqlite.db


+ 59 - 17
desktop/handler.go

@@ -2,22 +2,69 @@ package desktop
 
 import (
 	"context"
-	"github.com/astaxie/beego/logs"
+	"errors"
+	"fmt"
+	_ "io/ioutil"
 	"net/http"
+	"net/http/httputil"
+	"nginx-ui/server/config"
 	"strings"
 )
 
-const prefix = "/api/nginx-ui/api"
-
 type ApiHandler struct {
 	http.Handler
-	ctx context.Context
-	api *Api
+	ctx     context.Context
+	api     *Api
+	proxy   *httputil.ReverseProxy
+	handler http.Handler
+}
+
+func rewriteRequestURL(req *http.Request) {
+	logger.Printf("req.URL: %s, method: %s", req.URL, req.Method)
+
+	req.URL.Scheme = "http"
+	req.URL.Host = fmt.Sprintf("localhost:%d", config.Config.Port)
+	path := req.URL.Path
+	path = strings.TrimPrefix(path, "/api")
+	req.URL.Path = path
+	logger.Printf("Loading '%s'", req.URL)
 }
 
-func NewApiHandler() *ApiHandler {
+func NewApiHandler(baseHandler http.Handler) *ApiHandler {
+
+	var proxy *httputil.ReverseProxy
+	errSkipProxy := fmt.Errorf("skip proxying")
+	proxy = httputil.NewSingleHostReverseProxy(nil)
+	proxy.ModifyResponse = func(res *http.Response) error {
+		if baseHandler == nil {
+			return nil
+		}
+
+		if res.StatusCode == http.StatusSwitchingProtocols {
+			return nil
+		}
+
+		if res.StatusCode == http.StatusNotFound || res.StatusCode == http.StatusMethodNotAllowed {
+			return errSkipProxy
+		}
+
+		return nil
+	}
+
+	proxy.ErrorHandler = func(rw http.ResponseWriter, r *http.Request, err error) {
+		if baseHandler != nil && errors.Is(err, errSkipProxy) {
+			logger.Printf("'%s' returned not found, using AssetHandler", r.URL)
+			baseHandler.ServeHTTP(rw, r)
+		} else {
+			logger.Printf("Proxy error: %v", err)
+			rw.WriteHeader(http.StatusBadGateway)
+		}
+	}
+
 	return &ApiHandler{
-		api: NewApi(),
+		api:     NewApi(),
+		proxy:   proxy,
+		handler: baseHandler,
 	}
 }
 
@@ -25,18 +72,13 @@ func (h *ApiHandler) Startup(ctx context.Context) {
 	ApiSession.ctx = ctx
 	h.ctx = ctx
 	h.api.ctx = ctx
-
 }
 
 // 这也是一种方法,不过感觉比较复杂,需要自己处理请求参数之类的
-func (h *ApiHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) {
-	requestUrl := strings.TrimPrefix(req.URL.Path, prefix)
-	method := req.Method
-	switch method {
-
-	case http.MethodGet:
-
+func (h *ApiHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
+	h.proxy.Director = func(request *http.Request) {
+		rewriteRequestURL(request)
+		request.Body = req.Body
 	}
-	logs.Info("ServerHTTP: %s,%s", method, requestUrl)
-	res.Write([]byte("{}"))
+	h.proxy.ServeHTTP(rw, req)
 }

+ 1 - 0
frontend/.env.desktop

@@ -0,0 +1 @@
+VITE_BASE_API=/

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
frontend/dist/assets/index-d94fb700.js


+ 1 - 1
frontend/dist/index.html

@@ -4,7 +4,7 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>NginxUI</title>
     <script type="application/javascript" src="./config.js"></script>
-    <script crossorigin="">import('/assets/index-264e9fe8.js').finally(() => {
+    <script crossorigin="">import('/assets/index-d94fb700.js').finally(() => {
             
     const qiankunLifeCycle = window.moudleQiankunAppLifeCycles && window.moudleQiankunAppLifeCycles['nginx-ui'];
     if (qiankunLifeCycle) {

+ 2 - 1
frontend/package.json

@@ -10,8 +10,9 @@
   "type": "module",
   "scripts": {
     "dev": "vite",
+    "dev:desktop": "vite --mode=desktop",
     "build": "tsc && vite build --base=/nginx-ui/",
-    "desktop": "tsc && vite build --base=/",
+    "build:desktop": "tsc && vite build --base=/ --mode=desktop",
     "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
     "preview": "vite preview"
   },

+ 1 - 1
frontend/package.json.md5

@@ -1 +1 @@
-87f56136b1efe24b64fc6a9ab5e77599
+139e2a18d41f6061014aff378b6e48f2

+ 38 - 38
frontend/src/api/desktop.api.ts

@@ -1,38 +1,38 @@
-import {AxiosInstance, AxiosRequestConfig} from 'axios'
-import {isDesktop} from "../config/consts.ts";
-import * as DesktopApi from "../../wailsjs/go/desktop/Api";
-import {store} from "../store";
-import {UserActions} from "../store/slice/user.ts";
-import {desktop} from "../../wailsjs/go/models.ts";
-
-
-const checkResp = (resp: desktop.ApiResp) => {
-    if (resp.data?.code == 401){
-        store.dispatch(UserActions.clearUser())
-    }
-    return resp
-}
-
-// @ts-ignore
-export const checkDesktopApi = (request: AxiosInstance)=>{
-    if (!isDesktop){
-        return
-    }
-    // @ts-ignore
-    request.get = (url: string, config: AxiosRequestConfig<any>)=> {
-        const data = config?.params ? JSON.stringify(config.params) : "{}"
-        return DesktopApi.GetApi(url, data).then(res=>checkResp(res))
-    }
-    // @ts-ignore
-    request.post = (url: string, data?: any, config?: AxiosRequestConfig<any>) =>{
-        return DesktopApi.PostApi(url, data? JSON.stringify(data): "{}").then(res=>checkResp(res))
-    }
-    // @ts-ignore
-    request.delete = (url: string, config?: AxiosRequestConfig<any>) => {
-        return DesktopApi.DeleteApi(url, config?.data?JSON.stringify(config.data):"{}").then(res=>checkResp(res))
-    }
-    // @ts-ignore
-    request.put = (url: string, data?: any, config?: AxiosRequestConfig<any>) => {
-        return DesktopApi.PutApi(url, data ? JSON.stringify(data): "{}").then(res=>checkResp(res))
-    }
-}
+// import {AxiosInstance, AxiosRequestConfig} from 'axios'
+// import {isDesktop} from "../config/consts.ts";
+// import * as DesktopApi from "../../wailsjs/go/desktop/Api";
+// import {store} from "../store";
+// import {UserActions} from "../store/slice/user.ts";
+// import {desktop} from "../../wailsjs/go/models.ts";
+//
+//
+// const checkResp = (resp: desktop.ApiResp) => {
+//     if (resp.data?.code == 401){
+//         store.dispatch(UserActions.clearUser())
+//     }
+//     return resp
+// }
+//
+// // @ts-ignore
+// export const checkDesktopApi = (request: AxiosInstance)=>{
+//     if (!isDesktop){
+//         return
+//     }
+//     // @ts-ignore
+//     request.get = (url: string, config: AxiosRequestConfig<any>)=> {
+//         const data = config?.params ? JSON.stringify(config.params) : "{}"
+//         return DesktopApi.GetApi(url, data).then(res=>checkResp(res))
+//     }
+//     // @ts-ignore
+//     request.post = (url: string, data?: any, config?: AxiosRequestConfig<any>) =>{
+//         return DesktopApi.PostApi(url, data? JSON.stringify(data): "{}").then(res=>checkResp(res))
+//     }
+//     // @ts-ignore
+//     request.delete = (url: string, config?: AxiosRequestConfig<any>) => {
+//         return DesktopApi.DeleteApi(url, config?.data?JSON.stringify(config.data):"{}").then(res=>checkResp(res))
+//     }
+//     // @ts-ignore
+//     request.put = (url: string, data?: any, config?: AxiosRequestConfig<any>) => {
+//         return DesktopApi.PutApi(url, data ? JSON.stringify(data): "{}").then(res=>checkResp(res))
+//     }
+// }

+ 2 - 2
frontend/src/api/request.ts

@@ -3,7 +3,7 @@ import {BaseResp} from "../models/api.ts";
 import {Message, Notify} from "planning-tools";
 import {store} from "../store";
 import {UserActions} from "../store/slice/user.ts";
-import {checkDesktopApi} from "./desktop.api.ts";
+// import {checkDesktopApi} from "./desktop.api.ts";
 console.log('env', import.meta.env)
 
 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -71,7 +71,7 @@ request.interceptors.response.use((resp: AxiosResponse<BaseResp>)=>{
   return Promise.reject(errData)
 })
 
-checkDesktopApi(request)
+// checkDesktopApi(request)
 
 
 export default request

+ 49 - 36
frontend/vite.config.ts

@@ -1,4 +1,4 @@
-import { defineConfig } from 'vite'
+import { defineConfig, loadEnv } from 'vite'
 import react from '@vitejs/plugin-react-swc'
 import {AntdResolve, createStyleImportPlugin} from "vite-plugin-style-import";
 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -10,43 +10,56 @@ import * as path from 'path'
 
 
 
-// https://vitejs.dev/config/
-export default defineConfig({
-  plugins: [
-    mdx({
-      format: 'detect',
-      include: ["**/*.md",'**/*.mdx']
-    }),
-    react(),
-    createStyleImportPlugin({
-      resolves: [AntdResolve()]
-    }),
-    qiankun('nginx-ui',{
-      useDevMode: true
-    }),
+export default defineConfig(({command, mode})=>{
+  console.log('command,mode', command,mode)
+  const env = loadEnv(mode, process.cwd(),'')
+  console.log('env', env.VITE_BASE_API)
 
-  ],
-  css: {
-    preprocessorOptions: {
-      less: {
-        javascriptEnabled: true
+  return {
+    plugins: [
+      mdx({
+        format: 'detect',
+        include: ["**/*.md",'**/*.mdx']
+      }),
+      react(),
+      createStyleImportPlugin({
+        resolves: [AntdResolve()]
+      }),
+      qiankun('nginx-ui',{
+        useDevMode: true
+      }),
+
+    ],
+    css: {
+      preprocessorOptions: {
+        less: {
+          javascriptEnabled: true
+        }
       }
-    }
-  },
-  resolve:{
-    alias: {
-      '@': path.resolve(__dirname,'./src'),
-      'docs': path.resolve(__dirname,'./docs')
-    }
-  },
-  assetsInclude: ["**/*.md"],
-  server:{
-    proxy: {
-      "/api":{
-        target: 'http://10.10.0.1:8080',
-        // target: 'http://127.0.0.1:8080',
-        rewrite: path => path.replace(/^\/api/,"")
+    },
+    resolve:{
+      alias: {
+        '@': path.resolve(__dirname,'./src'),
+        'docs': path.resolve(__dirname,'./docs')
+      }
+    },
+    assetsInclude: ["**/*.md"],
+    server:{
+      proxy: {
+        ...(mode === 'desktop')? {
+          "/api":{
+            target: 'http://127.0.0.1:38080',
+            rewrite: path => path.replace(/^\/api/,"")
+          }
+        } : {
+          "/api":{
+            target: 'http://10.10.0.1:8080',
+            // target: 'http://127.0.0.1:8080',
+            rewrite: path => path.replace(/^\/api/,"")
+          }
+        }
       }
     }
   }
-})
+
+})

+ 0 - 11
frontend/wailsjs/go/desktop/Api.d.ts

@@ -1,11 +0,0 @@
-// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
-// This file is automatically generated. DO NOT EDIT
-import {desktop} from '../models';
-
-export function DeleteApi(arg1:string,arg2:string):Promise<desktop.ApiResp>;
-
-export function GetApi(arg1:string,arg2:string):Promise<desktop.ApiResp>;
-
-export function PostApi(arg1:string,arg2:string):Promise<desktop.ApiResp>;
-
-export function PutApi(arg1:string,arg2:string):Promise<desktop.ApiResp>;

+ 0 - 19
frontend/wailsjs/go/desktop/Api.js

@@ -1,19 +0,0 @@
-// @ts-check
-// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
-// This file is automatically generated. DO NOT EDIT
-
-export function DeleteApi(arg1, arg2) {
-  return window['go']['desktop']['Api']['DeleteApi'](arg1, arg2);
-}
-
-export function GetApi(arg1, arg2) {
-  return window['go']['desktop']['Api']['GetApi'](arg1, arg2);
-}
-
-export function PostApi(arg1, arg2) {
-  return window['go']['desktop']['Api']['PostApi'](arg1, arg2);
-}
-
-export function PutApi(arg1, arg2) {
-  return window['go']['desktop']['Api']['PutApi'](arg1, arg2);
-}

+ 0 - 56
frontend/wailsjs/go/models.ts

@@ -1,56 +0,0 @@
-export namespace desktop {
-	
-	export class ApiResp {
-	    data?: models.RespData;
-	
-	    static createFrom(source: any = {}) {
-	        return new ApiResp(source);
-	    }
-	
-	    constructor(source: any = {}) {
-	        if ('string' === typeof source) source = JSON.parse(source);
-	        this.data = this.convertValues(source["data"], models.RespData);
-	    }
-	
-		convertValues(a: any, classs: any, asMap: boolean = false): any {
-		    if (!a) {
-		        return a;
-		    }
-		    if (a.slice) {
-		        return (a as any[]).map(elem => this.convertValues(elem, classs));
-		    } else if ("object" === typeof a) {
-		        if (asMap) {
-		            for (const key of Object.keys(a)) {
-		                a[key] = new classs(a[key]);
-		            }
-		            return a;
-		        }
-		        return new classs(a);
-		    }
-		    return a;
-		}
-	}
-
-}
-
-export namespace models {
-	
-	export class RespData {
-	    code: number;
-	    msg: string;
-	    data: any;
-	
-	    static createFrom(source: any = {}) {
-	        return new RespData(source);
-	    }
-	
-	    constructor(source: any = {}) {
-	        if ('string' === typeof source) source = JSON.parse(source);
-	        this.code = source["code"];
-	        this.msg = source["msg"];
-	        this.data = source["data"];
-	    }
-	}
-
-}
-

+ 7 - 6
main.go

@@ -2,6 +2,7 @@ package main
 
 import (
 	"embed"
+	"github.com/astaxie/beego"
 	"github.com/wailsapp/wails/v2"
 	"github.com/wailsapp/wails/v2/pkg/options"
 	"github.com/wailsapp/wails/v2/pkg/options/assetserver"
@@ -17,22 +18,22 @@ func main() {
 
 	api := desktop.NewApi()
 
+	go beego.Run()
+
 	// Create application with options
 	err := wails.Run(&options.App{
 		Title:  "nginx-ui-desktop",
-		Width:  1024,
-		Height: 768,
+		Width:  1200,
+		Height: 800,
 		AssetServer: &assetserver.Options{
 			Assets: assets,
 			//如果你想为你的前端动态加载或生成资产,你可以使用 AssetsHandler 选项 来实现。
 			//AssetsHandler 是一个通用的 http.Handler,对于资产服务器上的任何非 GET 请求以及由于找不到文件而无法从捆绑资产提供服务的 GET 请求,都会调用它。
-			//Handler: desktop.NewApiHandler(),
+			Handler: desktop.NewApiHandler(nil),
 		},
 		BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
 		OnStartup:        api.Startup,
-		Bind: []interface{}{
-			api,
-		},
+		Bind:             []interface{}{},
 	})
 
 	if err != nil {

+ 4 - 1
server/config/config.go

@@ -15,6 +15,7 @@ import (
 )
 
 type AppConfig struct {
+	Port                 int
 	BaseApi              string
 	DataDir              string
 	DBDir                string
@@ -53,9 +54,11 @@ func init() {
 	beego.BConfig.CopyRequestBody = true
 	mode := beego.AppConfig.DefaultString("runmode", "prod")
 	beego.BConfig.RunMode = mode
-	port := beego.AppConfig.DefaultInt("httpport", 8080)
+	port := beego.AppConfig.DefaultInt("httpport", 38080)
 	beego.BConfig.Listen.HTTPPort = port
 
+	Config.Port = port
+
 	// 需要和前端配置好
 	baseApi := beego.AppConfig.DefaultString("baseApi", "/nginx-ui/api")
 	baseApi = strings.TrimSuffix(baseApi, "/")

+ 3 - 3
server/controllers/user.go

@@ -21,14 +21,14 @@ func NewUserController() *UserController {
 
 // Login 登录
 func (c *UserController) Login() {
-	var user *models.User
-	err := json.Unmarshal(c.Ctx.Input.RequestBody, user)
+	var user models.User
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
 	if err != nil {
 		logs.Error(err, string(c.Ctx.Input.RequestBody))
 		c.ErrorJson(err)
 		return
 	}
-	resp := c.service.Login(user)
+	resp := c.service.Login(&user)
 	if resp.Success() {
 		c.SetSession("user", user)
 	}

+ 2 - 0
server/init/init.go

@@ -3,9 +3,11 @@ package init
 import (
 	"encoding/gob"
 	"fmt"
+	_ "github.com/beego/beego/v2/server/web/session/redis"
 	"nginx-ui/server/config"
 	"nginx-ui/server/db"
 	"nginx-ui/server/models"
+	_ "nginx-ui/server/routers"
 )
 
 func init() {

+ 0 - 3
server/main.go

@@ -2,10 +2,7 @@ package main
 
 import (
 	"github.com/astaxie/beego"
-	_ "github.com/beego/beego/v2/server/web/session/redis"
-	_ "nginx-ui/server/config"
 	_ "nginx-ui/server/init"
-	_ "nginx-ui/server/routers"
 )
 
 func main() {

+ 2 - 2
wails.json

@@ -4,8 +4,8 @@
   "outputfilename": "nginx-ui",
   "frontend:dir": "frontend",
   "frontend:install": "yarn",
-  "frontend:build": "npm run desktop",
-  "frontend:dev:watcher": "npm run dev",
+  "frontend:build": "npm run build:desktop",
+  "frontend:dev:watcher": "npm run dev:desktop",
   "frontend:dev:serverUrl": "auto",
   "author": {
     "name": "tuon",

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott