feat: wrap message by fluent ui toast
This commit is contained in:
		@@ -7,6 +7,7 @@ export interface Resp<T> {
 | 
				
			|||||||
    data: T;
 | 
					    data: T;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 类型保护函数
 | 
					// 类型保护函数
 | 
				
			||||||
function isResp<T>(obj: any): obj is Resp<T> {
 | 
					function isResp<T>(obj: any): obj is Resp<T> {
 | 
				
			||||||
    return (
 | 
					    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)
 | 
					    const bs = JSON.stringify(req)
 | 
				
			||||||
    console.log(`[DEBUG] invoke req: path = ${path}, req =`, 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 {
 | 
					    try {
 | 
				
			||||||
 | 
					        const res = await Invoke(path, bs)
 | 
				
			||||||
        const parsed = JSON.parse(res);
 | 
					        const parsed = JSON.parse(res);
 | 
				
			||||||
        if (isResp<T>(parsed)) {
 | 
					        if (isResp<T>(parsed)) {
 | 
				
			||||||
            return parsed;
 | 
					            result = parsed;
 | 
				
			||||||
 | 
					            ok = true
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            console.error('[ERROR] invoke: resp not valid =', res)
 | 
					            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) {
 | 
					    } catch (error) {
 | 
				
			||||||
        console.error(`[ERROR] invoke: resp parse err, err = ${error}, res =`, res);
 | 
					        result = {status: 500, msg: "发生错误(-1)", err: "backend method(Invoke) not found in window"} as Resp<T>;
 | 
				
			||||||
        throw new Error('Invalid response format');
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
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 React from 'react'
 | 
				
			||||||
import {createRoot} from 'react-dom/client'
 | 
					import {createRoot} from 'react-dom/client'
 | 
				
			||||||
import './style.css'
 | 
					import './style.css'
 | 
				
			||||||
import { FluentProvider, webLightTheme } from '@fluentui/react-components';
 | 
					import {FluentProvider, webLightTheme} from '@fluentui/react-components';
 | 
				
			||||||
import {createBrowserRouter,RouterProvider} from "react-router-dom";
 | 
					import {createBrowserRouter, RouterProvider} from "react-router-dom";
 | 
				
			||||||
import Home from "./page/home/home";
 | 
					import Home from "./page/home/home";
 | 
				
			||||||
import Connection from "./page/connection/connection";
 | 
					import Connection from "./page/connection/connection";
 | 
				
			||||||
 | 
					import {ToastProvider} from "./message";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const container = document.getElementById('root')
 | 
					const container = document.getElementById('root')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const root = createRoot(container!)
 | 
					const root = createRoot(container!)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const router = createBrowserRouter([
 | 
					const router = createBrowserRouter([
 | 
				
			||||||
    {path:'/', element: <Home />},
 | 
					    {path: '/', element: <Home/>},
 | 
				
			||||||
    {path:'/connection', element: <Connection />},
 | 
					    {path: '/connection', element: <Connection/>},
 | 
				
			||||||
])
 | 
					])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
root.render(
 | 
					root.render(
 | 
				
			||||||
    <FluentProvider theme={webLightTheme}>
 | 
					    <FluentProvider theme={webLightTheme}>
 | 
				
			||||||
        <RouterProvider router={router} />
 | 
					        <ToastProvider>
 | 
				
			||||||
 | 
					            <RouterProvider router={router}/>
 | 
				
			||||||
 | 
					        </ToastProvider>
 | 
				
			||||||
    </FluentProvider>,
 | 
					    </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,
 | 
					    useId,
 | 
				
			||||||
    Button,
 | 
					    Button,
 | 
				
			||||||
    FieldProps,
 | 
					    FieldProps,
 | 
				
			||||||
    useToastController,
 | 
					    Spinner
 | 
				
			||||||
    Toast,
 | 
					 | 
				
			||||||
    ToastTitle,
 | 
					 | 
				
			||||||
    ToastIntent,
 | 
					 | 
				
			||||||
    Toaster
 | 
					 | 
				
			||||||
} from "@fluentui/react-components";
 | 
					} from "@fluentui/react-components";
 | 
				
			||||||
import {Field, Input} from "@fluentui/react-components";
 | 
					import {Field, Input} from "@fluentui/react-components";
 | 
				
			||||||
import {useNavigate} from "react-router-dom";
 | 
					import {useNavigate} from "react-router-dom";
 | 
				
			||||||
import {useState} from "react";
 | 
					import {useState} from "react";
 | 
				
			||||||
import {Dial} from "../../api";
 | 
					import {Dial} from "../../api";
 | 
				
			||||||
 | 
					import {useToast} from "../../message";
 | 
				
			||||||
 | 
					import {CheckmarkFilled, DismissRegular} from "@fluentui/react-icons";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Connection = (props: Partial<FieldProps>) => {
 | 
					const Connection = (props: Partial<FieldProps>) => {
 | 
				
			||||||
 | 
					    const { dispatchMessage } = useToast();
 | 
				
			||||||
    const toasterId = useId("toaster");
 | 
					 | 
				
			||||||
    const {dispatchToast} = useToastController(toasterId);
 | 
					 | 
				
			||||||
    const navigate = useNavigate();
 | 
					    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 }>({
 | 
					    const [value, setValue] = useState<{ name: string, endpoint: string, access: string, key: string }>({
 | 
				
			||||||
        name: '',
 | 
					        name: '',
 | 
				
			||||||
        endpoint: '',
 | 
					        endpoint: '',
 | 
				
			||||||
@@ -28,16 +33,24 @@ const Connection = (props: Partial<FieldProps>) => {
 | 
				
			|||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function test() {
 | 
					    function test() {
 | 
				
			||||||
        const val = JSON.stringify(value);
 | 
					        setTestLoading("loading")
 | 
				
			||||||
        console.log('[DEBUG] connection.test: value =', val)
 | 
					 | 
				
			||||||
        Dial<string>("/api/connection/test", value).then(res => {
 | 
					        Dial<string>("/api/connection/test", value).then(res => {
 | 
				
			||||||
 | 
					            let status: "success" | "error" = "error"
 | 
				
			||||||
            if (res.status === 200) {
 | 
					            if (res.status === 200) {
 | 
				
			||||||
                dispatchToast(
 | 
					                status = "success"
 | 
				
			||||||
                    <Toast>
 | 
					            }
 | 
				
			||||||
                        <ToastTitle>连接成功!</ToastTitle>
 | 
					
 | 
				
			||||||
                    </Toast>,
 | 
					            setTestLoading(status);
 | 
				
			||||||
                    {position: "top-end", intent: "success"}
 | 
					
 | 
				
			||||||
                )
 | 
					            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>
 | 
					                </Field>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div className='connection-form-field connection-form-field-actions'>
 | 
					            <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'}}>
 | 
					                <div style={{marginLeft: 'auto'}}>
 | 
				
			||||||
                    <Button style={{marginRight: '20px'}} className='connection-form-field-actions-cancel'
 | 
					                    <Button style={{marginRight: '20px'}} className='connection-form-field-actions-cancel'
 | 
				
			||||||
                            onClick={() => {
 | 
					                            onClick={() => {
 | 
				
			||||||
                                navigate("/")
 | 
					                                navigate("/")
 | 
				
			||||||
                            }}>取消</Button>
 | 
					                            }}>取消</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>
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <Toaster toasterId={toasterId}/>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import {useState} from 'react';
 | 
					import {useEffect, useState} from 'react';
 | 
				
			||||||
import './home.css';
 | 
					import './home.css';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    Button,
 | 
					    Button,
 | 
				
			||||||
@@ -6,25 +6,45 @@ import {
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
    CloudAddFilled, DismissRegular
 | 
					    CloudAddFilled, DismissRegular
 | 
				
			||||||
} from "@fluentui/react-icons";
 | 
					} 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() {
 | 
					function Home() {
 | 
				
			||||||
 | 
					    const {dispatchMessage} = useToast();
 | 
				
			||||||
    const [connectionFilterKeywords, setConnectionFilterKeywords] = useState<string>('');
 | 
					    const [connectionFilterKeywords, setConnectionFilterKeywords] = useState<string>('');
 | 
				
			||||||
    const navigate = useNavigate();
 | 
					    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 (
 | 
					    return (
 | 
				
			||||||
        <div className="container">
 | 
					        <div className="container">
 | 
				
			||||||
            <div className="header">
 | 
					            <div className="header">
 | 
				
			||||||
                        <Button appearance="primary" icon={<CloudAddFilled  />} onClick={() => {navigate("/connection")}}>
 | 
					                <Button appearance="primary" icon={<CloudAddFilled/>} onClick={() => {
 | 
				
			||||||
                            新建连接
 | 
					                    navigate("/connection")
 | 
				
			||||||
                        </Button>
 | 
					                }}>
 | 
				
			||||||
 | 
					                    新建连接
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div className="body">
 | 
					            <div className="body">
 | 
				
			||||||
                <div className="body-connections">
 | 
					                <div className="body-connections">
 | 
				
			||||||
                    <div className="body-connections-search">
 | 
					                    <div className="body-connections-search">
 | 
				
			||||||
                        <input className="body-connections-search-input" type={"text"} placeholder="搜索连接" value={connectionFilterKeywords} onChange={(e) => setConnectionFilterKeywords(e.target.value)} />
 | 
					                        <input className="body-connections-search-input" type={"text"} placeholder="搜索连接"
 | 
				
			||||||
                        <div className="body-connections-search-dismiss" onClick={() => {setConnectionFilterKeywords('')}}>
 | 
					                               value={connectionFilterKeywords}
 | 
				
			||||||
                            <DismissRegular />
 | 
					                               onChange={(e) => setConnectionFilterKeywords(e.target.value)}/>
 | 
				
			||||||
 | 
					                        <div className="body-connections-search-dismiss" onClick={() => {
 | 
				
			||||||
 | 
					                            setConnectionFilterKeywords('')
 | 
				
			||||||
 | 
					                        }}>
 | 
				
			||||||
 | 
					                            <DismissRegular/>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div className="body-connections-list"></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
 | 
					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/config v1.27.38
 | 
				
			||||||
	github.com/aws/aws-sdk-go-v2/credentials v1.17.36
 | 
						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/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/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
 | 
						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 (
 | 
					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/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/ssooidc v1.27.2 // indirect
 | 
				
			||||||
	github.com/aws/aws-sdk-go-v2/service/sts v1.31.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/bep/debounce v1.2.1 // indirect
 | 
				
			||||||
	github.com/fatih/color v1.17.0 // indirect
 | 
						github.com/fatih/color v1.17.0 // indirect
 | 
				
			||||||
	github.com/go-ole/go-ole v1.2.6 // 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/godbus/dbus/v5 v5.1.0 // indirect
 | 
				
			||||||
	github.com/google/uuid v1.6.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/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/echo/v4 v4.10.2 // indirect
 | 
				
			||||||
	github.com/labstack/gommon v0.4.0 // indirect
 | 
						github.com/labstack/gommon v0.4.0 // indirect
 | 
				
			||||||
	github.com/leaanthony/go-ansi-parser v1.6.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/leaanthony/u v1.1.0 // indirect
 | 
				
			||||||
	github.com/mattn/go-colorable v0.1.13 // indirect
 | 
						github.com/mattn/go-colorable v0.1.13 // indirect
 | 
				
			||||||
	github.com/mattn/go-isatty v0.0.20 // 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/browser v0.0.0-20210911075715-681adbf594b8 // indirect
 | 
				
			||||||
	github.com/pkg/errors v0.9.1 // indirect
 | 
						github.com/pkg/errors v0.9.1 // indirect
 | 
				
			||||||
	github.com/rivo/uniseg v0.4.4 // indirect
 | 
						github.com/rivo/uniseg v0.4.4 // indirect
 | 
				
			||||||
	github.com/samber/lo v1.38.1 // 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/tkrajina/go-reflector v0.5.6 // indirect
 | 
				
			||||||
	github.com/valyala/bytebufferpool v1.0.0 // indirect
 | 
						github.com/valyala/bytebufferpool v1.0.0 // indirect
 | 
				
			||||||
	github.com/valyala/fasttemplate v1.2.2 // indirect
 | 
						github.com/valyala/fasttemplate v1.2.2 // indirect
 | 
				
			||||||
	github.com/wailsapp/go-webview2 v1.0.16 // indirect
 | 
						github.com/wailsapp/go-webview2 v1.0.16 // indirect
 | 
				
			||||||
	github.com/wailsapp/mimetype v1.4.1 // 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/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
 | 
				
			||||||
	golang.org/x/net v0.25.0 // indirect
 | 
						golang.org/x/net v0.25.0 // indirect
 | 
				
			||||||
	golang.org/x/sys v0.20.0 // indirect
 | 
						golang.org/x/sync v0.8.0 // indirect
 | 
				
			||||||
	golang.org/x/text v0.15.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
 | 
					// 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/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 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
 | 
				
			||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 | 
					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 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
 | 
				
			||||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 | 
					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 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 | 
				
			||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
					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 h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
 | 
				
			||||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
 | 
					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 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
 | 
				
			||||||
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
 | 
					github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
 | 
				
			||||||
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
 | 
					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/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 h1:2n0d2BwPVXSUq5yhe8lJPHdxevE2qK5G99PMStMZMaI=
 | 
				
			||||||
github.com/leaanthony/u v1.1.0/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
 | 
					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 h1:W775exDO8eNAHT45WDhXekMYCuWahOW9t1aVmGh3u1o=
 | 
				
			||||||
github.com/loveuer/nf v0.2.11/go.mod h1:M6reF17/kJBis30H4DxR5hrtgo/oJL4AV4cBe4HzJLw=
 | 
					github.com/loveuer/nf v0.2.11/go.mod h1:M6reF17/kJBis30H4DxR5hrtgo/oJL4AV4cBe4HzJLw=
 | 
				
			||||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
 | 
					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.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 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 | 
					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 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
 | 
				
			||||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
 | 
					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 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
				
			||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
					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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					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.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 | 
				
			||||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
 | 
					github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
 | 
				
			||||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 | 
					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 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
 | 
				
			||||||
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
 | 
					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/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.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 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 | 
				
			||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 | 
					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 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE=
 | 
				
			||||||
github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
 | 
					github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
 | 
				
			||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 | 
					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/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 h1:Xb5YRTos1w5N7DTMyYegWaGukCP2fIaX9WF21kPPF2k=
 | 
				
			||||||
github.com/wailsapp/wails/v2 v2.9.2/go.mod h1:uehvlCwJSFcBq7rMCGfk4rxca67QQGsbg5Nm4m9UnBs=
 | 
					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.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
 | 
				
			||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
 | 
					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 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
 | 
					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.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 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
 | 
				
			||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
 | 
					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-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-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-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.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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
 | 
					golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
 | 
				
			||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 | 
					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/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.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.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
 | 
				
			||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 | 
					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=
 | 
					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/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-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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					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
 | 
					package controller
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"context"
 | 
						"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"
 | 
						"github.com/loveuer/nf/nft/log"
 | 
				
			||||||
	"nf-disk/internal/ndh"
 | 
					 | 
				
			||||||
	"nf-disk/internal/tool"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type App struct {
 | 
					type App struct {
 | 
				
			||||||
@@ -22,24 +22,11 @@ func NewApp() *App {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *App) Startup(ctx context.Context) {
 | 
					func (a *App) Startup(ctx context.Context) {
 | 
				
			||||||
	a.ctx = ctx
 | 
					 | 
				
			||||||
	log.Info("app startup!!!")
 | 
						log.Info("app startup!!!")
 | 
				
			||||||
	initApi(a)
 | 
					
 | 
				
			||||||
}
 | 
						a.ctx = ctx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *App) Invoke(path string, req string) (res string) {
 | 
						tool.Must(db.Init(ctx, "sqlite::memory", db.OptSqliteByMem(nil)))
 | 
				
			||||||
	log.Info("app invoke: path = %s, req = %s", path, req)
 | 
						tool.Must(model.Init(db.Default.Session()))
 | 
				
			||||||
	handler, ok := a.handlers[path]
 | 
						tool.Must(api.Init(ctx))
 | 
				
			||||||
	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()
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										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
 | 
					package handler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"nf-disk/internal/ndh"
 | 
						"github.com/loveuer/nf-disk/internal/db"
 | 
				
			||||||
	"nf-disk/internal/s3"
 | 
						"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 {
 | 
					func ConnectionTest(c *ndh.Ctx) error {
 | 
				
			||||||
@@ -30,5 +33,69 @@ func ConnectionTest(c *ndh.Ctx) error {
 | 
				
			|||||||
		return c.Send500(err.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
 | 
					package opt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						Msg200 = "操作成功"
 | 
				
			||||||
 | 
						Msg400 = "输入不正确"
 | 
				
			||||||
 | 
						Msg500 = "发生错误"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	Debug bool
 | 
						Debug bool
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,9 +7,9 @@ import (
 | 
				
			|||||||
	"github.com/aws/aws-sdk-go-v2/credentials"
 | 
						"github.com/aws/aws-sdk-go-v2/credentials"
 | 
				
			||||||
	"github.com/aws/aws-sdk-go-v2/service/s3"
 | 
						"github.com/aws/aws-sdk-go-v2/service/s3"
 | 
				
			||||||
	smithyendpoints "github.com/aws/smithy-go/endpoints"
 | 
						smithyendpoints "github.com/aws/smithy-go/endpoints"
 | 
				
			||||||
 | 
						"github.com/loveuer/nf-disk/internal/tool"
 | 
				
			||||||
	"github.com/loveuer/nf/nft/log"
 | 
						"github.com/loveuer/nf/nft/log"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"nf-disk/internal/tool"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type resolverV2 struct{}
 | 
					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 (
 | 
					import (
 | 
				
			||||||
	"embed"
 | 
						"embed"
 | 
				
			||||||
	"flag"
 | 
						"flag"
 | 
				
			||||||
 | 
						"github.com/loveuer/nf-disk/internal/controller"
 | 
				
			||||||
	"github.com/loveuer/nf/nft/nfctl/opt"
 | 
						"github.com/loveuer/nf/nft/nfctl/opt"
 | 
				
			||||||
	"nf-disk/internal/controller"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/loveuer/nf/nft/log"
 | 
						"github.com/loveuer/nf/nft/log"
 | 
				
			||||||
	"github.com/wailsapp/wails/v2"
 | 
						"github.com/wailsapp/wails/v2"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,11 +8,11 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type Ctx struct {
 | 
					type Ctx struct {
 | 
				
			||||||
	ctx context.Context
 | 
						ctx context.Context
 | 
				
			||||||
	req *json.Decoder
 | 
						req io.Reader
 | 
				
			||||||
	res io.Writer
 | 
						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{
 | 
						return &Ctx{
 | 
				
			||||||
		ctx: ctx,
 | 
							ctx: ctx,
 | 
				
			||||||
		req: req,
 | 
							req: req,
 | 
				
			||||||
@@ -29,7 +29,7 @@ func (c *Ctx) Write(bs []byte) (int, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Ctx) ReqParse(req any) 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 {
 | 
					func (c *Ctx) Send200(data any, msg ...string) error {
 | 
				
			||||||
		Reference in New Issue
	
	Block a user