diff options
-rw-r--r-- | main.go | 56 | ||||
-rw-r--r-- | public/style.css | 2 |
2 files changed, 52 insertions, 6 deletions
@@ -344,7 +344,7 @@ func handleAPIBrowse(w http.ResponseWriter, r *http.Request) { } defer tx.Rollback() - parent, err := idForPath(tx, decodeWebPath(params.Path), false) + parent, err := idForDirectoryPath(tx, decodeWebPath(params.Path), false) if err != nil { http.Error(w, err.Error(), http.StatusNotFound) return @@ -621,7 +621,7 @@ func getOrphanReplacement(webPath string) (*webOrphanImage, error) { return nil, nil } - parent, err := idForPath(tx, path[:len(path)-1], false) + parent, err := idForDirectoryPath(tx, path[:len(path)-1], false) if err != nil { return nil, err } @@ -1070,7 +1070,7 @@ func cmdWeb(args []string) error { // --- Import ------------------------------------------------------------------ -func idForPath(tx *sql.Tx, path []string, create bool) (int64, error) { +func idForDirectoryPath(tx *sql.Tx, path []string, create bool) (int64, error) { var parent sql.NullInt64 for _, name := range path { if err := tx.QueryRow(`SELECT id FROM node @@ -1130,7 +1130,7 @@ func (dm *directoryManager) IDForDirectoryPath( return id, nil } - id, err := idForPath(tx, list, true) + id, err := idForDirectoryPath(tx, list, true) if err != nil { return 0, err } @@ -1688,7 +1688,7 @@ func syncRoot(c *syncContext, fsPath string) error { // // Synchronizing F → D or * → F are special cases not worth implementing. crumbs := decodeWebPath(filepath.ToSlash(fsPath)) - dbParent, err := idForPath(c.tx, crumbs, true) + dbParent, err := idForDirectoryPath(c.tx, crumbs, true) if err != nil { return err } @@ -1881,6 +1881,51 @@ func cmdSync(args []string) error { return tx.Commit() } +// --- Removal ----------------------------------------------------------------- + +// cmdRemove is for manual removal of subtrees from the database. +// Beware that inputs are database, not filesystem paths. +func cmdRemove(args []string) error { + if len(args) < 2 { + return errors.New("usage: GD PATH...") + } + if err := openDB(args[0]); err != nil { + return err + } + + tx, err := db.BeginTx(context.Background(), nil) + if err != nil { + return err + } + defer tx.Rollback() + + for _, path := range args[1:] { + var id sql.NullInt64 + for _, name := range decodeWebPath(path) { + if err := tx.QueryRow(`SELECT id FROM node + WHERE parent IS ? AND name = ?`, + id, name).Scan(&id); err != nil { + return err + } + } + if id.Int64 == 0 { + return errors.New("can't remove root") + } + + if _, err = tx.Exec(disposeCTE+` + INSERT OR IGNORE INTO orphan(sha1, path) + SELECT sha1, path FROM orphaned`, id); err != nil { + return err + } + if _, err = tx.Exec(disposeCTE+` + DELETE FROM node WHERE id + IN (SELECT DISTINCT id FROM children)`, id); err != nil { + return err + } + } + return tx.Commit() +} + // --- Tagging ----------------------------------------------------------------- // cmdTag mass imports tags from data passed on stdin as a TSV @@ -2338,6 +2383,7 @@ var commands = map[string]struct { "import": {cmdImport}, "tag": {cmdTag}, "sync": {cmdSync}, + "remove": {cmdRemove}, "check": {cmdCheck}, "thumbnail": {cmdThumbnail}, "dhash": {cmdDhash}, diff --git a/public/style.css b/public/style.css index e8eb0bc..e0fc004 100644 --- a/public/style.css +++ b/public/style.css @@ -49,7 +49,7 @@ a { color: inherit; } .browser { overflow: auto; display: flex; flex-wrap: wrap; align-content: flex-start; justify-content: center; align-items: center; - gap: 3px; padding: 9px; } + gap: 3px; padding: 9px; flex-grow: 1; } .browser:focus-visible { outline: 0; box-shadow: none; } .tags { padding: .5rem; flex-grow: 1; overflow: auto; } |