274 lines
8.0 KiB
Go
274 lines
8.0 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
_ "embed"
|
|
"flag"
|
|
"fmt"
|
|
"go-nkode/internal/entities"
|
|
"go-nkode/internal/models"
|
|
"go-nkode/internal/repository"
|
|
sqlite_queue "go-nkode/internal/sqlc"
|
|
"go-nkode/sqlite"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
)
|
|
|
|
func main() {
|
|
if len(os.Args) < 2 {
|
|
log.Fatal("Please provide a command: build-db")
|
|
}
|
|
switch os.Args[1] {
|
|
case "build-db":
|
|
BuildDB()
|
|
case "create-customer":
|
|
CreateCustomer()
|
|
case "add-user":
|
|
AddUser()
|
|
default:
|
|
log.Fatalf("Unknown command: %s", os.Args[1])
|
|
}
|
|
}
|
|
|
|
func CreateCustomer() {
|
|
cliCmd := flag.NewFlagSet("create-customer", flag.ExitOnError)
|
|
customerIDStr := cliCmd.String("customer-id", "", "Customer UUID")
|
|
dbPath := cliCmd.String("db-path", "", "Path to sqlite database")
|
|
if err := cliCmd.Parse(os.Args[2:]); err != nil {
|
|
log.Fatalf("Failed to parse flags: %v", err)
|
|
}
|
|
customerID, err := models.CustomerIDFromString(*customerIDStr)
|
|
if err != nil {
|
|
log.Fatalf("Failed to parse flags: %v", err)
|
|
}
|
|
ctx := context.Background()
|
|
sqliteDb, err := sqlite_queue.OpenSqliteDb(*dbPath)
|
|
queue, err := sqlite_queue.NewQueue(ctx, sqliteDb)
|
|
queue.Start()
|
|
defer queue.Stop()
|
|
sqliteRepo := repository.NewSqliteRepository(ctx, queue)
|
|
if err != nil {
|
|
log.Fatal("error starting sqlite repo: ", err)
|
|
}
|
|
nkodePolicy := models.NewDefaultNKodePolicy()
|
|
customer, err := entities.NewCustomer(nkodePolicy)
|
|
customer.ID = customerID
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err = sqliteRepo.CreateCustomer(*customer); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func AddUser() {
|
|
cliCmd := flag.NewFlagSet("add-user", flag.ExitOnError)
|
|
imgPath := cliCmd.String("img-path", "", "Path to directory with image files to add to database. The total must number must equal attrs-per-key X numb-of-keys")
|
|
imgType := cliCmd.String("img-type", "webp", "Image types webp, svg, png, jpeg, default webp")
|
|
customerIDStr := cliCmd.String("customer-id", "", "Customer ID")
|
|
dbPath := cliCmd.String("db-path", "", "Path to the database")
|
|
userEmailStr := cliCmd.String("user-email", "", "User email")
|
|
attrsPerKey := cliCmd.Int("attrs-per-key", -1, "Attributes per key")
|
|
numbOfKeys := cliCmd.Int("numb-of-keys", -1, "Number of keys")
|
|
nkodeIcons := cliCmd.String("nkode-icons", "", "common separated file names of the users nKode icons with no space. filename order sets the nkode passcode order")
|
|
if err := cliCmd.Parse(os.Args[2:]); err != nil {
|
|
log.Fatalf("Failed to parse flags: %v", err)
|
|
}
|
|
fmt.Println("os args: ", os.Args)
|
|
ctx := context.Background()
|
|
sqliteDb, err := sqlite_queue.OpenSqliteDb(*dbPath)
|
|
queue, err := sqlite_queue.NewQueue(ctx, sqliteDb)
|
|
queue.Start()
|
|
defer queue.Stop()
|
|
sqliteRepo := repository.NewSqliteRepository(ctx, queue)
|
|
if err != nil {
|
|
log.Fatal("error starting sqlite repo: ", err)
|
|
}
|
|
customer, err := validCustomerID(*customerIDStr, &sqliteRepo)
|
|
if err != nil {
|
|
log.Println("db path: ", *dbPath)
|
|
log.Fatal("invalid customer id: ", err)
|
|
}
|
|
validateUserEmail(*userEmailStr, customer.ID, &sqliteRepo)
|
|
kp := entities.KeypadDimension{
|
|
AttrsPerKey: *attrsPerKey,
|
|
NumbOfKeys: *numbOfKeys,
|
|
}
|
|
if *attrsPerKey < entities.KeypadMin.AttrsPerKey || entities.KeypadMax.AttrsPerKey < *attrsPerKey {
|
|
log.Fatalf("invalid attributes per key valid range is %d-%d", entities.KeypadMin.AttrsPerKey, entities.KeypadMax.AttrsPerKey)
|
|
}
|
|
if *numbOfKeys < entities.KeypadMin.NumbOfKeys || entities.KeypadMax.NumbOfKeys < *numbOfKeys {
|
|
log.Fatalf("invalid number of keys. valid range is %d-%d", entities.KeypadMin.NumbOfKeys, entities.KeypadMax.NumbOfKeys)
|
|
}
|
|
if kp.IsDispersable() {
|
|
log.Fatal("Keypad can't be dispersable")
|
|
}
|
|
imgs := getImgs(*imgPath, *imgType)
|
|
if len(imgs) != kp.TotalAttrs() {
|
|
log.Fatal("svgs in directory not equal to keypad size")
|
|
}
|
|
imgIDs := make([]int, len(imgs))
|
|
for idx, img := range imgs {
|
|
id, err := sqliteRepo.AddSVGIcon(img)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
imgIDs[idx] = int(id)
|
|
}
|
|
iconNames := strings.Split(*nkodeIcons, ",")
|
|
passcodeIdxs := getPasscodeSvgIdx(iconNames, *imgPath)
|
|
if err = customer.IsValidNKode(kp, passcodeIdxs); err != nil {
|
|
log.Fatal("invalid nkode: ", err)
|
|
}
|
|
userInterface, err := entities.NewUserInterface(&kp, models.SvgIdInterface(imgIDs))
|
|
if err != nil {
|
|
log.Fatal("error creating user interface: ", err)
|
|
}
|
|
user, err := entities.NewUser(customer, *userEmailStr, passcodeIdxs, *userInterface, kp)
|
|
if err != nil {
|
|
log.Fatal("error creating user: ", err)
|
|
}
|
|
if err = sqliteRepo.WriteNewUser(*user); err != nil {
|
|
log.Fatal("error storing user: ", err)
|
|
}
|
|
}
|
|
|
|
func getPasscodeSvgIdx(nkodeSvgFileNames []string, svgDir string) []int {
|
|
files, err := os.ReadDir(svgDir)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fileNames := make([]string, 0)
|
|
for _, file := range files {
|
|
if file.IsDir() || filepath.Ext(file.Name()) != ".svg" {
|
|
continue
|
|
}
|
|
fileNames = append(fileNames, file.Name())
|
|
}
|
|
passcode := make([]int, 0)
|
|
for _, fileName := range nkodeSvgFileNames {
|
|
idx := indexOf(fileNames, fileName)
|
|
if idx == -1 {
|
|
log.Fatal("file does not exist in svg dir: ", fileName)
|
|
}
|
|
passcode = append(passcode, idx)
|
|
}
|
|
return passcode
|
|
}
|
|
|
|
func indexOf(slice []string, value string) int {
|
|
for i, v := range slice {
|
|
if v == value {
|
|
return i
|
|
}
|
|
}
|
|
return -1 // not found
|
|
}
|
|
|
|
func getImgs(imgDir, imgType string) []string {
|
|
files, err := os.ReadDir(imgDir)
|
|
if err != nil {
|
|
log.Fatalf("error opening dir: %s with err: %v", imgDir, err)
|
|
}
|
|
imgs := make([]string, 0)
|
|
for _, file := range files {
|
|
if file.IsDir() || filepath.Ext(file.Name()) != "."+imgType {
|
|
continue
|
|
}
|
|
filePath := filepath.Join(imgDir, file.Name())
|
|
content, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
log.Println("Error reading file:", filePath, err)
|
|
continue
|
|
}
|
|
imgs = append(imgs, string(content))
|
|
}
|
|
return imgs
|
|
}
|
|
|
|
func validCustomerID(id string, repo *repository.SqliteRepository) (entities.Customer, error) {
|
|
cID, err := models.CustomerIDFromString(id)
|
|
if err != nil {
|
|
return entities.Customer{}, err
|
|
}
|
|
customer, err := repo.GetCustomer(cID)
|
|
if err != nil {
|
|
return entities.Customer{}, err
|
|
}
|
|
return *customer, nil
|
|
}
|
|
|
|
func validateUserEmail(email string, customerID models.CustomerID, repo *repository.SqliteRepository) {
|
|
userEmail, err := models.ParseEmail(email)
|
|
if err != nil {
|
|
log.Fatal("user email error: ", err)
|
|
}
|
|
_, err = repo.GetUser(userEmail, customerID)
|
|
if err == nil {
|
|
log.Fatal("user already exists")
|
|
}
|
|
fmt.Println("user email is valid:", userEmail)
|
|
}
|
|
|
|
func BuildDB() {
|
|
sqliteSchema, err := sqlite.FS.ReadFile("schema.sql")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
cliCmd := flag.NewFlagSet("build-db", flag.ExitOnError)
|
|
dbPath := cliCmd.String("db-path", "", "Path to the database")
|
|
imgPath := cliCmd.String("img-path", "", "Path to the directory with images")
|
|
imgType := cliCmd.String("img-type", "svg", "Image types webp, svg, png, jpeg, default webp")
|
|
if err = cliCmd.Parse(os.Args[2:]); err != nil {
|
|
log.Fatalf("Failed to parse flags: %v", err)
|
|
}
|
|
if err = MakeTables(*dbPath, string(sqliteSchema)); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
ctx := context.Background()
|
|
sqliteDb, err := sqlite_queue.OpenSqliteDb(*dbPath)
|
|
queue, err := sqlite_queue.NewQueue(ctx, sqliteDb)
|
|
queue.Start()
|
|
defer queue.Stop()
|
|
sqliteRepo := repository.NewSqliteRepository(ctx, queue)
|
|
if err != nil {
|
|
log.Fatal("error starting sqlite repo: ", err)
|
|
}
|
|
numbImgs := IconImgToSqlite(&sqliteRepo, *imgPath, *imgType)
|
|
log.Printf("Successfully added %d Images in %s to the database at %s\n", numbImgs, *imgPath, *dbPath)
|
|
}
|
|
|
|
func IconImgToSqlite(repo *repository.SqliteRepository, imgDir, imgType string) int {
|
|
imgs := getImgs(imgDir, imgType)
|
|
for _, img := range imgs {
|
|
if _, err := repo.AddSVGIcon(img); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
return len(imgs)
|
|
}
|
|
|
|
func MakeTables(dbPath string, schema string) error {
|
|
if _, err := os.Stat(dbPath); os.IsNotExist(err) {
|
|
if err = os.MkdirAll(filepath.Dir(dbPath), 0755); err != nil {
|
|
return err
|
|
}
|
|
if _, err = os.Create(dbPath); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
db, err := sql.Open("sqlite3", dbPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err = db.Exec(schema); err != nil {
|
|
return err
|
|
}
|
|
return db.Close()
|
|
}
|