#[Copyright 2023 ITwrx. This file is part of EZ-Bkup. EZ-Bkup is released under the General Public License 3.0. See COPYING or for details.]# import owlkettle import std/osproc, std/os, std/logging import edit_routine_dialog import "../models/routine", "../shared" viewable RoutineList: routineModel: RoutineModel runStatus: string proc changed(state: bool) var thread: Thread[RoutineListState] proc rsyncThread(list: RoutineListState) {.thread.} = let appPath = getHomeDir() & ".ez-bkup" if not dirExists(appPath): createDir(appPath) var logger = newFileLogger(appPath & "/errors.log") #i'm not sure if using threadvar here is needed, but doing it just in case. :) var rsyncRunCmd {.threadvar.}: string var rsyncRun {.threadvar.}: tuple[output: string, exitCode: int] var rsyncErrors: seq[string] var routineRunCount: int routineRunCount = 0 for routine in list.routineModel.routineSeq(): #using this as "selected" for now. if routine.selByDef == true: #skip routines that don't have at least one source and one destination. if routine.sources.len != 0 and routine.destinations.len != 0: list.runStatus = "Horses, please to be holding..." routineRuncount += 1 for source in routine.sources: for destination in routine.destinations: #per source/dest compbo msgs still don't seem to be working right. #maybe it requires a separate thread per combo? deprioritized. #list.runStatus = "Bkup " & source & " to " & destination & "..." #try without requiring superuser privs by default. rsyncRunCmd = "rsync -aq " & source & " " & destination rsyncRun = execCmdEx(rsyncRunCmd) if rsyncRun.exitCode != 0: #handle permission denied error. if rsyncRun.exitCode == 23: rsyncRun.exitCode = 0 if getAskPassPath() == "": let err = "No ssh-askpass binary found. Please install an ssh-askpass package for your distro, and let us know if EZ-Bkup still can't detect it's location." rsyncErrors.add(err) else: rsyncRunCmd = "SUDO_ASKPASS=" & getAskPassPath() & " sudo -A rsync -aq " & source & " " & destination rsyncRun = execCmdEx(rsyncRunCmd) if rsyncRun.exitCode != 0: rsyncErrors.add("EZ-Bkup's rsync process(es) returned error (" & $rsyncRun.output & ") while attempting to back up " & source & " to " & destination) if rsyncErrors.len > 0: list.runStatus = "Error! Please see the log at ~/.ez-bkup/errors.log" for err in rsyncErrors: logger.log(lvlError, err) elif routineRunCount == 0: list.runStatus = "Meh. No Bkup Routines were run." else: list.runStatus = "Bkup Complete!" list.redrawFromThread() method view(list: RoutineListState): Widget {.locks: "unknown".} = result = gui: ScrolledWindow: Box: orient = OrientY if list.routineModel.routineSeq().len() > 0: ListBox: for it, routine in list.routineModel.routineSeq(): Box: orient = OrientY margin = 6 spacing = 6 Box: orient = OrientX margin = 6 spacing = 6 Label: text = "" & routine.name & "" xAlign = 0 useMarkup = true # Edit Button Button {.expand: false.}: #icon = "entity-edit" icon = "entity-edit-dark-theme" tooltip = "Edit this Routine." proc clicked() = ## Opens the EditRoutineDialog for updating the existing routine let (res, state) = list.app.open: gui: EditRoutineDialog: routine = routine mode = EditRoutineUpdate if res.kind == DialogAccept: # The "Update" button was clicked list.routineModel.update(EditRoutineDialogState(state).routine) # Delete Button Button {.expand: false.}: icon = "user-trash-symbolic" tooltip = "Delete this Routine. Warning: will not ask you to confirm." proc clicked() = list.routineModel.delete(routine.id) if routine.selByDef: Box: orient = OrientY margin = 6 spacing = 6 Label: text = "Sources:" xAlign = 0 useMarkup = true #routineSeq returns an empty string when no sources exist. if routine.sources.len != 0: for it, routineSource in routine.sources: Box: orient = OrientX spacing = 6 margin = 6 Label: text = routineSource xAlign = 0 else: Box: orient = OrientX spacing = 6 margin = 6 Label: text = "Routine will be ignored. Source required." xAlign = 0 useMarkup = true Label: text = "Destinations:" xAlign = 0 useMarkup = true #routineSeq returns an empty string when no destinations exist. if routine.destinations.len != 0: for it, routineDestination in routine.destinations: Box: orient = OrientX spacing = 6 margin = 6 Label: text = routineDestination xAlign = 0 else: Box: orient = OrientX spacing = 6 margin = 6 Label: text = "Routine will be ignored. Destination required." xAlign = 0 useMarkup = true Box {.expand: false.}: orient = OrientX spacing = 6 margin = 6 Label {.expand: false.}: text = "Status: " xAlign = 0 useMarkup = true Label: if list.runStatus != "": text = list.runStatus else: text = "Waiting patiently..." xAlign = 0 margin = 6 useMarkup = true # Run Button Button {.expand: false.}: Box: orient = OrientX spacing = 6 margin = 6 Icon: name = "media-floppy" pixelSize = 40 margin = 4 Label {.expand: false.}: text = "Run Bkup!" xAlign = 0 useMarkup = true proc clicked() = #list.runStatus = "Running Bkup..." createThread(thread, rsyncThread, list) else: Box {.expand: false.}: orient = OrientY margin = 6 spacing = 12 Label: text = "You don't have any Bkup Routines!!! 🙂" xAlign = 0 useMarkup = true Label: text = "Please click on the + Routine button above to create your first Routine." xAlign = 0 useMarkup = true export RoutineList