Add Scan
This commit is contained in:
parent
6caf1c1828
commit
5e15311d49
3 changed files with 171 additions and 0 deletions
167
server/scan.go
Normal file
167
server/scan.go
Normal file
|
@ -0,0 +1,167 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"git.lastassault.de/speatzle/morffix/constants"
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
func handleScan(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "GET" {
|
||||
scanStatus(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
id := r.PathValue("id")
|
||||
if id == "" {
|
||||
http.Error(w, "No ID Set", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var name string
|
||||
var path string
|
||||
var enabled bool
|
||||
err := db.QueryRow(r.Context(), "SELECT name, path, enable FROM libraries WHERE id = $1", id).Scan(&name, &path, &enabled)
|
||||
if err != nil {
|
||||
slog.ErrorContext(r.Context(), "Get Library", "err", err)
|
||||
http.Error(w, "Error Get Library: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
scanCtx := context.Background()
|
||||
go scan(scanCtx, id)
|
||||
|
||||
message := "Scan Started"
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
err = templates.ExecuteTemplate(&buf, constants.MESSAGE_TEMPLATE_NAME, message)
|
||||
if err != nil {
|
||||
slog.ErrorContext(r.Context(), "Executing Library Template", "err", err)
|
||||
http.Error(w, "Error Executing Template: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = w.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
slog.ErrorContext(r.Context(), "Writing http Response", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
func scanStatus(w http.ResponseWriter, r *http.Request) {
|
||||
message := "TODO"
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
err := templates.ExecuteTemplate(&buf, constants.MESSAGE_TEMPLATE_NAME, message)
|
||||
if err != nil {
|
||||
slog.ErrorContext(r.Context(), "Executing Library Template", "err", err)
|
||||
http.Error(w, "Error Executing Template: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = w.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
slog.ErrorContext(r.Context(), "Writing http Response", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
func scan(ctx context.Context, id string) {
|
||||
slog.InfoContext(ctx, "Starting Scan", "id", id)
|
||||
|
||||
var name string
|
||||
var lpath string
|
||||
var enabled bool
|
||||
err := db.QueryRow(ctx, "SELECT name, path, enable FROM libraries WHERE id = $1", id).Scan(&name, &lpath, &enabled)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "Get Library", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !enabled {
|
||||
slog.ErrorContext(ctx, "Scan Aborted, Library not Enabled", "id", id)
|
||||
return
|
||||
}
|
||||
|
||||
dirInfo, err := os.Stat(lpath)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "Stating Library Path", "id", id, "path", lpath, "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !dirInfo.IsDir() {
|
||||
slog.ErrorContext(ctx, "Library Path is not a Folder", "id", id, "path", lpath)
|
||||
return
|
||||
}
|
||||
|
||||
tx, err := db.Begin(ctx)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "Begin Transaction", "err", err)
|
||||
return
|
||||
}
|
||||
defer tx.Rollback(ctx)
|
||||
|
||||
slog.InfoContext(ctx, "Checking Files...", "id", id, "path", lpath)
|
||||
|
||||
// Mark all Files as Missing
|
||||
_, err = tx.Exec(ctx, "UPDATE files SET missing = true where library_id = $1", id)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "Setting Missing Status", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = filepath.Walk(lpath,
|
||||
func(path string, info os.FileInfo, err error) error {
|
||||
if errors.Is(err, os.ErrPermission) {
|
||||
slog.WarnContext(ctx, "Permission Denied While Scanning File", "path", path)
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
// We don't care about folders
|
||||
return nil
|
||||
}
|
||||
slog.InfoContext(ctx, "Scanning File", "path", path, "size", info.Size())
|
||||
|
||||
var fileID int
|
||||
err = tx.QueryRow(ctx, "SELECT id FROM files WHERE library_id = $1 AND path = $2", id, path).Scan(&fileID)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
// File Does not Exist Yet
|
||||
|
||||
// TODO check file extension and exclude
|
||||
slog.InfoContext(ctx, "File is New", "path", path)
|
||||
_, err = tx.Exec(ctx, "INSERT INTO files (library_id, path, size, missing) VALUES ($1, $2, $3, $4)", id, path, info.Size(), false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Creating New File: %w", err)
|
||||
}
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("Getting File: %w", err)
|
||||
}
|
||||
// File Exists so update Size and missing Status
|
||||
_, err = tx.Exec(ctx, "UPDATE files SET size = $1, missing = $2 WHERE id = $3", info.Size(), false, fileID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Creating New File: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "Error Walking Library", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = tx.Commit(ctx)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "Error Committing Changes", "err", err)
|
||||
return
|
||||
}
|
||||
slog.InfoContext(ctx, "Scan Done", "id", id)
|
||||
}
|
|
@ -74,6 +74,7 @@ func Start(_conf config.Config, tmplFS embed.FS, staticFS embed.FS, migrationsFS
|
|||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/worker", handleWorkerWebsocket)
|
||||
mux.Handle("/static/", fs)
|
||||
mux.HandleFunc("/scan/{id}", handleScan)
|
||||
mux.HandleFunc("/libraries/{id}", handleLibrary)
|
||||
mux.HandleFunc("/libraries", handleLibraries)
|
||||
mux.HandleFunc("/", handleIndex)
|
||||
|
|
3
tmpl/message.tmpl
Normal file
3
tmpl/message.tmpl
Normal file
|
@ -0,0 +1,3 @@
|
|||
{{template "head"}}
|
||||
<h2>{{.}}</h2>
|
||||
{{template "tail"}}
|
Loading…
Add table
Reference in a new issue