Skip to content

Commit

Permalink
chore: refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
Adis Durakovic committed Jan 6, 2024
1 parent f65e2e7 commit 2dfeb54
Show file tree
Hide file tree
Showing 31 changed files with 815 additions and 365 deletions.
191 changes: 20 additions & 171 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,64 +2,31 @@ package main

import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"os"
"resticity/restic"
"sync"
"time"

"github.com/adrg/xdg"
"github.com/energye/systray"
"github.com/energye/systray/icon"
"github.com/go-co-op/gocron/v2"
"github.com/google/uuid"
"github.com/wailsapp/wails/v2/pkg/runtime"
)

// App struct
type App struct {
ctx context.Context
jmu sync.Mutex
scheduler gocron.Scheduler
runningJobs []BackupJob
ctx context.Context
scheduler *Scheduler
restic *Restic
settings *Settings
}

type BackupJob struct {
BackupId string `json:"backup_id"`
RepositoryId string `json:"repository_id"`
JobId uuid.UUID `json:"job_id"`
}

type Settings struct {
Repositories []restic.Repository `json:"repositories"`
Backups []restic.Backup `json:"backups"`
Schedules []restic.Schedule `json:"schedules"`
}

func settingsFile() string {
return xdg.ConfigHome + "/resticity.json"
}

func GetSettings() Settings {
data := Settings{}
if file, err := os.Open(settingsFile()); err == nil {
if str, err := io.ReadAll(file); err == nil {
if err := json.Unmarshal([]byte(str), &data); err == nil {
return data
}
}
} else {
fmt.Println("error", err)
}
return data
JobId uuid.UUID `json:"job_id"`
Schedule Schedule `json:"schedule"`
}

