move src file to seperate direcotry
This commit is contained in:
84
src/cmd/add.go
Normal file
84
src/cmd/add.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"axolotl/models"
|
||||
"axolotl/output"
|
||||
"axolotl/service"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var cDue, cContent, cType, cStatus, cPrio, cNamespace, cAssignee string
|
||||
var cTags, cRels []string
|
||||
|
||||
var addCmd = &cobra.Command{
|
||||
Use: "add <title>", Short: "Create a new node", Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
svc, err := service.GetNodeService(cfg)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
|
||||
input := service.AddInput{
|
||||
Title: args[0],
|
||||
Content: cContent,
|
||||
DueDate: cDue,
|
||||
}
|
||||
|
||||
// --tag is an alias for --rel with no target.
|
||||
for _, tag := range cTags {
|
||||
input.Rels = append(input.Rels, service.RelInput{Type: models.RelType(tag), Target: ""})
|
||||
}
|
||||
|
||||
// Shorthand flags expand to property rels or edge rels.
|
||||
if cType != "" {
|
||||
input.Rels = append(input.Rels, service.RelInput{Type: models.RelType("_type::" + cType), Target: ""})
|
||||
}
|
||||
if cStatus != "" {
|
||||
input.Rels = append(input.Rels, service.RelInput{Type: models.RelType("_status::" + cStatus), Target: ""})
|
||||
}
|
||||
if cPrio != "" {
|
||||
input.Rels = append(input.Rels, service.RelInput{Type: models.RelType("_prio::" + cPrio), Target: ""})
|
||||
}
|
||||
if cNamespace != "" {
|
||||
input.Rels = append(input.Rels, service.RelInput{Type: models.RelInNamespace, Target: cNamespace})
|
||||
}
|
||||
if cAssignee != "" {
|
||||
input.Rels = append(input.Rels, service.RelInput{Type: models.RelAssignee, Target: cAssignee})
|
||||
}
|
||||
|
||||
for _, r := range cRels {
|
||||
ri, err := parseRelInput(r)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
input.Rels = append(input.Rels, ri)
|
||||
}
|
||||
|
||||
n, err := svc.Add(input)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "failed to create:", err)
|
||||
return
|
||||
}
|
||||
|
||||
output.PrintNode(cmd.OutOrStdout(), svc, n, jsonFlag)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(addCmd)
|
||||
f := addCmd.Flags()
|
||||
f.StringVar(&cType, "type", "", "node type (issue, note, …)")
|
||||
f.StringVar(&cStatus, "status", "", "initial status (open, done)")
|
||||
f.StringVar(&cPrio, "prio", "", "priority (high, medium, low)")
|
||||
f.StringVar(&cNamespace, "namespace", "", "namespace name or ID")
|
||||
f.StringVar(&cAssignee, "assignee", "", "assignee username or ID")
|
||||
f.StringVar(&cDue, "due", "", "due date")
|
||||
f.StringVar(&cContent, "content", "", "node body")
|
||||
f.StringArrayVar(&cTags, "tag", nil, "label tag (alias for --rel tagname)")
|
||||
f.StringArrayVar(&cRels, "rel", nil, "relation (prefix::value or relname:target)")
|
||||
}
|
||||
56
src/cmd/alias.go
Normal file
56
src/cmd/alias.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"axolotl/output"
|
||||
"axolotl/service"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var aliasDesc string
|
||||
|
||||
var aliasCmd = &cobra.Command{
|
||||
Use: "alias [name] [command]", Short: "Manage aliases", Args: cobra.MaximumNArgs(2),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
w := cmd.OutOrStdout()
|
||||
if len(args) == 0 {
|
||||
if aliases, err := cfg.ListAliases(); err == nil {
|
||||
output.PrintAliases(w, aliases, jsonFlag)
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(args) == 1 {
|
||||
a, err := cfg.GetAlias(args[0])
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "alias not found:", args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(a.Command)
|
||||
return
|
||||
}
|
||||
if err := cfg.SetAlias(&service.Alias{Name: args[0], Command: args[1], Description: aliasDesc}); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "failed to set alias:", err)
|
||||
} else {
|
||||
output.PrintAction(w, "Alias set", args[0], false)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
var aliasDelCmd = &cobra.Command{
|
||||
Use: "del <name>", Short: "Delete an alias", Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := cfg.DeleteAlias(args[0]); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
output.PrintAction(cmd.OutOrStdout(), "Alias deleted", args[0], false)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(aliasCmd)
|
||||
aliasCmd.AddCommand(aliasDelCmd)
|
||||
aliasCmd.Flags().StringVar(&aliasDesc, "desc", "", "description for the alias")
|
||||
}
|
||||
50
src/cmd/del.go
Normal file
50
src/cmd/del.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"axolotl/output"
|
||||
"axolotl/service"
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var dForce bool
|
||||
var delCmd = &cobra.Command{
|
||||
Use: "del <id>", Short: "Delete a node", Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
svc, err := service.GetNodeService(cfg)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
|
||||
n, err := svc.GetByID(args[0])
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "node not found: %s\n", args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !dForce {
|
||||
fmt.Printf("Delete %s '%s'? [y/N]: ", n.GetProperty("type"), n.Title)
|
||||
r, _ := bufio.NewReader(os.Stdin).ReadString('\n')
|
||||
if r = strings.TrimSpace(strings.ToLower(r)); r != "y" && r != "yes" {
|
||||
fmt.Println("Cancelled.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := svc.Delete(args[0]); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to delete: %v", err)
|
||||
} else {
|
||||
output.PrintAction(cmd.OutOrStdout(), "Deleted", args[0], true)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(delCmd)
|
||||
delCmd.Flags().BoolVarP(&dForce, "force", "f", false, "")
|
||||
}
|
||||
65
src/cmd/edit.go
Normal file
65
src/cmd/edit.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"axolotl/output"
|
||||
"axolotl/service"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var editCmd = &cobra.Command{
|
||||
Use: "edit <id>", Short: "Edit node content in $EDITOR", Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
svc, err := service.GetNodeService(cfg)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
|
||||
n, err := svc.GetByID(args[0])
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "node not found:", args[0])
|
||||
return
|
||||
}
|
||||
|
||||
tmp, err := os.CreateTemp("", "ax-*.md")
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "failed to create temp file:", err)
|
||||
return
|
||||
}
|
||||
tmp.WriteString(n.Content)
|
||||
tmp.Close()
|
||||
defer os.Remove(tmp.Name())
|
||||
|
||||
editor := os.Getenv("EDITOR")
|
||||
if editor == "" {
|
||||
editor = "vi"
|
||||
}
|
||||
c := exec.Command(editor, tmp.Name())
|
||||
c.Stdin, c.Stdout, c.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||
if err := c.Run(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "editor failed:", err)
|
||||
return
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(tmp.Name())
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "failed to read temp file:", err)
|
||||
return
|
||||
}
|
||||
s := string(content)
|
||||
n, err = svc.Update(args[0], service.UpdateInput{Content: &s})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "failed to update:", err)
|
||||
return
|
||||
}
|
||||
output.PrintNode(cmd.OutOrStdout(), svc, n, jsonFlag)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(editCmd)
|
||||
}
|
||||
33
src/cmd/init.go
Normal file
33
src/cmd/init.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"axolotl/output"
|
||||
"axolotl/service"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var initCmd = &cobra.Command{
|
||||
Use: "init [path]", Short: "Initialize a new database", Args: cobra.MaximumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
p := "."
|
||||
if len(args) > 0 {
|
||||
p = args[0]
|
||||
}
|
||||
dbPath := filepath.Join(p, ".ax.db")
|
||||
if _, err := os.Stat(dbPath); err == nil {
|
||||
fmt.Fprintln(os.Stderr, "database already exists:", dbPath)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := service.InitNodeService(dbPath); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "failed to initialize:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
output.PrintAction(cmd.OutOrStdout(), "Created", dbPath, false)
|
||||
},
|
||||
}
|
||||
|
||||
func init() { rootCmd.AddCommand(initCmd) }
|
||||
80
src/cmd/list.go
Normal file
80
src/cmd/list.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"axolotl/models"
|
||||
"axolotl/output"
|
||||
"axolotl/service"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var lTags, lRels []string
|
||||
var lStatus, lPrio, lType, lNamespace, lAssignee, lMention string
|
||||
|
||||
var listCmd = &cobra.Command{
|
||||
Use: "list", Short: "List nodes",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
svc, err := service.GetNodeService(cfg)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
|
||||
var filter service.ListFilter
|
||||
|
||||
// --tag is an alias for a label filter with no target.
|
||||
for _, tag := range lTags {
|
||||
filter.Rels = append(filter.Rels, service.RelInput{Type: models.RelType(tag), Target: ""})
|
||||
}
|
||||
|
||||
// Shorthand flags expand to property filters or edge filters.
|
||||
if lStatus != "" {
|
||||
filter.Rels = append(filter.Rels, service.RelInput{Type: models.RelType("_status::" + lStatus), Target: ""})
|
||||
}
|
||||
if lPrio != "" {
|
||||
filter.Rels = append(filter.Rels, service.RelInput{Type: models.RelType("_prio::" + lPrio), Target: ""})
|
||||
}
|
||||
if lType != "" {
|
||||
filter.Rels = append(filter.Rels, service.RelInput{Type: models.RelType("_type::" + lType), Target: ""})
|
||||
}
|
||||
if lNamespace != "" {
|
||||
filter.Rels = append(filter.Rels, service.RelInput{Type: models.RelInNamespace, Target: lNamespace})
|
||||
}
|
||||
if lAssignee != "" {
|
||||
filter.Rels = append(filter.Rels, service.RelInput{Type: models.RelAssignee, Target: lAssignee})
|
||||
}
|
||||
if lMention != "" {
|
||||
filter.Rels = append(filter.Rels, service.RelInput{Type: models.RelMentions, Target: lMention})
|
||||
}
|
||||
|
||||
for _, r := range lRels {
|
||||
ri, err := parseRelInput(r)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to parse relation flag: %v", err)
|
||||
return
|
||||
}
|
||||
filter.Rels = append(filter.Rels, ri)
|
||||
}
|
||||
|
||||
if nodes, err := svc.List(filter); err == nil {
|
||||
output.PrintNodes(cmd.OutOrStdout(), svc, nodes, jsonFlag)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "err: %v\n", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(listCmd)
|
||||
f := listCmd.Flags()
|
||||
f.StringArrayVar(&lTags, "tag", nil, "filter by label tag")
|
||||
f.StringArrayVar(&lRels, "rel", nil, "filter by relation (prefix::value or relname:target)")
|
||||
f.StringVar(&lStatus, "status", "", "filter by status")
|
||||
f.StringVar(&lPrio, "prio", "", "filter by priority")
|
||||
f.StringVar(&lType, "type", "", "filter by type")
|
||||
f.StringVar(&lNamespace, "namespace", "", "filter by namespace")
|
||||
f.StringVar(&lAssignee, "assignee", "", "filter by assignee")
|
||||
f.StringVar(&lMention, "mention", "", "filter by mention")
|
||||
}
|
||||
84
src/cmd/login.go
Normal file
84
src/cmd/login.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"axolotl/service"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var loginCmd = &cobra.Command{
|
||||
Use: "login",
|
||||
Short: "Authenticate with the remote server via OIDC",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
rc, ok := cfg.GetRemoteConfig()
|
||||
if !ok {
|
||||
fmt.Fprintln(os.Stderr, "no remote server configured; set remote.host in your config")
|
||||
os.Exit(1)
|
||||
}
|
||||
base := fmt.Sprintf("http://%s:%d", rc.Host, rc.Port)
|
||||
|
||||
resp, err := http.Post(base+"/auth/start", "application/json", nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to contact server: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
var start struct {
|
||||
URL string `json:"url"`
|
||||
SessionID string `json:"session_id"`
|
||||
}
|
||||
json.NewDecoder(resp.Body).Decode(&start)
|
||||
resp.Body.Close()
|
||||
|
||||
if start.URL == "" {
|
||||
fmt.Fprintln(os.Stderr, "server did not return an auth URL; is OIDC configured on the server?")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Open this URL in your browser:\n\n %s\n\nWaiting for login...\n", start.URL)
|
||||
|
||||
deadline := time.Now().Add(5 * time.Minute)
|
||||
for time.Now().Before(deadline) {
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
resp, err := http.Get(fmt.Sprintf("%s/auth/poll?session_id=%s", base, start.SessionID))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if resp.StatusCode == http.StatusAccepted {
|
||||
resp.Body.Close()
|
||||
continue
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
resp.Body.Close()
|
||||
fmt.Fprintln(os.Stderr, "login failed")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var result struct {
|
||||
Token string `json:"token"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
json.NewDecoder(resp.Body).Decode(&result)
|
||||
resp.Body.Close()
|
||||
|
||||
if err := service.SaveSession(&service.Session{Token: result.Token}); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to save session: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Logged in as %s\n", result.Username)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "login timed out")
|
||||
os.Exit(1)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(loginCmd)
|
||||
}
|
||||
26
src/cmd/rel.go
Normal file
26
src/cmd/rel.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"axolotl/models"
|
||||
"axolotl/service"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// parseRelInput parses a rel string into a RelInput.
|
||||
//
|
||||
// Formats:
|
||||
// - "prefix::value" → property rel with no target (tag)
|
||||
// - "relname:target" → edge rel with a target node
|
||||
// - "tagname" → simple label rel with no target (alias for --tag)
|
||||
func parseRelInput(s string) (service.RelInput, error) {
|
||||
if strings.Contains(s, "::") {
|
||||
// Property: name::value — no target node.
|
||||
return service.RelInput{Type: models.RelType(s), Target: ""}, nil
|
||||
}
|
||||
if idx := strings.Index(s, ":"); idx >= 0 {
|
||||
// Edge rel: relname:target.
|
||||
return service.RelInput{Type: models.RelType(s[:idx]), Target: s[idx+1:]}, nil
|
||||
}
|
||||
// Simple label tag — no target node.
|
||||
return service.RelInput{Type: models.RelType(s), Target: ""}, nil
|
||||
}
|
||||
86
src/cmd/root.go
Normal file
86
src/cmd/root.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"axolotl/service"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var jsonFlag bool
|
||||
var cfg service.Config
|
||||
var rootCmd = &cobra.Command{Use: "ax", Short: "The axolotl issue tracker"}
|
||||
|
||||
func Execute() {
|
||||
var err error
|
||||
cfg, err = service.LoadConfigFile()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "failed to load config:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
registerAliasCommands()
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().BoolVar(&jsonFlag, "json", false, "")
|
||||
}
|
||||
|
||||
func registerAliasCommands() {
|
||||
rootCmd.AddGroup(&cobra.Group{ID: "aliases", Title: "Aliases:"})
|
||||
aliases, _ := cfg.ListAliases()
|
||||
for _, a := range aliases {
|
||||
rootCmd.AddCommand(&cobra.Command{
|
||||
Use: a.Name,
|
||||
Short: a.Description,
|
||||
GroupID: "aliases",
|
||||
DisableFlagParsing: true,
|
||||
Run: func(ccmd *cobra.Command, args []string) {
|
||||
acmd := a.Command
|
||||
acmd = strings.ReplaceAll(acmd, "$me", cfg.GetUser())
|
||||
parts := strings.Fields(acmd)
|
||||
var expanded []string
|
||||
usedArgs := make([]bool, len(args))
|
||||
for _, part := range parts {
|
||||
if part == "$@" {
|
||||
expanded = append(expanded, args...)
|
||||
for i := range usedArgs {
|
||||
usedArgs[i] = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
hasCatchAll := strings.Contains(part, "$@")
|
||||
replaced := part
|
||||
if hasCatchAll {
|
||||
replaced = strings.ReplaceAll(replaced, "$@", strings.Join(args, " "))
|
||||
for i := range usedArgs {
|
||||
usedArgs[i] = true
|
||||
}
|
||||
}
|
||||
for i := len(args) - 1; i >= 0; i-- {
|
||||
placeholder := fmt.Sprintf("$%d", i+1)
|
||||
if strings.Contains(replaced, placeholder) {
|
||||
replaced = strings.ReplaceAll(replaced, placeholder, args[i])
|
||||
usedArgs[i] = true
|
||||
}
|
||||
}
|
||||
expanded = append(expanded, replaced)
|
||||
}
|
||||
// Forward any unconsumed args (e.g. --json flag).
|
||||
for i, arg := range args {
|
||||
if !usedArgs[i] {
|
||||
expanded = append(expanded, arg)
|
||||
}
|
||||
}
|
||||
rootCmd.SetArgs(expanded)
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
40
src/cmd/serve.go
Normal file
40
src/cmd/serve.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"axolotl/serve"
|
||||
"axolotl/service"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var serveCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "Start the JSON API server",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
sc := cfg.GetServerConfig()
|
||||
addr := fmt.Sprintf("%s:%d", sc.Host, sc.Port)
|
||||
|
||||
var oidcCfg *service.OIDCConfig
|
||||
if oc, ok := cfg.GetOIDCConfig(); ok {
|
||||
oidcCfg = &oc
|
||||
}
|
||||
|
||||
handler, err := serve.New(service.GetNodeServiceForUser, oidcCfg)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Fprintf(os.Stdout, "listening on %s\n", addr)
|
||||
if err := http.ListenAndServe(addr, handler); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(serveCmd)
|
||||
}
|
||||
32
src/cmd/show.go
Normal file
32
src/cmd/show.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"axolotl/output"
|
||||
"axolotl/service"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var showCmd = &cobra.Command{
|
||||
Use: "show <id>", Short: "Show node details", Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
svc, err := service.GetNodeService(cfg)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
|
||||
n, err := svc.GetByID(args[0])
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "node not found:", args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
output.PrintNode(cmd.OutOrStdout(), svc, n, jsonFlag)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(showCmd)
|
||||
}
|
||||
113
src/cmd/update.go
Normal file
113
src/cmd/update.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"axolotl/models"
|
||||
"axolotl/output"
|
||||
"axolotl/service"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
uTitle, uContent, uDue string
|
||||
uClearDue bool
|
||||
uStatus, uPrio, uType string
|
||||
uNamespace, uAssignee string
|
||||
uAddTags, uRmTags, uAddRels, uRmRels []string
|
||||
)
|
||||
|
||||
var updateCmd = &cobra.Command{
|
||||
Use: "update <id>", Short: "Update a node", Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
svc, err := service.GetNodeService(cfg)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
|
||||
var input service.UpdateInput
|
||||
|
||||
if cmd.Flags().Changed("title") {
|
||||
input.Title = &uTitle
|
||||
}
|
||||
if cmd.Flags().Changed("content") {
|
||||
input.Content = &uContent
|
||||
}
|
||||
if cmd.Flags().Changed("due") {
|
||||
input.DueDate = &uDue
|
||||
}
|
||||
if uClearDue {
|
||||
empty := ""
|
||||
input.DueDate = &empty
|
||||
}
|
||||
|
||||
// --tag / --tag-remove are aliases for --rel / --rel-remove with no target.
|
||||
for _, tag := range uAddTags {
|
||||
input.AddRels = append(input.AddRels, service.RelInput{Type: models.RelType(tag), Target: ""})
|
||||
}
|
||||
for _, tag := range uRmTags {
|
||||
input.RemoveRels = append(input.RemoveRels, service.RelInput{Type: models.RelType(tag), Target: ""})
|
||||
}
|
||||
|
||||
// Shorthand flags expand to property rels or edge rels.
|
||||
if cmd.Flags().Changed("type") {
|
||||
input.AddRels = append(input.AddRels, service.RelInput{Type: models.RelType("_type::" + uType), Target: ""})
|
||||
}
|
||||
if cmd.Flags().Changed("status") {
|
||||
input.AddRels = append(input.AddRels, service.RelInput{Type: models.RelType("_status::" + uStatus), Target: ""})
|
||||
}
|
||||
if cmd.Flags().Changed("prio") {
|
||||
input.AddRels = append(input.AddRels, service.RelInput{Type: models.RelType("_prio::" + uPrio), Target: ""})
|
||||
}
|
||||
if cmd.Flags().Changed("namespace") {
|
||||
input.AddRels = append(input.AddRels, service.RelInput{Type: models.RelInNamespace, Target: uNamespace})
|
||||
}
|
||||
if cmd.Flags().Changed("assignee") {
|
||||
input.AddRels = append(input.AddRels, service.RelInput{Type: models.RelAssignee, Target: uAssignee})
|
||||
}
|
||||
|
||||
for _, r := range uAddRels {
|
||||
ri, err := parseRelInput(r)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
input.AddRels = append(input.AddRels, ri)
|
||||
}
|
||||
for _, r := range uRmRels {
|
||||
ri, err := parseRelInput(r)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
input.RemoveRels = append(input.RemoveRels, ri)
|
||||
}
|
||||
|
||||
n, err := svc.Update(args[0], input)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
output.PrintNode(cmd.OutOrStdout(), svc, n, jsonFlag)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(updateCmd)
|
||||
f := updateCmd.Flags()
|
||||
f.StringVar(&uTitle, "title", "", "new title")
|
||||
f.StringVar(&uContent, "content", "", "new content")
|
||||
f.StringVar(&uDue, "due", "", "due date")
|
||||
f.BoolVar(&uClearDue, "clear-due", false, "clear due date")
|
||||
f.StringVar(&uStatus, "status", "", "status (open, done)")
|
||||
f.StringVar(&uPrio, "prio", "", "priority (high, medium, low)")
|
||||
f.StringVar(&uType, "type", "", "node type")
|
||||
f.StringVar(&uNamespace, "namespace", "", "namespace name or ID")
|
||||
f.StringVar(&uAssignee, "assignee", "", "assignee username or ID")
|
||||
f.StringArrayVar(&uAddTags, "tag", nil, "add label tag (alias for --rel tagname)")
|
||||
f.StringArrayVar(&uRmTags, "tag-remove", nil, "remove label tag")
|
||||
f.StringArrayVar(&uAddRels, "rel", nil, "add relation (prefix::value or relname:target)")
|
||||
f.StringArrayVar(&uRmRels, "rel-remove", nil, "remove relation (prefix::value or relname:target)")
|
||||
}
|
||||
Reference in New Issue
Block a user