feat: wrap message by fluent ui toast
This commit is contained in:
		@@ -7,6 +7,7 @@ export interface Resp<T> {
 | 
			
		||||
    data: T;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// 类型保护函数
 | 
			
		||||
function isResp<T>(obj: any): obj is Resp<T> {
 | 
			
		||||
    return (
 | 
			
		||||
@@ -18,23 +19,32 @@ function isResp<T>(obj: any): obj is Resp<T> {
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const invoke = async <T>(path: string, req: any): Promise<Resp<T>> => {
 | 
			
		||||
export async function Dial<T>(path: string, req: any = null): Promise<Resp<T>> {
 | 
			
		||||
    const bs = JSON.stringify(req)
 | 
			
		||||
    console.log(`[DEBUG] invoke req: path = ${path}, req =`, req)
 | 
			
		||||
    const res = await Invoke(path, bs)
 | 
			
		||||
    console.log(`[DEBUG] invoke res: path = ${path}, res =`, res)
 | 
			
		||||
 | 
			
		||||
    let result: Resp<T>;
 | 
			
		||||
    let ok = false;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        const res = await Invoke(path, bs)
 | 
			
		||||
        const parsed = JSON.parse(res);
 | 
			
		||||
        if (isResp<T>(parsed)) {
 | 
			
		||||
            return parsed;
 | 
			
		||||
            result = parsed;
 | 
			
		||||
            ok = true
 | 
			
		||||
        } else {
 | 
			
		||||
            console.error('[ERROR] invoke: resp not valid =', res)
 | 
			
		||||
            throw new Error('Parsed response does not match Resp<T> structure');
 | 
			
		||||
            result = {status: 500, msg: "发生错误(0)", err: res} as Resp<T>;
 | 
			
		||||
        }
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
        console.error(`[ERROR] invoke: resp parse err, err = ${error}, res =`, res);
 | 
			
		||||
        throw new Error('Invalid response format');
 | 
			
		||||
        result = {status: 500, msg: "发生错误(-1)", err: "backend method(Invoke) not found in window"} as Resp<T>;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const Dial = invoke;
 | 
			
		||||
    if (ok) {
 | 
			
		||||
        console.log(`[DEBUG] invoke res: path = ${path}, res =`, result)
 | 
			
		||||
    } else {
 | 
			
		||||
        console.error(`[ERROR] invoke res: path = ${path}, res =`, result)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return result
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								frontend/src/interfaces/connection.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								frontend/src/interfaces/connection.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
export interface Connection {
 | 
			
		||||
    id: number;
 | 
			
		||||
    created_at: number;
 | 
			
		||||
    updated_at: number;
 | 
			
		||||
    deleted_at: number;
 | 
			
		||||
    name: string;
 | 
			
		||||
    endpoint: string;
 | 
			
		||||
    active: boolean;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,22 +1,25 @@
 | 
			
		||||
import React from 'react'
 | 
			
		||||
import {createRoot} from 'react-dom/client'
 | 
			
		||||
import './style.css'
 | 
			
		||||
import { FluentProvider, webLightTheme } from '@fluentui/react-components';
 | 
			
		||||
import {createBrowserRouter,RouterProvider} from "react-router-dom";
 | 
			
		||||
import {FluentProvider, webLightTheme} from '@fluentui/react-components';
 | 
			
		||||
import {createBrowserRouter, RouterProvider} from "react-router-dom";
 | 
			
		||||
import Home from "./page/home/home";
 | 
			
		||||
import Connection from "./page/connection/connection";
 | 
			
		||||
import {ToastProvider} from "./message";
 | 
			
		||||
 | 
			
		||||
const container = document.getElementById('root')
 | 
			
		||||
 | 
			
		||||
const root = createRoot(container!)
 | 
			
		||||
 | 
			
		||||
const router = createBrowserRouter([
 | 
			
		||||
    {path:'/', element: <Home />},
 | 
			
		||||
    {path:'/connection', element: <Connection />},
 | 
			
		||||
    {path: '/', element: <Home/>},
 | 
			
		||||
    {path: '/connection', element: <Connection/>},
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
root.render(
 | 
			
		||||
    <FluentProvider theme={webLightTheme}>
 | 
			
		||||
        <RouterProvider router={router} />
 | 
			
		||||
        <ToastProvider>
 | 
			
		||||
            <RouterProvider router={router}/>
 | 
			
		||||
        </ToastProvider>
 | 
			
		||||
    </FluentProvider>,
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								frontend/src/message.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								frontend/src/message.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
import {createContext, FC, ReactNode, useContext} from "react";
 | 
			
		||||
import {Toast, Toaster, ToastTitle, useId, useToastController} from "@fluentui/react-components";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface ToastContextType {
 | 
			
		||||
    dispatchMessage: (content: string, type: "success" | "error" | "warning" | "info") => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ToastContext = createContext<ToastContextType | undefined>(undefined);
 | 
			
		||||
 | 
			
		||||
export const ToastProvider: FC<{ children: ReactNode }> = ({children}) => {
 | 
			
		||||
 | 
			
		||||
    const toasterId = useId("toaster");
 | 
			
		||||
    const {dispatchToast} = useToastController(toasterId);
 | 
			
		||||
 | 
			
		||||
    const dispatchMessage = (content: string, type: "success" | "error" | "warning" | "info" = "info") => {
 | 
			
		||||
        dispatchToast(
 | 
			
		||||
            <Toast>
 | 
			
		||||
                <ToastTitle>{content}</ToastTitle>
 | 
			
		||||
            </Toast>,
 | 
			
		||||
            {position: "top-end", intent: type}
 | 
			
		||||
        );
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return <ToastContext.Provider value={{dispatchMessage}}>
 | 
			
		||||
        {children}
 | 
			
		||||
        <Toaster toasterId={toasterId}/>
 | 
			
		||||
    </ToastContext.Provider>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const useToast = () => {
 | 
			
		||||
    const context = useContext(ToastContext);
 | 
			
		||||
    if (!context) {
 | 
			
		||||
        throw new Error("useToast must be used within a ToastProvider");
 | 
			
		||||
    }
 | 
			
		||||
    return context;
 | 
			
		||||
};
 | 
			
		||||
@@ -3,23 +3,28 @@ import {
 | 
			
		||||
    useId,
 | 
			
		||||
    Button,
 | 
			
		||||
    FieldProps,
 | 
			
		||||
    useToastController,
 | 
			
		||||
    Toast,
 | 
			
		||||
    ToastTitle,
 | 
			
		||||
    ToastIntent,
 | 
			
		||||
    Toaster
 | 
			
		||||
    Spinner
 | 
			
		||||
} from "@fluentui/react-components";
 | 
			
		||||
import {Field, Input} from "@fluentui/react-components";
 | 
			
		||||
import {useNavigate} from "react-router-dom";
 | 
			
		||||
import {useState} from "react";
 | 
			
		||||
import {Dial} from "../../api";
 | 
			
		||||
import {useToast} from "../../message";
 | 
			
		||||
import {CheckmarkFilled, DismissRegular} from "@fluentui/react-icons";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const Connection = (props: Partial<FieldProps>) => {
 | 
			
		||||
 | 
			
		||||
    const toasterId = useId("toaster");
 | 
			
		||||
    const {dispatchToast} = useToastController(toasterId);
 | 
			
		||||
    const { dispatchMessage } = useToast();
 | 
			
		||||
    const navigate = useNavigate();
 | 
			
		||||
    const [testLoading, setTestLoading] = useState<"initial" | "loading" | "success" | "error">("initial");
 | 
			
		||||
    const buttonIcon =
 | 
			
		||||
        testLoading === "loading" ? (
 | 
			
		||||
            <Spinner size="tiny"/>
 | 
			
		||||
        ) : testLoading === "success" ? (
 | 
			
		||||
            <CheckmarkFilled/>
 | 
			
		||||
        ) : testLoading === "error" ? (
 | 
			
		||||
            <DismissRegular/>
 | 
			
		||||
        ) : null;
 | 
			
		||||
    const [value, setValue] = useState<{ name: string, endpoint: string, access: string, key: string }>({
 | 
			
		||||
        name: '',
 | 
			
		||||
        endpoint: '',
 | 
			
		||||
@@ -28,16 +33,24 @@ const Connection = (props: Partial<FieldProps>) => {
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    function test() {
 | 
			
		||||
        const val = JSON.stringify(value);
 | 
			
		||||
        console.log('[DEBUG] connection.test: value =', val)
 | 
			
		||||
        setTestLoading("loading")
 | 
			
		||||
        Dial<string>("/api/connection/test", value).then(res => {
 | 
			
		||||
            let status: "success" | "error" = "error"
 | 
			
		||||
            if (res.status === 200) {
 | 
			
		||||
                dispatchToast(
 | 
			
		||||
                    <Toast>
 | 
			
		||||
                        <ToastTitle>连接成功!</ToastTitle>
 | 
			
		||||
                    </Toast>,
 | 
			
		||||
                    {position: "top-end", intent: "success"}
 | 
			
		||||
                )
 | 
			
		||||
                status = "success"
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            setTestLoading(status);
 | 
			
		||||
 | 
			
		||||
            dispatchMessage(res.msg, status)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function create() {
 | 
			
		||||
        Dial<unknown>("/api/connection/create", value).then(res => {
 | 
			
		||||
            dispatchMessage(res.msg, res.status === 200?"success":"error");
 | 
			
		||||
            if (res.status === 200) {
 | 
			
		||||
                navigate("/");
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
@@ -94,17 +107,17 @@ const Connection = (props: Partial<FieldProps>) => {
 | 
			
		||||
                </Field>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div className='connection-form-field connection-form-field-actions'>
 | 
			
		||||
                <Button appearance='transparent' onClick={() => test()}>测试连接</Button>
 | 
			
		||||
                <Button appearance='transparent' icon={buttonIcon} onClick={() => test()}>测试连接</Button>
 | 
			
		||||
                <div style={{marginLeft: 'auto'}}>
 | 
			
		||||
                    <Button style={{marginRight: '20px'}} className='connection-form-field-actions-cancel'
 | 
			
		||||
                            onClick={() => {
 | 
			
		||||
                                navigate("/")
 | 
			
		||||
                            }}>取消</Button>
 | 
			
		||||
                    <Button className='connection-form-field-actions-confirm' appearance='primary'>新建</Button>
 | 
			
		||||
                    <Button className='connection-form-field-actions-confirm' appearance='primary'
 | 
			
		||||
                            onClick={() => create()}>新建</Button>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <Toaster toasterId={toasterId}/>
 | 
			
		||||
    </div>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import {useState} from 'react';
 | 
			
		||||
import {useEffect, useState} from 'react';
 | 
			
		||||
import './home.css';
 | 
			
		||||
import {
 | 
			
		||||
    Button,
 | 
			
		||||
@@ -6,25 +6,45 @@ import {
 | 
			
		||||
import {
 | 
			
		||||
    CloudAddFilled, DismissRegular
 | 
			
		||||
} from "@fluentui/react-icons";
 | 
			
		||||
import { useNavigate } from "react-router-dom";
 | 
			
		||||
import {useNavigate} from "react-router-dom";
 | 
			
		||||
import {Dial} from "../../api";
 | 
			
		||||
import {useToast} from "../../message";
 | 
			
		||||
import {Connection} from "../../interfaces/connection";
 | 
			
		||||
 | 
			
		||||
function Home() {
 | 
			
		||||
    const {dispatchMessage} = useToast();
 | 
			
		||||
    const [connectionFilterKeywords, setConnectionFilterKeywords] = useState<string>('');
 | 
			
		||||
    const navigate = useNavigate();
 | 
			
		||||
    const [connectionList, setConnectionList] = useState<Connection[]>([]);
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        Dial<{ list: Connection[] }>("/api/connection/list").then(res => {
 | 
			
		||||
            dispatchMessage(res.msg, res.status === 200 ? "success" : "error");
 | 
			
		||||
            if (res.status === 200) {
 | 
			
		||||
                setConnectionList(res.data.list)
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <div className="container">
 | 
			
		||||
            <div className="header">
 | 
			
		||||
                        <Button appearance="primary" icon={<CloudAddFilled  />} onClick={() => {navigate("/connection")}}>
 | 
			
		||||
                            新建连接
 | 
			
		||||
                        </Button>
 | 
			
		||||
                <Button appearance="primary" icon={<CloudAddFilled/>} onClick={() => {
 | 
			
		||||
                    navigate("/connection")
 | 
			
		||||
                }}>
 | 
			
		||||
                    新建连接
 | 
			
		||||
                </Button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div className="body">
 | 
			
		||||
                <div className="body-connections">
 | 
			
		||||
                    <div className="body-connections-search">
 | 
			
		||||
                        <input className="body-connections-search-input" type={"text"} placeholder="搜索连接" value={connectionFilterKeywords} onChange={(e) => setConnectionFilterKeywords(e.target.value)} />
 | 
			
		||||
                        <div className="body-connections-search-dismiss" onClick={() => {setConnectionFilterKeywords('')}}>
 | 
			
		||||
                            <DismissRegular />
 | 
			
		||||
                        <input className="body-connections-search-input" type={"text"} placeholder="搜索连接"
 | 
			
		||||
                               value={connectionFilterKeywords}
 | 
			
		||||
                               onChange={(e) => setConnectionFilterKeywords(e.target.value)}/>
 | 
			
		||||
                        <div className="body-connections-search-dismiss" onClick={() => {
 | 
			
		||||
                            setConnectionFilterKeywords('')
 | 
			
		||||
                        }}>
 | 
			
		||||
                            <DismissRegular/>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div className="body-connections-list"></div>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								go.mod
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
module nf-disk
 | 
			
		||||
module github.com/loveuer/nf-disk
 | 
			
		||||
 | 
			
		||||
go 1.21
 | 
			
		||||
 | 
			
		||||
@@ -9,8 +9,15 @@ require (
 | 
			
		||||
	github.com/aws/aws-sdk-go-v2/config v1.27.38
 | 
			
		||||
	github.com/aws/aws-sdk-go-v2/credentials v1.17.36
 | 
			
		||||
	github.com/aws/aws-sdk-go-v2/service/s3 v1.63.2
 | 
			
		||||
	github.com/aws/smithy-go v1.21.0
 | 
			
		||||
	github.com/loveuer/go-sqlite3 v1.0.2
 | 
			
		||||
	github.com/loveuer/nf v0.2.11
 | 
			
		||||
	github.com/ncruces/go-sqlite3/gormlite v0.18.4
 | 
			
		||||
	github.com/psanford/httpreadat v0.1.0
 | 
			
		||||
	github.com/wailsapp/wails/v2 v2.9.2
 | 
			
		||||
	gorm.io/driver/mysql v1.5.7
 | 
			
		||||
	gorm.io/driver/postgres v1.5.9
 | 
			
		||||
	gorm.io/gorm v1.25.12
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
@@ -27,13 +34,19 @@ require (
 | 
			
		||||
	github.com/aws/aws-sdk-go-v2/service/sso v1.23.2 // indirect
 | 
			
		||||
	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.2 // indirect
 | 
			
		||||
	github.com/aws/aws-sdk-go-v2/service/sts v1.31.2 // indirect
 | 
			
		||||
	github.com/aws/smithy-go v1.21.0 // indirect
 | 
			
		||||
	github.com/bep/debounce v1.2.1 // indirect
 | 
			
		||||
	github.com/fatih/color v1.17.0 // indirect
 | 
			
		||||
	github.com/go-ole/go-ole v1.2.6 // indirect
 | 
			
		||||
	github.com/go-sql-driver/mysql v1.7.0 // indirect
 | 
			
		||||
	github.com/godbus/dbus/v5 v5.1.0 // indirect
 | 
			
		||||
	github.com/google/uuid v1.6.0 // indirect
 | 
			
		||||
	github.com/jackc/pgpassfile v1.0.0 // indirect
 | 
			
		||||
	github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
 | 
			
		||||
	github.com/jackc/pgx/v5 v5.5.5 // indirect
 | 
			
		||||
	github.com/jackc/puddle/v2 v2.2.1 // indirect
 | 
			
		||||
	github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
 | 
			
		||||
	github.com/jinzhu/inflection v1.0.0 // indirect
 | 
			
		||||
	github.com/jinzhu/now v1.1.5 // indirect
 | 
			
		||||
	github.com/labstack/echo/v4 v4.10.2 // indirect
 | 
			
		||||
	github.com/labstack/gommon v0.4.0 // indirect
 | 
			
		||||
	github.com/leaanthony/go-ansi-parser v1.6.0 // indirect
 | 
			
		||||
@@ -42,20 +55,24 @@ require (
 | 
			
		||||
	github.com/leaanthony/u v1.1.0 // indirect
 | 
			
		||||
	github.com/mattn/go-colorable v0.1.13 // indirect
 | 
			
		||||
	github.com/mattn/go-isatty v0.0.20 // indirect
 | 
			
		||||
	github.com/ncruces/go-sqlite3 v0.18.4 // indirect
 | 
			
		||||
	github.com/ncruces/julianday v1.0.0 // indirect
 | 
			
		||||
	github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
 | 
			
		||||
	github.com/pkg/errors v0.9.1 // indirect
 | 
			
		||||
	github.com/rivo/uniseg v0.4.4 // indirect
 | 
			
		||||
	github.com/samber/lo v1.38.1 // indirect
 | 
			
		||||
	github.com/tetratelabs/wazero v1.8.0 // indirect
 | 
			
		||||
	github.com/tkrajina/go-reflector v0.5.6 // indirect
 | 
			
		||||
	github.com/valyala/bytebufferpool v1.0.0 // indirect
 | 
			
		||||
	github.com/valyala/fasttemplate v1.2.2 // indirect
 | 
			
		||||
	github.com/wailsapp/go-webview2 v1.0.16 // indirect
 | 
			
		||||
	github.com/wailsapp/mimetype v1.4.1 // indirect
 | 
			
		||||
	golang.org/x/crypto v0.23.0 // indirect
 | 
			
		||||
	golang.org/x/crypto v0.27.0 // indirect
 | 
			
		||||
	golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
 | 
			
		||||
	golang.org/x/net v0.25.0 // indirect
 | 
			
		||||
	golang.org/x/sys v0.20.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.15.0 // indirect
 | 
			
		||||
	golang.org/x/sync v0.8.0 // indirect
 | 
			
		||||
	golang.org/x/sys v0.25.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.18.0 // indirect
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// replace github.com/wailsapp/wails/v2 v2.9.2 => C:\Users\loveuer\go\pkg\mod
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										48
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								go.sum
									
									
									
									
									
								
							@@ -43,12 +43,26 @@ github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
 | 
			
		||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
 | 
			
		||||
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/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 | 
			
		||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
 | 
			
		||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 | 
			
		||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 | 
			
		||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
 | 
			
		||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
 | 
			
		||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
 | 
			
		||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
 | 
			
		||||
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
 | 
			
		||||
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
 | 
			
		||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
 | 
			
		||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
 | 
			
		||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
 | 
			
		||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
 | 
			
		||||
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.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
 | 
			
		||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 | 
			
		||||
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
 | 
			
		||||
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
 | 
			
		||||
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
 | 
			
		||||
@@ -64,6 +78,8 @@ github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/
 | 
			
		||||
github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8=
 | 
			
		||||
github.com/leaanthony/u v1.1.0 h1:2n0d2BwPVXSUq5yhe8lJPHdxevE2qK5G99PMStMZMaI=
 | 
			
		||||
github.com/leaanthony/u v1.1.0/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
 | 
			
		||||
github.com/loveuer/go-sqlite3 v1.0.2 h1:kcENqm6mt0wPH/N9Sw+6UC74qtU8o+aMEO04I62pjDE=
 | 
			
		||||
github.com/loveuer/go-sqlite3 v1.0.2/go.mod h1:8+45etSlBYCtYP/ThX/e1wLgG+x6G6oXck2FhjC57tA=
 | 
			
		||||
github.com/loveuer/nf v0.2.11 h1:W775exDO8eNAHT45WDhXekMYCuWahOW9t1aVmGh3u1o=
 | 
			
		||||
github.com/loveuer/nf v0.2.11/go.mod h1:M6reF17/kJBis30H4DxR5hrtgo/oJL4AV4cBe4HzJLw=
 | 
			
		||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
 | 
			
		||||
@@ -75,21 +91,32 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
 | 
			
		||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 | 
			
		||||
github.com/ncruces/go-sqlite3 v0.18.4 h1:Je8o3y33MDwPYY/Cacas8yCsuoUzpNY/AgoSlN2ekyE=
 | 
			
		||||
github.com/ncruces/go-sqlite3 v0.18.4/go.mod h1:4HLag13gq1k10s4dfGBhMfRVsssJRT9/5hYqVM9RUYo=
 | 
			
		||||
github.com/ncruces/go-sqlite3/gormlite v0.18.4 h1:NdZkzS7SkcGlUafCmF6/fpqS/JkhxXP/DRPDYmSVdL4=
 | 
			
		||||
github.com/ncruces/go-sqlite3/gormlite v0.18.4/go.mod h1:laAntS4laxUO47GmxhIhSeJPrRSPF9TdsOQhaqlIifI=
 | 
			
		||||
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
 | 
			
		||||
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
 | 
			
		||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
 | 
			
		||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
 | 
			
		||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
			
		||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
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/psanford/httpreadat v0.1.0 h1:VleW1HS2zO7/4c7c7zNl33fO6oYACSagjJIyMIwZLUE=
 | 
			
		||||
github.com/psanford/httpreadat v0.1.0/go.mod h1:Zg7P+TlBm3bYbyHTKv/EdtSJZn3qwbPwpfZ/I9GKCRE=
 | 
			
		||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 | 
			
		||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
 | 
			
		||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 | 
			
		||||
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
 | 
			
		||||
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
 | 
			
		||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
			
		||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
			
		||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 | 
			
		||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 | 
			
		||||
github.com/tetratelabs/wazero v1.8.0 h1:iEKu0d4c2Pd+QSRieYbnQC9yiFlMS9D+Jr0LsRmcF4g=
 | 
			
		||||
github.com/tetratelabs/wazero v1.8.0/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs=
 | 
			
		||||
github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE=
 | 
			
		||||
github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
 | 
			
		||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 | 
			
		||||
@@ -103,13 +130,15 @@ github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhw
 | 
			
		||||
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
 | 
			
		||||
github.com/wailsapp/wails/v2 v2.9.2 h1:Xb5YRTos1w5N7DTMyYegWaGukCP2fIaX9WF21kPPF2k=
 | 
			
		||||
github.com/wailsapp/wails/v2 v2.9.2/go.mod h1:uehvlCwJSFcBq7rMCGfk4rxca67QQGsbg5Nm4m9UnBs=
 | 
			
		||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
 | 
			
		||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
 | 
			
		||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
 | 
			
		||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
 | 
			
		||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
 | 
			
		||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
 | 
			
		||||
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 | 
			
		||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
 | 
			
		||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
 | 
			
		||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
 | 
			
		||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
@@ -120,15 +149,22 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
 | 
			
		||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
 | 
			
		||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 | 
			
		||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
 | 
			
		||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 | 
			
		||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
			
		||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
			
		||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
 | 
			
		||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 | 
			
		||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
 | 
			
		||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
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.0-20210107192922-496545a6307b/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=
 | 
			
		||||
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
 | 
			
		||||
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
 | 
			
		||||
gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
 | 
			
		||||
gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
 | 
			
		||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
 | 
			
		||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
 | 
			
		||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								internal/api/api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								internal/api/api.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/handler"
 | 
			
		||||
	"github.com/loveuer/nf-disk/ndh"
 | 
			
		||||
	"github.com/loveuer/nf/nft/log"
 | 
			
		||||
	"reflect"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	apis = make(map[string]ndh.Handler)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func register(path string, h ndh.Handler) {
 | 
			
		||||
	name := reflect.ValueOf(h).String()
 | 
			
		||||
	log.Info("app register: path = %s, name = %s", path, name)
 | 
			
		||||
	apis[path] = h
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Resolve(path string) (ndh.Handler, bool) {
 | 
			
		||||
	h, ok := apis[path]
 | 
			
		||||
	return h, ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Init(ctx context.Context) error {
 | 
			
		||||
	register("/api/connection/test", handler.ConnectionTest)
 | 
			
		||||
	register("/api/connection/create", handler.ConnectionCreate)
 | 
			
		||||
	register("/api/connection/list", handler.ConnectionList)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
package controller
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/loveuer/nf/nft/log"
 | 
			
		||||
	"nf-disk/internal/handler"
 | 
			
		||||
	"nf-disk/internal/ndh"
 | 
			
		||||
	"reflect"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (a *App) register(path string, handler ndh.Handler) {
 | 
			
		||||
	name := reflect.ValueOf(handler).String()
 | 
			
		||||
	log.Info("app register: path = %s, name = %s", path, name)
 | 
			
		||||
	a.handlers[path] = handler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func initApi(a *App) {
 | 
			
		||||
	a.register("/api/connection/test", handler.ConnectionTest)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +1,13 @@
 | 
			
		||||
package controller
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/api"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/db"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/model"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/tool"
 | 
			
		||||
	"github.com/loveuer/nf-disk/ndh"
 | 
			
		||||
	"github.com/loveuer/nf/nft/log"
 | 
			
		||||
	"nf-disk/internal/ndh"
 | 
			
		||||
	"nf-disk/internal/tool"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type App struct {
 | 
			
		||||
@@ -22,24 +22,11 @@ func NewApp() *App {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *App) Startup(ctx context.Context) {
 | 
			
		||||
	a.ctx = ctx
 | 
			
		||||
	log.Info("app startup!!!")
 | 
			
		||||
	initApi(a)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *App) Invoke(path string, req string) (res string) {
 | 
			
		||||
	log.Info("app invoke: path = %s, req = %s", path, req)
 | 
			
		||||
	handler, ok := a.handlers[path]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return `{"err": "handler not found", "status": 404}`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	ctx := ndh.NewCtx(tool.Timeout(), json.NewDecoder(strings.NewReader(req)), &buf)
 | 
			
		||||
 | 
			
		||||
	if err := handler(ctx); err != nil {
 | 
			
		||||
		return err.Error()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return buf.String()
 | 
			
		||||
 | 
			
		||||
	a.ctx = ctx
 | 
			
		||||
 | 
			
		||||
	tool.Must(db.Init(ctx, "sqlite::memory", db.OptSqliteByMem(nil)))
 | 
			
		||||
	tool.Must(model.Init(db.Default.Session()))
 | 
			
		||||
	tool.Must(api.Init(ctx))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										51
									
								
								internal/controller/invoke.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								internal/controller/invoke.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
package controller
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/api"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/opt"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/tool"
 | 
			
		||||
	"github.com/loveuer/nf-disk/ndh"
 | 
			
		||||
	"github.com/loveuer/nf/nft/log"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func handleError(err error) string {
 | 
			
		||||
	bs, _ := json.Marshal(map[string]any{
 | 
			
		||||
		"err":    err.Error(),
 | 
			
		||||
		"msg":    opt.Msg500,
 | 
			
		||||
		"status": 500,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return string(bs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleNotFound(path string) string {
 | 
			
		||||
	bs, _ := json.Marshal(map[string]any{
 | 
			
		||||
		"err":    fmt.Sprintf("path not found, path: %s", path),
 | 
			
		||||
		"msg":    opt.Msg500,
 | 
			
		||||
		"status": 404,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return string(bs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *App) Invoke(path string, req string) (res string) {
 | 
			
		||||
	log.Info("app invoke: path = %s, req = %s", path, req)
 | 
			
		||||
	handler, ok := api.Resolve(path)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		log.Warn("app invoke: path not found, path = %s", path)
 | 
			
		||||
		return handleNotFound(path)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	ctx := ndh.NewCtx(tool.TimeoutCtx(a.ctx), strings.NewReader(req), &buf)
 | 
			
		||||
 | 
			
		||||
	if err := handler(ctx); err != nil {
 | 
			
		||||
		return handleError(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return buf.String()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										61
									
								
								internal/db/client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								internal/db/client.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
package db
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/opt"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/tool"
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	Default *Client
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Client struct {
 | 
			
		||||
	ctx       context.Context
 | 
			
		||||
	cli       *gorm.DB
 | 
			
		||||
	ttype     string
 | 
			
		||||
	cfgSqlite *cfgSqlite
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) Type() string {
 | 
			
		||||
	return c.ttype
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) Session(ctxs ...context.Context) *gorm.DB {
 | 
			
		||||
	var ctx context.Context
 | 
			
		||||
	if len(ctxs) > 0 && ctxs[0] != nil {
 | 
			
		||||
		ctx = ctxs[0]
 | 
			
		||||
	} else {
 | 
			
		||||
		ctx = tool.Timeout(30)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	session := c.cli.Session(&gorm.Session{Context: ctx})
 | 
			
		||||
 | 
			
		||||
	if opt.Debug {
 | 
			
		||||
		session = session.Debug()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return session
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) Close() {
 | 
			
		||||
	d, _ := c.cli.DB()
 | 
			
		||||
	d.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dump
 | 
			
		||||
// Only for sqlite with mem mode to dump data to bytes(io.Reader)
 | 
			
		||||
func (c *Client) Dump() (reader io.ReadSeekCloser, ok bool) {
 | 
			
		||||
	if c.ttype != "sqlite" {
 | 
			
		||||
		return nil, false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.cfgSqlite.fsType != "mem" {
 | 
			
		||||
		return nil, false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.cfgSqlite.memDump.Dump(), true
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										44
									
								
								internal/db/db_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								internal/db/db_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
package db
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestOpen(t *testing.T) {
 | 
			
		||||
	myClient, err := New(context.TODO(), "sqlite::", OptSqliteByMem())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("TestOpen: New err = %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type Start struct {
 | 
			
		||||
		Id   int     `json:"id" gorm:"column:id;primaryKey"`
 | 
			
		||||
		Name string  `json:"name" gorm:"column:name"`
 | 
			
		||||
		Dis  float64 `json:"dis" gorm:"column:dis"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = myClient.Session().AutoMigrate(&Start{}); err != nil {
 | 
			
		||||
		t.Fatalf("TestOpen: AutoMigrate err = %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = myClient.Session().Create(&Start{Name: "sun", Dis: 6631.76}).Error; err != nil {
 | 
			
		||||
		t.Fatalf("TestOpen: Create err = %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = myClient.Session().Create(&Start{Name: "mar", Dis: 786.35}).Error; err != nil {
 | 
			
		||||
		t.Fatalf("TestOpen: Create err = %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if reader, ok := myClient.Dump(); ok {
 | 
			
		||||
		bs, err := io.ReadAll(reader)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatalf("TestOpen: ReadAll err = %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		os.WriteFile("dump.db", bs, 0644)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	myClient.Close()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								internal/db/init.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								internal/db/init.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
package db
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"gorm.io/driver/mysql"
 | 
			
		||||
	"gorm.io/driver/postgres"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func New(ctx context.Context, uri string, opts ...Option) (*Client, error) {
 | 
			
		||||
	strs := strings.Split(uri, "::")
 | 
			
		||||
 | 
			
		||||
	if len(strs) != 2 {
 | 
			
		||||
		return nil, fmt.Errorf("db.Init: opt db uri invalid: %s", uri)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := &Client{ttype: strs[0], cfgSqlite: &cfgSqlite{fsType: "file"}}
 | 
			
		||||
	for _, f := range opts {
 | 
			
		||||
		f(c)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		err error
 | 
			
		||||
		dsn = strs[1]
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	switch strs[0] {
 | 
			
		||||
	case "sqlite":
 | 
			
		||||
		err = openSqlite(c, dsn)
 | 
			
		||||
	case "mysql":
 | 
			
		||||
		c.cli, err = gorm.Open(mysql.Open(dsn))
 | 
			
		||||
	case "postgres":
 | 
			
		||||
		c.cli, err = gorm.Open(postgres.Open(dsn))
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("db type only support: [sqlite, mysql, postgres], unsupported db type: %s", strs[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("db.Init: open %s with dsn:%s, err: %w", strs[0], dsn, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Init(ctx context.Context, uri string, opts ...Option) (err error) {
 | 
			
		||||
	if Default, err = New(ctx, uri, opts...); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								internal/db/option.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								internal/db/option.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
package db
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	_ "github.com/loveuer/go-sqlite3/embed"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Option func(c *Client)
 | 
			
		||||
 | 
			
		||||
func OptSqliteByUrl(address string) Option {
 | 
			
		||||
	return func(c *Client) {
 | 
			
		||||
		c.cfgSqlite.fsType = "url"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SqliteMemDumper interface {
 | 
			
		||||
	Dump() io.ReadSeekCloser
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 如果传 nil 则表示新生成一个 mem 的 sqlite
 | 
			
		||||
// 如果传了一个合法的 reader 则会从这个 reader 初始化 database
 | 
			
		||||
func OptSqliteByMem(reader io.ReadCloser) Option {
 | 
			
		||||
	return func(c *Client) {
 | 
			
		||||
		c.cfgSqlite.memReader = reader
 | 
			
		||||
		c.cfgSqlite.fsType = "mem"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										63
									
								
								internal/db/sqlite.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								internal/db/sqlite.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
package db
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	_ "github.com/loveuer/go-sqlite3/embed"
 | 
			
		||||
	"github.com/loveuer/go-sqlite3/vfs/memdb"
 | 
			
		||||
	"github.com/loveuer/go-sqlite3/vfs/readervfs"
 | 
			
		||||
	"github.com/ncruces/go-sqlite3/gormlite"
 | 
			
		||||
	"github.com/psanford/httpreadat"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type cfgSqlite struct {
 | 
			
		||||
	fsType    string // file, mem(bytes), url
 | 
			
		||||
	memDump   *memdb.MemDB
 | 
			
		||||
	memReader io.ReadCloser
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func openSqlite(c *Client, dsn string) error {
 | 
			
		||||
	var (
 | 
			
		||||
		db  gorm.Dialector
 | 
			
		||||
		err error
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	switch c.cfgSqlite.fsType {
 | 
			
		||||
	case "file":
 | 
			
		||||
		db = gormlite.Open("file:" + dsn)
 | 
			
		||||
	case "url":
 | 
			
		||||
		name := fmt.Sprintf("%d.db", time.Now().UnixNano())
 | 
			
		||||
		readervfs.Create(name, httpreadat.New(dsn))
 | 
			
		||||
		uri := fmt.Sprintf("file:%s?vfs=reader", name)
 | 
			
		||||
		db = gormlite.Open(uri)
 | 
			
		||||
	case "mem":
 | 
			
		||||
		var (
 | 
			
		||||
			bs   []byte
 | 
			
		||||
			name = fmt.Sprintf("%d.db", time.Now().UnixNano())
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		if c.cfgSqlite.memReader == nil {
 | 
			
		||||
			bs = make([]byte, 0)
 | 
			
		||||
		} else {
 | 
			
		||||
			if bs, err = io.ReadAll(c.cfgSqlite.memReader); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		memDump := memdb.Create(name, bs)
 | 
			
		||||
		c.cfgSqlite.memDump = memDump
 | 
			
		||||
		uri := fmt.Sprintf("file:/%s?vfs=memdb", name)
 | 
			
		||||
		db = gormlite.Open(uri)
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Errorf("unsupported sqlite fs type: %s", c.cfgSqlite.fsType)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.cli, err = gorm.Open(db); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +1,11 @@
 | 
			
		||||
package handler
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"nf-disk/internal/ndh"
 | 
			
		||||
	"nf-disk/internal/s3"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/db"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/manager"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/model"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/s3"
 | 
			
		||||
	"github.com/loveuer/nf-disk/ndh"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func ConnectionTest(c *ndh.Ctx) error {
 | 
			
		||||
@@ -30,5 +33,69 @@ func ConnectionTest(c *ndh.Ctx) error {
 | 
			
		||||
		return c.Send500(err.Error(), "连接失败")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.Send200("test success")
 | 
			
		||||
	return c.Send200("连接测试成功")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ConnectionCreate(c *ndh.Ctx) error {
 | 
			
		||||
	type Req struct {
 | 
			
		||||
		Name     string `json:"name"`
 | 
			
		||||
		Endpoint string `json:"endpoint"`
 | 
			
		||||
		Access   string `json:"access"`
 | 
			
		||||
		Key      string `json:"key"`
 | 
			
		||||
		Force    bool   `json:"force"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		err    error
 | 
			
		||||
		req    = new(Req)
 | 
			
		||||
		client *s3.Client
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	if err = c.ReqParse(req); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if req.Endpoint == "" || req.Access == "" || req.Key == "" {
 | 
			
		||||
		return c.Send400(nil, "endpoint, secret_access, secret_key 是必填项")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if client, err = s3.New(c.Context(), req.Endpoint, req.Access, req.Key); err != nil {
 | 
			
		||||
		return c.Send500(err.Error(), "连接失败")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if req.Name == "" {
 | 
			
		||||
		req.Name = req.Endpoint
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	connection := &model.Connection{
 | 
			
		||||
		Name:     req.Name,
 | 
			
		||||
		Endpoint: req.Endpoint,
 | 
			
		||||
		Access:   req.Access,
 | 
			
		||||
		Key:      req.Key,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = connection.Create(db.Default.Session()); err != nil {
 | 
			
		||||
		return c.Send500(err.Error(), "创建连接失败(1)")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = manager.Register(connection, client); err != nil {
 | 
			
		||||
		return c.Send500(err.Error(), "创建连接失败(2)")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.Send200(connection, "创建连接成功")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ConnectionList(c *ndh.Ctx) error {
 | 
			
		||||
	var (
 | 
			
		||||
		err  error
 | 
			
		||||
		list = make([]*model.Connection, 0)
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	if err = db.Default.Session().Model(&model.Connection{}).
 | 
			
		||||
		Find(&list).
 | 
			
		||||
		Error; err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.Send200(map[string]any{"list": list})
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								internal/manager/manager.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								internal/manager/manager.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
package manager
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/model"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/s3"
 | 
			
		||||
	"github.com/loveuer/nf/nft/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Init(ctx context.Context) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Register(m *model.Connection, c *s3.Client) error {
 | 
			
		||||
	log.Debug("manager: register connection-client: id = %d, name = %s", m.Id, m.Name)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								internal/model/init.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								internal/model/init.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import "gorm.io/gorm"
 | 
			
		||||
 | 
			
		||||
func Init(tx *gorm.DB) error {
 | 
			
		||||
	return tx.AutoMigrate(
 | 
			
		||||
		&Connection{},
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								internal/model/s3.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								internal/model/s3.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import "gorm.io/gorm"
 | 
			
		||||
 | 
			
		||||
type Connection struct {
 | 
			
		||||
	Id        uint64 `json:"id" gorm:"primaryKey;column:id"`
 | 
			
		||||
	CreatedAt int64  `json:"created_at" gorm:"column:created_at;autoCreateTime:milli"`
 | 
			
		||||
	UpdatedAt int64  `json:"updated_at" gorm:"column:updated_at;autoUpdateTime:milli"`
 | 
			
		||||
	DeletedAt int64  `json:"deleted_at" gorm:"index;column:deleted_at;default:0"`
 | 
			
		||||
	Name      string `json:"name" gorm:"unique;column:name"`
 | 
			
		||||
	Endpoint  string `json:"endpoint" gorm:"column:endpoint"`
 | 
			
		||||
	Access    string `json:"access" gorm:"column:access"`
 | 
			
		||||
	Key       string `json:"key" gorm:"column:key"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Connection) Create(tx *gorm.DB) error {
 | 
			
		||||
	return tx.Create(c).Error
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,11 @@
 | 
			
		||||
package opt
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	Msg200 = "操作成功"
 | 
			
		||||
	Msg400 = "输入不正确"
 | 
			
		||||
	Msg500 = "发生错误"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	Debug bool
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -7,9 +7,9 @@ import (
 | 
			
		||||
	"github.com/aws/aws-sdk-go-v2/credentials"
 | 
			
		||||
	"github.com/aws/aws-sdk-go-v2/service/s3"
 | 
			
		||||
	smithyendpoints "github.com/aws/smithy-go/endpoints"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/tool"
 | 
			
		||||
	"github.com/loveuer/nf/nft/log"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"nf-disk/internal/tool"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type resolverV2 struct{}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								internal/tool/must.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								internal/tool/must.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
package tool
 | 
			
		||||
 | 
			
		||||
import "github.com/loveuer/nf/nft/log"
 | 
			
		||||
 | 
			
		||||
func Must(errs ...error) {
 | 
			
		||||
	for _, err := range errs {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Panic(err.Error())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								main.go
									
									
									
									
									
								
							@@ -3,8 +3,8 @@ package main
 | 
			
		||||
import (
 | 
			
		||||
	"embed"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"github.com/loveuer/nf-disk/internal/controller"
 | 
			
		||||
	"github.com/loveuer/nf/nft/nfctl/opt"
 | 
			
		||||
	"nf-disk/internal/controller"
 | 
			
		||||
 | 
			
		||||
	"github.com/loveuer/nf/nft/log"
 | 
			
		||||
	"github.com/wailsapp/wails/v2"
 | 
			
		||||
 
 | 
			
		||||
@@ -8,11 +8,11 @@ import (
 | 
			
		||||
 | 
			
		||||
type Ctx struct {
 | 
			
		||||
	ctx context.Context
 | 
			
		||||
	req *json.Decoder
 | 
			
		||||
	req io.Reader
 | 
			
		||||
	res io.Writer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewCtx(ctx context.Context, req *json.Decoder, res io.Writer) *Ctx {
 | 
			
		||||
func NewCtx(ctx context.Context, req io.Reader, res io.Writer) *Ctx {
 | 
			
		||||
	return &Ctx{
 | 
			
		||||
		ctx: ctx,
 | 
			
		||||
		req: req,
 | 
			
		||||
@@ -29,7 +29,7 @@ func (c *Ctx) Write(bs []byte) (int, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Ctx) ReqParse(req any) error {
 | 
			
		||||
	return c.req.Decode(req)
 | 
			
		||||
	return json.NewDecoder(c.req).Decode(req)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Ctx) Send200(data any, msg ...string) error {
 | 
			
		||||
		Reference in New Issue
	
	Block a user