// NewApp creates a new App application struct
func NewApp() *App {
return &App{}
func NewApp(restic *Restic, scheduler *Scheduler, settings *Settings) *App {
return &App{restic: restic, scheduler: scheduler, settings: settings}
}

func (a *App) systemTray() {
Expand Down Expand Up @@ -92,159 +59,41 @@ func (a *App) systemTray() {
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
go systray.Run(a.systemTray, func() {})
if s, err := gocron.NewScheduler(); err == nil {
go s.Start()
a.scheduler = s
a.RescheduleBackups()
}

}

// Greet returns a greeting for the given name
func (a *App) Greet(name string) string {
return fmt.Sprintf("Hello %s, It's show time!", name)
}

func (a *App) GetBackupJobs() []BackupJob {
return a.runningJobs
return a.scheduler.RunningJobs
}

func (a *App) Snapshots(id string) []restic.Snapshot {
s := GetSettings()
var r *restic.Repository
func (a *App) Snapshots(id string) []Snapshot {
s := a.settings.Config
var r *Repository
for i := range s.Repositories {
if s.Repositories[i].Id == id {
r = &s.Repositories[i]
}
}
fmt.Println(r)
if r != nil {
return restic.Snapshots(*r)
return a.restic.Snapshots(*r)
}
return []restic.Snapshot{}

}

func (a *App) Settings() Settings {
return GetSettings()
}
return []Snapshot{}

func (a *App) SaveSettings(data Settings) {
a.RescheduleBackups()
fmt.Println("Saving settings")
if str, err := json.MarshalIndent(data, " ", " "); err == nil {
fmt.Println("Settings saved")
if err := os.WriteFile(settingsFile(), str, 0644); err != nil {
fmt.Println("error", err)
} else {
a.RescheduleBackups()
}
} else {
fmt.Println("error", err)
}
}

func (a *App) StopBackup(id uuid.UUID) {
a.scheduler.RemoveJob(id)
a.RescheduleBackups()
}

func (a *App) RescheduleBackups() {
s := GetSettings()
for _, b := range s.Backups {

// todo: run missed backups
/*
- get last snapshot
- parse cron as date/duration whatever
- compare last snapshot date with cron date and job.NextRun()
*/

for _, t := range b.Targets {

jobName := "BACKUP-" + b.Id + "-TARGET-" + t
var job gocron.Job
for j := range a.scheduler.Jobs() {
if a.scheduler.Jobs()[j].Name() == jobName {
job = a.scheduler.Jobs()[j]
break
}
}
if job != nil {
a.scheduler.RemoveJob(job.ID())
}
if b.Cron == "" {
continue
}
a.scheduler.NewJob(
gocron.CronJob(b.Cron, false),
gocron.NewTask(func(backup restic.Backup) {
// actual backup
log.Print("doing job", backup.Name)
time.Sleep(30 * time.Second)
}, b),
gocron.WithTags("backup:"+b.Id, "repository:"+t),
gocron.WithName(jobName),
gocron.WithEventListeners(
gocron.BeforeJobRuns(func(jobID uuid.UUID, jobName string) {
a.jmu.Lock()
defer a.jmu.Unlock()
a.runningJobs = append(
a.runningJobs,
BackupJob{
BackupId: b.Id,
RepositoryId: t,
JobId: jobID,
},
)
}),
gocron.AfterJobRuns(
func(jobID uuid.UUID, jobName string) {
a.jmu.Lock()
defer a.jmu.Unlock()
for i := range a.runningJobs {
if a.runningJobs[i].BackupId == b.Id &&
a.runningJobs[i].RepositoryId == t {
a.runningJobs = append(
a.runningJobs[:i],
a.runningJobs[i+1:]...)
break
}
}
// do something after the job completes

},
),
gocron.AfterJobRunsWithError(
func(jobID uuid.UUID, jobName string, err error) {
a.jmu.Lock()
defer a.jmu.Unlock()
for i := range a.runningJobs {
if a.runningJobs[i].BackupId == b.Id &&
a.runningJobs[i].RepositoryId == t {
a.runningJobs = append(
a.runningJobs[:i],
a.runningJobs[i+1:]...)
break
}
}
},
),
),
)
}

}

// a.scheduler.RemoveJob(id)
// a.RescheduleBackups()
}

func (a *App) CheckRepository(r restic.Repository) string {
func (a *App) CheckRepository(r Repository) string {
files, err := os.ReadDir(r.Path)
if err != nil {
return err.Error()
}
if len(files) > 0 {
if err := restic.Check(r); err != nil {
if err := a.restic.Check(r); err != nil {
return err.Error()
} else {
return "REPO_OK_EXISTING"
Expand All @@ -254,8 +103,8 @@ func (a *App) CheckRepository(r restic.Repository) string {
return "REPO_OK_EMPTY"
}

func (a *App) InitializeRepository(r restic.Repository) string {
if err := restic.Initialize(r); err != nil {
func (a *App) InitializeRepository(r Repository) string {
if err := a.restic.Initialize(r); err != nil {
return err.Error()
}

Expand Down
7 changes: 6 additions & 1 deletion frontend/app.config.ts
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
export default defineAppConfig({})
export default defineAppConfig({
ui: {
primary: 'sky',
gray: 'resticity',
},
})
1 change: 1 addition & 0 deletions frontend/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<script setup lang="ts">
onMounted(() => {
useSettings().init()
useSocket().init()
})
</script>

Expand Down
38 changes: 20 additions & 18 deletions frontend/components/Backup/ExcludeOptions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,31 @@
<div>
<h4 class="text-primary">Files and Folders</h4>
<p class="opacity-50">Pattern for files and folders to exclude. One per line.</p>
<textarea class="textarea textarea-bordered w-full h-32" placeholder="foo/**/bar" v-model="filesAndFolders"></textarea>
<UTextarea :rows="7" placeholder="foo/**/bar" v-model="filesAndFolders"></UTextarea>
<h4 class="text-primary">File</h4>
<p class="opacity-50">Exclude items listed in specific files.</p>
<textarea class="textarea textarea-bordered w-full h-32" placeholder="exclude.txt" v-model="listedInFiles"></textarea>
<UTextarea :padded="true" :rows="7" placeholder="exclude.txt" v-model="listedInFiles"></UTextarea>
</div>
<div>
<h4 class="text-primary">Exclude if present</h4>
<p class="opacity-50">Excludes a folder if it contains any of these files.</p>
<textarea class="textarea textarea-bordered w-full h-32" placeholder=".nobackup" v-model="ifPresent"></textarea>
<div class="form-control">
<label class="cursor-pointer label justify-normal">
<input type="checkbox" class="checkbox checkbox-info mr-3" v-model="cacheDir" />
<span class="label-text"
<UTextarea :rows="7" placeholder=".nobackup" v-model="ifPresent"></UTextarea>
<UCheckbox v-model="cacheDir" class="mt-5 mb-5">
<template #label>
<span
>Exclude if
<code class="text-warning">CACHEDIR.TAG</code>
file is present</span
>
</label>
</div>
</template>
</UCheckbox>

<h4 class="text-primary">Exclude files larger than</h4>
<p class="opacity-50">Exclude files if they exceed a specific file size</p>
<div class="join">
<input class="input input-bordered join-item input-sm w-32" placeholder="0" v-model="largerThan" />
<select class="select select-bordered select-sm join-item w-32" v-model="largerThanUnit">
<option value="K" selected>KiB</option>
<option value="M">MiB</option>
<option value="G">GiB</option>
<option value="T">TiB</option>
</select>
</div>
<UButtonGroup>
<UInput v-model="largerThan" type="number" placeholder="0" />
<USelect v-model="largerThanUnit" :options="units" option-attribute="name" class="w-20"></USelect>
</UButtonGroup>
</div>
</div>
</div>
Expand All @@ -50,6 +45,13 @@
},
})
const units = [
{ name: 'KiB', value: 'K' },
{ name: 'MiB', value: 'M' },
{ name: 'GiB', value: 'G' },
{ name: 'TiB', value: 'T' },
]
const filesAndFolders = ref(fromPropsArray('--exclude'))
const ifPresent = ref(fromPropsArray('--exclude-if-present'))
const listedInFiles = ref(fromPropsArray('--exclude-file'))
Expand Down
25 changes: 21 additions & 4 deletions frontend/components/HeaderNavBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,33 @@
<div class="navbar-center hidden lg:flex">
<ul class="menu menu-horizontal px-1 gap-3">
<li>
<NuxtLink to="/"><FaIcon icon="fire" />Dashboard</NuxtLink>
<!-- <NuxtLink to="/"><FaIcon icon="fire" />Dashboard</NuxtLink> -->
<UButton to="/" variant="ghost" color="gray" icon="i-heroicons-fire">Dashboard</UButton>
</li>
<li>
<NuxtLink to="/repositories" :class="useRoute().path.includes('repositories') ? 'active' : ''"><FaIcon icon="fa-server" />Repositories</NuxtLink>
<UButton
to="/repositories"
:variant="useRoute().path.includes('repositories') ? 'solid' : 'ghost'"
:color="useRoute().path.includes('repositories') ? 'blue' : 'gray'"
icon="i-heroicons-server-stack"
>Repositories</UButton
>
<!-- <NuxtLink to="/repositories" :class="useRoute().path.includes('repositories') ? 'active' : ''"><FaIcon icon="fa-server" />Repositories</NuxtLink> -->
</li>
<li>
<NuxtLink to="/backups" :class="useRoute().path.includes('backups') ? 'active' : ''"><FaIcon icon="fa-upload" />Backups</NuxtLink>
<UButton to="/backups" variant="ghost" :color="useRoute().path.includes('backups') ? 'blue' : 'gray'" icon="i-heroicons-arrow-up-tray">Backups</UButton>

<!-- <NuxtLink to="/backups" :class="useRoute().path.includes('backups') ? 'active' : ''"><FaIcon icon="fa-upload" />Backups</NuxtLink> -->
</li>
<li>
<NuxtLink to="/schedules" :class="useRoute().path.includes('schedules') ? 'active' : ''"><FaIcon icon="clock" />Schedules</NuxtLink>
<UButton to="/schedules" variant="ghost" :color="useRoute().path.includes('schedules') ? 'blue' : 'gray'" icon="i-heroicons-clock">Schedules</UButton>

<!-- <NuxtLink to="/schedules" :class="useRoute().path.includes('schedules') ? 'active' : ''"><FaIcon icon="clock" />Schedules</NuxtLink> -->
</li>
<li>
<UButton to="/logs" variant="ghost" :color="useRoute().path.includes('logs') ? 'blue' : 'gray'" icon="i-heroicons-clock">Logs</UButton>

<!-- <NuxtLink to="/schedules" :class="useRoute().path.includes('schedules') ? 'active' : ''"><FaIcon icon="clock" />Schedules</NuxtLink> -->
</li>
</ul>
</div>
Expand Down
3 changes: 3 additions & 0 deletions frontend/components/Repository/List.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
<div class="flex animate-pulse" v-if="useJobs().repoIsRunning(repo.id)">
<span class="loading loading-infinity loading-sm text-warning"></span><span class="text-sm ml-2 text-warning">Backup running</span>
</div>
<div class="flex animate-pulse" v-if="useJobs().repoIsSynching(repo.id)">
<span class="loading loading-infinity loading-sm text-warning"></span><span class="text-sm ml-2 text-warning">Sync running</span>
</div>
</div>
</NuxtLink>
</div>
Expand Down
Loading

0 comments on commit 2dfeb54

Please sign in to comment.