Compare commits
6 Commits
v0.2.1
...
909a016a44
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
909a016a44 | ||
|
|
96fe642175 | ||
|
|
d38fa7a507 | ||
|
|
8a063d15d9 | ||
|
|
56874b754e | ||
|
|
34930a3992 |
106
.github/workflows/release.yml
vendored
Normal file
106
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
name: Release Binaries
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Build and Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- goos: linux
|
||||||
|
goarch: amd64
|
||||||
|
suffix: linux-amd64
|
||||||
|
- goos: linux
|
||||||
|
goarch: arm64
|
||||||
|
suffix: linux-arm64
|
||||||
|
- goos: darwin
|
||||||
|
goarch: amd64
|
||||||
|
suffix: darwin-amd64
|
||||||
|
- goos: darwin
|
||||||
|
goarch: arm64
|
||||||
|
suffix: darwin-arm64
|
||||||
|
- goos: windows
|
||||||
|
goarch: amd64
|
||||||
|
suffix: windows-amd64
|
||||||
|
ext: .exe
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.24'
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v2
|
||||||
|
with:
|
||||||
|
version: 8
|
||||||
|
|
||||||
|
- name: Get pnpm store directory
|
||||||
|
id: pnpm-cache
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Setup pnpm cache
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||||
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-store-
|
||||||
|
|
||||||
|
- name: Print build info
|
||||||
|
run: |
|
||||||
|
echo "Tag: ${{ github.ref_name }}"
|
||||||
|
echo "GoOS: ${{ matrix.goos }}"
|
||||||
|
echo "GoArch: ${{ matrix.goarch }}"
|
||||||
|
|
||||||
|
- name: Build frontend
|
||||||
|
working-directory: frontend
|
||||||
|
run: |
|
||||||
|
pnpm install --registry=https://registry.npmmirror.com
|
||||||
|
pnpm run build
|
||||||
|
|
||||||
|
- name: Copy frontend dist for embedding
|
||||||
|
run: |
|
||||||
|
mkdir -p internal/static/frontend
|
||||||
|
cp -r frontend/dist internal/static/frontend/
|
||||||
|
|
||||||
|
- name: Build binary
|
||||||
|
env:
|
||||||
|
GOOS: ${{ matrix.goos }}
|
||||||
|
GOARCH: ${{ matrix.goarch }}
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
run: |
|
||||||
|
OUTPUT_NAME="ushare-${{ matrix.suffix }}${{ matrix.ext }}"
|
||||||
|
go build -ldflags '-s -w' -o "$OUTPUT_NAME" .
|
||||||
|
chmod +x "$OUTPUT_NAME"
|
||||||
|
|
||||||
|
- name: Generate checksums
|
||||||
|
run: |
|
||||||
|
OUTPUT_NAME="ushare-${{ matrix.suffix }}${{ matrix.ext }}"
|
||||||
|
sha256sum "$OUTPUT_NAME" > "$OUTPUT_NAME.sha256"
|
||||||
|
|
||||||
|
- name: Upload to release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
ushare-${{ matrix.suffix }}${{ matrix.ext }}
|
||||||
|
ushare-${{ matrix.suffix }}${{ matrix.ext }}.sha256
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1,5 +1,19 @@
|
|||||||
|
# IDE
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# Build output
|
||||||
dist
|
dist
|
||||||
xtest
|
|
||||||
|
# Data directories
|
||||||
|
data/
|
||||||
|
x-*/
|
||||||
|
|
||||||
|
# Temporary build files
|
||||||
|
internal/static/frontend
|
||||||
|
|
||||||
|
# Compiled binaries
|
||||||
|
ushare
|
||||||
|
|||||||
250
AGENTS.md
Normal file
250
AGENTS.md
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
## Build Commands
|
||||||
|
|
||||||
|
### Go Backend (Root)
|
||||||
|
```bash
|
||||||
|
# Build the Go backend binary
|
||||||
|
go build -o ushare .
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
# Run single test
|
||||||
|
go test -run TestFunctionName ./internal/pkg/tool
|
||||||
|
|
||||||
|
# Run tests in specific package
|
||||||
|
go test ./internal/pkg/tool
|
||||||
|
|
||||||
|
# Run tests with verbose output
|
||||||
|
go test -v ./...
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
./ushare -debug -address 0.0.0.0:9119 -data ./data -auth "admin:password"
|
||||||
|
```
|
||||||
|
|
||||||
|
### TypeScript Frontend (frontend/)
|
||||||
|
```bash
|
||||||
|
# Install dependencies (uses pnpm)
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
# Development server
|
||||||
|
pnpm run dev
|
||||||
|
|
||||||
|
# Build for production
|
||||||
|
pnpm run build
|
||||||
|
|
||||||
|
# Lint code
|
||||||
|
pnpm run lint
|
||||||
|
|
||||||
|
# Preview production build
|
||||||
|
pnpm run preview
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Build
|
||||||
|
```bash
|
||||||
|
# Build the complete Docker image
|
||||||
|
docker build -t ushare:latest .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Style Guidelines
|
||||||
|
|
||||||
|
### Go Backend
|
||||||
|
|
||||||
|
#### Imports
|
||||||
|
- Group imports in three sections: standard library, third-party, internal
|
||||||
|
- Keep one import per line for readability
|
||||||
|
- Example:
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
"github.com/loveuer/ushare/internal/model"
|
||||||
|
"github.com/loveuer/ushare/internal/opt"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Naming Conventions
|
||||||
|
- Exported functions/types: PascalCase (e.g., `UserManager`, `NewPassword`)
|
||||||
|
- Private functions/types: camelCase (e.g., `generateMeta`, `tokenFn`)
|
||||||
|
- Variables: camelCase (e.g., `filename`, `totalChunks`)
|
||||||
|
- Constants: PascalCase (e.g., `Meta`, `HeaderSize`, `CodeLength`)
|
||||||
|
- Interfaces: Usually implied, not explicitly declared unless needed
|
||||||
|
- Receiver names: Short, 1-2 letters (e.g., `m`, `um`, `c`)
|
||||||
|
|
||||||
|
#### Error Handling
|
||||||
|
- Use `github.com/pkg/errors` for error wrapping
|
||||||
|
- Check errors immediately after function calls
|
||||||
|
- Return errors for functions that can fail
|
||||||
|
- Use `errors.New()` for simple error messages
|
||||||
|
- Wrap errors with context when propagating up:
|
||||||
|
```go
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("invalid file code")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Struct Tags
|
||||||
|
- Use `json` tags for JSON serialization
|
||||||
|
- Use `mapstructure` tags for config parsing (viper)
|
||||||
|
- Use `-` for fields to exclude from JSON:
|
||||||
|
```go
|
||||||
|
type User struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"-"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Concurrency
|
||||||
|
- Use `sync.Mutex` for protecting shared state
|
||||||
|
- Always use `defer mutex.Unlock()` after `mutex.Lock()`
|
||||||
|
- Use goroutines with select statements for graceful shutdown
|
||||||
|
|
||||||
|
#### Testing
|
||||||
|
- Use standard `testing` package
|
||||||
|
- Test functions named `Test<FunctionName>`
|
||||||
|
- Table-driven tests are preferred:
|
||||||
|
```go
|
||||||
|
func TestFunction(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
arg ArgType
|
||||||
|
want ReturnType
|
||||||
|
}{
|
||||||
|
{"case 1", arg1, want1},
|
||||||
|
{"case 2", arg2, want2},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := Function(tt.arg); got != tt.want {
|
||||||
|
t.Errorf("Function() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Generics
|
||||||
|
- Use generics for utility functions with type parameters:
|
||||||
|
```go
|
||||||
|
func Min[T ~int | ~uint | ~float64](a, b T) T
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Context
|
||||||
|
- Use `context.Context` for cancellation signals
|
||||||
|
- Always check `ctx.Done()` in long-running goroutines
|
||||||
|
|
||||||
|
### TypeScript Frontend
|
||||||
|
|
||||||
|
#### Imports
|
||||||
|
- Use ES6 imports
|
||||||
|
- Third-party imports first, then relative imports:
|
||||||
|
```typescript
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { createUseStyles } from 'react-jss';
|
||||||
|
import { CloudBackground } from "../component/fluid/cloud.tsx";
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Naming Conventions
|
||||||
|
- Components: PascalCase (e.g., `UButton`, `Login`, `FileSharing`)
|
||||||
|
- Functions/hooks: camelCase (e.g., `useFileUpload`, `onLogin`)
|
||||||
|
- Variables: camelCase (e.g., `progress`, `loading`, `error`)
|
||||||
|
- Constants: UPPER_SNAKE_CASE (rarely used)
|
||||||
|
- Types/Interfaces: PascalCase (e.g., `UploadRes`, `LocalStore`)
|
||||||
|
|
||||||
|
#### Types
|
||||||
|
- Explicitly type function parameters and return values
|
||||||
|
- Use interfaces for object shapes:
|
||||||
|
```typescript
|
||||||
|
interface UploadRes {
|
||||||
|
code: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LocalStore {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
channel?: RTCDataChannel;
|
||||||
|
set: (id: string, name: string) => void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Components
|
||||||
|
- Use functional components with hooks
|
||||||
|
- Props as interface at top of component:
|
||||||
|
```typescript
|
||||||
|
type Props = {
|
||||||
|
onClick?: () => void;
|
||||||
|
children: ReactNode;
|
||||||
|
disabled?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const UButton: React.FC<Props> = ({ onClick, children, disabled }) => { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Styling
|
||||||
|
- Use `react-jss` with `createUseStyles` for component styles
|
||||||
|
- Define styles object with camelCase properties:
|
||||||
|
```typescript
|
||||||
|
const useStyle = createUseStyles({
|
||||||
|
container: {
|
||||||
|
display: "flex",
|
||||||
|
"&:hover": { backgroundColor: "#45a049" }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### State Management
|
||||||
|
- Use `useState` for local component state
|
||||||
|
- Use `zustand` for global state (defined in `store/` directory)
|
||||||
|
- Always destructure from store hooks:
|
||||||
|
```typescript
|
||||||
|
export const useLocalStore = create<LocalStore>()((_set) => ({
|
||||||
|
id: '',
|
||||||
|
set: (id: string) => _set({ id })
|
||||||
|
}))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Async Patterns
|
||||||
|
- Use async/await for API calls
|
||||||
|
- Handle errors with try-catch:
|
||||||
|
```typescript
|
||||||
|
try {
|
||||||
|
const result = await uploadFile(file);
|
||||||
|
} catch (err) {
|
||||||
|
setError(err.message);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Configuration
|
||||||
|
- Vite dev server proxies `/api` and `/ushare` to Go backend at `http://127.0.0.1:9119`
|
||||||
|
- WebSocket proxy configured for `/api/ulocal/ws`
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
ushare/
|
||||||
|
├── internal/
|
||||||
|
│ ├── api/ # HTTP API setup and routes
|
||||||
|
│ ├── controller/ # Business logic (user, meta, room management)
|
||||||
|
│ ├── handler/ # HTTP request handlers
|
||||||
|
│ ├── model/ # Data models (User, Meta, WS)
|
||||||
|
│ ├── opt/ # Configuration and constants
|
||||||
|
│ └── pkg/
|
||||||
|
│ ├── db/ # Database utilities
|
||||||
|
│ └── tool/ # Utility functions (password, random, etc.)
|
||||||
|
├── frontend/
|
||||||
|
│ └── src/
|
||||||
|
│ ├── api/ # API calls (auth, upload)
|
||||||
|
│ ├── component/ # Reusable UI components
|
||||||
|
│ ├── hook/ # Custom hooks (websocket, message)
|
||||||
|
│ ├── page/ # Page components (login, share, local)
|
||||||
|
│ ├── store/ # Zustand state stores
|
||||||
|
│ └── interface/ # TypeScript interfaces
|
||||||
|
└── deployment/ # Docker and nginx configs
|
||||||
|
```
|
||||||
168
README.md
Normal file
168
README.md
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
# UShare
|
||||||
|
|
||||||
|
一个简单易用的文件分享工具,支持大文件分片上传下载,以及局域网内 P2P 文件分享。
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
- 📁 大文件分片上传下载
|
||||||
|
- 🌐 网页端文件分享
|
||||||
|
- 🏠 局域网 P2P 文件分享(WebRTC)
|
||||||
|
- 📱 响应式设计,支持移动端
|
||||||
|
- 🔐 简单的用户认证
|
||||||
|
- 🚀 单一二进制,开箱即用
|
||||||
|
- 🐳 Docker 支持
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 开发模式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 克隆仓库
|
||||||
|
git clone https://github.com/loveuer/ushare.git
|
||||||
|
cd ushare
|
||||||
|
|
||||||
|
# 启动开发服务器
|
||||||
|
./dev.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
开发服务器会同时启动后端(http://0.0.0.0:9119)和前端(http://0.0.0.0:5173),前端支持热重载。
|
||||||
|
|
||||||
|
### 生产模式
|
||||||
|
|
||||||
|
#### 使用预编译二进制
|
||||||
|
|
||||||
|
从 [Releases](https://github.com/loveuer/ushare/releases) 下载对应平台的二进制文件。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux amd64
|
||||||
|
wget https://github.com/loveuer/ushare/releases/download/v0.3.2/ushare-linux-amd64
|
||||||
|
chmod +x ushare-linux-amd64
|
||||||
|
|
||||||
|
# 运行
|
||||||
|
./ushare-linux-amd64 -data ./data
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 从源码构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 构建单一二进制(包含嵌入的前端)
|
||||||
|
./make.sh
|
||||||
|
|
||||||
|
# 运行
|
||||||
|
./dist/ushare -data ./data
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Docker 部署
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t ushare:latest .
|
||||||
|
docker run -d -p 80:80 -v $(pwd)/data:/data ushare:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置
|
||||||
|
|
||||||
|
### 命令行参数
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ushare [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-debug 启用调试模式
|
||||||
|
-address string 监听地址 (default "0.0.0.0:9119")
|
||||||
|
-data string 数据目录 (default "/data")
|
||||||
|
-clean int 文件清理周期,单位:小时,0 表示不自动清理 (default 24)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 环境变量
|
||||||
|
|
||||||
|
| 环境变量 | 说明 | 默认值 |
|
||||||
|
|---------|------|--------|
|
||||||
|
| `USHARE_USERNAME` | 用户名 | `admin` |
|
||||||
|
| `USHARE_PASSWORD` | 密码 | `ushare@123` |
|
||||||
|
|
||||||
|
#### 示例
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用默认凭据(admin / ushare@123)
|
||||||
|
./ushare -data ./data
|
||||||
|
|
||||||
|
# 使用自定义凭据
|
||||||
|
export USHARE_USERNAME=myuser
|
||||||
|
export USHARE_PASSWORD=mypass
|
||||||
|
./ushare -data ./data
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
### 文件分享
|
||||||
|
|
||||||
|
1. 访问 http://localhost:9119/share
|
||||||
|
2. 点击"选择文件"选择要上传的文件
|
||||||
|
3. 点击"上传文件",等待上传完成
|
||||||
|
4. 复制生成的下载码分享给他人
|
||||||
|
|
||||||
|
### 文件下载
|
||||||
|
|
||||||
|
1. 访问 http://localhost:9119/share
|
||||||
|
2. 在"获取文件"区域输入下载码
|
||||||
|
3. 点击"获取文件"下载文件
|
||||||
|
|
||||||
|
### 局域网 P2P 分享
|
||||||
|
|
||||||
|
1. 访问 http://localhost:9119
|
||||||
|
2. 自动注册到局域网设备列表
|
||||||
|
3. 选择要分享的设备,建立 P2P 连接
|
||||||
|
4. 开始文件传输
|
||||||
|
|
||||||
|
## 移动端支持
|
||||||
|
|
||||||
|
- 响应式设计,自动适配手机、平板等设备
|
||||||
|
- 移动端(≤768px)采用垂直布局,优化触摸操作
|
||||||
|
- 按钮尺寸优化,适合手指点击
|
||||||
|
|
||||||
|
## 开发
|
||||||
|
|
||||||
|
### 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
ushare/
|
||||||
|
├── internal/ # 后端代码
|
||||||
|
│ ├── api/ # HTTP API 路由
|
||||||
|
│ ├── controller/ # 业务逻辑
|
||||||
|
│ ├── handler/ # 请求处理器
|
||||||
|
│ ├── model/ # 数据模型
|
||||||
|
│ ├── opt/ # 配置管理
|
||||||
|
│ └── pkg/ # 工具包
|
||||||
|
├── frontend/ # 前端代码
|
||||||
|
│ └── src/
|
||||||
|
│ ├── api/ # API 调用
|
||||||
|
│ ├── component/ # UI 组件
|
||||||
|
│ ├── hook/ # 自定义 Hooks
|
||||||
|
│ ├── page/ # 页面组件
|
||||||
|
│ └── store/ # 状态管理
|
||||||
|
├── .github/workflows/ # GitHub Actions
|
||||||
|
├── dist/ # 构建输出(.gitignore)
|
||||||
|
└── data/ # 数据目录(.gitignore)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 开发构建(前后端分离)
|
||||||
|
./dev.sh
|
||||||
|
|
||||||
|
# 生产构建(单一二进制)
|
||||||
|
./make.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 代码风格
|
||||||
|
|
||||||
|
请参考 [AGENTS.md](./AGENTS.md) 了解代码风格指南。
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
## 贡献
|
||||||
|
|
||||||
|
欢迎提交 Issue 和 Pull Request!
|
||||||
@@ -36,6 +36,16 @@ http {
|
|||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /api/ulocal/ws {
|
||||||
|
proxy_pass http://localhost:9119;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
|
||||||
location /ushare {
|
location /ushare {
|
||||||
proxy_pass http://localhost:9119;
|
proxy_pass http://localhost:9119;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
|
|||||||
63
dev.sh
Executable file
63
dev.sh
Executable file
@@ -0,0 +1,63 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 捕获 Ctrl+C 信号
|
||||||
|
trap 'echo ""; echo "Stopping..."; kill $(jobs -p); exit 0' SIGINT SIGTERM
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo " Starting UShare Development Server"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 构建前端(如果需要)
|
||||||
|
if [ ! -d "frontend/dist" ]; then
|
||||||
|
echo "[Frontend] Building..."
|
||||||
|
cd frontend && pnpm run build && cd ..
|
||||||
|
echo "[Frontend] Build complete!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建临时嵌入目录用于编译
|
||||||
|
mkdir -p internal/static/frontend
|
||||||
|
if [ ! -d "internal/static/frontend/dist" ]; then
|
||||||
|
echo "[Setup] Creating frontend embed directory..."
|
||||||
|
cp -r frontend/dist internal/static/frontend/
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查后端是否已构建
|
||||||
|
if [ ! -f "./ushare" ]; then
|
||||||
|
echo "[Backend] Building..."
|
||||||
|
go build -o ushare .
|
||||||
|
echo "[Backend] Build complete!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建数据目录
|
||||||
|
mkdir -p ./data
|
||||||
|
|
||||||
|
# 启动后端
|
||||||
|
echo "[Backend] Starting..."
|
||||||
|
./ushare -debug -address 0.0.0.0:9119 -data ./data &
|
||||||
|
BACKEND_PID=$!
|
||||||
|
echo "[Backend] Running on http://0.0.0.0:9119 (PID: $BACKEND_PID)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 启动前端
|
||||||
|
echo "[Frontend] Starting..."
|
||||||
|
cd frontend && pnpm run dev &
|
||||||
|
FRONTEND_PID=$!
|
||||||
|
cd ..
|
||||||
|
echo "[Frontend] Running on http://localhost:5173 (PID: $FRONTEND_PID)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo " All services started!"
|
||||||
|
echo " - Backend: http://0.0.0.0:9119"
|
||||||
|
echo " - Frontend: http://0.0.0.0:5173"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
echo "Note: Frontend hot-reload is enabled. Changes to backend code require rebuilding."
|
||||||
|
echo "Press Ctrl+C to stop all services"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 等待所有后台进程
|
||||||
|
wait
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { createUseStyles } from "react-jss";
|
import { createUseStyles } from "react-jss";
|
||||||
import { CloudBackground } from "../component/fluid/cloud.tsx";
|
import { useAuth } from "../api/auth.ts";
|
||||||
import {useAuth} from "../api/auth.ts";
|
import { UButton } from "../component/button/u-button.tsx";
|
||||||
|
|
||||||
const useClass = createUseStyles({
|
const useClass = createUseStyles({
|
||||||
container: {
|
container: {
|
||||||
@@ -14,88 +14,42 @@ const useClass = createUseStyles({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
position: 'relative',
|
backgroundColor: "#e3f2fd",
|
||||||
},
|
|
||||||
login_container: {
|
|
||||||
background: "rgba(255,255,255,.5)",
|
|
||||||
boxShadow: "0 2px 10px rgba(0, 0, 0, 0.1)",
|
|
||||||
width: "350px",
|
|
||||||
height: '100%',
|
|
||||||
position: 'absolute',
|
|
||||||
left: '70%',
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
height: '100%',
|
backgroundColor: "#C8E6C9",
|
||||||
width: '100%',
|
boxShadow: "inset 0 0 15px rgba(56, 142, 60, 0.15)",
|
||||||
|
padding: "30px",
|
||||||
|
borderRadius: "15px",
|
||||||
|
width: "350px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
flexDirection: "column",
|
||||||
alignItems: "center",
|
|
||||||
flexDirection: 'column',
|
|
||||||
color: "#1a73e8",
|
|
||||||
padding: '40px',
|
|
||||||
},
|
},
|
||||||
input: {
|
title: {
|
||||||
width: '100%',
|
color: "#2c9678",
|
||||||
marginTop: '20px',
|
marginTop: 0,
|
||||||
"& > input": {
|
marginBottom: "25px",
|
||||||
width: "calc(100% - 30px)",
|
|
||||||
padding: "12px 15px",
|
|
||||||
border: "1px solid #ddd",
|
|
||||||
borderRadius: "6px",
|
|
||||||
fontSize: "16px",
|
|
||||||
transition: "border-color 0.3s",
|
|
||||||
|
|
||||||
"&:focus": {
|
|
||||||
outline: "none",
|
|
||||||
borderColor: "#1a73e8",
|
|
||||||
boxShadow: "0 0 0 2px rgba(26, 115, 232, 0.2)",
|
|
||||||
},
|
|
||||||
"&:hover": {
|
|
||||||
borderColor: "#1a73e8",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
button: {
|
|
||||||
marginTop: '20px',
|
|
||||||
width: '100%',
|
|
||||||
"& > button": {
|
|
||||||
width: "100%",
|
|
||||||
padding: "12px",
|
|
||||||
background: "#1a73e8",
|
|
||||||
color: "white",
|
|
||||||
border: "none",
|
|
||||||
borderRadius: "6px",
|
|
||||||
fontSize: "16px",
|
|
||||||
cursor: "pointer",
|
|
||||||
transition: "background 0.3s",
|
|
||||||
"&:hover": {
|
|
||||||
background: "#1557b0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
inputContainer: {
|
inputContainer: {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
marginTop: '20px',
|
marginTop: '15px',
|
||||||
},
|
},
|
||||||
inputField: {
|
inputField: {
|
||||||
width: "calc(100% - 52px)",
|
width: "100%",
|
||||||
padding: "12px 35px 12px 15px",
|
padding: "11px",
|
||||||
border: "1px solid #ddd",
|
border: "2px solid #ddd",
|
||||||
borderRadius: "6px",
|
borderRadius: "5px",
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
|
boxSizing: "border-box",
|
||||||
transition: "border-color 0.3s",
|
transition: "border-color 0.3s",
|
||||||
|
background: "rgba(255,255,255,0.8)",
|
||||||
"&:focus": {
|
"&:focus": {
|
||||||
outline: "none",
|
outline: "none",
|
||||||
borderColor: "#1a73e8",
|
borderColor: "#2c9678",
|
||||||
boxShadow: "0 0 0 2px rgba(26, 115, 232, 0.2)",
|
|
||||||
},
|
},
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
borderColor: "#1a73e8",
|
borderColor: "#2c9678",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
iconButton: {
|
iconButton: {
|
||||||
@@ -110,12 +64,19 @@ const useClass = createUseStyles({
|
|||||||
"&:hover": {
|
"&:hover": {
|
||||||
color: '#333',
|
color: '#333',
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
marginTop: '25px',
|
||||||
|
width: '100%',
|
||||||
|
"& > button": {
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const Login: React.FC = () => {
|
export const Login: React.FC = () => {
|
||||||
const classes = useClass()
|
const classes = useClass()
|
||||||
const {login} = useAuth()
|
const { login } = useAuth()
|
||||||
const [username, setUsername] = useState("")
|
const [username, setUsername] = useState("")
|
||||||
const [password, setPassword] = useState("")
|
const [password, setPassword] = useState("")
|
||||||
const [showPassword, setShowPassword] = useState(false)
|
const [showPassword, setShowPassword] = useState(false)
|
||||||
@@ -125,59 +86,54 @@ export const Login: React.FC = () => {
|
|||||||
await login(username, password)
|
await login(username, password)
|
||||||
window.location.href = "/"
|
window.location.href = "/"
|
||||||
} catch (_e) {
|
} catch (_e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className={classes.container}>
|
return <div className={classes.container}>
|
||||||
<CloudBackground/>
|
<div className={classes.form}>
|
||||||
<div className={classes.login_container}>
|
<h2 className={classes.title}>UShare</h2>
|
||||||
<div className={classes.form}>
|
|
||||||
<h2>UShare</h2>
|
|
||||||
|
|
||||||
{/* 用户名输入框 */}
|
<div className={classes.inputContainer}>
|
||||||
<div className={classes.inputContainer}>
|
<input
|
||||||
<input
|
className={classes.inputField}
|
||||||
className={classes.inputField}
|
placeholder="请输入账号"
|
||||||
placeholder="请输入账号"
|
value={username}
|
||||||
value={username}
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
/>
|
||||||
/>
|
{username && (
|
||||||
{username && (
|
|
||||||
<button
|
|
||||||
className={classes.iconButton}
|
|
||||||
onClick={() => setUsername("")}
|
|
||||||
style={{ right: '10px', fontSize: '16px' }}
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 密码输入框 */}
|
|
||||||
<div className={classes.inputContainer}>
|
|
||||||
<input
|
|
||||||
className={classes.inputField}
|
|
||||||
placeholder="请输入密码"
|
|
||||||
type={showPassword ? "text" : "password"}
|
|
||||||
value={password}
|
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
|
||||||
/>
|
|
||||||
<button
|
<button
|
||||||
className={classes.iconButton}
|
className={classes.iconButton}
|
||||||
onMouseDown={() => setShowPassword(true)}
|
onClick={() => setUsername("")}
|
||||||
onMouseUp={() => setShowPassword(false)}
|
style={{ right: '10px', fontSize: '16px' }}
|
||||||
onMouseLeave={() => setShowPassword(false)}
|
|
||||||
style={{ right: '10px', fontSize: '12px' }}
|
|
||||||
>
|
>
|
||||||
{showPassword ? "👁" : "👁"}
|
×
|
||||||
</button>
|
</button>
|
||||||
</div>
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className={classes.button}>
|
<div className={classes.inputContainer}>
|
||||||
<button onClick={onLogin}>登录</button>
|
<input
|
||||||
</div>
|
className={classes.inputField}
|
||||||
|
placeholder="请输入密码"
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className={classes.iconButton}
|
||||||
|
onMouseDown={() => setShowPassword(true)}
|
||||||
|
onMouseUp={() => setShowPassword(false)}
|
||||||
|
onMouseLeave={() => setShowPassword(false)}
|
||||||
|
style={{ right: '10px', fontSize: '12px' }}
|
||||||
|
>
|
||||||
|
{showPassword ? "🫣" : "🙈"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={classes.button}>
|
||||||
|
<UButton onClick={onLogin}>登录</UButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ const useUploadStyle = createUseStyles({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
minHeight: "50vh",
|
||||||
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
minHeight: "auto",
|
||||||
|
padding: "20px 10px",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
backgroundColor: "#C8E6C9",
|
backgroundColor: "#C8E6C9",
|
||||||
@@ -19,10 +25,20 @@ const useUploadStyle = createUseStyles({
|
|||||||
borderRadius: "15px",
|
borderRadius: "15px",
|
||||||
width: "70%",
|
width: "70%",
|
||||||
margin: "20px 60px 20px 0",
|
margin: "20px 60px 20px 0",
|
||||||
/*todo margin 不用 px*/
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
width: "90%",
|
||||||
|
margin: "20px 0",
|
||||||
|
padding: "20px",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
color: "#2c9678"
|
color: "#2c9678",
|
||||||
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
fontSize: "1.5rem",
|
||||||
|
marginBottom: "15px",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
file: {
|
file: {
|
||||||
display: 'none',
|
display: 'none',
|
||||||
@@ -33,7 +49,11 @@ const useUploadStyle = createUseStyles({
|
|||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
color: "#2c9678",
|
color: "#2c9678",
|
||||||
marginLeft: '10px'
|
marginLeft: '10px',
|
||||||
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
fontSize: "14px",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
clean: {
|
clean: {
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
@@ -48,14 +68,12 @@ const useShowStyle = createUseStyles({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
position: "relative", // 为关闭按钮提供定位基准
|
minHeight: "50vh",
|
||||||
},
|
|
||||||
title: {
|
"@media (max-width: 768px)": {
|
||||||
color: "#2c9678",
|
minHeight: "auto",
|
||||||
marginTop: 0,
|
padding: "20px 10px",
|
||||||
display: "flex",
|
},
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center",
|
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
backgroundColor: "#C8E6C9",
|
backgroundColor: "#C8E6C9",
|
||||||
@@ -65,6 +83,25 @@ const useShowStyle = createUseStyles({
|
|||||||
width: "70%",
|
width: "70%",
|
||||||
margin: "20px 60px 20px 0",
|
margin: "20px 60px 20px 0",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
width: "90%",
|
||||||
|
margin: "20px 0",
|
||||||
|
padding: "20px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
color: "#2c9678",
|
||||||
|
marginTop: 0,
|
||||||
|
marginBottom: "25px",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
fontSize: "1.5rem",
|
||||||
|
marginBottom: "15px",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
closeButton: {
|
closeButton: {
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
@@ -78,9 +115,7 @@ const useShowStyle = createUseStyles({
|
|||||||
height: "24px",
|
height: "24px",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
// background: "#cc0000",
|
|
||||||
boxShadow: "20px 20px 60px #fff, -20px -20px 60px #fff",
|
boxShadow: "20px 20px 60px #fff, -20px -20px 60px #fff",
|
||||||
// boxShadow: "20px 20px 60px #eee",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
codeWrapper: {
|
codeWrapper: {
|
||||||
@@ -89,6 +124,10 @@ const useShowStyle = createUseStyles({
|
|||||||
borderRadius: "8px",
|
borderRadius: "8px",
|
||||||
margin: "15px 0",
|
margin: "15px 0",
|
||||||
overflowX: "auto",
|
overflowX: "auto",
|
||||||
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
padding: "0 10px",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
pre: {
|
pre: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -98,6 +137,12 @@ const useShowStyle = createUseStyles({
|
|||||||
height: '24px',
|
height: '24px',
|
||||||
"& > code": {
|
"& > code": {
|
||||||
marginLeft: "0",
|
marginLeft: "0",
|
||||||
|
fontSize: "14px",
|
||||||
|
wordBreak: "break-all",
|
||||||
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
fontSize: "12px",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
copyButton: {
|
copyButton: {
|
||||||
@@ -112,6 +157,11 @@ const useShowStyle = createUseStyles({
|
|||||||
"&:hover": {
|
"&:hover": {
|
||||||
background: "#1f6d5a",
|
background: "#1f6d5a",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
padding: "6px 12px",
|
||||||
|
fontSize: "12px",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ const useStyle = createUseStyles({
|
|||||||
backgroundColor: 'lightgray',
|
backgroundColor: 'lightgray',
|
||||||
position: "relative",
|
position: "relative",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
display: "none",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
left: {
|
left: {
|
||||||
backgroundColor: "#e3f2fd",
|
backgroundColor: "#e3f2fd",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {createUseStyles} from "react-jss";
|
import { createUseStyles } from "react-jss";
|
||||||
import {UButton} from "../../../component/button/u-button.tsx";
|
import { UButton } from "../../../component/button/u-button.tsx";
|
||||||
import {useStore} from "../../../store/share.ts";
|
import { useStore } from "../../../store/share.ts";
|
||||||
|
|
||||||
const useStyle = createUseStyles({
|
const useStyle = createUseStyles({
|
||||||
container: {
|
container: {
|
||||||
@@ -8,6 +8,12 @@ const useStyle = createUseStyles({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
minHeight: "50vh",
|
||||||
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
minHeight: "auto",
|
||||||
|
padding: "20px 10px",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
backgroundColor: "#BBDEFB",
|
backgroundColor: "#BBDEFB",
|
||||||
@@ -16,25 +22,58 @@ const useStyle = createUseStyles({
|
|||||||
borderRadius: "15px",
|
borderRadius: "15px",
|
||||||
width: "70%",
|
width: "70%",
|
||||||
margin: "20px 0 20px 60px",
|
margin: "20px 0 20px 60px",
|
||||||
/*todo margin 不用 px*/
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
width: "90%",
|
||||||
|
margin: "20px 0",
|
||||||
|
padding: "20px",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
color: '#1661ab', // 靛青
|
color: '#1661ab',
|
||||||
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
fontSize: "1.5rem",
|
||||||
|
marginBottom: "15px",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
code: {
|
code: {
|
||||||
padding: '11px',
|
padding: '11px',
|
||||||
margin: '20px 0',
|
margin: '0',
|
||||||
width: '200px',
|
width: '200px',
|
||||||
border: '2px solid #ddd',
|
border: '2px solid #ddd',
|
||||||
borderRadius: '5px',
|
borderRadius: '5px',
|
||||||
'&:active': {
|
'&:active': {
|
||||||
border: '2px solid #1661ab',
|
border: '2px solid #1661ab',
|
||||||
}
|
},
|
||||||
}
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
width: '100%',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
padding: '10px',
|
||||||
|
fontSize: '14px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
inputContainer: {
|
||||||
|
display: 'flex',
|
||||||
|
gap: '10px',
|
||||||
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'stretch',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
buttonContainer: {
|
||||||
|
display: 'flex',
|
||||||
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
justifyContent: 'left',
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
export const PanelRight = () => {
|
export const PanelRight = () => {
|
||||||
const style = useStyle()
|
const style = useStyle()
|
||||||
const {code, setCode} = useStore()
|
const { code, setCode } = useStore()
|
||||||
|
|
||||||
function onCodeChange(e: React.ChangeEvent<HTMLInputElement>) {
|
function onCodeChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||||
setCode(e.currentTarget.value)
|
setCode(e.currentTarget.value)
|
||||||
@@ -52,7 +91,7 @@ export const PanelRight = () => {
|
|||||||
return <div className={style.container}>
|
return <div className={style.container}>
|
||||||
<div className={style.form}>
|
<div className={style.form}>
|
||||||
<h2 className={style.title}>获取文件</h2>
|
<h2 className={style.title}>获取文件</h2>
|
||||||
<div>
|
<div className={style.inputContainer}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className={style.code}
|
className={style.code}
|
||||||
@@ -60,7 +99,9 @@ export const PanelRight = () => {
|
|||||||
value={code}
|
value={code}
|
||||||
onChange={onCodeChange}
|
onChange={onCodeChange}
|
||||||
/>
|
/>
|
||||||
<UButton style={{marginLeft: '10px'}} onClick={onFetchFile}>获取文件</UButton>
|
<div className={style.buttonContainer}>
|
||||||
|
<UButton onClick={onFetchFile}>获取文件</UButton>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ const useStyle = createUseStyles({
|
|||||||
height: "100vh",
|
height: "100vh",
|
||||||
display: "grid",
|
display: "grid",
|
||||||
gridTemplateColumns: "40% 20% 40%",
|
gridTemplateColumns: "40% 20% 40%",
|
||||||
|
|
||||||
|
"@media (max-width: 768px)": {
|
||||||
|
gridTemplateColumns: "100%",
|
||||||
|
gridTemplateRows: "auto auto",
|
||||||
|
overflowY: "auto",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -20,7 +26,7 @@ export const FileSharing = () => {
|
|||||||
const style = useStyle()
|
const style = useStyle()
|
||||||
return <div className={style.container}>
|
return <div className={style.container}>
|
||||||
<PanelLeft />
|
<PanelLeft />
|
||||||
<PanelMid/>
|
<PanelMid />
|
||||||
<PanelRight/>
|
<PanelRight/>
|
||||||
</div>
|
</div>
|
||||||
};
|
};
|
||||||
@@ -5,6 +5,7 @@ import react from '@vitejs/plugin-react'
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
server: {
|
server: {
|
||||||
|
host: '0.0.0.0',
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://127.0.0.1:9119',
|
target: 'http://127.0.0.1:9119',
|
||||||
|
|||||||
33
go.mod
33
go.mod
@@ -3,50 +3,65 @@ module github.com/loveuer/ushare
|
|||||||
go 1.24.2
|
go 1.24.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/glebarez/sqlite v1.11.0
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/gorilla/websocket v1.5.3
|
||||||
|
github.com/jedib0t/go-pretty/v6 v6.6.7
|
||||||
github.com/loveuer/nf v0.3.5
|
github.com/loveuer/nf v0.3.5
|
||||||
|
github.com/matoous/go-nanoid/v2 v2.1.0
|
||||||
|
github.com/mileusna/useragent v1.3.5
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
|
github.com/spf13/cast v1.7.1
|
||||||
github.com/spf13/viper v1.20.1
|
github.com/spf13/viper v1.20.1
|
||||||
|
golang.org/x/crypto v0.32.0
|
||||||
|
gorm.io/driver/mysql v1.6.0
|
||||||
|
gorm.io/driver/postgres v1.6.0
|
||||||
|
gorm.io/gorm v1.30.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.0 // indirect
|
dario.cat/mergo v1.0.0 // indirect
|
||||||
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||||
github.com/ProtonMail/go-crypto v1.0.0 // indirect
|
github.com/ProtonMail/go-crypto v1.0.0 // indirect
|
||||||
github.com/cloudflare/circl v1.3.7 // indirect
|
github.com/cloudflare/circl v1.3.7 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/emirpasic/gods v1.18.1 // indirect
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
github.com/fatih/color v1.17.0 // indirect
|
github.com/fatih/color v1.17.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||||
|
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
||||||
github.com/go-git/go-git/v5 v5.12.0 // indirect
|
github.com/go-git/go-git/v5 v5.12.0 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/gorilla/websocket v1.5.3 // indirect
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
|
github.com/jackc/pgx/v5 v5.6.0 // indirect
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
github.com/jedib0t/go-pretty/v6 v6.6.7 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||||
github.com/matoous/go-nanoid/v2 v2.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/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
github.com/mileusna/useragent v1.3.5 // indirect
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||||
github.com/skeema/knownhosts v1.2.2 // indirect
|
github.com/skeema/knownhosts v1.2.2 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/afero v1.12.0 // indirect
|
github.com/spf13/afero v1.12.0 // indirect
|
||||||
github.com/spf13/cast v1.7.1 // indirect
|
|
||||||
github.com/spf13/pflag v1.0.6 // indirect
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
golang.org/x/crypto v0.32.0 // indirect
|
|
||||||
golang.org/x/mod v0.17.0 // indirect
|
golang.org/x/mod v0.17.0 // indirect
|
||||||
golang.org/x/net v0.33.0 // indirect
|
golang.org/x/net v0.33.0 // indirect
|
||||||
golang.org/x/sync v0.11.0 // indirect
|
golang.org/x/sync v0.11.0 // indirect
|
||||||
@@ -55,4 +70,8 @@ require (
|
|||||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
modernc.org/libc v1.22.5 // indirect
|
||||||
|
modernc.org/mathutil v1.5.0 // indirect
|
||||||
|
modernc.org/memory v1.5.0 // indirect
|
||||||
|
modernc.org/sqlite v1.23.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
54
go.sum
54
go.sum
@@ -1,5 +1,7 @@
|
|||||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||||
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||||
@@ -18,6 +20,8 @@ github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
||||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||||
@@ -28,6 +32,10 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
|||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
||||||
|
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||||
|
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||||
|
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||||
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
|
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
|
||||||
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
|
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||||
@@ -38,20 +46,36 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj
|
|||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||||
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
|
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
|
||||||
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 h1:y3N7Bm7Y9/CtpiVkw/ZWj6lSlDF3F74SfKwfTCer72Q=
|
||||||
|
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||||
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/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
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-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||||
|
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
github.com/jedib0t/go-pretty/v6 v6.6.7 h1:m+LbHpm0aIAPLzLbMfn8dc3Ht8MW7lsSO4MPItz/Uuo=
|
github.com/jedib0t/go-pretty/v6 v6.6.7 h1:m+LbHpm0aIAPLzLbMfn8dc3Ht8MW7lsSO4MPItz/Uuo=
|
||||||
github.com/jedib0t/go-pretty/v6 v6.6.7/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
|
github.com/jedib0t/go-pretty/v6 v6.6.7/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
|
||||||
|
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/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
@@ -84,6 +108,9 @@ 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/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
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.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
@@ -110,6 +137,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
@@ -144,8 +172,7 @@ golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
|||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -161,8 +188,6 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/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.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
|
||||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.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=
|
||||||
@@ -170,8 +195,8 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
|
|||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
@@ -179,8 +204,6 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
|
||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
|
||||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
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=
|
||||||
@@ -198,5 +221,20 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
|||||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 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.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
|
||||||
|
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
|
||||||
|
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
|
||||||
|
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
|
||||||
|
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
|
||||||
|
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
|
||||||
|
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
||||||
|
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||||
|
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||||
|
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||||
|
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||||
|
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||||
|
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
||||||
|
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||||
|
|||||||
@@ -2,19 +2,20 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/loveuer/nf"
|
"github.com/loveuer/nf"
|
||||||
"github.com/loveuer/nf/nft/log"
|
"github.com/loveuer/nf/nft/log"
|
||||||
"github.com/loveuer/nf/nft/tool"
|
"github.com/loveuer/nf/nft/tool"
|
||||||
"github.com/loveuer/ushare/internal/handler"
|
"github.com/loveuer/ushare/internal/handler"
|
||||||
"github.com/loveuer/ushare/internal/opt"
|
"github.com/loveuer/ushare/internal/opt"
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Start(ctx context.Context) <-chan struct{} {
|
func Start(ctx context.Context) <-chan struct{} {
|
||||||
app := nf.New(nf.Config{BodyLimit: 10 * 1024 * 1024 * 1024})
|
app := nf.New(nf.Config{BodyLimit: 10 * 1024 * 1024 * 1024})
|
||||||
|
|
||||||
app.Get("/api/available", func(c *nf.Ctx) error {
|
app.Get("/api/healthz", func(c *nf.Ctx) error {
|
||||||
return c.SendStatus(http.StatusOK)
|
return c.SendStatus(http.StatusOK)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -33,6 +34,9 @@ func Start(ctx context.Context) <-chan struct{} {
|
|||||||
api.Get("/ws", handler.LocalWS())
|
api.Get("/ws", handler.LocalWS())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 静态文件服务 - 作为中间件处理
|
||||||
|
app.Use(handler.ServeFrontendMiddleware())
|
||||||
|
|
||||||
ready := make(chan struct{})
|
ready := make(chan struct{})
|
||||||
ln, err := net.Listen("tcp", opt.Cfg.Address)
|
ln, err := net.Listen("tcp", opt.Cfg.Address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ func (um *userManager) Login(username string, password string) (*model.User, err
|
|||||||
now = time.Now()
|
now = time.Now()
|
||||||
)
|
)
|
||||||
|
|
||||||
if username != "admin" {
|
if username != opt.Cfg.Username {
|
||||||
return nil, errors.New("账号或密码错误")
|
return nil, errors.New("账号或密码错误")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tool.ComparePassword(password, opt.Cfg.Auth) {
|
if !tool.ComparePassword(password, opt.Cfg.Password) {
|
||||||
return nil, errors.New("账号或密码错误")
|
return nil, errors.New("账号或密码错误")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func AuthVerify() nf.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return func(c *nf.Ctx) error {
|
return func(c *nf.Ctx) error {
|
||||||
if opt.Cfg.Auth == "" {
|
if opt.Cfg.Username == "" || opt.Cfg.Password == "" {
|
||||||
return c.Next()
|
return c.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
114
internal/handler/static.go
Normal file
114
internal/handler/static.go
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/loveuer/nf"
|
||||||
|
"github.com/loveuer/nf/nft/log"
|
||||||
|
"github.com/loveuer/ushare/internal/static"
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ServeFrontend() nf.HandlerFunc {
|
||||||
|
assets := static.Frontend()
|
||||||
|
|
||||||
|
return func(c *nf.Ctx) error {
|
||||||
|
path := strings.TrimPrefix(c.Path(), "/")
|
||||||
|
if path == "" || path == "/" {
|
||||||
|
path = "index.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := assets.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() == "file does not exist" {
|
||||||
|
return serveIndex(assets, c)
|
||||||
|
}
|
||||||
|
return c.SendStatus(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
stat, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return c.SendStatus(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stat.IsDir() {
|
||||||
|
return serveIndex(assets, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
io.Copy(c.Writer, file)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServeFrontendMiddleware() nf.HandlerFunc {
|
||||||
|
assets := static.Frontend()
|
||||||
|
|
||||||
|
return func(c *nf.Ctx) error {
|
||||||
|
path := c.Path()
|
||||||
|
|
||||||
|
if strings.HasPrefix(path, "/api") || strings.HasPrefix(path, "/ushare") {
|
||||||
|
return c.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
filePath := strings.TrimPrefix(path, "/")
|
||||||
|
if filePath == "" || filePath == "/" {
|
||||||
|
filePath = "index.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := assets.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return serveIndex(assets, c)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
stat, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return c.SendStatus(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stat.IsDir() {
|
||||||
|
return serveIndex(assets, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetHeader("Content-Type", getContentType(filePath))
|
||||||
|
io.Copy(c.Writer, file)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func serveIndex(assets fs.FS, c *nf.Ctx) error {
|
||||||
|
index, err := assets.Open("index.html")
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to open index.html: %v", err)
|
||||||
|
return c.SendStatus(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
defer index.Close()
|
||||||
|
|
||||||
|
c.SetHeader("Content-Type", "text/html; charset=utf-8")
|
||||||
|
io.Copy(c.Writer, index)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getContentType(path string) string {
|
||||||
|
if strings.HasSuffix(path, ".html") {
|
||||||
|
return "text/html; charset=utf-8"
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(path, ".css") {
|
||||||
|
return "text/css; charset=utf-8"
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(path, ".js") {
|
||||||
|
return "application/javascript; charset=utf-8"
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(path, ".png") {
|
||||||
|
return "image/png"
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(path, ".jpg") || strings.HasSuffix(path, ".jpeg") {
|
||||||
|
return "image/jpeg"
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(path, ".svg") {
|
||||||
|
return "image/svg+xml"
|
||||||
|
}
|
||||||
|
return "application/octet-stream"
|
||||||
|
}
|
||||||
@@ -4,13 +4,15 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"github.com/loveuer/nf/nft/log"
|
"github.com/loveuer/nf/nft/log"
|
||||||
"github.com/loveuer/ushare/internal/pkg/tool"
|
"github.com/loveuer/ushare/internal/pkg/tool"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
Debug bool
|
Debug bool
|
||||||
Address string
|
Address string
|
||||||
DataPath string
|
DataPath string
|
||||||
Auth string
|
Username string
|
||||||
|
Password string
|
||||||
CleanInterval int
|
CleanInterval int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,8 +21,22 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Init(_ context.Context) {
|
func Init(_ context.Context) {
|
||||||
if Cfg.Auth != "" {
|
if Cfg.Username == "" {
|
||||||
Cfg.Auth = tool.NewPassword(Cfg.Auth)
|
Cfg.Username = "admin"
|
||||||
log.Debug("opt.Init: encrypted password = %s", Cfg.Auth)
|
}
|
||||||
|
if Cfg.Password == "" {
|
||||||
|
Cfg.Password = "ushare@123"
|
||||||
|
}
|
||||||
|
|
||||||
|
Cfg.Password = tool.NewPassword(Cfg.Password)
|
||||||
|
log.Debug("opt.Init: username = %s, encrypted password = %s", Cfg.Username, Cfg.Password)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadFromEnv() {
|
||||||
|
if username := os.Getenv("USHARE_USERNAME"); username != "" {
|
||||||
|
Cfg.Username = username
|
||||||
|
}
|
||||||
|
if password := os.Getenv("USHARE_PASSWORD"); password != "" {
|
||||||
|
Cfg.Password = password
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"au99999/internal/opt"
|
|
||||||
"au99999/pkg/tool"
|
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/loveuer/ushare/internal/opt"
|
||||||
|
"github.com/loveuer/ushare/internal/pkg/tool"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
14
internal/static/static.go
Normal file
14
internal/static/static.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package static
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"io/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed frontend/dist
|
||||||
|
var FrontendFS embed.FS
|
||||||
|
|
||||||
|
func Frontend() fs.FS {
|
||||||
|
sub, _ := fs.Sub(FrontendFS, "frontend/dist")
|
||||||
|
return sub
|
||||||
|
}
|
||||||
3
main.go
3
main.go
@@ -16,10 +16,11 @@ func init() {
|
|||||||
flag.BoolVar(&opt.Cfg.Debug, "debug", false, "debug mode")
|
flag.BoolVar(&opt.Cfg.Debug, "debug", false, "debug mode")
|
||||||
flag.StringVar(&opt.Cfg.Address, "address", "0.0.0.0:9119", "")
|
flag.StringVar(&opt.Cfg.Address, "address", "0.0.0.0:9119", "")
|
||||||
flag.StringVar(&opt.Cfg.DataPath, "data", "/data", "")
|
flag.StringVar(&opt.Cfg.DataPath, "data", "/data", "")
|
||||||
flag.StringVar(&opt.Cfg.Auth, "auth", "", "auth required(admin, password)")
|
|
||||||
flag.IntVar(&opt.Cfg.CleanInterval, "clean", 24, "清理文件的周期, 单位: 小时, 0 则表示不自动清理")
|
flag.IntVar(&opt.Cfg.CleanInterval, "clean", 24, "清理文件的周期, 单位: 小时, 0 则表示不自动清理")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
opt.LoadFromEnv()
|
||||||
|
|
||||||
if opt.Cfg.Debug {
|
if opt.Cfg.Debug {
|
||||||
log.SetLogLevel(log.LogLevelDebug)
|
log.SetLogLevel(log.LogLevelDebug)
|
||||||
tool.TablePrinter(opt.Cfg)
|
tool.TablePrinter(opt.Cfg)
|
||||||
|
|||||||
49
make.sh
Executable file
49
make.sh
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo " Building UShare Single Binary"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 清理旧的构建产物
|
||||||
|
echo "[Cleanup] Removing old build files..."
|
||||||
|
rm -rf dist
|
||||||
|
rm -f ushare
|
||||||
|
rm -rf internal/static/frontend
|
||||||
|
|
||||||
|
# 构建前端
|
||||||
|
echo ""
|
||||||
|
echo "[Frontend] Building..."
|
||||||
|
cd frontend
|
||||||
|
pnpm run build
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# 复制前端构建产物到 internal/static
|
||||||
|
echo "[Frontend] Copying dist files..."
|
||||||
|
mkdir -p internal/static/frontend
|
||||||
|
cp -r frontend/dist internal/static/frontend/
|
||||||
|
|
||||||
|
# 构建后端(包含嵌入的前端文件)
|
||||||
|
echo ""
|
||||||
|
echo "[Backend] Building with embedded frontend..."
|
||||||
|
mkdir -p dist
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags '-s -w' -o dist/ushare .
|
||||||
|
|
||||||
|
# 清理临时文件
|
||||||
|
echo ""
|
||||||
|
echo "[Cleanup] Removing temporary files..."
|
||||||
|
rm -rf internal/static/frontend
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo " Build Complete!"
|
||||||
|
echo " Binary: dist/ushare"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
echo "Usage:"
|
||||||
|
echo " ./dist/ushare -debug -address 0.0.0.0:9119 -data ./data -auth \"admin:password\""
|
||||||
|
echo ""
|
||||||
|
echo " Development: ./dev.sh"
|
||||||
|
echo " Production: ./make.sh && ./dist/ushare ..."
|
||||||
Reference in New Issue
Block a user