From 459b87480e76d9be275b0ee8a30e4b691af8aaba Mon Sep 17 00:00:00 2001 From: Alexander Neonxp Kiryukhin Date: Wed, 28 Aug 2024 13:47:44 +0300 Subject: [PATCH] initial --- Dockerfile | 20 ++++++++++ go.mod | 8 ++++ go.sum | 4 ++ main.go | 98 ++++++++++++++++++++++++++++++++++++++++++++++ storage/index.html | 13 ++++++ upload.go | 46 ++++++++++++++++++++++ 6 files changed, 189 insertions(+) create mode 100644 Dockerfile create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 storage/index.html create mode 100644 upload.go diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1035e2b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +# syntax=docker/dockerfile:1 +FROM gitrepo.ru/base/go:latest AS go + +COPY go.mod go.sum ./ + +RUN go mod download + +COPY . . + +RUN go generate && CGO_ENABLED=0 GOOS=linux go build -o nixshare + +FROM scratch + +WORKDIR /app + +COPY --from=go /app/nixshare ./nixshare + +EXPOSE 8000 + +CMD ["./nixshare"] \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ad3e9c2 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module gitrepo.ru/neonxp/nixshare + +go 1.23.0 + +require ( + github.com/google/uuid v1.6.0 + golang.org/x/sync v0.8.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b1eeaf3 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +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= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= diff --git a/main.go b/main.go new file mode 100644 index 0000000..b9154b8 --- /dev/null +++ b/main.go @@ -0,0 +1,98 @@ +package main + +import ( + "context" + "flag" + "io/fs" + "log" + "net/http" + "os" + "os/signal" + "path/filepath" + "time" + + "golang.org/x/sync/errgroup" +) + +var ( + host string + listen string + path string + ttl time.Duration +) + +func init() { + flag.StringVar(&host, "host", "https://nixshare.ru/", "host of nixshare") + flag.StringVar(&listen, "listen", ":8000", "port to listen") + flag.StringVar(&path, "path", "./storage", "storage directory") + flag.DurationVar(&ttl, "ttl", 24*time.Hour, "time to delete uploaded file") +} + +func main() { + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill) + defer cancel() + + flag.Parse() + + r := http.NewServeMux() + + r.Handle("POST /upload", http.HandlerFunc(uploadHandler)) + r.Handle("/", http.FileServer(http.Dir(path))) + + srv := http.Server{ + Addr: listen, + Handler: r, + } + + eg, egCtx := errgroup.WithContext(ctx) + + eg.Go(func() error { + <-egCtx.Done() + srv.Close() + + return nil + }) + + eg.Go(func() error { + if err := srv.ListenAndServe(); err != http.ErrServerClosed { + return err + } + + return nil + }) + + eg.Go(func() error { + timer := time.NewTimer(1 * time.Minute) + defer timer.Stop() + for { + select { + case <-timer.C: + now := time.Now() + err := filepath.Walk(path, func(p string, info fs.FileInfo, err error) error { + if info == nil { + return nil + } + if info.IsDir() { + return nil + } + if now.Sub(info.ModTime()).Seconds() > ttl.Seconds() { + log.Println("delete old file:", p) + if err := os.Remove(p); err != nil { + return err + } + } + return nil + }) + if err != nil { + log.Println(err) + } + case <-egCtx.Done(): + return nil + } + } + }) + + if err := eg.Wait(); err != nil { + log.Fatal(err) + } +} diff --git a/storage/index.html b/storage/index.html new file mode 100644 index 0000000..a83c858 --- /dev/null +++ b/storage/index.html @@ -0,0 +1,13 @@ + + + + + + NixShare + + +
+        curl -d @FILENAME https://nixshare.ru/upload
+    
+ + \ No newline at end of file diff --git a/upload.go b/upload.go new file mode 100644 index 0000000..719e519 --- /dev/null +++ b/upload.go @@ -0,0 +1,46 @@ +package main + +import ( + "fmt" + "io" + "log" + "net/http" + "net/url" + "os" + "path/filepath" + + "github.com/google/uuid" +) + +func uploadHandler(w http.ResponseWriter, r *http.Request) { + uid := uuid.New().String() + section := string(uid[len(uid)-1]) + if err := os.MkdirAll(filepath.Join(path, section), 0777); err != nil { + log.Println(err) + http.Error(w, "can't create storage dir", http.StatusInternalServerError) + return + } + fp, err := os.Create(filepath.Join(path, section, uid)) + if err != nil { + log.Println(err) + http.Error(w, "can't create storage file", http.StatusInternalServerError) + return + } + defer fp.Close() + if _, err := io.Copy(fp, r.Body); err != nil { + log.Println(err) + http.Error(w, "can't upload storage file", http.StatusInternalServerError) + return + } + h := w.Header() + h.Set("Content-Type", "text/plain; charset=utf-8") + h.Set("X-Content-Type-Options", "nosniff") + w.WriteHeader(http.StatusCreated) + u, err := url.JoinPath(host, section, uid) + if err != nil { + log.Println(err) + http.Error(w, "can't get file url", http.StatusInternalServerError) + return + } + fmt.Fprintln(w, u) +}