1 Commits

Author SHA1 Message Date
c5e95239b5 refactor nkode-core 2025-01-21 01:19:27 -06:00
55 changed files with 423 additions and 975 deletions

View File

@@ -1,11 +1,11 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "bin"
tmp_dir = "tmp"
[build]
args_bin = []
bin = "./bin/restapi"
cmd = "go build -o ./bin/restapi ./cmd/restapi"
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ."
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []

1
.gitignore vendored
View File

@@ -10,4 +10,3 @@ secrets.json
flaticon_svgs
flaticon_colored_svgs
icons
bin

View File

@@ -6,6 +6,7 @@ WORKDIR /app
# volume for nkode.db
VOLUME /app/data/sqlite
VOLUME /app/data/icons
# Copy go.mod and go.sum files
COPY go.mod go.sum ./
@@ -18,7 +19,7 @@ RUN go mod download
COPY . .
# Build the application
RUN go build -o go-nkode ./cmd/restapi
RUN go build -o go-nkode ./cmd
# Stage 2: Runtime
FROM debian:bookworm-slim
@@ -31,7 +32,9 @@ RUN apt-get update && \
#ENV FRONTEND_HOST=https://app.nkode.tech
#ENV FRONTEND_HOST=http://localhost:8090
ENV SQLITE_DB=/app/data/sqlite/demo.db
ENV SVG_DIR=/app/data/icons
ENV DB_PATH=/app/data/sqlite/nkode.db
ENV SQLITE_DB=/app/data/sqlite/nkode.db
# Set the working directory inside the runtime container
WORKDIR /app
@@ -40,7 +43,7 @@ WORKDIR /app
COPY --from=builder /app/go-nkode .
# Expose the port the application will run on
EXPOSE 8090
EXPOSE 8080
# Command to run the application
CMD ["./go-nkode"]

View File

@@ -4,17 +4,8 @@ vars:
compose_file: "./compose/local-compose.yaml"
cache_bust:
sh: "date +%s"
test_db: "~/databases/demo.db"
schema_db: "./sqlite/schema.sql"
svg_path: "~/svgs/flaticon_colored_svgs"
session_secret: "c3fca773c8889eb3352745c4fe503df0"
frontend: "http://localhost:8080"
tasks:
demo_run:
cmds:
- sh -c "SQLITE_DB={{.test_db}} JWT_SECRET={{.session_secret}} FRONTEND_HOST={{.frontend}} air"
build:
cmds:
- docker compose -f {{.compose_file}} build --no-cache
@@ -29,7 +20,7 @@ tasks:
- docker system prune -f
push:
cmds:
- docker buildx build --platform linux/amd64,linux/arm64 -t registry.infra.nkode.tech/go-nkode:dod --push .
- docker buildx build --platform linux/amd64,linux/arm64 -t registry.infra.nkode.tech/go-nkode:latest --push .
exec:
cmds:
- docker exec -it cron-nkode bash

View File

@@ -1,273 +0,0 @@
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()
}

View File

