From 3b83d85d7e3ba7d1fe2e5cf8497df06668a44d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Fri, 26 Jan 2024 15:25:16 +0100 Subject: [PATCH] Implement everything else from JS --- dockerfile | 30 ++++++++++++ go.mod | 4 ++ go.sum | 4 ++ main.go | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 dockerfile create mode 100644 go.sum diff --git a/dockerfile b/dockerfile new file mode 100644 index 0000000..09e12cb --- /dev/null +++ b/dockerfile @@ -0,0 +1,30 @@ +# syntax=docker/dockerfile:1 +FROM golang:1.22-rc-alpine + +# Set destination for COPY +WORKDIR /app + +# Download Go modules +COPY go.mod go.sum ./ +RUN go mod download + +# Copy the source code. Note the slash at the end, as explained in +# https://docs.docker.com/engine/reference/builder/#copy +# May not be enough for complex projects +COPY *.go ./ + +# Build +# Add GOOS=linux for linux +# GOOS=windows +# GOOS=darwin +RUN go build -o /main + +# Optional: +# To bind to a TCP port, runtime parameters must be supplied to the docker command. +# But we can document in the Dockerfile what ports +# the application is going to listen on by default. +# https://docs.docker.com/engine/reference/builder/#expose +# EXPOSE 8080 + +# Run +CMD ["/main"] \ No newline at end of file diff --git a/go.mod b/go.mod index 562491d..d6b85ca 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,7 @@ module main go 1.21.6 + +require github.com/djherbis/times v1.6.0 + +require golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ea2151f --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= +github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/main.go b/main.go index 0f29906..165bb73 100644 --- a/main.go +++ b/main.go @@ -3,9 +3,13 @@ package main import ( "log" "os" + "path/filepath" "regexp" "strconv" "strings" + "time" + + "github.com/djherbis/times" ) var timeUnits = map[string]int64{ @@ -60,24 +64,151 @@ func getEnv(key, def string) string { return def } +func scanRoot() { + log.Println("Scanning root directory...") + filepath.Walk(constants.ROOT, func(path string, info os.FileInfo, err error) error { + if err != nil { + log.Printf("Error scanning %s: %s\n", path, err) + return nil + } + path = filepath.ToSlash(path) + + if path == constants.ROOT { + log.Printf("Skipping root directory %s...\n", path) + return nil + } + + if info.IsDir() { + log.Printf("Skipping directory %s...\n", path) + return nil + } + + // We hope that IGNORED_DIRECTORIES is a small list, so we can afford to iterate over it + // In fact iteration should be faster for small lists rather than hashing + for _, ignoredDir := range constants.IGNORED_DIRECTORIES { + if strings.HasPrefix(path, ignoredDir) { + log.Printf("Ignoring directory %s\n", path) + return nil + } + } + + processFile(path, info) + return nil + }) +} + +func scanArchive() { + log.Println("Scanning archive...") + filepath.Walk(constants.ROOT_ARCHIVE, func(path string, info os.FileInfo, err error) error { + if err != nil { + log.Printf("Error scanning %s: %s\n", path, err) + return nil + } + path = filepath.ToSlash(path) + + if path == constants.ROOT_ARCHIVE { + log.Printf("Skipping root directory %s...\n", path) + return nil + } + + processArchiveFile(path, info) + return nil + }) +} + +func processFile(path string, info os.FileInfo) { + var now = time.Now().UnixMilli() + + var fileATime int64 = times.Get(info).AccessTime().UnixMilli() + var accessTimeDelta = now - fileATime + log.Printf("File %s last accessed at %d, %dms ago\n", path, fileATime, accessTimeDelta) + if accessTimeDelta > constants.ARCHIVE_THRESHOLD { + log.Printf("File %s was accessed more than %dms ago, archiving...\n", path, constants.ARCHIVE_THRESHOLD) + archiveFile(path) + } +} + +func processArchiveFile(path string, info os.FileInfo) { + var now = time.Now().UnixMilli() + + var fileATime int64 = times.Get(info).AccessTime().UnixMilli() + var accessTimeDelta = now - fileATime + log.Printf("File %s last accessed at %d, %dms ago\n", path, fileATime, accessTimeDelta) + if accessTimeDelta > constants.DELETE_THRESHOLD { + log.Printf("File %s was accessed more than %dms ago, deleting...\n", path, constants.DELETE_THRESHOLD) + deleteFile(path) + } +} + +func archiveFile(path string) { + // defer os.Exit(1) + var newPath = constants.ROOT_ARCHIVE + strings.Replace(path, constants.ROOT, "", 1) + log.Printf("Archiving file %s to %s...\n", path, newPath) + + os.MkdirAll(filepath.Dir(newPath), os.ModePerm) + var err = os.Rename(path, newPath) + if err != nil { + log.Printf("Error archiving file %s: %s\n", path, err) + return + } +} + +func deleteFile(path string) { + // defer os.Exit(1) + log.Printf("Deleting file %s...\n", path) + var err = os.Remove(path) + if err != nil { + log.Printf("Error deleting file %s: %s\n", path, err) + return + } +} + +type Constants struct { + ROOT string + ROOT_ARCHIVE string + IGNORED_DIRECTORIES []string + ARCHIVE_THRESHOLD int64 + DELETE_THRESHOLD int64 + SCAN_INTERVAL time.Duration +} + +var constants = Constants{} + func main() { log.SetFlags(0b111) - var ROOT = strings.TrimSpace(getEnv("ROOT", "/tmp")) - var ROOT_ARCHIVE = strings.TrimSpace(getEnv("ROOT_ARCHIVE", ROOT+"/archive")) + var ROOT = filepath.ToSlash(strings.TrimSpace(getEnv("ROOT", "/tmp"))) + var ROOT_ARCHIVE = filepath.ToSlash(strings.TrimSpace(getEnv("ROOT_ARCHIVE", ROOT+"/archive"))) + os.Mkdir(ROOT_ARCHIVE, os.ModePerm) var IGNORED_DIRECTORIES = strings.Split(getEnv("IGNORED_DIRECTORIES", ""), ",") IGNORED_DIRECTORIES = append(IGNORED_DIRECTORIES, ROOT_ARCHIVE) for key, dir := range IGNORED_DIRECTORIES { - IGNORED_DIRECTORIES[key] = strings.TrimSpace(dir) + IGNORED_DIRECTORIES[key] = filepath.ToSlash(strings.TrimSpace(dir)) } var ARCHIVE_THRESHOLD = parseDuration(getEnv("ARCHIVE_THRESHOLD", "1d")) var DELETE_THRESHOLD = parseDuration(getEnv("DELETE_THRESHOLD", "12h")) - var SCAN_INTERVAL = parseDuration(getEnv("SCAN_INTERVAL", "1m")) + var SCAN_INTERVAL = time.Duration(parseDuration(getEnv("SCAN_INTERVAL", "1m")) * 1e6) + + constants.ROOT = ROOT + constants.ROOT_ARCHIVE = ROOT_ARCHIVE + constants.IGNORED_DIRECTORIES = IGNORED_DIRECTORIES + constants.ARCHIVE_THRESHOLD = ARCHIVE_THRESHOLD + constants.DELETE_THRESHOLD = DELETE_THRESHOLD + constants.SCAN_INTERVAL = SCAN_INTERVAL + log.Println("Input args parsed as:") log.Printf("ROOT: %s\n", ROOT) log.Printf("ROOT_ARCHIVE: %s\n", ROOT_ARCHIVE) log.Printf("IGNORED_DIRECTORIES: %s\n", IGNORED_DIRECTORIES) log.Printf("ARCHIVE_THRESHOLD: %d\n", ARCHIVE_THRESHOLD) log.Printf("DELETE_THRESHOLD: %d\n", DELETE_THRESHOLD) - log.Printf("SCAN_INTERVAL: %d\n", SCAN_INTERVAL) + log.Printf("SCAN_INTERVAL: %d\n", SCAN_INTERVAL.Milliseconds()) + + // scanRoot() + for { + log.Printf("Running at %d", time.Now().UnixMilli()) + time.Sleep(SCAN_INTERVAL) + scanRoot() + scanArchive() + } }