feat: implement legacy config auth storage and core api
This commit is contained in:
@@ -1,11 +1,33 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"chfs-ng/internal/storage"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"chfs-ng/internal/auth"
|
||||
"chfs-ng/internal/storage"
|
||||
)
|
||||
|
||||
type Server struct{ Store storage.Storage }
|
||||
type Server struct {
|
||||
Store storage.Storage
|
||||
Auth *auth.Manager
|
||||
Sessions map[string]*auth.Account
|
||||
}
|
||||
type fileInfo struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Size int64 `json:"size"`
|
||||
Modified string `json:"modified"`
|
||||
Dir bool `json:"dir"`
|
||||
Mask string `json:"mask"`
|
||||
GuestMask string `json:"guestMask"`
|
||||
HasSubDir bool `json:"hasSubDir"`
|
||||
}
|
||||
|
||||
func (s *Server) Handler() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
@@ -15,12 +37,192 @@ func (s *Server) Handler() http.Handler {
|
||||
mux.HandleFunc("/chfs/rmfiles", s.remove)
|
||||
mux.HandleFunc("/chfs/exist", s.exist)
|
||||
mux.HandleFunc("/chfs/session", s.session)
|
||||
mux.HandleFunc("/chfs/newtext", s.newtext)
|
||||
mux.HandleFunc("/chfs/updatetext", s.updatetext)
|
||||
mux.HandleFunc("/chfs/upload", s.upload)
|
||||
mux.HandleFunc("/chfs/upload2", s.upload)
|
||||
return mux
|
||||
}
|
||||
func notImpl(w http.ResponseWriter) { http.Error(w, "not implemented", http.StatusNotImplemented) }
|
||||
func (s *Server) files(w http.ResponseWriter, r *http.Request) { notImpl(w) }
|
||||
func (s *Server) newdir(w http.ResponseWriter, r *http.Request) { notImpl(w) }
|
||||
func (s *Server) rename(w http.ResponseWriter, r *http.Request) { notImpl(w) }
|
||||
func (s *Server) remove(w http.ResponseWriter, r *http.Request) { notImpl(w) }
|
||||
func (s *Server) exist(w http.ResponseWriter, r *http.Request) { notImpl(w) }
|
||||
func (s *Server) session(w http.ResponseWriter, r *http.Request) { notImpl(w) }
|
||||
func account(r *http.Request, s *Server) *auth.Account {
|
||||
if c, e := r.Cookie("chfs_token"); e == nil {
|
||||
if a := s.Sessions[c.Value]; a != nil {
|
||||
return a
|
||||
}
|
||||
}
|
||||
return s.Auth.Guest
|
||||
}
|
||||
func writeJSON(w http.ResponseWriter, v any) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
func qpath(r *http.Request) string {
|
||||
return strings.Trim(strings.ReplaceAll(r.URL.Query().Get("filepath"), "\\", "/"), "/")
|
||||
}
|
||||
func permString(p auth.PermMask) string {
|
||||
if p.HasDelete() {
|
||||
return "d"
|
||||
}
|
||||
if p.HasWrite() {
|
||||
return "w"
|
||||
}
|
||||
if p.HasRead() {
|
||||
return "r"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
func (s *Server) files(w http.ResponseWriter, r *http.Request) {
|
||||
a := account(r, s)
|
||||
p := qpath(r)
|
||||
m := a.MaskFor(p)
|
||||
if !m.HasRead() {
|
||||
http.Error(w, "forbidden", 403)
|
||||
return
|
||||
}
|
||||
list, e := s.Store.List(p)
|
||||
if e != nil {
|
||||
http.Error(w, e.Error(), 404)
|
||||
return
|
||||
}
|
||||
out := make([]fileInfo, 0, len(list))
|
||||
for _, fi := range list {
|
||||
fp := path.Join(p, fi.Name())
|
||||
sub := false
|
||||
if fi.IsDir() {
|
||||
if xs, e := s.Store.List(fp); e == nil {
|
||||
sub = len(xs) > 0
|
||||
}
|
||||
}
|
||||
out = append(out, fileInfo{Name: fi.Name(), Path: fp, Size: fi.Size(), Modified: fi.ModTime().Format(time.RFC3339), Dir: fi.IsDir(), Mask: permString(a.MaskFor(fp)), HasSubDir: sub})
|
||||
}
|
||||
writeJSON(w, map[string]any{"files": out, "currentMask": permString(m), "virtualRoot": false})
|
||||
}
|
||||
func (s *Server) newdir(w http.ResponseWriter, r *http.Request) {
|
||||
a := account(r, s)
|
||||
p := qpath(r)
|
||||
if !a.MaskFor(p).HasWrite() {
|
||||
http.Error(w, "forbidden", 403)
|
||||
return
|
||||
}
|
||||
name := r.FormValue("name")
|
||||
if name == "" {
|
||||
name = r.FormValue("new")
|
||||
}
|
||||
if name == "" {
|
||||
http.Error(w, "name required", 400)
|
||||
return
|
||||
}
|
||||
if e := s.Store.MkdirAll(path.Join(p, name)); e != nil {
|
||||
http.Error(w, e.Error(), 500)
|
||||
return
|
||||
}
|
||||
writeJSON(w, map[string]any{"ok": true})
|
||||
}
|
||||
func (s *Server) rename(w http.ResponseWriter, r *http.Request) {
|
||||
a := account(r, s)
|
||||
old := strings.Trim(r.FormValue("old"), "/")
|
||||
neu := strings.Trim(r.FormValue("new"), "/")
|
||||
if !a.MaskFor(old).HasWrite() {
|
||||
http.Error(w, "forbidden", 403)
|
||||
return
|
||||
}
|
||||
if e := s.Store.Rename(old, neu); e != nil {
|
||||
http.Error(w, e.Error(), 500)
|
||||
return
|
||||
}
|
||||
writeJSON(w, map[string]any{"ok": true})
|
||||
}
|
||||
func (s *Server) remove(w http.ResponseWriter, r *http.Request) {
|
||||
a := account(r, s)
|
||||
_ = r.ParseForm()
|
||||
items := r.Form["path"]
|
||||
if len(items) == 0 {
|
||||
items = r.Form["paths"]
|
||||
}
|
||||
for _, p := range items {
|
||||
if !a.MaskFor(p).HasDelete() {
|
||||
http.Error(w, "forbidden", 403)
|
||||
return
|
||||
}
|
||||
if e := s.Store.Remove(p); e != nil {
|
||||
http.Error(w, e.Error(), 500)
|
||||
return
|
||||
}
|
||||
}
|
||||
writeJSON(w, map[string]any{"ok": true})
|
||||
}
|
||||
func (s *Server) exist(w http.ResponseWriter, r *http.Request) {
|
||||
p := qpath(r)
|
||||
_, e := s.Store.Stat(p)
|
||||
writeJSON(w, map[string]any{"exist": e == nil})
|
||||
}
|
||||
func (s *Server) session(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "GET" {
|
||||
a := account(r, s)
|
||||
writeJSON(w, map[string]any{"login": a != nil, "user": a.Username})
|
||||
return
|
||||
}
|
||||
user, pwd := r.FormValue("user"), r.FormValue("pwd")
|
||||
a := s.Auth.Authenticate(user, pwd)
|
||||
if a == nil {
|
||||
http.Error(w, "invalid account", 401)
|
||||
return
|
||||
}
|
||||
token := auth.NewToken()
|
||||
s.Sessions[token] = a
|
||||
http.SetCookie(w, &http.Cookie{Name: "chfs_token", Value: token, Path: "/", HttpOnly: true, SameSite: http.SameSiteLaxMode})
|
||||
writeJSON(w, map[string]any{"ok": true, "user": a.Username})
|
||||
}
|
||||
func (s *Server) newtext(w http.ResponseWriter, r *http.Request) {
|
||||
a := account(r, s)
|
||||
p := strings.Trim(r.FormValue("path"), "/")
|
||||
if !a.MaskFor(p).HasWrite() {
|
||||
http.Error(w, "forbidden", 403)
|
||||
return
|
||||
}
|
||||
f, e := s.Store.Create(p)
|
||||
if e != nil {
|
||||
http.Error(w, e.Error(), 500)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
io.WriteString(f, r.FormValue("text"))
|
||||
writeJSON(w, map[string]any{"ok": true})
|
||||
}
|
||||
func (s *Server) updatetext(w http.ResponseWriter, r *http.Request) { s.newtext(w, r) }
|
||||
func (s *Server) upload(w http.ResponseWriter, r *http.Request) {
|
||||
a := account(r, s)
|
||||
dir := strings.Trim(r.FormValue("path"), "/")
|
||||
if !a.MaskFor(dir).HasWrite() {
|
||||
http.Error(w, "forbidden", 403)
|
||||
return
|
||||
}
|
||||
if e := r.ParseMultipartForm(64 << 20); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
var saved []string
|
||||
for _, hs := range r.MultipartForm.File {
|
||||
for _, h := range hs {
|
||||
if e := savePart(s.Store, dir, h); e != nil {
|
||||
http.Error(w, e.Error(), 500)
|
||||
return
|
||||
}
|
||||
saved = append(saved, h.Filename)
|
||||
}
|
||||
}
|
||||
writeJSON(w, map[string]any{"ok": true, "files": saved})
|
||||
}
|
||||
func savePart(st storage.Storage, dir string, h *multipart.FileHeader) error {
|
||||
in, e := h.Open()
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer in.Close()
|
||||
f, e := st.Create(path.Join(dir, h.Filename))
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer f.Close()
|
||||
_, e = io.Copy(f, in)
|
||||
return e
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user