fix/sqlite #10

Merged
jake merged 17 commits from fix/sqlite into main 2025-06-19 21:58:10 +00:00
25 changed files with 456 additions and 488 deletions
Showing only changes of commit fe37cac2da - Show all commits

View File

@ -3,8 +3,7 @@ mctl is a terminal-friendly remote console client
# Index
1. [Installation](#installation)
2. [Setup](#setup)
3. [Documentation](#documentation)
2. [Use](#use)
4. [Security](#security)
5. [Development](#development)
@ -13,18 +12,31 @@ mctl is a terminal-friendly remote console client
# Installation
Install mctl using golang
```bash
go install code.jakeyoungdev.com/jake/mctl@main #it is recommended to use a tagged version
go install code.jakeyoungdev.com/jake/mctl@main
```
<br />
# Setup
# Use
### Configuring mctl
mctl requires a one-time setup via the 'config' command before interacting with any servers, password is entered securely from the terminal and encrypted
mctl requires a one-time setup via the 'init' command before using any other commands
```bash
mctl config -s <serveraddress> -p <rconport>
mctl init
```
### Add a server
A
### Connecting to server
Once the client has been configured commands can be sent to the server using the 'login' command. This will authenticate you with the game server and enter the command loop, the session is logged out when the command loop is broken
@ -94,23 +106,6 @@ mctl clear
<br />
# Documentation
### Commands
|Command|Description|
|---|---|
|config|used to update the config file|
|login|makes connection request to the server using saved configuration and enters command loop|
|save \<name>|saves specific command for reuse|
|view \<name>|displays saved command|
|delete \<name>|deletes saved command|
|run \<name> args...|runs saved command filling placeholders with supplied args|
|clear|clears config file|
### Configuration file
All configuration data will be kept in the home directory and any sensitive data is encrypted for added security
<br />
# Security
RCon is an inherently insecure protocol, passwords are sent in plaintext and, if possible, the port should not be exposed to the internet. It is best to keep these connections local or over a VPN.
@ -122,8 +117,10 @@ mctl utilizes [govulncheck](https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck
this repo is currently in development and may encounter breaking changes, use a tag to prevent any surprises
# TODO
1. continue sqlite update
2. migrate the credentials to sqlite as well
3. login too
4. MCR client also has viper stuff, gut it
5. Since we are now using sqlite should we just encode the pwd and insert
6. Do we use CheckErr or use RunE
7. update readme, the commands are all broken
9. do saved commands run on default server only? Yes, but add -s
mctl command run tp nutlift -s server
10. RUN COMMAND
11. Command descriptions, examples, and silence usage
12. lol "default" server functionality (which means setting which one w default command)

View File

@ -9,8 +9,7 @@ import (
)
/*
This is a simple wrapper for the MCR client to provide easy use of mcr without having to manually
decrypt the password/hit viper each time.
This is a simple wrapper for the MCR client to provide easy use of rcon
*/
type Client struct {

View File

@ -1,32 +0,0 @@
/*
Copyright © 2025 Jake jake.young.dev@gmail.com
*/
package cmd
import (
"fmt"
"code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra"
)
// clearCmd represents the clear command
var clearCmd = &cobra.Command{
Use: "clear",
Short: "Clear config file",
Long: `Clears all configuration values for mctl. [WARNING] all server configuration will be lost`,
Run: func(cmd *cobra.Command, args []string) {
db, err := database.New()
cobra.CheckErr(err)
defer db.Close()
err = db.Destroy()
cobra.CheckErr(err)
fmt.Println("Configuration is cleared, the 'config' command must be run again.")
},
}
func init() {
rootCmd.AddCommand(clearCmd)
}

50
cmd/command/add.go Normal file
View File

@ -0,0 +1,50 @@
/*
Copyright © 2025 Jake jake.young.dev@gmail.com
*/
package command
import (
"bufio"
"fmt"
"os"
"code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra"
)
var addCmd = &cobra.Command{
Use: "add",
Example: "mctl command add",
// Short: "Saves a new server configuration",
// Long: `Saves server address, alias, port, and password.`,
Run: func(cmd *cobra.Command, args []string) {
scanner := bufio.NewScanner(os.Stdin)
//get server information
var cfgname string
fmt.Printf("Command alias: ")
if scanner.Scan() {
cfgname = scanner.Text()
}
var cfgcmd string
fmt.Printf("Command: ")
if scanner.Scan() {
cfgcmd = scanner.Text()
}
db, err := database.New()
cobra.CheckErr(err)
defer db.Close()
err = db.Init()
cobra.CheckErr(err)
err = db.SaveCmd(cfgname, cfgcmd)
cobra.CheckErr(err)
},
}
func init() {
CommandCmd.AddCommand(addCmd)
}

22
cmd/command/command.go Normal file
View File

@ -0,0 +1,22 @@
/*
Copyright © 2025 Jake jake.young.dev@gmail.com
*/
package command
import (
"github.com/spf13/cobra"
)
// CommandCmd is such a cool name lol
var CommandCmd = &cobra.Command{
Use: "command",
Example: "mctl command <subcommand>",
// Short: "A remote console client",
// Long: `mctl is a terminal-friendly remote console client made to manage game servers.`,
// Version: "v0.3.4",
// Run: func(cmd *cobra.Command, args []string) { },
}
func init() {
// rootCmd.AddCommand()
}

View File

@ -1,7 +1,7 @@
/*
Copyright © 2025 Jake jake.young.dev@gmail.com
*/
package cmd
package command
import (
"errors"
@ -11,12 +11,11 @@ import (
"github.com/spf13/cobra"
)
// deleteCmd represents the delete command
var deleteCmd = &cobra.Command{
Use: "delete <name>",
Example: "mctl delete newcmd",
Short: "Delete a saved command",
Long: `Deletes a command stored using the save command`,
Use: "delete",
Example: "mctl command delete <name>",
// Short: "Deletes a server",
// Long: `Deletes server configuration`,
Run: func(cmd *cobra.Command, args []string) {
db, err := database.New()
cobra.CheckErr(err)
@ -24,17 +23,17 @@ var deleteCmd = &cobra.Command{
err = db.DeleteCmd(args[0])
cobra.CheckErr(err)
fmt.Println("Command deleted")
fmt.Println("Command deleted!")
},
PreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("name argument is required")
return errors.New("name parameter missing")
}
return nil
},
}
func init() {
rootCmd.AddCommand(deleteCmd)
CommandCmd.AddCommand(deleteCmd)
}

35
cmd/command/view.go Normal file
View File

@ -0,0 +1,35 @@
/*
Copyright © 2025 Jake jake.young.dev@gmail.com
*/
package command
import (
"fmt"
"code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra"
)
var viewCmd = &cobra.Command{
Use: "view",
Example: "mctl command view",
// Short: "view all commands",
Run: func(cmd *cobra.Command, args []string) {
db, err := database.New()
cobra.CheckErr(err)
defer db.Close()
ts, err := db.GetAllCmds()
cobra.CheckErr(err)
for _, s := range ts {
fmt.Println("-----")
fmt.Printf("Name: %s\n", s.Name)
fmt.Printf("Command: %s\n", s.Command)
}
},
}
func init() {
CommandCmd.AddCommand(viewCmd)
}

View File

@ -1,65 +0,0 @@
/*
Copyright © 2025 Jake jake.young.dev@gmail.com
*/
package cmd
import (
"encoding/base64"
"fmt"
"os"
"code.jakeyoungdev.com/jake/mctl/database"
"code.jakeyoungdev.com/jake/mctl/model"
"github.com/spf13/cobra"
"golang.org/x/term"
)
var (
cfgserver string
cfgport int
cfgname string
)
// configCmd represents the config command
var configCmd = &cobra.Command{
Use: "config",
Example: "mctl config -n serverAlias -s x.x.x.x -p 61695",
Short: "Create and populate config file",
Long: `Creates the .mctl file in the user home directory
populating it with the server address, rcon password, and
rcon port to be pulled when using Login command`,
Run: func(cmd *cobra.Command, args []string) {
//read in password using term to keep it secure/hidden from bash history
fmt.Printf("Password: ")
ps, err := term.ReadPassword(int(os.Stdin.Fd()))
cobra.CheckErr(err)
db, err := database.New()
cobra.CheckErr(err)
defer db.Close()
err = db.Init()
cobra.CheckErr(err)
err = db.SaveServer(model.Server{
Name: cfgname,
Server: cfgserver,
Port: cfgport,
Password: base64.StdEncoding.EncodeToString(ps),
})
cobra.CheckErr(err)
},
}
func init() {
configCmd.Flags().StringVarP(&cfgserver, "server", "s", "", "server address")
err := configCmd.MarkFlagRequired("server")
cobra.CheckErr(err)
configCmd.Flags().IntVarP(&cfgport, "port", "p", 0, "server rcon port")
err = configCmd.MarkFlagRequired("port")
cobra.CheckErr(err)
configCmd.Flags().StringVarP(&cfgname, "name", "n", "", "server alias")
err = configCmd.MarkFlagRequired("name")
cobra.CheckErr(err)
rootCmd.AddCommand(configCmd)
}

32
cmd/destroy.go Normal file
View File

@ -0,0 +1,32 @@
/*
Copyright © 2025 Jake jake.young.dev@gmail.com
*/
package cmd
import (
"fmt"
"code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra"
)
// destroyCmd represents the destroy command
var destroyCmd = &cobra.Command{
Use: "destroy",
// Short: "Clear all configuration",
// Long: `Clears all configuration values for mctl. [WARNING] all server configuration will be lost`,
Run: func(cmd *cobra.Command, args []string) {
db, err := database.New()
cobra.CheckErr(err)
defer db.Close()
err = db.Destroy()
cobra.CheckErr(err)
fmt.Println("Configuration is cleared, the 'init' command must be run again.")
},
}
func init() {
rootCmd.AddCommand(destroyCmd)
}

30
cmd/init.go Normal file
View File

@ -0,0 +1,30 @@
/*
Copyright © 2025 Jake jake.young.dev@gmail.com
*/
package cmd
import (
"code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra"
)
var initCmd = &cobra.Command{
Use: "init",
Example: "mctl init",
SilenceUsage: true,
// Short: "Login to server and send commands",
// Long: `Login to default server and enter command loop. The default server
// is used unless the server flag is set`,
Run: func(cmd *cobra.Command, args []string) {
db, err := database.New()
cobra.CheckErr(err)
defer db.Close()
err = db.Init()
cobra.CheckErr(err)
},
}
func init() {
rootCmd.AddCommand(loginCmd)
}

View File

@ -5,7 +5,6 @@ package cmd
import (
"bufio"
"errors"
"fmt"
"os"
@ -15,19 +14,30 @@ import (
)
var (
name string
server string
)
// loginCmd represents the login command
var loginCmd = &cobra.Command{
Use: "login",
Example: "mctl login <name>",
Example: "mctl login -s <server>",
SilenceUsage: true,
Short: "Login to server and send commands",
Long: `Login to server using saved config and enter command loop
sending commands to server and printing the response.`,
Long: `Login to default server and enter command loop. The default server
is used unless the server flag is set`,
Run: func(cmd *cobra.Command, args []string) {
server := args[0]
db, err := database.New()
cobra.CheckErr(err)
defer db.Close()
//get default server
if server == "" {
ds, err := db.GetServer("default")
cobra.CheckErr(err)
server = ds.Name
}
cli, err := client.New(server)
cobra.CheckErr(err)
defer cli.Close()
@ -36,10 +46,6 @@ var loginCmd = &cobra.Command{
fmt.Println("Connected! Type 'mctl' to close")
scanner := bufio.NewScanner(os.Stdin)
db, err := database.New()
cobra.CheckErr(err)
defer db.Close()
var runningCmd string
for runningCmd != "mctl" {
fmt.Printf("RCON@%s /> ", server)
@ -56,11 +62,6 @@ var loginCmd = &cobra.Command{
break
}
if runningCmd == ".run" {
}
//hmm this gets weird af tbh
dbcmd, err := db.GetCmd(runningCmd)
res, err := cli.Command(runningCmd)
cobra.CheckErr(err)
fmt.Printf("\n%s\n", res)
@ -69,31 +70,9 @@ var loginCmd = &cobra.Command{
fmt.Printf("Disconnected from %s\n", server)
},
PreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("must specify which server to login to")
}
srv := args[0]
db, err := database.New()
if err != nil {
return err
}
defer db.Close()
server, err := db.GetServer(srv)
if err != nil {
return err
}
if server.Name == "" {
return fmt.Errorf("server %s not found", server.Name)
}
return nil
},
}
func init() {
loginCmd.Flags().StringVarP(&server, "server", "s", "", "server alias")
rootCmd.AddCommand(loginCmd)
}

View File

@ -6,6 +6,8 @@ package cmd
import (
"os"
"code.jakeyoungdev.com/jake/mctl/cmd/command"
srv "code.jakeyoungdev.com/jake/mctl/cmd/server"
"github.com/spf13/cobra"
)
@ -31,4 +33,6 @@ func init() {
rootCmd.CompletionOptions = cobra.CompletionOptions{
DisableDefaultCmd: true,
}
rootCmd.AddCommand(srv.ServerCmd)
rootCmd.AddCommand(command.CommandCmd) //the word command is in this four times, that can't be good.
}

View File

@ -1,87 +0,0 @@
/*
Copyright © 2025 Jake jake.young.dev@gmail.com
*/
package cmd
import (
"database/sql"
"errors"
"fmt"
"strings"
"code.jakeyoungdev.com/jake/mctl/client"
"code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// runCmd represents the run command
var runCmd = &cobra.Command{
Use: "run <name> args...",
Example: "mctl run savedcmd 63 jake",
SilenceUsage: true,
Short: "Runs a previously saved command with supplied arguments on remote server",
Long: `Loads a saved command, injects the supplied arguments into the command, and sends the command to the remove server
printing the response`,
Run: func(cmd *cobra.Command, args []string) {
db, err := database.New()
cobra.CheckErr(err)
defer db.Close()
sc, err := db.GetCmd(args[0])
if err == sql.ErrNoRows {
fmt.Printf("command %s not found", args[0])
return
}
cobra.CheckErr(err)
//convert arguments to interface
var nargs []any
for _, a := range args[1:] {
nargs = append(nargs, a)
}
//inject arguments
fixed := fmt.Sprintf(sc, nargs...)
fmt.Printf("Running saved command %s\n", fixed)
//create client and send command
cli, err := client.New()
cobra.CheckErr(err)
defer cli.Close()
res, err := cli.Command(fixed)
cobra.CheckErr(err)
fmt.Println(res)
},
PreRunE: func(cmd *cobra.Command, args []string) error {
//ensure config command has been run
if !viper.IsSet("server") || !viper.IsSet("password") || !viper.IsSet("port") {
return errors.New("the 'config' command must be run before you can interact with servers")
}
if !viper.IsSet("customcmd") {
return errors.New("no saved commands to run")
}
//ensure we have a command name
al := len(args)
if al == 0 {
return errors.New("name argument is required")
}
cmdMap := viper.Get("customcmd").(map[string]any)
count := strings.Count(cmdMap[args[0]].(string), "%s")
//make sure enough arguments are sent to fill command placeholders
if al < count+1 {
return fmt.Errorf("not enough arguments to populate command. Supplied: %d, Needed: %d", al-1, count)
}
return nil
},
}
func init() {
rootCmd.AddCommand(runCmd)
}

View File

@ -1,51 +0,0 @@
/*
Copyright © 2025 Jake jake.young.dev@gmail.com
*/
package cmd
import (
"bufio"
"errors"
"fmt"
"os"
"code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra"
)
// saveCmd represents the save command
var saveCmd = &cobra.Command{
Use: "save <name>",
Example: "mctl save newcmd",
Short: "Saves a server command under an alias for quick execution",
Long: `Saves supplied command using alias <name> to allow the command to be executed using the run command. The %s placeholder can be
used as a wildcard to be injected when running the command`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Use %s as a wildcard in your command\n", "%s") //this is ugly
fmt.Printf("Command: ")
sc := bufio.NewScanner(os.Stdin)
if sc.Scan() {
txt := sc.Text()
if txt != "" {
db, err := database.New()
cobra.CheckErr(err)
defer db.Close()
err = db.SaveCmd(args[0], txt)
cobra.CheckErr(err)
fmt.Println("\nSaved!")
}
}
},
PreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("name argument is required")
}
return nil
},
}
func init() {
rootCmd.AddCommand(saveCmd)
}

70
cmd/server/add.go Normal file
View File

@ -0,0 +1,70 @@
/*
Copyright © 2025 Jake jake.young.dev@gmail.com
*/
package server
import (
"bufio"
"encoding/base64"
"fmt"
"os"
"strconv"
"code.jakeyoungdev.com/jake/mctl/database"
"code.jakeyoungdev.com/jake/mctl/model"
"github.com/spf13/cobra"
"golang.org/x/term"
)
var addCmd = &cobra.Command{
Use: "add",
Example: "mctl server add",
Short: "Saves a new server configuration",
Long: `Saves server address, alias, port, and password.`,
Run: func(cmd *cobra.Command, args []string) {
scanner := bufio.NewScanner(os.Stdin)
//get server information
var cfgname string
fmt.Printf("Server alias: ")
if scanner.Scan() {
cfgname = scanner.Text()
}
var cfgaddress string
fmt.Printf("Server address: ")
if scanner.Scan() {
cfgaddress = scanner.Text()
}
var cfgport string
fmt.Printf("Server port: ")
if scanner.Scan() {
cfgport = scanner.Text()
}
//read in password using term to keep it secure/hidden from bash history
fmt.Printf("Password: ")
ps, err := term.ReadPassword(int(os.Stdin.Fd()))
cobra.CheckErr(err)
db, err := database.New()
cobra.CheckErr(err)
defer db.Close()
fp, err := strconv.Atoi(cfgport)
cobra.CheckErr(err)
err = db.SaveServer(model.Server{
Name: cfgname,
Server: cfgaddress,
Port: fp,
Password: base64.StdEncoding.EncodeToString(ps),
})
cobra.CheckErr(err)
},
}
func init() {
ServerCmd.AddCommand(addCmd)
}

36
cmd/server/delete.go Normal file
View File

@ -0,0 +1,36 @@
/*
Copyright © 2025 Jake jake.young.dev@gmail.com
*/
package server
import (
"errors"
"code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra"
)
var deleteCmd = &cobra.Command{
Use: "delete",
Example: "mctl server delete <server>",
// Short: "Deletes a server",
// Long: `Deletes server configuration`,
Run: func(cmd *cobra.Command, args []string) {
db, err := database.New()
cobra.CheckErr(err)
defer db.Close()
err = db.DeleteServer(args[0])
cobra.CheckErr(err)
},
PreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("server parameter missing")
}
return nil
},
}
func init() {
ServerCmd.AddCommand(deleteCmd)
}

21
cmd/server/server.go Normal file
View File

@ -0,0 +1,21 @@
/*
Copyright © 2025 Jake jake.young.dev@gmail.com
*/
package server
import (
"github.com/spf13/cobra"
)
var ServerCmd = &cobra.Command{
Use: "server",
Example: "mctl server <subcommand>",
// Short: "A remote console client",
// Long: `mctl is a terminal-friendly remote console client made to manage game servers.`,
// Version: "v0.3.4",
// Run: func(cmd *cobra.Command, args []string) { },
}
func init() {
// rootCmd.AddCommand()
}

48
cmd/server/update.go Normal file
View File

@ -0,0 +1,48 @@
/*
Copyright © 2025 Jake jake.young.dev@gmail.com
*/
package server
import (
"encoding/base64"
"errors"
"fmt"
"os"
"code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra"
"golang.org/x/term"
)
var updateCmd = &cobra.Command{
Use: "update",
Example: "mctl server update <name>",
// Short: "Saves a new server configuration",
// Long: `Saves server address, alias, port, and password.`,
Run: func(cmd *cobra.Command, args []string) {
//read in password using term to keep it secure/hidden from bash history
fmt.Printf("Password: ")
ps, err := term.ReadPassword(int(os.Stdin.Fd()))
cobra.CheckErr(err)
db, err := database.New()
cobra.CheckErr(err)
defer db.Close()
err = db.UpdateServer(args[0], base64.StdEncoding.EncodeToString(ps))
cobra.CheckErr(err)
fmt.Printf("%s password updated!", args[0])
},
PreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("name command required")
}
return nil
},
}
func init() {
ServerCmd.AddCommand(updateCmd)
}

37
cmd/server/view.go Normal file
View File

@ -0,0 +1,37 @@
/*
Copyright © 2025 Jake jake.young.dev@gmail.com
*/
package server
import (
"fmt"
"code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra"
)
var viewCmd = &cobra.Command{
Use: "view",
Example: "mctl server view",
Short: "view all servers",
Run: func(cmd *cobra.Command, args []string) {
db, err := database.New()
cobra.CheckErr(err)
defer db.Close()
ts, err := db.GetAllServers()
cobra.CheckErr(err)
for _, s := range ts {
fmt.Println("-----")
fmt.Printf("Name: %s\n", s.Name)
fmt.Printf("Address: %s\n", s.Server)
fmt.Printf("Port: %d\n", s.Port)
fmt.Printf("Password: %s\n", s.Password)
}
},
}
func init() {
ServerCmd.AddCommand(viewCmd)
}

View File

@ -1,57 +0,0 @@
/*
Copyright © 2025 Jake jake.young.dev@gmail.com
*/
package cmd
import (
"errors"
"fmt"
"strings"
"code.jakeyoungdev.com/jake/mctl/database"
"github.com/spf13/cobra"
)
// viewCmd represents the view command
var viewCmd = &cobra.Command{
Use: "view <name>",
Example: "mctl view test",
Short: "View saved commands",
Long: `Load command using the supplied name and displays it in the terminal, 'all' will list every saved command`,
Run: func(cmd *cobra.Command, args []string) {
db, err := database.New()
cobra.CheckErr(err)
defer db.Close()
if strings.EqualFold(args[0], "all") {
cmds, err := db.GetAllCmds()
cobra.CheckErr(err)
fmt.Println("\nCommands: ")
for _, c := range cmds {
fmt.Printf("%s - %s\n", c.Name, c.Command)
}
fmt.Println()
} else {
cmds, err := db.GetCmd(args[0])
cobra.CheckErr(err)
if cmds == "" {
fmt.Println("Command not found")
return
}
fmt.Printf("Command: %s\n", cmds)
}
},
PreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("name argument is required")
}
return nil
},
}
func init() {
rootCmd.AddCommand(viewCmd)
}

View File

@ -1,52 +0,0 @@
package cryptography
import (
"crypto/aes"
"crypto/cipher"
"github.com/spf13/viper"
)
func EncryptPassword(b []byte) ([]byte, error) {
nonce := viper.Get("nonce").(string)
dev := viper.Get("device").(string)
block, err := aes.NewCipher([]byte(dev))
if err != nil {
return nil, err
}
aesg, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
//adding #nosec trigger here since gosec interprets this as a hardcoded nonce value. The nonce is calculated using crypto/rand when the
//config command is ran and is pulled from memory when used any times after, for now we must prevent the scan from catching here until gosec
//is updated to account for this properly
ct := aesg.Seal(nil, []byte(nonce), []byte(b), nil) // #nosec
return ct, nil
}
func DecryptPassword(b []byte) (string, error) {
nonce := viper.Get("nonce").(string)
password := viper.Get("password").(string)
dev := viper.Get("device").(string)
block, err := aes.NewCipher([]byte(dev))
if err != nil {
return "", err
}
aesg, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
op, err := aesg.Open(nil, []byte(nonce), []byte(password), nil)
if err != nil {
return "", err
}
return string(op), nil
}

View File

@ -10,6 +10,10 @@ import (
_ "github.com/ncruces/go-sqlite3/embed"
)
/*
all sqlx methods for CRUD functionalities of commands and servers
*/
type database struct {
*sqlx.DB
}
@ -32,6 +36,8 @@ type Database interface {
DeleteServer(name string) error
}
// creates a new sqlite connection to the mctl database. Database files are
// kept in the the user home directory
func New() (Database, error) {
home, err := os.UserHomeDir()
if err != nil {
@ -47,29 +53,32 @@ func New() (Database, error) {
}, nil
}
// intitial database setup, creates commands and servers tables
func (d *database) Init() error {
query := `
CREATE TABLE IF NOT EXISTS commands(
name TEXT PRIMARY KEY,
command TEXT
)
);
CREATE TABLE IF NOT EXISTS servers(
name TEXT PRIMARY KEY,
server TEXT,
password TEXT,
port NUMBER
)
port NUMBER,
--active TEXT? bit? or something
);
`
_, err := d.Exec(query)
return err
}
// drops commands and servers tables
func (d *database) Destroy() error {
query := `
DROP TABLE commands
DROP TABLE servers
DROP TABLE commands;
DROP TABLE servers;
`
_, err := d.Exec(query)
@ -77,9 +86,10 @@ func (d *database) Destroy() error {
}
func (d *database) Close() error {
return d.Close()
return d.DB.Close()
}
// gets command using name
func (d *database) GetCmd(name string) (string, error) {
query := `
SELECT
@ -99,6 +109,7 @@ func (d *database) GetCmd(name string) (string, error) {
return name, nil
}
// gets all saved commands
func (d *database) GetAllCmds() ([]model.Command, error) {
query := `
SELECT
@ -128,6 +139,7 @@ func (d *database) GetAllCmds() ([]model.Command, error) {
return res, nil
}
// save a new command
func (d *database) SaveCmd(name, cmd string) error {
query := `
INSERT INTO commands(name, command)
@ -138,6 +150,7 @@ func (d *database) SaveCmd(name, cmd string) error {
return err
}
// DO WE NEED THIS?
func (d *database) UpdateCmd(name, cmd string) error {
query := `
UPDATE

13
go.mod
View File

@ -7,26 +7,13 @@ require (
github.com/jmoiron/sqlx v1.4.0
github.com/ncruces/go-sqlite3 v0.26.1
github.com/spf13/cobra v1.9.1
github.com/spf13/viper v1.20.1
golang.org/x/term v0.31.0
)
require (
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/ncruces/julianday v1.0.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tetratelabs/wazero v1.9.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

44
go.sum
View File

@ -1,29 +1,14 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jake-young-dev/mcr v1.4.0 h1:cXZImkfI8aNIiVPrONE6qP+nfblTGsD2iXpPKTcA25U=
github.com/jake-young-dev/mcr v1.4.0/go.mod h1:74yZHGf9h3tLUDUpInA17grKLrNp9lVesWvisCFCXKY=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
@ -32,39 +17,13 @@ github.com/ncruces/go-sqlite3 v0.26.1 h1:lBXmbmucH1Bsj57NUQR6T84UoMN7jnNImhF+ibE
github.com/ncruces/go-sqlite3 v0.26.1/go.mod h1:XFTPtFIo1DmGCh+XVP8KGn9b/o2f+z0WZuT09x2N6eo=
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
@ -72,7 +31,4 @@ golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,8 +1,5 @@
package model
//list of all fields kept in config file
// var ConfigFields = [6]string{"customcmd", "device", "nonce", "port", "server", "password"}
type Command struct {
Name string `db:"name"`
Command string `db:"command"`