Added update command
All checks were successful
build and deploy kleinTodo / build (push) Successful in 34s
All checks were successful
build and deploy kleinTodo / build (push) Successful in 34s
Updated delete logic Added last modified and deleted to the todo item
This commit is contained in:
parent
bb685be5d5
commit
6e78c1948e
@ -2,10 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/mail"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime/debug"
|
||||
|
||||
"gitea.kleinsense.nl/DariusKlein/kleinTodo/client/todo/clientCommon/config"
|
||||
"github.com/urfave/cli/v3"
|
||||
@ -48,5 +51,44 @@ func commands() []*cli.Command {
|
||||
Sync(),
|
||||
Add(),
|
||||
Todo(),
|
||||
{
|
||||
Name: "update",
|
||||
Usage: "Update the vennexCLI to a specific version",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "version",
|
||||
Usage: "The version label to install (e.g., v1.2.0, main, latest)",
|
||||
Value: "latest",
|
||||
},
|
||||
},
|
||||
Action: runUpdate,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runUpdate(ctx context.Context, command *cli.Command) error {
|
||||
var pkgPath = "unknown"
|
||||
info, ok := debug.ReadBuildInfo()
|
||||
if ok {
|
||||
pkgPath = info.Main.Path
|
||||
}
|
||||
|
||||
pkg := fmt.Sprintf("%s@%s", pkgPath, command.String("version"))
|
||||
|
||||
slog.Info(fmt.Sprintf("Updating to %s...\n", pkg))
|
||||
|
||||
cmd := exec.Command("go", "install", pkg)
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
slog.Error("Update failed:", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
slog.Info("Update successful!")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ func syncAction(context context.Context, c *cli.Command) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serverTodos := store.GetTodoList(cfg.Server.Credentials.Username)
|
||||
serverTodos := store.GetTodoList(cfg.Server.Credentials.Username, true)
|
||||
|
||||
var todos []common.Todo
|
||||
|
||||
|
||||
@ -4,10 +4,12 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.kleinsense.nl/DariusKlein/kleinTodo/common"
|
||||
"github.com/urfave/cli/v3"
|
||||
@ -32,7 +34,7 @@ func todo(context context.Context, c *cli.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
todos := store.GetTodoList(cfg.Server.Credentials.Username)
|
||||
todos := store.GetTodoList(cfg.Server.Credentials.Username, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -89,7 +91,9 @@ func handleDelete(scanner *bufio.Scanner, todos []common.Todo, store *common.Bol
|
||||
|
||||
removedItem := todos[index-1]
|
||||
|
||||
err = store.RemoveValueFromBucket(cfg.Server.Credentials.Username, removedItem.Name)
|
||||
removedItem.Deleted = true
|
||||
|
||||
err = removedItem.Store(store, cfg.Server.Credentials.Username)
|
||||
if err != nil {
|
||||
slog.Error(err.Error())
|
||||
return todos
|
||||
@ -159,8 +163,16 @@ func handleAdd(scanner *bufio.Scanner, todos []common.Todo, store *common.BoltSt
|
||||
scanner.Scan()
|
||||
description := strings.TrimSpace(scanner.Text())
|
||||
|
||||
todoList := common.TodoList{Todos: todos}
|
||||
|
||||
_, exists := todoList.FindByName(name)
|
||||
if exists {
|
||||
log.Fatalf("Item '%s' already exists.", name)
|
||||
return todos
|
||||
}
|
||||
|
||||
newTodo := common.Todo{
|
||||
Name: name, Description: description, Status: common.NotStarted, Owner: cfg.Server.Credentials.Username,
|
||||
Name: name, Description: description, Status: common.NotStarted, Owner: cfg.Server.Credentials.Username, LastModified: time.Now(),
|
||||
}
|
||||
err := newTodo.Store(store, cfg.Server.Credentials.Username)
|
||||
if err != nil {
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
bolt "go.etcd.io/bbolt"
|
||||
@ -24,8 +25,26 @@ type BoltStore struct {
|
||||
DB *bolt.DB
|
||||
}
|
||||
|
||||
const configDirName = "kleinTodo"
|
||||
|
||||
func getStoragePath() (path string, err error) {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not get user config directory: %w", err)
|
||||
}
|
||||
|
||||
appConfigDir := filepath.Join(configDir, configDirName)
|
||||
|
||||
// Ensure the directory exists before we try to use it.
|
||||
if err := os.MkdirAll(appConfigDir, 0755); err != nil {
|
||||
return "", fmt.Errorf("could not create app config directory: %w", err)
|
||||
}
|
||||
|
||||
return appConfigDir, nil
|
||||
}
|
||||
|
||||
func NewBoltStore(path string) (*BoltStore, error) {
|
||||
os.Mkdir("data", 0755)
|
||||
os.MkdirAll(filepath.Dir(path), 0755)
|
||||
db, err := bolt.Open(path, 0600, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not open db: %w", err)
|
||||
@ -45,8 +64,10 @@ var (
|
||||
|
||||
func GetTodoDataStore() (*BoltStore, error) {
|
||||
once.Do(func() {
|
||||
var path string
|
||||
path, err = getStoragePath()
|
||||
// We assign to the outer 'dataStore' and 'err' variables.
|
||||
dataStore, err = NewBoltStore("data/todo.db")
|
||||
dataStore, err = NewBoltStore(path + "/todo.db")
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -193,15 +214,23 @@ func (s *BoltStore) GetTodoMap(user string) map[string]Todo {
|
||||
return serverTodos
|
||||
}
|
||||
|
||||
func (s *BoltStore) GetTodoList(user string) []Todo {
|
||||
func (s *BoltStore) GetTodoList(user string, includeDeleted bool) []Todo {
|
||||
storedTodoJsons := s.GetAllFromBucket(user)
|
||||
|
||||
var serverTodos []Todo
|
||||
var storedTodos []Todo
|
||||
for _, val := range storedTodoJsons {
|
||||
var todo Todo
|
||||
if json.Unmarshal([]byte(val), &todo) == nil {
|
||||
serverTodos = append(serverTodos, todo)
|
||||
var include = false
|
||||
if includeDeleted {
|
||||
include = true
|
||||
} else {
|
||||
include = todo.Deleted
|
||||
}
|
||||
if include {
|
||||
storedTodos = append(storedTodos, todo)
|
||||
}
|
||||
}
|
||||
return serverTodos
|
||||
}
|
||||
return storedTodos
|
||||
}
|
||||
|
||||
@ -4,12 +4,14 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (todo Todo) Store(store *BoltStore, user string) error {
|
||||
if todo.Owner != user {
|
||||
return fmt.Errorf("unauthorized user")
|
||||
}
|
||||
todo.LastModified = time.Now()
|
||||
todoJson, err := json.Marshal(todo)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -23,6 +25,7 @@ func (todoRequest StoreTodoRequest) Store(store *BoltStore, user string) error {
|
||||
Description: todoRequest.Description,
|
||||
Status: todoRequest.Status,
|
||||
Owner: user,
|
||||
LastModified: time.Now(),
|
||||
}
|
||||
todoJson, err := json.Marshal(todo)
|
||||
if err != nil {
|
||||
@ -41,6 +44,10 @@ func (todoList TodoList) FindByName(name string) (Todo, bool) {
|
||||
}
|
||||
|
||||
func (todo Todo) PrintIndexed(index int) {
|
||||
|
||||
if todo.Deleted {
|
||||
return
|
||||
}
|
||||
var statusColor string
|
||||
|
||||
// Select color based on the status (case-insensitive)
|
||||
@ -57,7 +64,19 @@ func (todo Todo) PrintIndexed(index int) {
|
||||
statusColor = ColorReset // No color for unknown statuses
|
||||
}
|
||||
|
||||
lastMod := todo.LastModified.Format("2006-01-02 15:04")
|
||||
|
||||
fmt.Printf("%d) %s - %s%s%s\n", index, todo.Name, statusColor, strings.ToUpper(todo.Status), ColorReset)
|
||||
|
||||
fmt.Printf("\t%s\n", todo.Description)
|
||||
|
||||
fmt.Printf("\t%sLast Modified: %s%s\n", ColorReset, lastMod, ColorReset)
|
||||
}
|
||||
|
||||
func (t Todo) IsEqual(other Todo) bool {
|
||||
return t.Name == other.Name &&
|
||||
t.Description == other.Description &&
|
||||
t.Status == other.Status &&
|
||||
t.Owner == other.Owner &&
|
||||
t.Deleted == other.Deleted
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package common
|
||||
|
||||
import "time"
|
||||
|
||||
type Credentials struct {
|
||||
Username string `json:"username" toml:"username"`
|
||||
Password string `json:"password" toml:"password"`
|
||||
@ -10,6 +12,8 @@ type Todo struct {
|
||||
Description string `json:"description"`
|
||||
Status string `json:"status"`
|
||||
Owner string `json:"owner"`
|
||||
LastModified time.Time `json:"last_modified"`
|
||||
Deleted bool `json:"deleted"`
|
||||
}
|
||||
|
||||
type StoreTodoRequest struct {
|
||||
|
||||
@ -18,5 +18,7 @@ FROM gcr.io/distroless/base-debian12
|
||||
|
||||
COPY --from=build /app/serverBinary .
|
||||
|
||||
ENV XDG_CONFIG_HOME=/data
|
||||
|
||||
# Define the command to run the app when the container starts
|
||||
CMD ["./serverBinary"]
|
||||
|
||||
@ -3,7 +3,6 @@ package handler
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"gitea.kleinsense.nl/DariusKlein/kleinTodo/common"
|
||||
"gitea.kleinsense.nl/DariusKlein/kleinTodo/common/jwt"
|
||||
@ -36,14 +35,19 @@ func SyncHandler(w http.ResponseWriter, r *http.Request) {
|
||||
for _, clientTodo := range todoList.Todos {
|
||||
serverTodo, exists := serverTodos[clientTodo.Name]
|
||||
|
||||
if !exists {
|
||||
if clientTodo.Deleted && clientTodo.LastModified.After(serverTodo.LastModified) {
|
||||
err = store.RemoveValueFromBucket(user, clientTodo.Name)
|
||||
if handleError(w, http.StatusInternalServerError, err) {
|
||||
return
|
||||
}
|
||||
} else if !exists {
|
||||
err = clientTodo.Store(store, user)
|
||||
if handleError(w, http.StatusInternalServerError, err) {
|
||||
return
|
||||
}
|
||||
serverTodos[clientTodo.Name] = clientTodo
|
||||
} else {
|
||||
if !reflect.DeepEqual(serverTodo, clientTodo) {
|
||||
if !serverTodo.IsEqual(clientTodo) {
|
||||
response.MisMatchingTodos = append(response.MisMatchingTodos, common.MisMatchingTodo{
|
||||
ServerTodo: serverTodo,
|
||||
LocalTodo: clientTodo,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user