Files
oxpoe 581cd07dba Perbaiki backup/restore dan script yang tidak jalan dari shortcut
Backup/restore:
- berkas .lazy (format versi 2) kini menyimpan ISI berkas: binary/script dan ikon
  (maks 512 MB per berkas); format versi 1 tetap bisa diimpor
- impor memakai shortcut.Restore: binary ditulis ke ~/.local/bin lokal, ikon dari
  cadangan (atau diunduh ulang dari icon_source), .desktop ditulis dengan path
  lokal, salinan Desktop dibuat sesuai target
- path dari komputer lama tidak dipakai mentah -> tidak ada lagi 'banyak file
  missing'; entri yang tidak bisa dipulihkan ditandai dan bisa dibetulkan via edit
- tes end-to-end: pasang 3 shortcut -> ekspor -> pulihkan di HOME lain -> semua
  berkas lokal, script hasil restore benar-benar jalan

Script:
- script dipasang sebagai wrapper + <slug>.real: wrapper menambahkan
  ~/.local/bin ke PATH dan cd ke folder asli script (desktop environment
  menjalankan dengan cwd = home, PATH sesi)
- .desktop dapat Path= sesuai folder asli script
- opsi 'Run in a terminal window (scripts)' di dialog install & edit
- uninstall membuang wrapper + script aslinya
2026-10-13 09:25:49 +07:00

129 lines
3.3 KiB
Go

// Package store menyimpan daftar shortcut milik LazyRun dalam satu file JSON.
package store
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
)
const (
TypeExecutable = "executable"
TypeWebApp = "webapp"
)
// Targets adalah lokasi shortcut yang dipilih user.
type Targets struct {
Menu bool `json:"menu"`
Desktop bool `json:"desktop"`
}
// Shortcut adalah satu baris metadata di shortcuts.json.
type Shortcut struct {
Slug string `json:"slug"`
Name string `json:"name"`
Type string `json:"type"`
Kind string `json:"kind,omitempty"` // hasil auto-detect: go, appimage, electron, script, windows, binary, pwa
URL string `json:"url,omitempty"`
Browser string `json:"browser,omitempty"`
ExecArgs []string `json:"exec_args,omitempty"`
Categories string `json:"categories,omitempty"`
Terminal bool `json:"terminal,omitempty"`
BinaryPath string `json:"binary_path,omitempty"`
IconPath string `json:"icon_path,omitempty"`
IconSource string `json:"icon_source,omitempty"`
DesktopPath string `json:"desktop_path"`
DesktopCopyPath string `json:"desktop_copy_path,omitempty"`
Targets Targets `json:"targets"`
InstalledAt time.Time `json:"installed_at"`
}
// Broken berarti file .desktop sudah hilang (dihapus manual oleh user).
func (s Shortcut) Broken() bool {
_, err := os.Stat(s.DesktopPath)
return err != nil
}
// Store adalah isi shortcuts.json (array di level atas).
type Store struct {
Path string
Items []Shortcut
}
// Load membaca store dari path. File yang belum ada bukan error.
// File yang rusak di-rename ke *.bak supaya tidak menimpa data lama.
func Load(path string) (*Store, error) {
s := &Store{Path: path}
data, err := os.ReadFile(path)
if os.IsNotExist(err) {
return s, nil
}
if err != nil {
return s, err
}
if err := json.Unmarshal(data, &s.Items); err != nil {
bak := path + ".bak"
_ = os.Rename(path, bak)
return s, fmt.Errorf("shortcuts.json rusak, dipindahkan ke %s: %w", bak, err)
}
return s, nil
}
// Save menulis store secara atomik supaya file tidak pernah setengah tertulis.
func (s *Store) Save() error {
if err := os.MkdirAll(filepath.Dir(s.Path), 0o755); err != nil {
return err
}
data, err := json.MarshalIndent(s.Items, "", " ")
if err != nil {
return err
}
tmp, err := os.CreateTemp(filepath.Dir(s.Path), ".shortcuts-*")
if err != nil {
return err
}
defer os.Remove(tmp.Name())
if _, err := tmp.Write(append(data, '\n')); err != nil {
tmp.Close()
return err
}
if err := tmp.Close(); err != nil {
return err
}
if err := os.Chmod(tmp.Name(), 0o644); err != nil {
return err
}
return os.Rename(tmp.Name(), s.Path)
}
// Add menambahkan shortcut; slug yang sama menimpa entry lama.
func (s *Store) Add(sc Shortcut) {
if i, ok := s.Get(sc.Slug); ok {
s.Items[i] = sc
return
}
s.Items = append(s.Items, sc)
}
// Get mencari index shortcut berdasarkan slug.
func (s *Store) Get(slug string) (int, bool) {
for i, sc := range s.Items {
if sc.Slug == slug {
return i, true
}
}
return 0, false
}
// Remove menghapus shortcut berdasarkan slug.
func (s *Store) Remove(slug string) bool {
i, ok := s.Get(slug)
if !ok {
return false
}
s.Items = append(s.Items[:i], s.Items[i+1:]...)
return true
}