init: 0.1.0
feat: 基本功能有了 1. 分片上传 2. code 回显 3. 下载 todo: 断点上传
This commit is contained in:
parent
cfd4e8cb6d
commit
440aa96ad6
31
Dockerfile
Normal file
31
Dockerfile
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# 第一阶段:构建前端
|
||||||
|
FROM node:20-alpine AS frontend-builder
|
||||||
|
WORKDIR /app
|
||||||
|
RUN npm install -g pnpm
|
||||||
|
COPY frontend/package.json frontend/pnpm-lock.yaml* ./
|
||||||
|
RUN pnpm install
|
||||||
|
COPY frontend .
|
||||||
|
RUN pnpm run build
|
||||||
|
|
||||||
|
# 第二阶段:构建 Golang 后端
|
||||||
|
FROM golang:alpine AS backend-builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
COPY main.go internal ./
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux go build -o main .
|
||||||
|
|
||||||
|
# 第三阶段:生成最终镜像
|
||||||
|
FROM nginx:alpine
|
||||||
|
COPY --from=frontend-builder /app/dist /usr/share/nginx/html
|
||||||
|
COPY --from=backend-builder /app/main /app/main
|
||||||
|
|
||||||
|
# 配置 Nginx
|
||||||
|
RUN rm /etc/nginx/conf.d/default.conf
|
||||||
|
COPY deployment/nginx.conf /etc/nginx/conf.d
|
||||||
|
|
||||||
|
# 开放端口
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
# 启动服务
|
||||||
|
CMD sh -c "/app/main & nginx -g 'daemon off;'"
|
16
deployment/nginx.conf
Normal file
16
deployment/nginx.conf
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api {
|
||||||
|
proxy_pass http://localhost:9119;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
}
|
76
frontend/src/api/upload.ts
Normal file
76
frontend/src/api/upload.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
interface UploadRes {
|
||||||
|
code: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useFileUpload = () => {
|
||||||
|
const [progress, setProgress] = useState(0);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const uploadFile = async (file: File): Promise<string> => {
|
||||||
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
|
setProgress(0);
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`[D] api.Upload: upload file = ${file.name}, size = ${file.size}`, file);
|
||||||
|
const url = `/api/share/${file.name}`;
|
||||||
|
|
||||||
|
// 1. 初始化上传
|
||||||
|
const res1 = await fetch(url, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {"X-File-Size": file.size.toString()}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res1.ok) {
|
||||||
|
throw new Error("上传失败<1>");
|
||||||
|
}
|
||||||
|
|
||||||
|
const j1 = await res1.json() as UploadRes;
|
||||||
|
if (!j1.code) {
|
||||||
|
throw new Error("上传失败<2>");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 准备分片上传
|
||||||
|
const CHUNK_SIZE = 1024 * 1024;
|
||||||
|
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
|
||||||
|
const code = j1.code;
|
||||||
|
|
||||||
|
// 3. 上传分片
|
||||||
|
for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
|
||||||
|
const start = chunkIndex * CHUNK_SIZE;
|
||||||
|
const end = Math.min(start + CHUNK_SIZE, file.size);
|
||||||
|
const chunk = file.slice(start, end);
|
||||||
|
|
||||||
|
const res = await fetch(`/api/share/${code}`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Range": `bytes=${start}-${end - 1}`,
|
||||||
|
"Content-Type": "application/octet-stream"
|
||||||
|
},
|
||||||
|
body: chunk
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const err = await res.text();
|
||||||
|
throw new Error(`上传失败<3>: ${err}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新进度
|
||||||
|
// const currentProgress = Number(((chunkIndex + 1) / totalChunks * 100).toFixed(2)); // 小数
|
||||||
|
const currentProgress = Math.round(((chunkIndex + 1) / totalChunks) * 100); // 整数 0-100
|
||||||
|
setProgress(currentProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
return code;
|
||||||
|
} catch (err) {
|
||||||
|
throw err; // 将错误继续抛出以便组件处理
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { uploadFile, progress, loading, error };
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
import React, {ReactNode} from 'react';
|
import React, {ReactNode} from 'react';
|
||||||
import {createUseStyles} from "react-jss";
|
import {createUseStyles} from 'react-jss';
|
||||||
|
|
||||||
const useStyle = createUseStyles({
|
const useStyle = createUseStyles({
|
||||||
ubutton: {
|
ubutton: {
|
||||||
@ -10,6 +10,8 @@ const useStyle = createUseStyles({
|
|||||||
borderRadius: "5px",
|
borderRadius: "5px",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
transition: "background-color 0.3s",
|
transition: "background-color 0.3s",
|
||||||
|
position: 'relative',
|
||||||
|
overflow: 'hidden',
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
backgroundColor: "#45a049",
|
backgroundColor: "#45a049",
|
||||||
},
|
},
|
||||||
@ -17,17 +19,84 @@ const useStyle = createUseStyles({
|
|||||||
backgroundColor: "#a5d6a7",
|
backgroundColor: "#a5d6a7",
|
||||||
cursor: "not-allowed",
|
cursor: "not-allowed",
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
})
|
loadingContent: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '8px',
|
||||||
|
},
|
||||||
|
spinner: {
|
||||||
|
animation: '$spin 1s linear infinite',
|
||||||
|
border: '2px solid white',
|
||||||
|
borderTopColor: 'transparent',
|
||||||
|
borderRadius: '50%',
|
||||||
|
width: '16px',
|
||||||
|
height: '16px',
|
||||||
|
},
|
||||||
|
'@keyframes spin': {
|
||||||
|
'0%': {transform: 'rotate(0deg)'},
|
||||||
|
'100%': {transform: 'rotate(360deg)'},
|
||||||
|
},
|
||||||
|
progressBar: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
height: '3px',
|
||||||
|
backgroundColor: 'rgba(255,255,255,0.5)',
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
progressFill: {
|
||||||
|
height: '100%',
|
||||||
|
backgroundColor: 'white',
|
||||||
|
transition: 'width 0.3s ease',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onClick: () => void;
|
onClick?: () => void;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
style?: React.CSSProperties | undefined;
|
style?: React.CSSProperties;
|
||||||
|
loading?: boolean;
|
||||||
|
process?: number;
|
||||||
};
|
};
|
||||||
export const UButton: React.FC<Props> = ({onClick, children, style,disabled = false}) => {
|
|
||||||
const classes= useStyle()
|
export const UButton: React.FC<Props> = ({
|
||||||
return <button style={style} className={classes.ubutton} disabled={disabled} onClick={onClick}>
|
onClick,
|
||||||
|
children,
|
||||||
|
disabled = false,
|
||||||
|
style,
|
||||||
|
loading,
|
||||||
|
process
|
||||||
|
}) => {
|
||||||
|
const classes = useStyle();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
style={style}
|
||||||
|
className={classes.ubutton}
|
||||||
|
disabled={disabled || loading}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{/* 显示加载状态或默认内容 */}
|
||||||
|
{loading ? (
|
||||||
|
<div className={classes.loadingContent}>
|
||||||
|
<span className={classes.spinner}/>
|
||||||
{children}
|
{children}
|
||||||
|
{process && `(${process}%)`}
|
||||||
|
</div>
|
||||||
|
) : children}
|
||||||
|
|
||||||
|
{/* 显示进度条 */}
|
||||||
|
{process !== undefined && (
|
||||||
|
<div className={classes.progressBar}>
|
||||||
|
<div
|
||||||
|
className={classes.progressFill}
|
||||||
|
style={{width: `${Math.min(Math.max(process, 0), 100)}%`}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
}
|
);
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
import {createRoot} from "react-dom/client";
|
import {createRoot} from "react-dom/client";
|
||||||
import {useEffect, useState} from "react";
|
import {useState} from "react";
|
||||||
import {createUseStyles} from "react-jss";
|
import {createUseStyles} from "react-jss";
|
||||||
|
|
||||||
const useStyle = createUseStyles({
|
const useStyle = createUseStyles({
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
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 React from "react";
|
import React, {useState} from "react";
|
||||||
import {useStore} from "../../store/share.ts";
|
import {useStore} from "../../store/share.ts";
|
||||||
import {message} from "../../component/message/u-message.tsx";
|
import {message} from "../../component/message/u-message.tsx";
|
||||||
|
import {useFileUpload} from "../../api/upload.ts";
|
||||||
|
|
||||||
const useStyle = createUseStyles({
|
const useUploadStyle = createUseStyles({
|
||||||
container: {
|
container: {
|
||||||
backgroundColor: "#e3f2fd",
|
backgroundColor: "#e3f2fd",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -41,13 +42,93 @@ const useStyle = createUseStyles({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
interface RespNew {
|
const useShowStyle = createUseStyles({
|
||||||
code: string
|
container: {
|
||||||
|
backgroundColor: "#e3f2fd",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
position: "relative", // 为关闭按钮提供定位基准
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
color: "#2c9678",
|
||||||
|
marginTop: 0,
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
backgroundColor: "#C8E6C9",
|
||||||
|
boxShadow: "inset 0 0 15px rgba(56, 142, 60, 0.15)",
|
||||||
|
padding: "30px",
|
||||||
|
borderRadius: "15px",
|
||||||
|
width: "70%",
|
||||||
|
margin: "20px 60px 20px 0",
|
||||||
|
position: "relative",
|
||||||
|
},
|
||||||
|
closeButton: {
|
||||||
|
position: "absolute",
|
||||||
|
top: "10px",
|
||||||
|
right: "10px",
|
||||||
|
background: "transparent",
|
||||||
|
color: "white",
|
||||||
|
border: "none",
|
||||||
|
borderRadius: "50%",
|
||||||
|
width: "24px",
|
||||||
|
height: "24px",
|
||||||
|
cursor: "pointer",
|
||||||
|
"&:hover": {
|
||||||
|
// background: "#cc0000",
|
||||||
|
boxShadow: "20px 20px 60px #fff, -20px -20px 60px #fff",
|
||||||
|
// boxShadow: "20px 20px 60px #eee",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
codeWrapper: {
|
||||||
|
backgroundColor: "rgba(255,255,255,0.8)",
|
||||||
|
padding: "0 15px",
|
||||||
|
borderRadius: "8px",
|
||||||
|
margin: "15px 0",
|
||||||
|
overflowX: "auto",
|
||||||
|
},
|
||||||
|
pre: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
color: 'black',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: '24px',
|
||||||
|
"& > code": {
|
||||||
|
marginLeft: "0",
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
copyButton: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
background: "#2c9678",
|
||||||
|
color: "white",
|
||||||
|
border: "none",
|
||||||
|
padding: "8px 16px",
|
||||||
|
borderRadius: "4px",
|
||||||
|
cursor: "pointer",
|
||||||
|
transition: "background 0.3s",
|
||||||
|
"&:hover": {
|
||||||
|
background: "#1f6d5a",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export const PanelLeft = () => {
|
export const PanelLeft = () => {
|
||||||
const style = useStyle()
|
const [code, set_code] = useState("")
|
||||||
|
|
||||||
|
if (code) {
|
||||||
|
return <PanelLeftShow code={code} set_code={set_code} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return <PanelLeftUpload set_code={set_code}/>
|
||||||
|
};
|
||||||
|
|
||||||
|
const PanelLeftUpload: React.FC<{ set_code: (code:string) => void }> = ({set_code}) => {
|
||||||
|
const style = useUploadStyle()
|
||||||
const {file, setFile} = useStore()
|
const {file, setFile} = useStore()
|
||||||
|
const {uploadFile, progress, loading} = useFileUpload();
|
||||||
|
|
||||||
function onFileSelect() {
|
function onFileSelect() {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -64,55 +145,8 @@ export const PanelLeft = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[D] onFileUpload: upload file = ${file.name}, size = ${file.size}`, file)
|
const code = await uploadFile(file)
|
||||||
|
set_code(code)
|
||||||
let res1 = await fetch(`/api/share/${file.name}`, {
|
|
||||||
method: "PUT",
|
|
||||||
headers: {"X-File-Size": file.size.toString()}
|
|
||||||
})
|
|
||||||
let j1 = await res1.json() as RespNew
|
|
||||||
console.log('[D] onFileUpload: json 1 =', j1)
|
|
||||||
if (!j1.code) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// todo: for 循环上传文件分片直到上传完成
|
|
||||||
|
|
||||||
// 2. 分片上传配置
|
|
||||||
const CHUNK_SIZE = 1024 * 1024 // 1MB分片
|
|
||||||
const totalChunks = Math.ceil(file.size / CHUNK_SIZE)
|
|
||||||
const code = j1.code
|
|
||||||
|
|
||||||
// 3. 循环上传所有分片
|
|
||||||
for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
|
|
||||||
const start = chunkIndex * CHUNK_SIZE
|
|
||||||
const end = Math.min(start + CHUNK_SIZE, file.size)
|
|
||||||
const chunk = file.slice(start, end)
|
|
||||||
|
|
||||||
// 4. 构造Range头(bytes=start-end)
|
|
||||||
const rangeHeader = `bytes=${start}-${end - 1}` // end-1因为Range是闭区间
|
|
||||||
|
|
||||||
// 5. 上传分片
|
|
||||||
const res = await fetch(`/api/share/${code}`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Range": rangeHeader,
|
|
||||||
"Content-Type": "application/octet-stream" // 二进制流类型
|
|
||||||
},
|
|
||||||
body: chunk
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
const err = await res.text()
|
|
||||||
throw new Error(`分片 ${chunkIndex} 上传失败: ${err}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`[D] 分片进度: ${chunkIndex + 1}/${totalChunks}`,
|
|
||||||
`(${Math.round((chunkIndex + 1)/totalChunks*100)}%)`)
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[D] 所有分片上传完成')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFileClean() {
|
function onFileClean() {
|
||||||
@ -123,13 +157,17 @@ export const PanelLeft = () => {
|
|||||||
<div className={style.form}>
|
<div className={style.form}>
|
||||||
<h2 className={style.title}>上传文件</h2>
|
<h2 className={style.title}>上传文件</h2>
|
||||||
{
|
{
|
||||||
!file &&
|
!file && !loading &&
|
||||||
<UButton onClick={onFileSelect}>选择文件</UButton>
|
<UButton onClick={onFileSelect}>选择文件</UButton>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
file &&
|
file && !loading &&
|
||||||
<UButton onClick={onFileUpload}>上传文件</UButton>
|
<UButton onClick={onFileUpload}>上传文件</UButton>
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
loading &&
|
||||||
|
<UButton process={progress} loading={loading}>上传中</UButton>
|
||||||
|
}
|
||||||
<input type="file" className={style.file} id="real-file-input" onChange={onFileChange}/>
|
<input type="file" className={style.file} id="real-file-input" onChange={onFileChange}/>
|
||||||
{
|
{
|
||||||
file &&
|
file &&
|
||||||
@ -140,4 +178,44 @@ export const PanelLeft = () => {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const PanelLeftShow: React.FC<{ code: string; set_code: (code: string) => void }> = ({ code, set_code }) => {
|
||||||
|
const classes = useShowStyle();
|
||||||
|
|
||||||
|
const handleCopy = async () => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(code);
|
||||||
|
message.success("复制成功!");
|
||||||
|
} catch (err) {
|
||||||
|
message.warning("复制失败,请手动选择文本复制");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.container}>
|
||||||
|
|
||||||
|
<div className={classes.form}>
|
||||||
|
<button
|
||||||
|
className={classes.closeButton}
|
||||||
|
onClick={() => set_code('')}
|
||||||
|
aria-label="关闭"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
<h2 className={classes.title}>
|
||||||
|
上传成功!
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div className={classes.codeWrapper}>
|
||||||
|
<pre className={classes.pre}>
|
||||||
|
<code>{code}</code>
|
||||||
|
<button className={classes.copyButton} onClick={handleCopy}>
|
||||||
|
一键复制
|
||||||
|
</button>
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
@ -40,11 +40,11 @@ var (
|
|||||||
MetaManager = &meta{m: make(map[string]*metaInfo)}
|
MetaManager = &meta{m: make(map[string]*metaInfo)}
|
||||||
)
|
)
|
||||||
|
|
||||||
const letters = "1234567890abcdefghijklmnopqrstuvwxyz"
|
const letters = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
|
||||||
func (m *meta) New(size int64, filename, ip string) (string, error) {
|
func (m *meta) New(size int64, filename, ip string) (string, error) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
code, err := gonanoid.Generate(letters, 16)
|
code, err := gonanoid.Generate(letters, opt.CodeLength)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ func ShareUpload() nf.HandlerFunc {
|
|||||||
rangeValidator := regexp.MustCompile(`^bytes=\d+-\d+$`)
|
rangeValidator := regexp.MustCompile(`^bytes=\d+-\d+$`)
|
||||||
return func(c *nf.Ctx) error {
|
return func(c *nf.Ctx) error {
|
||||||
code := strings.TrimSpace(c.Param("code"))
|
code := strings.TrimSpace(c.Param("code"))
|
||||||
if len(code) != 16 {
|
if len(code) != opt.CodeLength {
|
||||||
return c.Status(http.StatusBadRequest).JSON(map[string]string{"msg": "invalid file code"})
|
return c.Status(http.StatusBadRequest).JSON(map[string]string{"msg": "invalid file code"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import "path/filepath"
|
|||||||
const (
|
const (
|
||||||
Meta = ".meta."
|
Meta = ".meta."
|
||||||
HeaderSize = "X-File-Size"
|
HeaderSize = "X-File-Size"
|
||||||
|
CodeLength = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
func FilePath(code string) string {
|
func FilePath(code string) string {
|
||||||
|
3
main.go
3
main.go
@ -13,12 +13,13 @@ import (
|
|||||||
|
|
||||||
func init() {
|
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:80", "")
|
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.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if opt.Cfg.Debug {
|
if opt.Cfg.Debug {
|
||||||
log.SetLogLevel(log.LogLevelDebug)
|
log.SetLogLevel(log.LogLevelDebug)
|
||||||
|
log.Debug("start server with debug mode")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user