@@ -8,10 +8,11 @@ import (
httpSwagger "github.com/swaggo/http-swagger"
_ "go-nkode/docs"
"go-nkode/internal/api"
"go-nkode/internal/email"
"go-nkode/internal/models"
"go-nkode/internal/repository"
sqliteQueue "go-nkode/internal/sqlc"
api2 "go-nkode/pkg/nkode-core/api"
"go-nkode/pkg/nkode-core/email"
"go-nkode/pkg/nkode-core/entities"
"go-nkode/pkg/nkode-core/repository"
sqliteQueue "go-nkode/pkg/nkode-core/sqlc"
"log"
"net/http"
"os"
@@ -56,7 +57,7 @@ func main() {
}
ctx := context.Background()
queue, err := sqliteQueue.NewQueue(ctx, sqliteDb)
queue, err := sqliteQueue.NewQueue(sqliteDb, ctx)
if err != nil {
log.Fatal(err)
}
@@ -67,14 +68,18 @@ func main() {
log.Fatal(err)
}
}(queue)
sesClient := email.NewSESClient()
emailQueue := email.NewEmailQueue(emailQueueBufferSize, maxEmailsPerSecond, &sesClient)
emailQueue.Start()
defer emailQueue.Stop()
sqlitedb := repository.NewSqliteRepository(ctx, queue)
nkodeApi := api.NewNKodeAPI(&sqlitedb, emailQueue)
sqlitedb := repository.NewSqliteRepository(queue, ctx)
nkodeApi := api2.NewNKodeAPI(&sqlitedb, emailQueue)
AddDefaultCustomer(nkodeApi)
handler := api.NKodeHandler{Api: nkodeApi}
mux := http.NewServeMux()
mux.Handle(api.CreateNewCustomer, &handler)
mux.Handle(api.GenerateSignupResetInterface, &handler)
@@ -86,10 +91,12 @@ func main() {
mux.Handle(api.RandomSvgInterface, &handler)
mux.Handle(api.RefreshToken, &handler)
mux.Handle(api.ResetNKode, &handler)
// Serve Swagger UI
mux.Handle("/swagger/", httpSwagger.WrapHandler)
fmt.Println("Running on localhost:8090...")
log.Fatal(http.ListenAndServe(":8090", corsMiddleware(mux)))
fmt.Println("Running on localhost:8080...")
log.Fatal(http.ListenAndServe(":8080", corsMiddleware(mux)))
}
func corsMiddleware(next http.Handler) http.Handler {
@@ -110,13 +117,13 @@ func corsMiddleware(next http.Handler) http.Handler {
})
}
func AddDefaultCustomer(nkodeApi api.NKodeAPI) {
func AddDefaultCustomer(nkodeApi api2.NKodeAPI) {
newId, err := uuid.Parse("ed9ed6e0-082c-4b57-8d8c-f00ed6493457")
if err != nil {
log.Fatal(err)
}
customerId := models.CustomerID(newId)
nkodePolicy := models.NewDefaultNKodePolicy()
customerId := entities.CustomerId(newId)
nkodePolicy := entities.NewDefaultNKodePolicy()
_, err = nkodeApi.CreateNewCustomer(nkodePolicy, &customerId)
if err != nil {
log.Println(err)

View File

@@ -6,9 +6,9 @@ import (
"fmt"
"github.com/stretchr/testify/assert"
"go-nkode/internal/api"
"go-nkode/internal/entities"
"go-nkode/internal/models"
"go-nkode/internal/security"
"go-nkode/pkg/nkode-core/entities"
security2 "go-nkode/pkg/nkode-core/security"
"io"
"net/http"
"strings"
@@ -18,7 +18,7 @@ import (
func TestApi(t *testing.T) {
base := "http://localhost:8080"
newCustomerBody := models.NewCustomerPost{
NKodePolicy: models.NewDefaultNKodePolicy(),
NKodePolicy: entities.NewDefaultNKodePolicy(),
}
kp := entities.KeypadDimension{
AttrsPerKey: 14,
@@ -27,7 +27,7 @@ func TestApi(t *testing.T) {
var customerResp models.CreateNewCustomerResp
testApiPost(t, base+api.CreateNewCustomer, newCustomerBody, &customerResp)
userEmail := "test_username" + security.GenerateRandomString(12) + "@example.com"
userEmail := "test_username" + security2.GenerateRandomString(12) + "@example.com"
signupInterfaceBody := models.GenerateSignupRestInterfacePost{
CustomerId: customerResp.CustomerId,
AttrsPerKey: kp.AttrsPerKey,
@@ -35,7 +35,7 @@ func TestApi(t *testing.T) {
UserEmail: strings.ToUpper(userEmail), // should be case-insensitive
Reset: false,
}
var signupInterfaceResp models.GenerateSignupResetInterfaceResp
var signupInterfaceResp entities.SignupResetInterface
testApiPost(t, base+api.GenerateSignupResetInterface, signupInterfaceBody, &signupInterfaceResp)
assert.Len(t, signupInterfaceResp.SvgInterface, kp.TotalAttrs())
passcodeLen := 4
@@ -66,7 +66,7 @@ func TestApi(t *testing.T) {
UserEmail: userEmail,
}
var loginInterfaceResp models.GetLoginInterfaceResp
var loginInterfaceResp entities.LoginInterface
testApiPost(t, base+api.GetLoginInterface, loginInterfaceBody, &loginInterfaceResp)
assert.Equal(t, loginInterfaceResp.AttrsPerKey, kp.AttrsPerKey)
assert.Equal(t, loginInterfaceResp.NumbOfKeys, kp.NumbOfKeys)
@@ -78,11 +78,11 @@ func TestApi(t *testing.T) {
KeySelection: loginKeySelection,
}
var jwtTokens security.AuthenticationTokens
var jwtTokens security2.AuthenticationTokens
testApiPost(t, base+api.Login, loginBody, &jwtTokens)
refreshClaims, err := security.ParseRegisteredClaimToken(jwtTokens.RefreshToken)
refreshClaims, err := security2.ParseRegisteredClaimToken(jwtTokens.RefreshToken)
assert.Equal(t, refreshClaims.Subject, userEmail)
accessClaims, err := security.ParseRegisteredClaimToken(jwtTokens.AccessToken)
accessClaims, err := security2.ParseRegisteredClaimToken(jwtTokens.AccessToken)
assert.Equal(t, accessClaims.Subject, userEmail)
renewBody := models.RenewAttributesPost{CustomerId: customerResp.CustomerId}
testApiPost(t, base+api.RenewAttributes, renewBody, nil)
@@ -104,7 +104,7 @@ func TestApi(t *testing.T) {
var refreshTokenResp models.RefreshTokenResp
testApiGet(t, base+api.RefreshToken, &refreshTokenResp, jwtTokens.RefreshToken)
accessClaims, err = security.ParseRegisteredClaimToken(refreshTokenResp.AccessToken)
accessClaims, err = security2.ParseRegisteredClaimToken(refreshTokenResp.AccessToken)
assert.NoError(t, err)
assert.Equal(t, accessClaims.Subject, userEmail)
}

View File

@@ -1,9 +1,10 @@
services:
go-nkode:
container_name: go-nkode
image: registry.infra.nkode.tech/go-nkode:dod
image: registry.infra.nkode.tech/go-nkode
volumes:
- /var/go-nkode/sqlite:/app/data/sqlite
- /var/go-nkode/icons:/app/data/icons
environment:
- JWT_SECRET=${JWT_SECRET}

25
go.mod
View File

@@ -3,6 +3,7 @@ module go-nkode
go 1.23.0
require (
github.com/DonovanKelly/sugar-n-spice v1.0.1
github.com/aws/aws-sdk-go-v2 v1.31.0
github.com/aws/aws-sdk-go-v2/config v1.27.37
github.com/aws/aws-sdk-go-v2/service/ses v1.27.1
@@ -13,12 +14,10 @@ require (
github.com/stretchr/testify v1.10.0
github.com/swaggo/http-swagger v1.3.4
github.com/swaggo/swag v1.16.4
github.com/swaggo/swag/example/celler v0.0.0-20241025062444-99698582709d
golang.org/x/crypto v0.29.0
)
require (
github.com/DonovanKelly/sugar-n-spice v1.0.1 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.35 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 // indirect
@@ -31,39 +30,17 @@ require (
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.31.1 // indirect
github.com/aws/smithy-go v1.21.0 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.9.1 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/swaggo/files v1.0.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/tools v0.27.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

71
go.sum
View File

@@ -30,21 +30,9 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.31.1 h1:8K0UNOkZiK9Uh3HIF6Bx0rcNCftq
github.com/aws/aws-sdk-go-v2/service/sts v1.31.1/go.mod h1:yMWe0F+XG0DkRZK5ODZhG7BEFYhLXi2dqGsv6tX0cgI=
github.com/aws/smithy-go v1.21.0 h1:H7L8dtDRk0P1Qm6y0ji7MCYMQObJ5R9CRpyPhRUkLYA=
github.com/aws/smithy-go v1.21.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
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/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
@@ -53,21 +41,8 @@ github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9Z
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
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/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
@@ -76,48 +51,21 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
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/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
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.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
@@ -126,16 +74,7 @@ github.com/swaggo/http-swagger v1.3.4 h1:q7t/XLx0n15H1Q9/tk3Y9L4n210XzJF5WtnDX64
github.com/swaggo/http-swagger v1.3.4/go.mod h1:9dAh0unqMBAlbp1uE2Uc2mQTxNMU/ha4UbucIg1MFkQ=
github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
github.com/swaggo/swag/example/celler v0.0.0-20241025062444-99698582709d h1:zyYK35EKMhtoXGSxZJm9yxO9KzYEr0M9/63FhaBKr4c=
github.com/swaggo/swag/example/celler v0.0.0-20241025062444-99698582709d/go.mod h1:kw/fmH4DXH7Dp7d8aLEU1ub7UA82GhJJ0ZABDxEJaM0=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
@@ -157,12 +96,8 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -170,23 +105,17 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View File

@@ -5,16 +5,17 @@ import (
"errors"
"github.com/google/uuid"
"go-nkode/config"
"go-nkode/internal/entities"
"go-nkode/internal/models"
"go-nkode/internal/security"
"go-nkode/pkg/nkode-core/api"
"go-nkode/pkg/nkode-core/entities"
"go-nkode/pkg/nkode-core/security"
"log"
"net/http"
"strings"
)
type NKodeHandler struct {
Api NKodeAPI
Api api.NKodeAPI
}
const (
@@ -103,10 +104,12 @@ func (h *NKodeHandler) GenerateSignupResetInterfaceHandler(w http.ResponseWriter
methodNotAllowed(w)
return
}
var signupResetPost models.GenerateSignupRestInterfacePost
if err := decodeJson(w, r, &signupResetPost); err != nil {
return
}
kp := entities.KeypadDimension{
AttrsPerKey: signupResetPost.AttrsPerKey,
NumbOfKeys: signupResetPost.NumbOfKeys,
@@ -120,16 +123,17 @@ func (h *NKodeHandler) GenerateSignupResetInterfaceHandler(w http.ResponseWriter
badRequest(w, malformedCustomerId)
return
}
userEmail, err := models.ParseEmail(signupResetPost.UserEmail)
userEmail, err := entities.ParseEmail(signupResetPost.UserEmail)
if err != nil {
badRequest(w, malformedUserEmail)
return
}
resp, err := h.Api.GenerateSignupResetInterface(userEmail, models.CustomerID(customerId), kp, signupResetPost.Reset)
resp, err := h.Api.GenerateSignupResetInterface(userEmail, entities.CustomerId(customerId), kp, signupResetPost.Reset)
if err != nil {
handleError(w, err)
return
}
marshalAndWriteBytes(w, resp)
}
@@ -153,7 +157,7 @@ func (h *NKodeHandler) SetNKodeHandler(w http.ResponseWriter, r *http.Request) {
badRequest(w, malformedSessionId)
return
}
confirmInterface, err := h.Api.SetNKode(models.CustomerID(customerId), models.SessionId(sessionId), setNKodePost.KeySelection)
confirmInterface, err := h.Api.SetNKode(entities.CustomerId(customerId), entities.SessionId(sessionId), setNKodePost.KeySelection)
if err != nil {
handleError(w, err)
return
@@ -183,7 +187,7 @@ func (h *NKodeHandler) ConfirmNKodeHandler(w http.ResponseWriter, r *http.Reques
badRequest(w, malformedSessionId)
return
}
if err = h.Api.ConfirmNKode(models.CustomerID(customerId), models.SessionId(sessionId), confirmNKodePost.KeySelection); err != nil {
if err = h.Api.ConfirmNKode(entities.CustomerId(customerId), entities.SessionId(sessionId), confirmNKodePost.KeySelection); err != nil {
handleError(w, err)
return
}
@@ -205,11 +209,11 @@ func (h *NKodeHandler) GetLoginInterfaceHandler(w http.ResponseWriter, r *http.R
badRequest(w, malformedCustomerId)
return
}
userEmail, err := models.ParseEmail(loginInterfacePost.UserEmail)
userEmail, err := entities.ParseEmail(loginInterfacePost.UserEmail)
if err != nil {
badRequest(w, malformedUserEmail)
}
loginInterface, err := h.Api.GetLoginInterface(userEmail, models.CustomerID(customerId))
loginInterface, err := h.Api.GetLoginInterface(userEmail, entities.CustomerId(customerId))
if err != nil {
handleError(w, err)
return
@@ -233,12 +237,12 @@ func (h *NKodeHandler) LoginHandler(w http.ResponseWriter, r *http.Request) {
badRequest(w, malformedCustomerId)
return
}
userEmail, err := models.ParseEmail(loginPost.UserEmail)
userEmail, err := entities.ParseEmail(loginPost.UserEmail)
if err != nil {
badRequest(w, malformedUserEmail)
return
}
jwtTokens, err := h.Api.Login(models.CustomerID(customerId), userEmail, loginPost.KeySelection)
jwtTokens, err := h.Api.Login(entities.CustomerId(customerId), userEmail, loginPost.KeySelection)
if err != nil {
handleError(w, err)
return
@@ -262,7 +266,7 @@ func (h *NKodeHandler) RenewAttributesHandler(w http.ResponseWriter, r *http.Req
badRequest(w, malformedCustomerId)
return
}
if err = h.Api.RenewAttributes(models.CustomerID(customerId)); err != nil {
if err = h.Api.RenewAttributes(entities.CustomerId(customerId)); err != nil {
handleError(w, err)
return
}
@@ -283,7 +287,7 @@ func (h *NKodeHandler) RandomSvgInterfaceHandler(w http.ResponseWriter, r *http.
respBody := models.RandomSvgInterfaceResp{
Svgs: svgs,
Colors: models.SetColors,
Colors: entities.SetColors,
}
marshalAndWriteBytes(w, respBody)
@@ -305,13 +309,13 @@ func (h *NKodeHandler) RefreshTokenHandler(w http.ResponseWriter, r *http.Reques
badRequest(w, malformedCustomerId)
return
}
userEmail, err := models.ParseEmail(refreshClaims.Subject)
userEmail, err := entities.ParseEmail(refreshClaims.Subject)
if err != nil {
badRequest(w, malformedUserEmail)
log.Println(err)
return
}
accessToken, err := h.Api.RefreshToken(userEmail, models.CustomerID(customerId), refreshToken)
accessToken, err := h.Api.RefreshToken(userEmail, entities.CustomerId(customerId), refreshToken)
if err != nil {
handleError(w, err)
@@ -337,13 +341,13 @@ func (h *NKodeHandler) ResetNKode(w http.ResponseWriter, r *http.Request) {
return
}
userEmail, err := models.ParseEmail(resetNKodePost.UserEmail)
userEmail, err := entities.ParseEmail(resetNKodePost.UserEmail)
if err != nil {
badRequest(w, malformedUserEmail)
return
}
if err = h.Api.ResetNKode(userEmail, models.CustomerID(customerId)); err != nil {
if err = h.Api.ResetNKode(userEmail, entities.CustomerId(customerId)); err != nil {
internalServerError(w)
log.Println(err)
return

View File

@@ -1,10 +1,7 @@
package models
import (
"fmt"
"github.com/google/uuid"
"net/mail"
"strings"
"go-nkode/pkg/nkode-core/entities"
)
type SetNKodeResp struct {
@@ -13,7 +10,7 @@ type SetNKodeResp struct {
type RandomSvgInterfaceResp struct {
Svgs []string `json:"svgs"`
Colors []RGBColor `json:"colors"`
Colors []entities.RGBColor `json:"colors"`
}
type RefreshTokenResp struct {
@@ -21,7 +18,7 @@ type RefreshTokenResp struct {
}
type NewCustomerPost struct {
NKodePolicy NKodePolicy `json:"nkode_policy"`
NKodePolicy entities.NKodePolicy `json:"nkode_policy"`
}
type GenerateSignupRestInterfacePost struct {
@@ -40,7 +37,7 @@ type SetNKodePost struct {
type ConfirmNKodePost struct {
CustomerId string `json:"customer_id"`
KeySelection KeySelection `json:"key_selection"`
KeySelection entities.KeySelection `json:"key_selection"`
SessionId string `json:"session_id"`
}
@@ -52,18 +49,13 @@ type GetLoginInterfacePost struct {
type LoginPost struct {
CustomerId string `json:"customer_id"`
UserEmail string `json:"email"`
KeySelection KeySelection `json:"key_selection"`
KeySelection entities.KeySelection `json:"key_selection"`
}
type RenewAttributesPost struct {
CustomerId string `json:"customer_id"`
}
type RefreshTokenPost struct {
UserEmail string `json:"email"`
CustomerId string `json:"customer_id"`
}
type ResetNKodePost struct {
UserEmail string `json:"email"`
CustomerId string `json:"customer_id"`
@@ -72,104 +64,3 @@ type ResetNKodePost struct {
type CreateNewCustomerResp struct {
CustomerId string `json:"customer_id"`
}
type GenerateSignupResetInterfaceResp struct {
SessionId string `json:"session_id"`
UserIdxInterface IdxInterface `json:"user_interface"`
SvgInterface []string `json:"svg_interface"`
Colors []RGBColor `json:"colors"`
}
type GetLoginInterfaceResp struct {
UserIdxInterface IdxInterface `json:"user_interface"`
SvgInterface []string `json:"svg_interface"`
AttrsPerKey int `json:"attrs_per_key"`
NumbOfKeys int `json:"numb_of_keys"`
Colors []RGBColor `json:"colors"`
}
type KeySelection []int
type CustomerID uuid.UUID
func CustomerIdToString(customerId CustomerID) string {
customerUuid := uuid.UUID(customerId)
return customerUuid.String()
}
func CustomerIDFromString(customerID string) (CustomerID, error) {
id, err := uuid.Parse(customerID)
if err != nil {
return CustomerID{}, err
}
return CustomerID(id), nil
}
type SessionId uuid.UUID
type UserId uuid.UUID
func UserIdFromString(userId string) UserId {
id, err := uuid.Parse(userId)
if err != nil {
fmt.Errorf("unable to parse user id %+v", err)
}
return UserId(id)
}
func (s *SessionId) String() string {
id := uuid.UUID(*s)
return id.String()
}
type UserEmail string
func ParseEmail(email string) (UserEmail, error) {
_, err := mail.ParseAddress(email)
if err != nil {
return "", err
}
return UserEmail(strings.ToLower(email)), err
}
type IdxInterface []int
type SvgIdInterface []int
func SessionIdFromString(sessionId string) (SessionId, error) {
id, err := uuid.Parse(sessionId)
if err != nil {
return SessionId{}, err
}
return SessionId(id), nil
}
type EncipheredNKode struct {
Code string
Mask string
}
type RGBColor struct {
Red int `json:"red"`
Green int `json:"green"`
Blue int `json:"blue"`
}
var SetColors = []RGBColor{
{0, 0, 0}, // Black
{255, 0, 0}, // Red
{0, 128, 0}, // Dark Green
{0, 0, 255}, // Blue
{244, 200, 60}, // Yellow
{255, 0, 255}, // Magenta
{0, 200, 200}, // Cyan
{127, 0, 127}, // Purple
{232, 92, 13}, // Orange
{0, 127, 127}, // Teal
{127, 127, 0}, // Olive
{127, 0, 0}, // Dark Red
{128, 128, 128}, // Gray
{228, 102, 102}, // Dark Purple
{185, 17, 240}, // Salmon
{16, 200, 100}, // Green
}

View File

@@ -1,22 +0,0 @@
package repository
import (
"go-nkode/internal/entities"
"go-nkode/internal/models"
)
type CustomerUserRepository interface {
GetCustomer(models.CustomerID) (*entities.Customer, error)
GetUser(models.UserEmail, models.CustomerID) (*entities.User, error)
CreateCustomer(entities.Customer) error
WriteNewUser(entities.User) error
UpdateUserNKode(entities.User) error
UpdateUserInterface(models.UserId, entities.UserInterface) error
UpdateUserRefreshToken(models.UserId, string) error
Renew(models.CustomerID) error
RefreshUserPasscode(entities.User, []int, entities.CustomerAttributes) error
RandomSvgInterface(entities.KeypadDimension) ([]string, error)
RandomSvgIdxInterface(entities.KeypadDimension) (models.SvgIdInterface, error)
GetSvgStringInterface(models.SvgIdInterface) ([]string, error)
AddSVGIcon(svgStr string) (int64, error)
}

View File

@@ -5,11 +5,10 @@ import (
"github.com/google/uuid"
"github.com/patrickmn/go-cache"
"go-nkode/config"
"go-nkode/internal/email"
"go-nkode/internal/entities"
"go-nkode/internal/models"
"go-nkode/internal/repository"
"go-nkode/internal/security"
"go-nkode/pkg/nkode-core/email"
"go-nkode/pkg/nkode-core/entities"
"go-nkode/pkg/nkode-core/repository"
"go-nkode/pkg/nkode-core/security"
"log"
"os"
"time"
@@ -34,22 +33,23 @@ func NewNKodeAPI(db repository.CustomerUserRepository, queue *email.Queue) NKode
}
}
func (n *NKodeAPI) CreateNewCustomer(nkodePolicy models.NKodePolicy, id *models.CustomerID) (*models.CustomerID, error) {
func (n *NKodeAPI) CreateNewCustomer(nkodePolicy entities.NKodePolicy, id *entities.CustomerId) (*entities.CustomerId, error) {
newCustomer, err := entities.NewCustomer(nkodePolicy)
if id != nil {
newCustomer.ID = *id
newCustomer.Id = *id
}
if err != nil {
return nil, err
}
err = n.Db.CreateCustomer(*newCustomer)
if err != nil {
return nil, err
}
return &newCustomer.ID, nil
return &newCustomer.Id, nil
}
func (n *NKodeAPI) GenerateSignupResetInterface(userEmail models.UserEmail, customerId models.CustomerID, kp entities.KeypadDimension, reset bool) (*models.GenerateSignupResetInterfaceResp, error) {
func (n *NKodeAPI) GenerateSignupResetInterface(userEmail entities.UserEmail, customerId entities.CustomerId, kp entities.KeypadDimension, reset bool) (*entities.SignupResetInterface, error) {
user, err := n.Db.GetUser(userEmail, customerId)
if err != nil {
return nil, err
@@ -58,27 +58,23 @@ func (n *NKodeAPI) GenerateSignupResetInterface(userEmail models.UserEmail, cust
log.Printf("user %s already exists", string(userEmail))
return nil, config.ErrUserAlreadyExists
}
//svgIdxInterface, err := n.Db.RandomSvgIdxInterface(kp)
//if err != nil {
// return nil, err
//}
svgIdxInterface := make(models.SvgIdInterface, 54)
for idx := range 54 {
svgIdxInterface[idx] = idx + 1
svgIdxInterface, err := n.Db.RandomSvgIdxInterface(kp)
if err != nil {
return nil, err
}
signupSession, err := entities.NewSignupResetSession(userEmail, kp, customerId, svgIdxInterface, reset)
if err != nil {
return nil, err
}
//n.SignupSessions[signupSession.ID] = *signupSession
if err := n.SignupSessionCache.Add(signupSession.Id.String(), *signupSession, sessionExpiration); err != nil {
return nil, err
}
svgInterface, err := n.Db.GetSvgStringInterface(signupSession.LoginUserInterface.SvgId)
if err != nil {
return nil, err
}
resp := models.GenerateSignupResetInterfaceResp{
resp := entities.SignupResetInterface{
UserIdxInterface: signupSession.SetIdxInterface,
SvgInterface: svgInterface,
SessionId: uuid.UUID(signupSession.Id).String(),
@@ -87,41 +83,9 @@ func (n *NKodeAPI) GenerateSignupResetInterface(userEmail models.UserEmail, cust
return &resp, nil
}
func (n *NKodeAPI) GenerateSignupResetInterfaceRigged(userEmail models.UserEmail, customerId models.CustomerID, kp entities.KeypadDimension, reset bool) (*models.GenerateSignupResetInterfaceResp, error) {
user, err := n.Db.GetUser(userEmail, customerId)
if err != nil {
return nil, err
}
if user != nil && !reset {
log.Printf("user %s already exists", string(userEmail))
return nil, config.ErrUserAlreadyExists
}
svgIdxInterface := make(models.SvgIdInterface, kp.TotalAttrs())
for idx := range kp.TotalAttrs() {
svgIdxInterface[idx] = idx
}
signupSession, err := entities.NewSignupResetSessionRigged(userEmail, kp, customerId, svgIdxInterface, reset)
if err != nil {
return nil, err
}
if err := n.SignupSessionCache.Add(signupSession.Id.String(), *signupSession, sessionExpiration); err != nil {
return nil, err
}
svgInterface, err := n.Db.GetSvgStringInterface(signupSession.LoginUserInterface.SvgId)
if err != nil {
return nil, err
}
resp := models.GenerateSignupResetInterfaceResp{
UserIdxInterface: signupSession.SetIdxInterface,
SvgInterface: svgInterface,
SessionId: uuid.UUID(signupSession.Id).String(),
Colors: signupSession.Colors,
}
return &resp, nil
}
func (n *NKodeAPI) SetNKode(customerId models.CustomerID, sessionId models.SessionId, keySelection models.KeySelection) (models.IdxInterface, error) {
func (n *NKodeAPI) SetNKode(customerId entities.CustomerId, sessionId entities.SessionId, keySelection entities.KeySelection) (entities.IdxInterface, error) {
_, err := n.Db.GetCustomer(customerId)
if err != nil {
return nil, err
}
@@ -143,7 +107,7 @@ func (n *NKodeAPI) SetNKode(customerId models.CustomerID, sessionId models.Sessi
return confirmInterface, nil
}
func (n *NKodeAPI) ConfirmNKode(customerId models.CustomerID, sessionId models.SessionId, keySelection models.KeySelection) error {
func (n *NKodeAPI) ConfirmNKode(customerId entities.CustomerId, sessionId entities.SessionId, keySelection entities.KeySelection) error {
session, exists := n.SignupSessionCache.Get(sessionId.String())
if !exists {
log.Printf("session id does not exist %s", sessionId)
@@ -178,7 +142,7 @@ func (n *NKodeAPI) ConfirmNKode(customerId models.CustomerID, sessionId models.S
return err
}
func (n *NKodeAPI) GetLoginInterface(userEmail models.UserEmail, customerId models.CustomerID) (*models.GetLoginInterfaceResp, error) {
func (n *NKodeAPI) GetLoginInterface(userEmail entities.UserEmail, customerId entities.CustomerId) (*entities.LoginInterface, error) {
user, err := n.Db.GetUser(userEmail, customerId)
if err != nil {
return nil, err
@@ -191,17 +155,17 @@ func (n *NKodeAPI) GetLoginInterface(userEmail models.UserEmail, customerId mode
if err != nil {
return nil, err
}
resp := models.GetLoginInterfaceResp{
resp := entities.LoginInterface{
UserIdxInterface: user.Interface.IdxInterface,
SvgInterface: svgInterface,
NumbOfKeys: user.Kp.NumbOfKeys,
AttrsPerKey: user.Kp.AttrsPerKey,
Colors: models.SetColors,
Colors: entities.SetColors,
}
return &resp, nil
}
func (n *NKodeAPI) Login(customerId models.CustomerID, userEmail models.UserEmail, keySelection models.KeySelection) (*security.AuthenticationTokens, error) {
func (n *NKodeAPI) Login(customerId entities.CustomerId, userEmail entities.UserEmail, keySelection entities.KeySelection) (*security.AuthenticationTokens, error) {
customer, err := n.Db.GetCustomer(customerId)
if err != nil {
return nil, err
@@ -218,6 +182,7 @@ func (n *NKodeAPI) Login(customerId models.CustomerID, userEmail models.UserEmai
if err != nil {
return nil, err
}
if user.Renew {
err = n.Db.RefreshUserPasscode(*user, passcode, customer.Attributes)
if err != nil {
@@ -240,15 +205,15 @@ func (n *NKodeAPI) Login(customerId models.CustomerID, userEmail models.UserEmai
return &jwtToken, nil
}
func (n *NKodeAPI) RenewAttributes(customerId models.CustomerID) error {
func (n *NKodeAPI) RenewAttributes(customerId entities.CustomerId) error {
return n.Db.Renew(customerId)
}
func (n *NKodeAPI) RandomSvgInterface() ([]string, error) {
return n.Db.RandomSvgInterface(entities.KeypadDefault)
return n.Db.RandomSvgInterface(entities.KeypadMax)
}
func (n *NKodeAPI) RefreshToken(userEmail models.UserEmail, customerId models.CustomerID, refreshToken string) (string, error) {
func (n *NKodeAPI) RefreshToken(userEmail entities.UserEmail, customerId entities.CustomerId, refreshToken string) (string, error) {
user, err := n.Db.GetUser(userEmail, customerId)
if err != nil {
return "", err
@@ -271,14 +236,16 @@ func (n *NKodeAPI) RefreshToken(userEmail models.UserEmail, customerId models.Cu
return security.EncodeAndSignClaims(newAccessClaims)
}
func (n *NKodeAPI) ResetNKode(userEmail models.UserEmail, customerId models.CustomerID) error {
func (n *NKodeAPI) ResetNKode(userEmail entities.UserEmail, customerId entities.CustomerId) error {
user, err := n.Db.GetUser(userEmail, customerId)
if err != nil {
return fmt.Errorf("error getting user in rest nkode %v", err)
}
if user == nil {
return nil
}
nkodeResetJwt, err := security.ResetNKodeToken(string(userEmail), uuid.UUID(customerId))
if err != nil {
return err
@@ -288,12 +255,12 @@ func (n *NKodeAPI) ResetNKode(userEmail models.UserEmail, customerId models.Cust
frontendHost = config.FrontendHost
}
htmlBody := fmt.Sprintf("<h1>Hello!</h1><p>Click the link to reset your nKode.</p><a href=\"%s?token=%s\">Reset nKode</a>", frontendHost, nkodeResetJwt)
emailData := email.Email{
email := email.Email{
Sender: "no-reply@nkode.tech",
Recipient: string(userEmail),
Subject: "nKode Reset",
Content: htmlBody,
}
n.EmailQueue.AddEmail(emailData)
n.EmailQueue.AddEmail(email)
return nil
}

View File

@@ -3,12 +3,11 @@ package api
import (
"context"
"github.com/stretchr/testify/assert"
"go-nkode/internal/email"
"go-nkode/internal/entities"
"go-nkode/internal/models"
"go-nkode/internal/repository"
"go-nkode/internal/security"
sqlite_queue "go-nkode/internal/sqlc"
"go-nkode/pkg/nkode-core/email"
"go-nkode/pkg/nkode-core/entities"
repository2 "go-nkode/pkg/nkode-core/repository"
"go-nkode/pkg/nkode-core/security"
sqlite_queue "go-nkode/pkg/nkode-core/sqlc"
"log"
"os"
"testing"
@@ -23,7 +22,7 @@ func TestNKodeAPI(t *testing.T) {
sqliteDb, err := sqlite_queue.OpenSqliteDb(dbPath)
assert.NoError(t, err)
queue, err := sqlite_queue.NewQueue(ctx, sqliteDb)
queue, err := sqlite_queue.NewQueue(sqliteDb, ctx)
assert.NoError(t, err)
queue.Start()
defer func(queue *sqlite_queue.Queue) {
@@ -31,7 +30,7 @@ func TestNKodeAPI(t *testing.T) {
log.Fatal(err)
}
}(queue)
sqlitedb := repository.NewSqliteRepository(ctx, queue)
sqlitedb := repository2.NewSqliteRepository(queue, ctx)
testNKodeAPI(t, &sqlitedb)
//if _, err := os.Stat(dbPath); err == nil {
@@ -42,7 +41,7 @@ func TestNKodeAPI(t *testing.T) {
//}
}
func testNKodeAPI(t *testing.T, db repository.CustomerUserRepository) {
func testNKodeAPI(t *testing.T, db repository2.CustomerUserRepository) {
bufferSize := 100
emailsPerSec := 14
testClient := email.TestEmailClient{}
@@ -52,9 +51,9 @@ func testNKodeAPI(t *testing.T, db repository.CustomerUserRepository) {
attrsPerKey := 5
numbOfKeys := 4
for idx := 0; idx < 1; idx++ {
userEmail := models.UserEmail("test_username" + security.GenerateRandomString(12) + "@example.com")
userEmail := entities.UserEmail("test_username" + security.GenerateRandomString(12) + "@example.com")
passcodeLen := 4
nkodePolicy := models.NewDefaultNKodePolicy()
nkodePolicy := entities.NewDefaultNKodePolicy()
keypadSize := entities.KeypadDimension{AttrsPerKey: attrsPerKey, NumbOfKeys: numbOfKeys}
nkodeApi := NewNKodeAPI(db, queue)
customerId, err := nkodeApi.CreateNewCustomer(nkodePolicy, nil)
@@ -63,7 +62,7 @@ func testNKodeAPI(t *testing.T, db repository.CustomerUserRepository) {
assert.NoError(t, err)
setInterface := signupResponse.UserIdxInterface
sessionIdStr := signupResponse.SessionId
sessionId, err := models.SessionIdFromString(sessionIdStr)
sessionId, err := entities.SessionIdFromString(sessionIdStr)
assert.NoError(t, err)
keypadSize = entities.KeypadDimension{AttrsPerKey: numbOfKeys, NumbOfKeys: numbOfKeys}
userPasscode := setInterface[:passcodeLen]
@@ -100,7 +99,7 @@ func testNKodeAPI(t *testing.T, db repository.CustomerUserRepository) {
assert.NoError(t, err)
setInterface = resetResponse.UserIdxInterface
sessionIdStr = resetResponse.SessionId
sessionId, err = models.SessionIdFromString(sessionIdStr)
sessionId, err = entities.SessionIdFromString(sessionIdStr)
assert.NoError(t, err)
keypadSize = entities.KeypadDimension{AttrsPerKey: numbOfKeys, NumbOfKeys: numbOfKeys}
userPasscode = setInterface[:passcodeLen]

View File

@@ -18,7 +18,6 @@ type Client interface {
SendEmail(Email) error
}
// Email represents a dummy email structure
type Email struct {
Sender string
Recipient string
@@ -28,9 +27,7 @@ type Email struct {
type TestEmailClient struct{}
// SendEmail simulates sending an email via AWS SES
func (c *TestEmailClient) SendEmail(email Email) error {
// Simulate sending email (replace with actual AWS SES API call)
fmt.Printf("Sending email to %s\n", email.Recipient)
return nil
}
@@ -127,7 +124,6 @@ func NewEmailQueue(bufferSize int, emailsPerSecond int, client Client) *Queue {
}
}
// AddEmail queues a new email to be sent
func (q *Queue) AddEmail(email Email) {
if q.stop {
log.Printf("email %s with subject %s not add. Stopping queue", email.Recipient, email.Subject)
@@ -137,20 +133,17 @@ func (q *Queue) AddEmail(email Email) {
q.emailQueue <- email
}
// Start begins processing the email queue with rate limiting
func (q *Queue) Start() {
q.stop = false
// Worker goroutine that processes emails from the queue
go func() {
for email := range q.emailQueue {
<-q.rateLimit // Wait for the rate limiter to allow the next email
<-q.rateLimit
q.sendEmail(email)
q.wg.Done() // Mark the email as processed
q.wg.Done()
}
}()
}
// sendEmail sends an email using the SES client
func (q *Queue) sendEmail(email Email) {
if err := q.client.SendEmail(email); err != nil {
q.FailedSendCount += 1
@@ -158,11 +151,8 @@ func (q *Queue) sendEmail(email Email) {
}
}
// Stop stops the queue after all emails have been processed
func (q *Queue) Stop() {
q.stop = true
// Wait for all emails to be processed
q.wg.Wait()
// Stop the email queue
close(q.emailQueue)
}

View File

@@ -4,25 +4,24 @@ import (
"github.com/DonovanKelly/sugar-n-spice/set"
"github.com/google/uuid"
"go-nkode/config"
"go-nkode/internal/models"
"go-nkode/internal/security"
"go-nkode/internal/sqlc"
"go-nkode/internal/utils"
"go-nkode/pkg/nkode-core/security"
"go-nkode/pkg/nkode-core/sqlc"
"go-nkode/pkg/nkode-core/utils"
)
type Customer struct {
ID models.CustomerID
NKodePolicy models.NKodePolicy
Id CustomerId
NKodePolicy NKodePolicy
Attributes CustomerAttributes
}
func NewCustomer(nkodePolicy models.NKodePolicy) (*Customer, error) {
func NewCustomer(nkodePolicy NKodePolicy) (*Customer, error) {
customerAttrs, err := NewCustomerAttributes()
if err != nil {
return nil, err
}
customer := Customer{
ID: models.CustomerID(uuid.New()),
Id: CustomerId(uuid.New()),
NKodePolicy: nkodePolicy,
Attributes: *customerAttrs,
}
@@ -88,7 +87,7 @@ func (c *Customer) RenewKeys() ([]uint64, []uint64, error) {
func (c *Customer) ToSqlcCreateCustomerParams() sqlc.CreateCustomerParams {
return sqlc.CreateCustomerParams{
ID: uuid.UUID(c.ID).String(),
ID: uuid.UUID(c.Id).String(),
MaxNkodeLen: int64(c.NKodePolicy.MaxNkodeLen),
MinNkodeLen: int64(c.NKodePolicy.MinNkodeLen),
DistinctSets: int64(c.NKodePolicy.DistinctSets),

View File

@@ -1,7 +1,7 @@
package entities
import (
"go-nkode/internal/security"
"go-nkode/pkg/nkode-core/security"
"log"
)

View File

@@ -2,7 +2,6 @@ package entities
import (
"github.com/stretchr/testify/assert"
"go-nkode/internal/models"
"testing"
)
@@ -19,10 +18,10 @@ func testNewCustomerAttributes(t *testing.T) {
func testCustomerValidKeyEntry(t *testing.T) {
kp := KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 9}
nkodePolicy := models.NewDefaultNKodePolicy()
nkodePolicy := NewDefaultNKodePolicy()
customer, err := NewCustomer(nkodePolicy)
assert.NoError(t, err)
mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
mockSvgInterface := make(SvgIdInterface, kp.TotalAttrs())
userInterface, err := NewUserInterface(&kp, mockSvgInterface)
assert.NoError(t, err)
userEmail := "testing@example.com"
@@ -43,10 +42,10 @@ func testCustomerValidKeyEntry(t *testing.T) {
func testCustomerIsValidNKode(t *testing.T) {
kp := KeypadDimension{AttrsPerKey: 10, NumbOfKeys: 7}
nkodePolicy := models.NewDefaultNKodePolicy()
nkodePolicy := NewDefaultNKodePolicy()
customer, err := NewCustomer(nkodePolicy)
assert.NoError(t, err)
mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
mockSvgInterface := make(SvgIdInterface, kp.TotalAttrs())
userInterface, err := NewUserInterface(&kp, mockSvgInterface)
assert.NoError(t, err)
userEmail := "testing123@example.com"

View File

@@ -3,7 +3,7 @@ package entities
import (
"errors"
"fmt"
"go-nkode/internal/security"
"go-nkode/pkg/nkode-core/security"
)
func SelectKeyByAttrIdx(interfaceUser []int, passcodeIdxs []int, keypadSize KeypadDimension) ([]int, error) {

View File

@@ -0,0 +1,101 @@
package entities
import (
"fmt"
"github.com/google/uuid"
"net/mail"
"strings"
)
type KeySelection []int
type CustomerId uuid.UUID
func CustomerIdToString(customerId CustomerId) string {
customerUuid := uuid.UUID(customerId)
return customerUuid.String()
}
type SessionId uuid.UUID
type UserId uuid.UUID
func UserIdFromString(userId string) UserId {
id, err := uuid.Parse(userId)
if err != nil {
fmt.Errorf("unable to parse user id %+v", err)
}
return UserId(id)
}
func (s *SessionId) String() string {
id := uuid.UUID(*s)
return id.String()
}
type UserEmail string
func ParseEmail(email string) (UserEmail, error) {
_, err := mail.ParseAddress(email)
if err != nil {
return "", err
}
return UserEmail(strings.ToLower(email)), err
}
type IdxInterface []int
type SvgIdInterface []int
func SessionIdFromString(sessionId string) (SessionId, error) {
id, err := uuid.Parse(sessionId)
if err != nil {
return SessionId{}, err
}
return SessionId(id), nil
}
type EncipheredNKode struct {
Code string
Mask string
}
type RGBColor struct {
Red int `json:"red"`
Green int `json:"green"`
Blue int `json:"blue"`
}
var SetColors = []RGBColor{
{0, 0, 0}, // Black
{255, 0, 0}, // Red
{0, 128, 0}, // Dark Green
{0, 0, 255}, // Blue
{244, 200, 60}, // Yellow
{255, 0, 255}, // Magenta
{0, 200, 200}, // Cyan
{127, 0, 127}, // Purple
{232, 92, 13}, // Orange
{0, 127, 127}, // Teal
{127, 127, 0}, // Olive
{127, 0, 0}, // Dark Red
{128, 128, 128}, // Gray
{228, 102, 102}, // Dark Purple
{185, 17, 240}, // Salmon
{16, 200, 100}, // Green
}
type SignupResetInterface struct {
SessionId string `json:"session_id"`
UserIdxInterface IdxInterface `json:"user_interface"`
SvgInterface []string `json:"svg_interface"`
Colors []RGBColor `json:"colors"`
}
type LoginInterface struct {
UserIdxInterface IdxInterface `json:"user_interface"`
SvgInterface []string `json:"svg_interface"`
AttrsPerKey int `json:"attrs_per_key"`
NumbOfKeys int `json:"numb_of_keys"`
Colors []RGBColor `json:"colors"`
}

View File

@@ -1,4 +1,4 @@
package models
package entities
import "go-nkode/config"

View File

@@ -3,16 +3,15 @@ package entities
import (
"github.com/google/uuid"
"go-nkode/config"
"go-nkode/internal/models"
"go-nkode/internal/security"
"go-nkode/pkg/nkode-core/security"
"log"
)
type User struct {
Id models.UserId
CustomerId models.CustomerID
Email models.UserEmail
EncipheredPasscode models.EncipheredNKode
Id UserId
CustomerId CustomerId
Email UserEmail
EncipheredPasscode EncipheredNKode
Kp KeypadDimension
CipherKeys UserCipherKeys
Interface UserInterface
@@ -114,7 +113,7 @@ func ValidKeyEntry(user User, customer Customer, selectedKeys []int) ([]int, err
}
func NewUser(customer Customer, userEmail string, passcodeIdx []int, ui UserInterface, kp KeypadDimension) (*User, error) {
_, err := models.ParseEmail(userEmail)
_, err := ParseEmail(userEmail)
if err != nil {
return nil, err
}
@@ -131,13 +130,13 @@ func NewUser(customer Customer, userEmail string, passcodeIdx []int, ui UserInte
return nil, err
}
newUser := User{
Id: models.UserId(uuid.New()),
Email: models.UserEmail(userEmail),
Id: UserId(uuid.New()),
Email: UserEmail(userEmail),
EncipheredPasscode: *encipheredNKode,
CipherKeys: *newKeys,
Interface: ui,
Kp: kp,
CustomerId: customer.ID,
CustomerId: customer.Id,
}
return &newUser, nil
}

View File

@@ -4,8 +4,7 @@ import (
"crypto/sha256"
"errors"
"go-nkode/config"
"go-nkode/internal/models"
"go-nkode/internal/security"
"go-nkode/pkg/nkode-core/security"
"golang.org/x/crypto/bcrypt"
)
@@ -167,7 +166,7 @@ func (u *UserCipherKeys) DecipherMask(mask string, setVals []uint64, passcodeLen
return passcodeSet, nil
}
func (u *UserCipherKeys) EncipherNKode(passcodeAttrIdx []int, customerAttrs CustomerAttributes) (*models.EncipheredNKode, error) {
func (u *UserCipherKeys) EncipherNKode(passcodeAttrIdx []int, customerAttrs CustomerAttributes) (*EncipheredNKode, error) {
attrVals, err := customerAttrs.AttrValsForKp(*u.Kp)
code, err := u.EncipherSaltHashCode(passcodeAttrIdx, attrVals)
if err != nil {
@@ -186,7 +185,7 @@ func (u *UserCipherKeys) EncipherNKode(passcodeAttrIdx []int, customerAttrs Cust
if err != nil {
return nil, err
}
encipheredCode := models.EncipheredNKode{
encipheredCode := EncipheredNKode{
Code: code,
Mask: mask,
}

View File

@@ -3,18 +3,17 @@ package entities
import (
"github.com/DonovanKelly/sugar-n-spice/set"
"go-nkode/config"
"go-nkode/internal/models"
"go-nkode/internal/security"
"go-nkode/pkg/nkode-core/security"
"log"
)
type UserInterface struct {
IdxInterface models.IdxInterface
SvgId models.SvgIdInterface
IdxInterface IdxInterface
SvgId SvgIdInterface
Kp *KeypadDimension
}
func NewUserInterface(kp *KeypadDimension, svgId models.SvgIdInterface) (*UserInterface, error) {
func NewUserInterface(kp *KeypadDimension, svgId SvgIdInterface) (*UserInterface, error) {
idxInterface := security.IdentityArray(kp.TotalAttrs())
userInterface := UserInterface{
IdxInterface: idxInterface,

View File

@@ -5,51 +5,26 @@ import (
"github.com/DonovanKelly/sugar-n-spice/set"
"github.com/google/uuid"
"go-nkode/config"
"go-nkode/internal/models"
"go-nkode/internal/security"
"go-nkode/pkg/nkode-core/security"
"log"
"sort"
)
type UserSignSession struct {
Id models.SessionId
CustomerId models.CustomerID
Id SessionId
CustomerId CustomerId
LoginUserInterface UserInterface
Kp KeypadDimension
SetIdxInterface models.IdxInterface
ConfirmIdxInterface models.IdxInterface
SetKeySelection models.KeySelection
UserEmail models.UserEmail
SetIdxInterface IdxInterface
ConfirmIdxInterface IdxInterface
SetKeySelection KeySelection
UserEmail UserEmail
Reset bool
Expire int
Colors []models.RGBColor
Colors []RGBColor
}
func NewSignupResetSessionRigged(userEmail models.UserEmail, kp KeypadDimension, customerId models.CustomerID, svgInterface models.SvgIdInterface, reset bool) (*UserSignSession, error) {
loginInterface, err := NewUserInterface(&kp, svgInterface)
if err != nil {
return nil, err
}
setIdxInterface := make(models.IdxInterface, 36)
for idx := range 36 {
setIdxInterface[idx] = idx
}
session := UserSignSession{
Id: models.SessionId(uuid.New()),
CustomerId: customerId,
LoginUserInterface: *loginInterface,
SetIdxInterface: setIdxInterface,
ConfirmIdxInterface: nil,
SetKeySelection: nil,
UserEmail: userEmail,
Kp: kp,
Reset: reset,
Colors: []models.RGBColor{},
}
return &session, nil
}
func NewSignupResetSession(userEmail models.UserEmail, kp KeypadDimension, customerId models.CustomerID, svgInterface models.SvgIdInterface, reset bool) (*UserSignSession, error) {
func NewSignupResetSession(userEmail UserEmail, kp KeypadDimension, customerId CustomerId, svgInterface SvgIdInterface, reset bool) (*UserSignSession, error) {
loginInterface, err := NewUserInterface(&kp, svgInterface)
if err != nil {
return nil, err
@@ -59,7 +34,7 @@ func NewSignupResetSession(userEmail models.UserEmail, kp KeypadDimension, custo
return nil, err
}
session := UserSignSession{
Id: models.SessionId(uuid.New()),
Id: SessionId(uuid.New()),
CustomerId: customerId,
LoginUserInterface: *loginInterface,
SetIdxInterface: signupInterface.IdxInterface,
@@ -70,10 +45,11 @@ func NewSignupResetSession(userEmail models.UserEmail, kp KeypadDimension, custo
Reset: reset,
Colors: colors,
}
return &session, nil
}
func (s *UserSignSession) DeducePasscode(confirmKeyEntry models.KeySelection) ([]int, error) {
func (s *UserSignSession) DeducePasscode(confirmKeyEntry KeySelection) ([]int, error) {
validEntry := all.All[int](confirmKeyEntry, func(i int) bool {
return 0 <= i && i < s.Kp.NumbOfKeys
})
@@ -134,7 +110,7 @@ func (s *UserSignSession) DeducePasscode(confirmKeyEntry models.KeySelection) ([
return passcode, nil
}
func (s *UserSignSession) SetUserNKode(keySelection models.KeySelection) (models.IdxInterface, error) {
func (s *UserSignSession) SetUserNKode(keySelection KeySelection) (IdxInterface, error) {
validKeySelection := all.All[int](keySelection, func(i int) bool {
return 0 <= i && i < s.Kp.NumbOfKeys
})
@@ -154,7 +130,7 @@ func (s *UserSignSession) SetUserNKode(keySelection models.KeySelection) (models
return s.ConfirmIdxInterface, nil
}
func (s *UserSignSession) getSelectedKeyVals(keySelections models.KeySelection, userInterface []int) ([][]int, error) {
func (s *UserSignSession) getSelectedKeyVals(keySelections KeySelection, userInterface []int) ([][]int, error) {
signupKp := s.SignupKeypad()
keypadInterface, err := security.ListToMatrix(userInterface, signupKp.AttrsPerKey)
if err != nil {
@@ -168,7 +144,7 @@ func (s *UserSignSession) getSelectedKeyVals(keySelections models.KeySelection,
return keyVals, nil
}
func signupInterface(baseUserInterface UserInterface, kp KeypadDimension) (*UserInterface, []models.RGBColor, error) {
func signupInterface(baseUserInterface UserInterface, kp KeypadDimension) (*UserInterface, []RGBColor, error) {
// This method randomly drops sets from the base user interface so it is a square and dispersable matrix
if kp.IsDispersable() {
return nil, nil, config.ErrKeypadIsNotDispersible
@@ -195,11 +171,11 @@ func signupInterface(baseUserInterface UserInterface, kp KeypadDimension) (*User
setIdxs = setIdxs[:kp.NumbOfKeys]
sort.Ints(setIdxs)
selectedSets := make([][]int, kp.NumbOfKeys)
selectedColors := make([]models.RGBColor, kp.NumbOfKeys)
selectedColors := make([]RGBColor, kp.NumbOfKeys)
for idx, setIdx := range setIdxs {
selectedSets[idx] = attrSetView[setIdx]
selectedColors[idx] = models.SetColors[setIdx]
selectedColors[idx] = SetColors[setIdx]
}
// convert set view back into key view
selectedSets, err = security.MatrixTranspose(selectedSets)

View File

@@ -3,7 +3,6 @@ package entities
import (
"github.com/DonovanKelly/sugar-n-spice/all"
"github.com/stretchr/testify/assert"
"go-nkode/internal/models"
"testing"
)
@@ -65,7 +64,7 @@ func TestUserInterface_RandomShuffle(t *testing.T) {
AttrsPerKey: 10,
NumbOfKeys: 8,
}
mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
mockSvgInterface := make(SvgIdInterface, kp.TotalAttrs())
userInterface, err := NewUserInterface(&kp, mockSvgInterface)
assert.NoError(t, err)
userInterfaceCopy := make([]int, len(userInterface.IdxInterface))
@@ -88,7 +87,7 @@ func TestUserInterface_DisperseInterface(t *testing.T) {
for idx := 0; idx < 10000; idx++ {
kp := KeypadDimension{AttrsPerKey: 7, NumbOfKeys: 10}
mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
mockSvgInterface := make(SvgIdInterface, kp.TotalAttrs())
userInterface, err := NewUserInterface(&kp, mockSvgInterface)
assert.NoError(t, err)
preDispersion, err := userInterface.AttributeAdjacencyGraph()
@@ -107,7 +106,7 @@ func TestUserInterface_DisperseInterface(t *testing.T) {
func TestUserInterface_PartialInterfaceShuffle(t *testing.T) {
kp := KeypadDimension{AttrsPerKey: 7, NumbOfKeys: 10}
mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
mockSvgInterface := make(SvgIdInterface, kp.TotalAttrs())
userInterface, err := NewUserInterface(&kp, mockSvgInterface)
assert.NoError(t, err)
preShuffle := userInterface.IdxInterface

View File

@@ -0,0 +1,20 @@
package repository
import (
"go-nkode/pkg/nkode-core/entities"
)
type CustomerUserRepository interface {
GetCustomer(entities.CustomerId) (*entities.Customer, error)
GetUser(entities.UserEmail, entities.CustomerId) (*entities.User, error)
CreateCustomer(entities.Customer) error
WriteNewUser(entities.User) error
UpdateUserNKode(entities.User) error
UpdateUserInterface(entities.UserId, entities.UserInterface) error
UpdateUserRefreshToken(entities.UserId, string) error
Renew(entities.CustomerId) error
RefreshUserPasscode(entities.User, []int, entities.CustomerAttributes) error
RandomSvgInterface(entities.KeypadDimension) ([]string, error)
RandomSvgIdxInterface(entities.KeypadDimension) (entities.SvgIdInterface, error)
GetSvgStringInterface(entities.SvgIdInterface) ([]string, error)
}

View File

@@ -3,33 +3,32 @@ package repository
import (
"errors"
"fmt"
"go-nkode/internal/entities"
"go-nkode/internal/models"
"go-nkode/pkg/nkode-core/entities"
)
type InMemoryDb struct {
Customers map[models.CustomerID]entities.Customer
Users map[models.UserId]entities.User
userIdMap map[string]models.UserId
Customers map[entities.CustomerId]entities.Customer
Users map[entities.UserId]entities.User
userIdMap map[string]entities.UserId
}
func NewInMemoryDb() InMemoryDb {
return InMemoryDb{
Customers: make(map[models.CustomerID]entities.Customer),
Users: make(map[models.UserId]entities.User),
userIdMap: make(map[string]models.UserId),
Customers: make(map[entities.CustomerId]entities.Customer),
Users: make(map[entities.UserId]entities.User),
userIdMap: make(map[string]entities.UserId),
}
}
func (db *InMemoryDb) GetCustomer(id models.CustomerID) (*entities.Customer, error) {
func (db *InMemoryDb) GetCustomer(id entities.CustomerId) (*entities.Customer, error) {
customer, exists := db.Customers[id]
if !exists {
return nil, errors.New(fmt.Sprintf("customer %s dne", customer.ID))
return nil, errors.New(fmt.Sprintf("customer %s dne", customer.Id))
}
return &customer, nil
}
func (db *InMemoryDb) GetUser(username models.UserEmail, customerId models.CustomerID) (*entities.User, error) {
func (db *InMemoryDb) GetUser(username entities.UserEmail, customerId entities.CustomerId) (*entities.User, error) {
key := userIdKey(customerId, username)
userId, exists := db.userIdMap[key]
if !exists {
@@ -43,12 +42,12 @@ func (db *InMemoryDb) GetUser(username models.UserEmail, customerId models.Custo
}
func (db *InMemoryDb) CreateCustomer(customer entities.Customer) error {
_, exists := db.Customers[customer.ID]
_, exists := db.Customers[customer.Id]
if exists {
return errors.New(fmt.Sprintf("can write customer %s; already exists", customer.ID))
return errors.New(fmt.Sprintf("can write customer %s; already exists", customer.Id))
}
db.Customers[customer.ID] = customer
db.Customers[customer.Id] = customer
return nil
}
@@ -72,7 +71,7 @@ func (db *InMemoryDb) UpdateUserNKode(user entities.User) error {
return errors.ErrUnsupported
}
func (db *InMemoryDb) UpdateUserInterface(userId models.UserId, ui entities.UserInterface) error {
func (db *InMemoryDb) UpdateUserInterface(userId entities.UserId, ui entities.UserInterface) error {
user, exists := db.Users[userId]
if !exists {
return errors.New(fmt.Sprintf("can't update user %s, dne", user.Id))
@@ -82,11 +81,11 @@ func (db *InMemoryDb) UpdateUserInterface(userId models.UserId, ui entities.User
return nil
}
func (db *InMemoryDb) UpdateUserRefreshToken(userId models.UserId, refreshToken string) error {
func (db *InMemoryDb) UpdateUserRefreshToken(userId entities.UserId, refreshToken string) error {
return nil
}
func (db *InMemoryDb) Renew(id models.CustomerID) error {
func (db *InMemoryDb) Renew(id entities.CustomerId) error {
customer, exists := db.Customers[id]
if !exists {
return errors.New(fmt.Sprintf("customer %s does not exist", id))
@@ -121,19 +120,19 @@ func (db *InMemoryDb) RandomSvgInterface(kp entities.KeypadDimension) ([]string,
return make([]string, kp.TotalAttrs()), nil
}
func (db *InMemoryDb) RandomSvgIdxInterface(kp entities.KeypadDimension) (models.SvgIdInterface, error) {
svgs := make(models.SvgIdInterface, kp.TotalAttrs())
func (db *InMemoryDb) RandomSvgIdxInterface(kp entities.KeypadDimension) (entities.SvgIdInterface, error) {
svgs := make(entities.SvgIdInterface, kp.TotalAttrs())
for idx := range svgs {
svgs[idx] = idx
}
return svgs, nil
}
func (db *InMemoryDb) GetSvgStringInterface(idxs models.SvgIdInterface) ([]string, error) {
func (db *InMemoryDb) GetSvgStringInterface(idxs entities.SvgIdInterface) ([]string, error) {
return make([]string, len(idxs)), nil
}
func userIdKey(customerId models.CustomerID, username models.UserEmail) string {
func userIdKey(customerId entities.CustomerId, username entities.UserEmail) string {
key := fmt.Sprintf("%s:%s", customerId, username)
return key
}

View File

@@ -26,7 +26,7 @@ type Root struct {
func main() {
testDbPath := os.Getenv("TEST_DB_PATH")
dbPath := os.Getenv("SQLITE_PATH")
dbPath := os.Getenv("DB_PATH")
dbPaths := []string{testDbPath, dbPath}
flaticonSvgDir := os.Getenv("SVG_DIR")
//dbPath := "/Users/donov/Desktop/nkode.db"

View File

@@ -8,20 +8,19 @@ import (
"github.com/google/uuid"
_ "github.com/mattn/go-sqlite3" // Import the SQLite3 driver
"go-nkode/config"
"go-nkode/internal/entities"
"go-nkode/internal/models"
"go-nkode/internal/security"
"go-nkode/internal/sqlc"
"go-nkode/internal/utils"
"go-nkode/pkg/nkode-core/entities"
"go-nkode/pkg/nkode-core/security"
sqlc2 "go-nkode/pkg/nkode-core/sqlc"
"go-nkode/pkg/nkode-core/utils"
"log"
)
type SqliteRepository struct {
Queue *sqlc.Queue
Queue *sqlc2.Queue
ctx context.Context
}
func NewSqliteRepository(ctx context.Context, queue *sqlc.Queue) SqliteRepository {
func NewSqliteRepository(queue *sqlc2.Queue, ctx context.Context) SqliteRepository {
return SqliteRepository{
Queue: queue,
ctx: ctx,
@@ -29,33 +28,33 @@ func NewSqliteRepository(ctx context.Context, queue *sqlc.Queue) SqliteRepositor
}
func (d *SqliteRepository) CreateCustomer(c entities.Customer) error {
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) (any, error) {
params, ok := args.(sqlc.CreateCustomerParams)
queryFunc := func(q *sqlc2.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc2.CreateCustomerParams)
if !ok {
return nil, fmt.Errorf("invalid argument type: expected CreateCustomerParams")
return fmt.Errorf("invalid argument type: expected CreateCustomerParams")
}
return nil, q.CreateCustomer(ctx, params)
return q.CreateCustomer(ctx, params)
}
_, err := d.Queue.EnqueueWriteTx(queryFunc, c.ToSqlcCreateCustomerParams())
return err
return d.Queue.EnqueueWriteTx(queryFunc, c.ToSqlcCreateCustomerParams())
}
func (d *SqliteRepository) WriteNewUser(u entities.User) error {
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) (any, error) {
params, ok := args.(sqlc.CreateUserParams)
queryFunc := func(q *sqlc2.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc2.CreateUserParams)
if !ok {
return nil, fmt.Errorf("invalid argument type: expected CreateUserParams")
return fmt.Errorf("invalid argument type: expected CreateUserParams")
}
return nil, q.CreateUser(ctx, params)
return q.CreateUser(ctx, params)
}
// Use the wrapped function in EnqueueWriteTx
renew := 0
if u.Renew {
renew = 1
}
// Map entities.User to CreateUserParams
params := sqlc.CreateUserParams{
params := sqlc2.CreateUserParams{
ID: uuid.UUID(u.Id).String(),
Email: string(u.Email),
Renew: int64(renew),
@@ -75,24 +74,23 @@ func (d *SqliteRepository) WriteNewUser(u entities.User) error {
SvgIDInterface: security.IntArrToByteArr(u.Interface.SvgId),
CreatedAt: sql.NullString{String: utils.TimeStamp(), Valid: true},
}
_, err := d.Queue.EnqueueWriteTx(queryFunc, params)
return err
return d.Queue.EnqueueWriteTx(queryFunc, params)
}
func (d *SqliteRepository) UpdateUserNKode(u entities.User) error {
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) (any, error) {
params, ok := args.(sqlc.UpdateUserParams)
queryFunc := func(q *sqlc2.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc2.UpdateUserParams)
if !ok {
return nil, fmt.Errorf("invalid argument type: expected UpdateUserParams")
return fmt.Errorf("invalid argument type: expected UpdateUserParams")
}
return nil, q.UpdateUser(ctx, params)
return q.UpdateUser(ctx, params)
}
// Use the wrapped function in EnqueueWriteTx
renew := 0
if u.Renew {
renew = 1
}
params := sqlc.UpdateUserParams{
params := sqlc2.UpdateUserParams{
Email: string(u.Email),
Renew: int64(renew),
RefreshToken: sql.NullString{String: u.RefreshToken, Valid: u.RefreshToken != ""},
@@ -110,81 +108,80 @@ func (d *SqliteRepository) UpdateUserNKode(u entities.User) error {
IdxInterface: security.IntArrToByteArr(u.Interface.IdxInterface),
SvgIDInterface: security.IntArrToByteArr(u.Interface.SvgId),
}
_, err := d.Queue.EnqueueWriteTx(queryFunc, params)
return err
return d.Queue.EnqueueWriteTx(queryFunc, params)
}
func (d *SqliteRepository) UpdateUserInterface(id models.UserId, ui entities.UserInterface) error {
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) (any, error) {
params, ok := args.(sqlc.UpdateUserInterfaceParams)
func (d *SqliteRepository) UpdateUserInterface(id entities.UserId, ui entities.UserInterface) error {
queryFunc := func(q *sqlc2.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc2.UpdateUserInterfaceParams)
if !ok {
return nil, fmt.Errorf("invalid argument type: expected UpdateUserInterfaceParams")
return fmt.Errorf("invalid argument type: expected UpdateUserInterfaceParams")
}
return nil, q.UpdateUserInterface(ctx, params)
return q.UpdateUserInterface(ctx, params)
}
params := sqlc.UpdateUserInterfaceParams{
params := sqlc2.UpdateUserInterfaceParams{
IdxInterface: security.IntArrToByteArr(ui.IdxInterface),
LastLogin: utils.TimeStamp(),
ID: uuid.UUID(id).String(),
}
_, err := d.Queue.EnqueueWriteTx(queryFunc, params)
return err
return d.Queue.EnqueueWriteTx(queryFunc, params)
}
func (d *SqliteRepository) UpdateUserRefreshToken(id models.UserId, refreshToken string) error {
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) (any, error) {
params, ok := args.(sqlc.UpdateUserRefreshTokenParams)
func (d *SqliteRepository) UpdateUserRefreshToken(id entities.UserId, refreshToken string) error {
queryFunc := func(q *sqlc2.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc2.UpdateUserRefreshTokenParams)
if !ok {
return nil, fmt.Errorf("invalid argument type: expected UpdateUserRefreshToken")
return fmt.Errorf("invalid argument type: expected UpdateUserRefreshToken")
}
return nil, q.UpdateUserRefreshToken(ctx, params)
return q.UpdateUserRefreshToken(ctx, params)
}
params := sqlc.UpdateUserRefreshTokenParams{
params := sqlc2.UpdateUserRefreshTokenParams{
RefreshToken: sql.NullString{
String: refreshToken,
Valid: true,
},
ID: uuid.UUID(id).String(),
}
_, err := d.Queue.EnqueueWriteTx(queryFunc, params)
return err
return d.Queue.EnqueueWriteTx(queryFunc, params)
}
func (d *SqliteRepository) RenewCustomer(renewParams sqlc.RenewCustomerParams) error {
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) (any, error) {
params, ok := args.(sqlc.RenewCustomerParams)
func (d *SqliteRepository) RenewCustomer(renewParams sqlc2.RenewCustomerParams) error {
queryFunc := func(q *sqlc2.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc2.RenewCustomerParams)
if !ok {
return nil, fmt.Errorf("invalid argument type: expected RenewCustomerParams")
}
return nil, q.RenewCustomer(ctx, params)
return q.RenewCustomer(ctx, params)
}
_, err := d.Queue.EnqueueWriteTx(queryFunc, renewParams)
return err
return d.Queue.EnqueueWriteTx(queryFunc, renewParams)
}
func (d *SqliteRepository) Renew(id models.CustomerID) error {
func (d *SqliteRepository) Renew(id entities.CustomerId) error {
setXor, attrXor, err := d.renewCustomer(id)
if err != nil {
return err
}
customerId := models.CustomerIdToString(id)
customerId := entities.CustomerIdToString(id)
userRenewRows, err := d.Queue.Queries.GetUserRenew(d.ctx, customerId)
if err != nil {
return err
}
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) (any, error) {
params, ok := args.(sqlc.RenewUserParams)
queryFunc := func(q *sqlc2.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc2.RenewUserParams)
if !ok {
return nil, fmt.Errorf("invalid argument type: expected RenewUserParams")
return fmt.Errorf("invalid argument type: expected RenewUserParams")
}
return nil, q.RenewUser(ctx, params)
return q.RenewUser(ctx, params)
}
for _, row := range userRenewRows {
user := entities.User{
Id: models.UserIdFromString(row.ID),
CustomerId: models.CustomerID{},
Id: entities.UserIdFromString(row.ID),
CustomerId: entities.CustomerId{},
Email: "",
EncipheredPasscode: models.EncipheredNKode{},
EncipheredPasscode: entities.EncipheredNKode{},
Kp: entities.KeypadDimension{
AttrsPerKey: int(row.AttributesPerKey),
NumbOfKeys: int(row.NumberOfKeys),
@@ -196,23 +193,24 @@ func (d *SqliteRepository) Renew(id models.CustomerID) error {
Interface: entities.UserInterface{},
Renew: false,
}
if err = user.RenewKeys(setXor, attrXor); err != nil {
return err
}
params := sqlc.RenewUserParams{
params := sqlc2.RenewUserParams{
AlphaKey: security.Uint64ArrToByteArr(user.CipherKeys.AlphaKey),
SetKey: security.Uint64ArrToByteArr(user.CipherKeys.SetKey),
Renew: 1,
ID: uuid.UUID(user.Id).String(),
}
if _, err = d.Queue.EnqueueWriteTx(queryFunc, params); err != nil {
if err = d.Queue.EnqueueWriteTx(queryFunc, params); err != nil {
return err
}
}
return nil
}
func (d *SqliteRepository) renewCustomer(id models.CustomerID) ([]uint64, []uint64, error) {
func (d *SqliteRepository) renewCustomer(id entities.CustomerId) ([]uint64, []uint64, error) {
customer, err := d.GetCustomer(id)
if err != nil {
return nil, nil, err
@@ -221,19 +219,21 @@ func (d *SqliteRepository) renewCustomer(id models.CustomerID) ([]uint64, []uint
if err != nil {
return nil, nil, err
}
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) (any, error) {
params, ok := args.(sqlc.RenewCustomerParams)
queryFunc := func(q *sqlc2.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc2.RenewCustomerParams)
if !ok {
return nil, fmt.Errorf("invalid argument type: expected RenewCustomerParams")
return fmt.Errorf("invalid argument type: expected RenewCustomerParams")
}
return nil, q.RenewCustomer(ctx, params)
return q.RenewCustomer(ctx, params)
}
params := sqlc.RenewCustomerParams{
params := sqlc2.RenewCustomerParams{
AttributeValues: security.Uint64ArrToByteArr(customer.Attributes.AttrVals),
SetValues: security.Uint64ArrToByteArr(customer.Attributes.SetVals),
ID: uuid.UUID(customer.ID).String(),
ID: uuid.UUID(customer.Id).String(),
}
if _, err = d.Queue.EnqueueWriteTx(queryFunc, params); err != nil {
if err = d.Queue.EnqueueWriteTx(queryFunc, params); err != nil {
return nil, nil, err
}
return setXor, attrXor, nil
@@ -243,14 +243,14 @@ func (d *SqliteRepository) RefreshUserPasscode(user entities.User, passcodeIdx [
if err := user.RefreshPasscode(passcodeIdx, customerAttr); err != nil {
return err
}
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) (any, error) {
params, ok := args.(sqlc.RefreshUserPasscodeParams)
queryFunc := func(q *sqlc2.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc2.RefreshUserPasscodeParams)
if !ok {
return nil, fmt.Errorf("invalid argument type: expected RefreshUserPasscodeParams")
return fmt.Errorf("invalid argument type: expected RefreshUserPasscodeParams")
}
return nil, q.RefreshUserPasscode(ctx, params)
return q.RefreshUserPasscode(ctx, params)
}
params := sqlc.RefreshUserPasscodeParams{
params := sqlc2.RefreshUserPasscodeParams{
Renew: 0,
Code: user.EncipheredPasscode.Code,
Mask: user.EncipheredPasscode.Mask,
@@ -261,18 +261,18 @@ func (d *SqliteRepository) RefreshUserPasscode(user entities.User, passcodeIdx [
Salt: user.CipherKeys.Salt,
ID: uuid.UUID(user.Id).String(),
}
_, err := d.Queue.EnqueueWriteTx(queryFunc, params)
return err
return d.Queue.EnqueueWriteTx(queryFunc, params)
}
func (d *SqliteRepository) GetCustomer(id models.CustomerID) (*entities.Customer, error) {
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 {
return nil, err
}
return &entities.Customer{
ID: id,
NKodePolicy: models.NKodePolicy{
Id: id,
NKodePolicy: entities.NKodePolicy{
MaxNkodeLen: int(customer.MaxNkodeLen),
MinNkodeLen: int(customer.MinNkodeLen),
DistinctSets: int(customer.DistinctSets),
@@ -284,8 +284,8 @@ func (d *SqliteRepository) GetCustomer(id models.CustomerID) (*entities.Customer
}, nil
}
func (d *SqliteRepository) GetUser(email models.UserEmail, customerId models.CustomerID) (*entities.User, error) {
userRow, err := d.Queue.Queries.GetUser(d.ctx, sqlc.GetUserParams{
func (d *SqliteRepository) GetUser(email entities.UserEmail, customerId entities.CustomerId) (*entities.User, error) {
userRow, err := d.Queue.Queries.GetUser(d.ctx, sqlc2.GetUserParams{
Email: string(email),
CustomerID: uuid.UUID(customerId).String(),
})
@@ -295,19 +295,21 @@ func (d *SqliteRepository) GetUser(email models.UserEmail, customerId models.Cus
}
return nil, fmt.Errorf("failed to get user: %w", err)
}
kp := entities.KeypadDimension{
AttrsPerKey: int(userRow.AttributesPerKey),
NumbOfKeys: int(userRow.NumberOfKeys),
}
renew := false
if userRow.Renew == 1 {
renew = true
}
user := entities.User{
Id: models.UserIdFromString(userRow.ID),
Id: entities.UserIdFromString(userRow.ID),
CustomerId: customerId,
Email: email,
EncipheredPasscode: models.EncipheredNKode{
EncipheredPasscode: entities.EncipheredNKode{
Code: userRow.Code,
Mask: userRow.Mask,
},
@@ -340,33 +342,14 @@ func (d *SqliteRepository) RandomSvgInterface(kp entities.KeypadDimension) ([]st
return d.getSvgsById(ids)
}
func (d *SqliteRepository) RandomSvgIdxInterface(kp entities.KeypadDimension) (models.SvgIdInterface, error) {
func (d *SqliteRepository) RandomSvgIdxInterface(kp entities.KeypadDimension) (entities.SvgIdInterface, error) {
return d.getRandomIds(kp.TotalAttrs())
}
func (d *SqliteRepository) GetSvgStringInterface(idxs models.SvgIdInterface) ([]string, error) {
func (d *SqliteRepository) GetSvgStringInterface(idxs entities.SvgIdInterface) ([]string, error) {
return d.getSvgsById(idxs)
}
func (d *SqliteRepository) AddSVGIcon(svgStr string) (int64, error) {
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) (any, error) {
params, ok := args.(string)
if !ok {
return nil, fmt.Errorf("invalid argument type: expected string")
}
return q.AddSVGIcon(ctx, params)
}
svgID, err := d.Queue.EnqueueWriteTx(queryFunc, svgStr)
if err != nil {
return -1, err
}
svgIDInt64, ok := svgID.(int64)
if !ok {
return -1, errors.New("svgID in DB isn't int64")
}
return svgIDInt64, nil
}
func (d *SqliteRepository) getSvgsById(ids []int) ([]string, error) {
svgs := make([]string, len(ids))
for idx, id := range ids {

View File

@@ -3,9 +3,8 @@ package repository
import (
"context"
"github.com/stretchr/testify/assert"
"go-nkode/internal/entities"
"go-nkode/internal/models"
sqlite_queue "go-nkode/internal/sqlc"
"go-nkode/pkg/nkode-core/entities"
sqlite_queue "go-nkode/pkg/nkode-core/sqlc"
"os"
"testing"
)
@@ -17,41 +16,41 @@ func TestNewSqliteDB(t *testing.T) {
sqliteDb, err := sqlite_queue.OpenSqliteDb(dbPath)
assert.NoError(t, err)
queue, err := sqlite_queue.NewQueue(ctx, sqliteDb)
queue, err := sqlite_queue.NewQueue(sqliteDb, ctx)
assert.NoError(t, err)
queue.Start()
defer queue.Stop()
db := NewSqliteRepository(ctx, queue)
db := NewSqliteRepository(queue, ctx)
assert.NoError(t, err)
testSignupLoginRenew(t, &db)
testSqliteDBRandomSvgInterface(t, &db)
}
func testSignupLoginRenew(t *testing.T, db CustomerUserRepository) {
nkodePolicy := models.NewDefaultNKodePolicy()
nkodePolicy := entities.NewDefaultNKodePolicy()
customerOrig, err := entities.NewCustomer(nkodePolicy)
assert.NoError(t, err)
err = db.CreateCustomer(*customerOrig)
assert.NoError(t, err)
customer, err := db.GetCustomer(customerOrig.ID)
customer, err := db.GetCustomer(customerOrig.Id)
assert.NoError(t, err)
assert.Equal(t, customerOrig, customer)
username := "test_user@example.com"
kp := entities.KeypadDefault
passcodeIdx := []int{0, 1, 2, 3}
mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
mockSvgInterface := make(entities.SvgIdInterface, kp.TotalAttrs())
ui, err := entities.NewUserInterface(&kp, mockSvgInterface)
assert.NoError(t, err)
userOrig, err := entities.NewUser(*customer, username, passcodeIdx, *ui, kp)
assert.NoError(t, err)
err = db.WriteNewUser(*userOrig)
assert.NoError(t, err)
user, err := db.GetUser(models.UserEmail(username), customer.ID)
user, err := db.GetUser(entities.UserEmail(username), customer.Id)
assert.NoError(t, err)
assert.Equal(t, userOrig, user)
err = db.Renew(customer.ID)
err = db.Renew(customer.Id)
assert.NoError(t, err)
}

View File

@@ -10,19 +10,6 @@ import (
"database/sql"
)
const addSVGIcon = `-- name: AddSVGIcon :one
INSERT INTO svg_icon (svg)
VALUES (?)
RETURNING id
`
func (q *Queries) AddSVGIcon(ctx context.Context, svg string) (int64, error) {
row := q.db.QueryRowContext(ctx, addSVGIcon, svg)
var id int64
err := row.Scan(&id)
return id, err
}
const createCustomer = `-- name: CreateCustomer :exec
INSERT INTO customer (
id

View File

@@ -10,13 +10,12 @@ import (
const writeBufferSize = 100
type GenericQuery func(*Queries, context.Context, any) (any, error)
type SqlcGeneric func(*Queries, context.Context, any) error
type WriteTx struct {
ErrChan chan error
ReturnChan chan any
Query GenericQuery
Args any
Query SqlcGeneric
Args interface{}
}
type Queue struct {
@@ -28,7 +27,7 @@ type Queue struct {
cancel context.CancelFunc
}
func NewQueue(ctx context.Context, sqlDb *sql.DB) (*Queue, error) {
func NewQueue(sqlDb *sql.DB, ctx context.Context) (*Queue, error) {
ctx, cancel := context.WithCancel(context.Background())
sqldb := &Queue{
Queries: New(sqlDb),
@@ -43,19 +42,15 @@ func NewQueue(ctx context.Context, sqlDb *sql.DB) (*Queue, error) {
func (d *Queue) Start() {
d.wg.Add(1)
go func() {
// TODO: I think this might be a naive approach.
defer d.wg.Done()
go func() {
for {
select {
case <-d.ctx.Done():
return
case writeTx := <-d.WriteQueue:
ret, err := writeTx.Query(d.Queries, d.ctx, writeTx.Args)
err := writeTx.Query(d.Queries, d.ctx, writeTx.Args)
writeTx.ErrChan <- err
writeTx.ReturnChan <- ret
close(writeTx.ErrChan)
close(writeTx.ReturnChan)
}
}
}()
@@ -68,24 +63,21 @@ func (d *Queue) Stop() error {
return d.Db.Close()
}
func (d *Queue) EnqueueWriteTx(queryFunc GenericQuery, args any) (any, error) {
func (d *Queue) EnqueueWriteTx(queryFunc SqlcGeneric, args any) error {
select {
case <-d.ctx.Done():
return nil, errors.New("database is shutting down")
return errors.New("database is shutting down")
default:
}
errChan := make(chan error, 1)
retChan := make(chan any, 1)
writeTx := WriteTx{
Query: queryFunc,
Args: args,
ErrChan: errChan,
ReturnChan: retChan,
}
d.WriteQueue <- writeTx
err := <-errChan
val := <-retChan
return val, err
return <-errChan
}
func OpenSqliteDb(dbPath string) (*sql.DB, error) {

View File

@@ -134,8 +134,3 @@ WHERE id = ?;
-- name: GetSvgCount :one
SELECT COUNT(*) as count FROM svg_icon;
-- name: AddSVGIcon :one
INSERT INTO svg_icon (svg)
VALUES (?)
RETURNING id;

View File

@@ -1,48 +0,0 @@
#!/bin/bash
sqlite_db="$HOME/databases/demo.db"
db_schema="../../sqlite/schema.sql"
# svg_path="$HOME/svgs/flaticon_colored_svgs"
# svg_path="$HOME/svgs/flaticon_colored_pngs"
#svg_path="$HOME/icons"
svg_path="$HOME/svgs/warfighter_icons"
# remove existing test database
if [ -f "$sqlite_db" ]; then
echo "Removing existing test database at $sqlite_db"
rm "$sqlite_db"
else
echo "No existing test database found at $sqlite_db"
fi
# rebuild database
sqlite3 "$sqlite_db" < "$db_schema"
cli="../../bin/cli"
# build go cli
echo "building cli"
go build -o $cli ../../cmd/cli/main.go
# build db
echo "building db"
$cli build-db -db-path "$sqlite_db" -img-path "$svg_path"
# create customer
echo "creating customer"
customer_id="ed9ed6e0-082c-4b57-8d8c-f00ed6493457"
$cli create-customer -customer-id "$customer_id" -db-path "$sqlite_db"
## create admin user
#user_email="donovan.a.kelly@pm.me"
#keypad_path="$HOME/svgs/my_icons/"
#$nkode_cli add-user \
# -img-path "$keypad_path" \
# -img-type "svg" \
# -customer-id "$customer_id" \
# -user-email "$user_email" \
# -attrs-per-key 9 -numb-of-keys 6 \
# -db-path "$sqlite_db" \
# -role admin \
# -nkode-icons ae-86.svg,arkansas.svg,banana-slug.svg,blockchain.svg
#

View File

@@ -1,7 +0,0 @@
#!/bin/bash
set -euo pipefail
./rebuild_db.sh
sqlite_db="$HOME/databases/demo.db"
scp $sqlite_db root@nkode.tech:/var/go-nkode/sqlite/demo.db

View File

@@ -1,9 +1,9 @@
version: "2"
sql:
- engine: "sqlite"
queries: "./sqlite/query.sql"
schema: "./sqlite/schema.sql"
queries: "./pkg/nkode-core/sqlite/query.sql"
schema: "./pkg/nkode-core/sqlite/schema.sql"
gen:
go:
package: "sqlc"
out: "./internal/sqlc"
out: "./pkg/nkode-core/sqlc"

View File

@@ -1,6 +0,0 @@
package sqlite
import "embed"
//go:embed schema.sql
var FS embed.FS