移动设备改进
This commit is contained in:
88
afi.go
88
afi.go
@@ -31,6 +31,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const ver = "26.04.03"
|
||||
|
||||
// rowTemplate 定义文件列表中的每一行数据结构
|
||||
type rowTemplate struct {
|
||||
Name string // 文件/文件夹名称
|
||||
@@ -53,15 +55,15 @@ type pageTemplate struct {
|
||||
}
|
||||
|
||||
// 命令行参数定义
|
||||
var host = flag.String("h", "127.0.0.1", "监听的主机地址") // 监听的主机地址
|
||||
var port = flag.String("p", "8001", "监听的端口") // 监听的端口
|
||||
var extraPath = flag.String("prefix", "/", "afi 的 URL 访问路径前缀, 例如 /afi/ (斜杠很重要)") // URL前缀路径
|
||||
var symlinks = flag.Bool("symlinks", false, "跟随符号链接 \033[4m警告\033[0m: 符号链接可能会跳出已定义的\"根目录\" (默认值: false)") // 是否跟随符号链接
|
||||
var verb = flag.Bool("verb", false, "详细输出") // 是否输出详细日志
|
||||
var skipHidden = flag.Bool("k", true, "\n跳过隐藏文件") // 是否跳过隐藏文件(以.开头)
|
||||
var host = flag.String("h", "127.0.0.1", "监听的主机地址") // 监听的主机地址
|
||||
var port = flag.String("p", "8001", "监听的端口") // 监听的端口
|
||||
var extraPath = flag.String("prefix", "/", "afi 的 URL 访问路径前缀, 例如 /afi/ (斜杠很重要)") // URL前缀路径
|
||||
var symlinks = flag.Bool("symlinks", false, "跟随符号链接 警告: 启用后符号链接可能会跳出已定义的\"根目录\" (default false)") // 是否跟随符号链接
|
||||
var verb = flag.Bool("verb", false, "详细输出") // 是否输出详细日志
|
||||
var skipHidden = flag.Bool("k", true, "\n跳过隐藏文件") // 是否跳过隐藏文件(以.开头)
|
||||
// var ro = flag.Bool("ro", false, "只读模式(无法修改文件系统)") // 是否只读模式
|
||||
var title = flag.String("title", "%PATH%", "页面标题, 用%PATH%指代完整路径, %ITEM%指代末端文件/目录名, 不会泄漏根目录目录名")
|
||||
var authstr = flag.String("auth", `{"admin": "password"}`, "可写用户的认证数据") // 如果非要安全 应该用stdin注入密码防盗窃, 这个flag只能用来调试 不然服务器一个ps就泄露了
|
||||
var title = flag.String("title", "%PATH%", "页面标题, 用 %PATH% 指代完整路径, %ITEM% 指代末端文件/目录名, 不会泄露根目录目录名")
|
||||
var authstr = flag.String("auth", `{"admin": "password"}`, "可写用户的认证数据 (也可以用环境变量 AFI_AUTH 设置, AFI_AUTH 是最优先且最安全的)") // 这个flag只能用来调试 不然服务器一个ps就泄露了
|
||||
|
||||
// rpcCall 定义RPC调用的JSON数据结构
|
||||
// 用于处理前端发送的远程调用请求(创建目录、移动、删除、计算校验和)
|
||||
@@ -256,7 +258,7 @@ func replyList(w http.ResponseWriter, r *http.Request, fullPath string, path str
|
||||
|
||||
// 根据类型添加到不同的列表
|
||||
if el.IsDir() {
|
||||
row := rowTemplate{name + "/", template.URL(href), "4.0kB", "folder", desc, "inode/directory"}
|
||||
row := rowTemplate{html.EscapeString(name + "/"), template.URL(href), "4.0kB", "folder", html.EscapeString(desc), "inode/directory"}
|
||||
p.RowsFolders = append(p.RowsFolders, row)
|
||||
} else {
|
||||
// 提取文件扩展名
|
||||
@@ -270,7 +272,7 @@ func replyList(w http.ResponseWriter, r *http.Request, fullPath string, path str
|
||||
filemime = "unknown/unknown"
|
||||
}
|
||||
}
|
||||
row := rowTemplate{name, template.URL(href), humanize(el.Size()), ext, desc, filemime}
|
||||
row := rowTemplate{html.EscapeString(name), template.URL(href), humanize(el.Size()), html.EscapeString(ext), html.EscapeString(desc), filemime}
|
||||
p.RowsFiles = append(p.RowsFiles, row)
|
||||
}
|
||||
}
|
||||
@@ -337,34 +339,34 @@ func checkAuthRequest(w http.ResponseWriter, r *http.Request) bool {
|
||||
}
|
||||
|
||||
// 拒绝现代JWT, 古法auth, 我们有先进的https
|
||||
// 这样不能防 XSS
|
||||
// 这样不利好防御 XSS 虽说已经把模板搞安全了
|
||||
// 但是至于XSS, 用户都在浏览器随便运行tampermonkey了我还能说什么呢, 说不定是用户想写脚本调试呢, 我把xss预防了让他们费劲去找token反而不好
|
||||
// 这才叫利好curl, 而且还防 CSRF
|
||||
func authLogin(w http.ResponseWriter, r *http.Request) {
|
||||
username, password, ok := r.BasicAuth()
|
||||
if !ok {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="AFI File Manager"`)
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
if checkAuth(username, password) {
|
||||
http.Redirect(w, r, *extraPath, http.StatusFound)
|
||||
} else {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="AFI File Manager"`)
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
}
|
||||
username, password, ok := r.BasicAuth()
|
||||
if !ok {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="AFI File Manager"`)
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
if checkAuth(username, password) {
|
||||
http.Redirect(w, r, *extraPath, http.StatusFound)
|
||||
} else {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="AFI File Manager"`)
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
}
|
||||
}
|
||||
func authState(w http.ResponseWriter, r *http.Request) {
|
||||
username, password, ok := r.BasicAuth()
|
||||
if !ok {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
if checkAuth(username, password) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
} else {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
}
|
||||
username, password, ok := r.BasicAuth()
|
||||
if !ok {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
if checkAuth(username, password) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
} else {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
}
|
||||
}
|
||||
|
||||
// upload 处理文件上传请求
|
||||
@@ -540,7 +542,11 @@ func enforcePath(p string) string {
|
||||
|
||||
func main() {
|
||||
// 解析命令行参数
|
||||
if flag.Parse(); len(flag.Args()) == 1 {
|
||||
flag.Parse()
|
||||
if envAuth := os.Getenv("AFI_AUTH"); envAuth != "" {
|
||||
*authstr = envAuth
|
||||
}
|
||||
if len(flag.Args()) == 1 {
|
||||
rootPath = flag.Args()[0]
|
||||
} else {
|
||||
fmt.Printf("Usage: ./afi [OPTION]... [ROOTDIR]\n\n")
|
||||
@@ -558,18 +564,18 @@ func main() {
|
||||
server := &http.Server{Addr: *host + ":" + *port, Handler: handler}
|
||||
|
||||
// 注册路由处理器
|
||||
http.HandleFunc(*extraPath+"rpc", rpc) // RPC接口 (增删移与校验和)
|
||||
http.HandleFunc(*extraPath+"post", upload) // 文件上传接口
|
||||
http.HandleFunc(*extraPath+"dav", upload) // TODO: webdav 接口
|
||||
http.HandleFunc(*extraPath+"auth", authState) // 仅检查登录接口(不会弹窗)
|
||||
http.HandleFunc(*extraPath+"rpc", rpc) // RPC接口 (增删移与校验和)
|
||||
http.HandleFunc(*extraPath+"post", upload) // 文件上传接口
|
||||
http.HandleFunc(*extraPath+"dav", upload) // TODO: webdav 接口
|
||||
http.HandleFunc(*extraPath+"auth", authState) // 仅检查登录接口(不会弹窗)
|
||||
http.HandleFunc(*extraPath+"login", authLogin) // 登录接口
|
||||
http.HandleFunc(*extraPath+"zip", zipRPC) // ZIP打包下载接口
|
||||
http.HandleFunc("/", doContent) // 主内容处理接口
|
||||
http.HandleFunc(*extraPath+"zip", zipRPC) // ZIP打包下载接口
|
||||
http.HandleFunc("/", doContent) // 主内容处理接口
|
||||
// 创建静态文件服务器, 用于直接提供文件下载
|
||||
handler = http.StripPrefix(*extraPath, http.FileServer(http.Dir(rootPath)))
|
||||
|
||||
// 输出启动信息
|
||||
fmt.Printf("Agile File Indexer\n")
|
||||
fmt.Printf("Agile File Indexer 版本 %s\n", ver)
|
||||
fmt.Printf("AFI 是 Gossa 的增强分支, 但是并不完全向下兼容原版 Gossa 的参数\n")
|
||||
fmt.Printf("AFI 已启动, 根目录为 %s\n", rootPath)
|
||||
fmt.Printf("详细输出: %t, 符号链接跟随: %t, 跳过隐藏文件: %t\n", *verb, *symlinks, *skipHidden)
|
||||
|
||||
Reference in New Issue
Block a user