diff --git a/.gitignore b/.gitignore index 92673aa..f9a0507 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ icons/ json_icon/ flaticon_colored_svgs/ +nkode \ No newline at end of file diff --git a/api/nkode_api_test.go b/api/nkode_api_test.go index 4d10418..a9dedf9 100644 --- a/api/nkode_api_test.go +++ b/api/nkode_api_test.go @@ -6,7 +6,6 @@ import ( "git.infra.nkode.tech/dkelly/nkode-core/entities" "git.infra.nkode.tech/dkelly/nkode-core/repository" "git.infra.nkode.tech/dkelly/nkode-core/security" - "git.infra.nkode.tech/dkelly/nkode-core/sqlc" "github.com/stretchr/testify/assert" "log" "os" @@ -19,26 +18,18 @@ func TestNKodeAPI(t *testing.T) { dbPath := os.Getenv("TEST_DB") ctx := context.Background() - sqliteDb, err := sqlc.OpenSqliteDb(dbPath) - assert.NoError(t, err) - - queue, err := sqlc.NewQueue(sqliteDb, ctx) - assert.NoError(t, err) - queue.Start() - defer func(queue *sqlc.Queue) { - if err := queue.Stop(); err != nil { + sqlitedb, err := repository.NewSqliteRepository(dbPath, ctx) + if err != nil { + log.Fatal(err) + } + sqlitedb.Start() + defer func(sqldb *repository.SqliteRepository) { + if err := sqldb.Stop(); err != nil { log.Fatal(err) } - }(queue) - sqlitedb := repository.NewSqliteRepository(queue, ctx) - testNKodeAPI(t, &sqlitedb) + }(sqlitedb) + testNKodeAPI(t, sqlitedb) - //if _, err := os.Stat(dbPath); err == nil { - // err = os.Remove(dbPath) - // assert.NoError(t, err) - //} else { - // assert.NoError(t, err) - //} } func testNKodeAPI(t *testing.T, db repository.CustomerUserRepository) { diff --git a/cmd/nkode/nkode.go b/cmd/nkode/nkode.go index 3ee5324..1e541d4 100644 --- a/cmd/nkode/nkode.go +++ b/cmd/nkode/nkode.go @@ -1,258 +1,81 @@ package main import ( + "context" "database/sql" - "encoding/json" - "fmt" - _ "github.com/mattn/go-sqlite3" // Import the SQLite3 driver - "io/ioutil" + _ "embed" + "flag" + "git.infra.nkode.tech/dkelly/nkode-core/repository" + "git.infra.nkode.tech/dkelly/nkode-core/sqlite" + _ "github.com/mattn/go-sqlite3" "log" "os" "path/filepath" - "strings" ) -type Icon struct { - Body string `json:"body"` - Width *int `json:"width,omitempty"` -} - -// Root Define the Root struct to represent the entire JSON structure -type Root struct { - Prefix string `json:"prefix"` - Icons map[string]Icon `json:"icons"` - Height int `json:"height"` -} - func main() { - testDbPath := os.Getenv("TEST_DB_PATH") - dbPath := os.Getenv("DB_PATH") - dbPaths := []string{testDbPath, dbPath} - flaticonSvgDir := os.Getenv("SVG_DIR") - //dbPath := "/Users/donov/Desktop/nkode.db" - //dbPaths := []string{dbPath} - //outputStr := MakeSvgFiles() - for _, path := range dbPaths { - MakeTables(path) - FlaticonToSqlite(path, flaticonSvgDir) - //SvgToSqlite(path, outputStr) - } -} - -func FlaticonToSqlite(dbPath string, svgDir string) { - db, err := sql.Open("sqlite3", dbPath) + sqliteSchema, err := sqlite.FS.ReadFile("schema.sql") if err != nil { log.Fatal(err) } - defer db.Close() + commandLine := flag.NewFlagSet(os.Args[0], flag.ExitOnError) + dbPath := commandLine.String("db-path", "", "Path to the database") + svgPath := commandLine.String("svg-path", "", "Path to the SVG directory") + ctx := context.Background() + sqliteRepo, err := repository.NewSqliteRepository(*dbPath, ctx) + err = commandLine.Parse(os.Args[1:]) + if err != nil { + log.Fatalf("Failed to parse flags: %v", err) + } + if err = MakeTables(*dbPath, string(sqliteSchema)); err != nil { + log.Fatal(err) + } - // Open the directory + FlaticonToSqlite(sqliteRepo, *svgPath) +} + +func FlaticonToSqlite(repo *repository.SqliteRepository, svgDir string) { files, err := os.ReadDir(svgDir) if err != nil { log.Fatal(err) } for _, file := range files { - // Check if it is a regular file (not a directory) and has a .svg extension if file.IsDir() || filepath.Ext(file.Name()) != ".svg" { continue } filePath := filepath.Join(svgDir, file.Name()) - // Read the file contents content, err := os.ReadFile(filePath) if err != nil { log.Println("Error reading file:", filePath, err) continue } - // Print the file name and first few bytes of the file content - insertSql := ` -INSERT INTO svg_icon (svg) -VALUES (?) -` - _, err = db.Exec(insertSql, string(content)) - if err != nil { + if err = repo.AddSvg(string(content)); err != nil { log.Fatal(err) } } } -func SvgToSqlite(dbPath string, outputStr string) { +func MakeTables(dbPath string, schema string) error { + // check if the database file and path exists. make them if they don't + 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 { - log.Fatal(err) + return err } defer db.Close() - - lines := strings.Split(outputStr, "\n") - insertSql := ` -INSERT INTO svg_icon (svg) -VALUES (?) -` - for _, line := range lines { - if line == "" { - continue - } - _, err := db.Exec(insertSql, line) - if err != nil { - log.Fatal(err) - } - } - -} - -func MakeSvgFiles() string { - jsonFiles, err := GetAllFiles("./core/sqlite-init/json") - if err != nil { - log.Fatalf("Error getting JSON files: %v", err) - } - - if len(jsonFiles) == 0 { - log.Fatal("No JSON files found in ./json folder") - } - - var outputStr string - strSet := make(map[string]struct{}) - for _, filename := range jsonFiles { - fileData, err := LoadJson(filename) - if err != nil { - log.Print("Error loading JSON file: ", err) - continue - } - height := fileData.Height - for name, icon := range fileData.Icons { - - width := height - parts := strings.Split(name, "-") - if len(parts) <= 0 { - log.Print(name, " has no parts") - continue - } - part0 := parts[0] - _, exists := strSet[part0] - if exists { - continue - } - if icon.Width != nil { - width = *icon.Width - } - strSet[part0] = struct{}{} - if icon.Body == "" { - continue - } - outputStr = fmt.Sprintf("%s%s\n", outputStr, width, height, icon.Body) - } - } - return outputStr -} - -func GetAllFiles(dir string) ([]string, error) { - // Use ioutil.ReadDir to list all files in the directory - files, err := ioutil.ReadDir(dir) - if err != nil { - return nil, fmt.Errorf("unable to read directory: %v", err) - } - - // Create a slice to hold the JSON filenames - var jsonFiles []string - - // Loop through the files and filter out JSON files - for _, file := range files { - if !file.IsDir() && filepath.Ext(file.Name()) == ".json" { - jsonFiles = append(jsonFiles, filepath.Join(dir, file.Name())) - } - } - - return jsonFiles, nil -} - -func LoadJson(filename string) (*Root, error) { - data, err := ioutil.ReadFile(filename) - if err != nil { - return nil, fmt.Errorf("failed to read file %s: %v", filename, err) - } - - var root Root - err = json.Unmarshal(data, &root) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal JSON: %v", err) - } - - return &root, nil -} - -func MakeTables(dbPath string) { - db, err := sql.Open("sqlite3", dbPath) - if err != nil { - log.Fatal(err) - } - defer db.Close() - createTable := ` -PRAGMA journal_mode=WAL; ---PRAGMA busy_timeout = 5000; -- Wait up to 5 seconds ---PRAGMA synchronous = NORMAL; -- Reduce sync frequency for less locking ---PRAGMA cache_size = -16000; -- Increase cache size (16MB)PRAGMA - -CREATE TABLE IF NOT EXISTS customer ( - id TEXT NOT NULL PRIMARY KEY - ,max_nkode_len INTEGER NOT NULL - ,min_nkode_len INTEGER NOT NULL - ,distinct_sets INTEGER NOT NULL - ,distinct_attributes INTEGER NOT NULL - ,lock_out INTEGER NOT NULL - ,expiration INTEGER NOT NULL - ,attribute_values BLOB NOT NULL - ,set_values BLOB NOT NULL - ,last_renew TEXT NOT NULL - ,created_at TEXT NOT NULL -); - -CREATE TABLE IF NOT EXISTS user ( - id TEXT NOT NULL PRIMARY KEY - ,email TEXT NOT NULL --- first_name TEXT NOT NULL --- last_name TEXT NOT NULL - ,renew INT NOT NULL - ,refresh_token TEXT - ,customer_id TEXT NOT NULL - --- Enciphered Passcode - ,code TEXT NOT NULL - ,mask TEXT NOT NULL - --- Keypad Dimensions - ,attributes_per_key INT NOT NULL - ,number_of_keys INT NOT NULL - --- User Keys - ,alpha_key BLOB NOT NULL - ,set_key BLOB NOT NULL - ,pass_key BLOB NOT NULL - ,mask_key BLOB NOT NULL - ,salt BLOB NOT NULL - ,max_nkode_len INT NOT NULL - --- User Interface - ,idx_interface BLOB NOT NULL - ,svg_id_interface BLOB NOT NULL - - ,last_login TEXT NULL - ,created_at TEXT - - ,FOREIGN KEY (customer_id) REFERENCES customer(id) - ,UNIQUE(customer_id, email) -); - -CREATE TABLE IF NOT EXISTS svg_icon ( - id INTEGER PRIMARY KEY AUTOINCREMENT - ,svg TEXT NOT NULL -); - -` - _, err = db.Exec(createTable) - if err != nil { - log.Fatal(err) + if _, err = db.Exec(schema); err != nil { + return err } + return nil } diff --git a/repository/sqlite_repository.go b/repository/sqlite_repository.go index 9765e47..2592772 100644 --- a/repository/sqlite_repository.go +++ b/repository/sqlite_repository.go @@ -20,11 +20,27 @@ type SqliteRepository struct { ctx context.Context } -func NewSqliteRepository(queue *sqlc.Queue, ctx context.Context) SqliteRepository { - return SqliteRepository{ +func NewSqliteRepository(dbPath string, ctx context.Context) (*SqliteRepository, error) { + sqliteDb, err := sqlc.OpenSqliteDb(dbPath) + if err != nil { + return nil, err + } + queue, err := sqlc.NewQueue(sqliteDb, ctx) + if err != nil { + return nil, err + } + return &SqliteRepository{ Queue: queue, ctx: ctx, - } + }, nil +} + +func (d *SqliteRepository) Start() { + d.Queue.Start() +} + +func (d *SqliteRepository) Stop() error { + return d.Queue.Stop() } func (d *SqliteRepository) CreateCustomer(c entities.Customer) error { @@ -264,6 +280,17 @@ func (d *SqliteRepository) RefreshUserPasscode(user entities.User, passcodeIdx [ return d.Queue.EnqueueWriteTx(queryFunc, params) } +func (d *SqliteRepository) AddSvg(svg string) error { + queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error { + params, ok := args.(string) + if !ok { + return fmt.Errorf("invalid argument type: expected AddSvg") + } + return q.AddSvg(ctx, params) + } + return d.Queue.EnqueueWriteTx(queryFunc, svg) +} + func (d *SqliteRepository) GetCustomer(id entities.CustomerId) (*entities.Customer, error) { customer, err := d.Queue.Queries.GetCustomer(d.ctx, uuid.UUID(id).String()) if err != nil { diff --git a/repository/sqlite_repository_test.go b/repository/sqlite_repository_test.go index f2e860b..09a3250 100644 --- a/repository/sqlite_repository_test.go +++ b/repository/sqlite_repository_test.go @@ -3,7 +3,6 @@ package repository import ( "context" "git.infra.nkode.tech/dkelly/nkode-core/entities" - "git.infra.nkode.tech/dkelly/nkode-core/sqlc" "github.com/stretchr/testify/assert" "os" "testing" @@ -11,20 +10,16 @@ import ( func TestNewSqliteDB(t *testing.T) { dbPath := os.Getenv("TEST_DB") - // sql_driver.MakeTables(dbFile) ctx := context.Background() - sqliteDb, err := sqlc.OpenSqliteDb(dbPath) + sqliteDb, err := NewSqliteRepository(dbPath, ctx) assert.NoError(t, err) - - queue, err := sqlc.NewQueue(sqliteDb, ctx) - assert.NoError(t, err) - - queue.Start() - defer queue.Stop() - db := NewSqliteRepository(queue, ctx) - assert.NoError(t, err) - testSignupLoginRenew(t, &db) - testSqliteDBRandomSvgInterface(t, &db) + sqliteDb.Start() + defer func(t *testing.T, sqliteDb *SqliteRepository) { + err := sqliteDb.Stop() + assert.NoError(t, err) + }(t, sqliteDb) + testSignupLoginRenew(t, sqliteDb) + testSqliteDBRandomSvgInterface(t, sqliteDb) } func testSignupLoginRenew(t *testing.T, db CustomerUserRepository) { diff --git a/sqlc.yaml b/sqlc.yaml index 323e48f..2191256 100644 --- a/sqlc.yaml +++ b/sqlc.yaml @@ -6,4 +6,4 @@ sql: gen: go: package: "sqlc" - out: "./pkg/nkode-core/sqlc" \ No newline at end of file + out: "./sqlc" \ No newline at end of file diff --git a/sqlc/query.sql.go b/sqlc/query.sql.go index c11071c..c5956f8 100644 --- a/sqlc/query.sql.go +++ b/sqlc/query.sql.go @@ -10,6 +10,15 @@ import ( "database/sql" ) +const addSvg = `-- name: AddSvg :exec +INSERT INTO svg_icon (svg) VALUES (?) +` + +func (q *Queries) AddSvg(ctx context.Context, svg string) error { + _, err := q.db.ExecContext(ctx, addSvg, svg) + return err +} + const createCustomer = `-- name: CreateCustomer :exec INSERT INTO customer ( id diff --git a/sqlite/embed.go b/sqlite/embed.go new file mode 100644 index 0000000..dec0d88 --- /dev/null +++ b/sqlite/embed.go @@ -0,0 +1,6 @@ +package sqlite + +import "embed" + +//go:embed schema.sql +var FS embed.FS diff --git a/sqlite/query.sql b/sqlite/query.sql index b3c50cf..abc7e7c 100644 --- a/sqlite/query.sql +++ b/sqlite/query.sql @@ -37,6 +37,9 @@ INSERT INTO user ( ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?); +-- name: AddSvg :exec +INSERT INTO svg_icon (svg) VALUES (?); + -- name: UpdateUser :exec UPDATE user SET renew = ?