2 Commits

Author SHA1 Message Date
c23adb0150 stuff 2024-12-17 16:38:24 -06:00
4f69a3ad45 add instructions 2024-12-09 17:25:27 -06:00
50 changed files with 725 additions and 527 deletions

View File

@@ -1,18 +1,14 @@
package main
import (
"context"
"database/sql"
"fmt"
"github.com/google/uuid"
httpSwagger "github.com/swaggo/http-swagger"
_ "go-nkode/docs"
"go-nkode/internal/api"
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"
"go-nkode/internal/db"
"go-nkode/internal/email"
"go-nkode/internal/models"
"log"
"net/http"
"os"
@@ -41,42 +37,24 @@ const (
// @securityDefinitions.apiKey ApiKeyAuth
// @in header
// @name Authorization
func main() {
dbPath := os.Getenv("SQLITE_DB")
if dbPath == "" {
log.Fatal("SQLITE_DB=/path/to/nkode.db not set")
log.Fatalf("SQLITE_DB=/path/to/nkode.db not set")
}
sqliteDb, err := sql.Open("sqlite3", dbPath)
sqlitedb, err := db.NewSqliteDB(dbPath)
if err != nil {
log.Fatalf("failed to open database: %v", err)
log.Fatalf("%v", err)
}
if err := sqliteDb.Ping(); err != nil {
log.Fatalf("failed to connect to database: %v", err)
}
ctx := context.Background()
queue, err := sqliteQueue.NewQueue(sqliteDb, ctx)
if err != nil {
log.Fatal(err)
}
queue.Start()
defer func(queue *sqliteQueue.Queue) {
if err := queue.Stop(); err != nil {
log.Fatal(err)
}
}(queue)
defer sqlitedb.Close()
sesClient := email.NewSESClient()
emailQueue := email.NewEmailQueue(emailQueueBufferSize, maxEmailsPerSecond, &sesClient)
emailQueue.Start()
defer emailQueue.Stop()
sqlitedb := repository.NewSqliteRepository(queue, ctx)
nkodeApi := api2.NewNKodeAPI(&sqlitedb, emailQueue)
nkodeApi := api.NewNKodeAPI(sqlitedb, emailQueue)
AddDefaultCustomer(nkodeApi)
handler := api.NKodeHandler{Api: nkodeApi}
@@ -117,13 +95,13 @@ func corsMiddleware(next http.Handler) http.Handler {
})
}
func AddDefaultCustomer(nkodeApi api2.NKodeAPI) {
func AddDefaultCustomer(nkodeApi api.NKodeAPI) {
newId, err := uuid.Parse("ed9ed6e0-082c-4b57-8d8c-f00ed6493457")
if err != nil {
log.Fatal(err)
}
customerId := entities.CustomerId(newId)
nkodePolicy := entities.NewDefaultNKodePolicy()
customerId := models.CustomerId(newId)
nkodePolicy := models.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/pkg/nkode-core/entities"
security2 "go-nkode/pkg/nkode-core/security"
"go-nkode/internal/security"
"io"
"net/http"
"strings"
@@ -18,7 +18,7 @@ import (
func TestApi(t *testing.T) {
base := "http://localhost:8080"
newCustomerBody := models.NewCustomerPost{
NKodePolicy: entities.NewDefaultNKodePolicy(),
NKodePolicy: models.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" + security2.GenerateRandomString(12) + "@example.com"
userEmail := "test_username" + security.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 entities.SignupResetInterface
var signupInterfaceResp models.GenerateSignupResetInterfaceResp
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 entities.LoginInterface
var loginInterfaceResp models.GetLoginInterfaceResp
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 security2.AuthenticationTokens
var jwtTokens security.AuthenticationTokens
testApiPost(t, base+api.Login, loginBody, &jwtTokens)
refreshClaims, err := security2.ParseRegisteredClaimToken(jwtTokens.RefreshToken)
refreshClaims, err := security.ParseRegisteredClaimToken(jwtTokens.RefreshToken)
assert.Equal(t, refreshClaims.Subject, userEmail)
accessClaims, err := security2.ParseRegisteredClaimToken(jwtTokens.AccessToken)
accessClaims, err := security.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 = security2.ParseRegisteredClaimToken(refreshTokenResp.AccessToken)
accessClaims, err = security.ParseRegisteredClaimToken(refreshTokenResp.AccessToken)
assert.NoError(t, err)
assert.Equal(t, accessClaims.Subject, userEmail)
}

30
go.mod
View File

@@ -1,9 +1,10 @@
module go-nkode
go 1.23.0
go 1.22.0
toolchain go1.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
@@ -11,9 +12,10 @@ require (
github.com/google/uuid v1.6.0
github.com/mattn/go-sqlite3 v1.14.22
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/stretchr/testify v1.10.0
github.com/stretchr/testify v1.9.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
)
@@ -30,17 +32,39 @@ 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
)

75
go.sum
View File

@@ -1,5 +1,3 @@
github.com/DonovanKelly/sugar-n-spice v1.0.1 h1:VsybiCHSziAqyPtbYF6GtkiJYYECWMHKN+EyEa6UVpA=
github.com/DonovanKelly/sugar-n-spice v1.0.1/go.mod h1:/HQWoablLFCwsa4gwfzVBu80cI5A3dyO1uCiB11sup0=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U=
@@ -30,9 +28,21 @@ 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=
@@ -41,8 +51,21 @@ 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=
@@ -51,30 +74,64 @@ 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/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
github.com/swaggo/http-swagger v1.3.4 h1:q7t/XLx0n15H1Q9/tk3Y9L4n210XzJF5WtnDX64a5ww=
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=
@@ -96,8 +153,12 @@ 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=
@@ -105,17 +166,23 @@ 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

@@ -0,0 +1,29 @@
# Task: Get nKode running on you system and document the process for setting up in the Readme.md
Prereq:
1. sign into gitea
2. clone go-nkode
3. install docker
4. install sqlite3
Do this stuff later
- install flutter
- clone flutter-nkode
## Install Swaggo
Install [swaggo](https://github.com/swaggo/swag)
you'll know you're successful when you see this in the cli:
```
➜ go-nkode git:(main) ✗ swag --version
swag version v1.16.4
```
## Setup Sqlite DB
Follow this [video](https://1drv.ms/v/s!Agm1aJyS_E11lPVCt_uoJFEn9HPFrA?e=mvntuY)
Download flaticon zip [here](https://1drv.ms/u/s!Agm1aJyS_E11lPVAI9Af279oZGnC2w?e=WbY1Xd)
Add this to your environment variables in your Goland config for sqlite_init.go
`DB_PATH=/path/to/databases/nkode.db;SVG_DIR=/path/to/flaticon_colored_svgs;TEST_DB_PATH=/path/to/databases/test.db`

View File

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

View File

@@ -5,10 +5,11 @@ import (
"github.com/google/uuid"
"github.com/patrickmn/go-cache"
"go-nkode/config"
"go-nkode/pkg/nkode-core/email"
"go-nkode/pkg/nkode-core/entities"
"go-nkode/pkg/nkode-core/repository"
"go-nkode/pkg/nkode-core/security"
"go-nkode/internal/db"
"go-nkode/internal/email"
"go-nkode/internal/entities"
"go-nkode/internal/models"
"go-nkode/internal/security"
"log"
"os"
"time"
@@ -20,12 +21,12 @@ const (
)
type NKodeAPI struct {
Db repository.CustomerUserRepository
Db db.CustomerUserRepository
SignupSessionCache *cache.Cache
EmailQueue *email.Queue
}
func NewNKodeAPI(db repository.CustomerUserRepository, queue *email.Queue) NKodeAPI {
func NewNKodeAPI(db db.CustomerUserRepository, queue *email.Queue) NKodeAPI {
return NKodeAPI{
Db: db,
EmailQueue: queue,
@@ -33,7 +34,7 @@ func NewNKodeAPI(db repository.CustomerUserRepository, queue *email.Queue) NKode
}
}
func (n *NKodeAPI) CreateNewCustomer(nkodePolicy entities.NKodePolicy, id *entities.CustomerId) (*entities.CustomerId, error) {
func (n *NKodeAPI) CreateNewCustomer(nkodePolicy models.NKodePolicy, id *models.CustomerId) (*models.CustomerId, error) {
newCustomer, err := entities.NewCustomer(nkodePolicy)
if id != nil {
newCustomer.Id = *id
@@ -49,7 +50,7 @@ func (n *NKodeAPI) CreateNewCustomer(nkodePolicy entities.NKodePolicy, id *entit
return &newCustomer.Id, nil
}
func (n *NKodeAPI) GenerateSignupResetInterface(userEmail entities.UserEmail, customerId entities.CustomerId, kp entities.KeypadDimension, reset bool) (*entities.SignupResetInterface, error) {
func (n *NKodeAPI) GenerateSignupResetInterface(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
@@ -66,6 +67,7 @@ func (n *NKodeAPI) GenerateSignupResetInterface(userEmail entities.UserEmail, cu
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
}
@@ -74,7 +76,7 @@ func (n *NKodeAPI) GenerateSignupResetInterface(userEmail entities.UserEmail, cu
if err != nil {
return nil, err
}
resp := entities.SignupResetInterface{
resp := models.GenerateSignupResetInterfaceResp{
UserIdxInterface: signupSession.SetIdxInterface,
SvgInterface: svgInterface,
SessionId: uuid.UUID(signupSession.Id).String(),
@@ -83,7 +85,7 @@ func (n *NKodeAPI) GenerateSignupResetInterface(userEmail entities.UserEmail, cu
return &resp, nil
}
func (n *NKodeAPI) SetNKode(customerId entities.CustomerId, sessionId entities.SessionId, keySelection entities.KeySelection) (entities.IdxInterface, error) {
func (n *NKodeAPI) SetNKode(customerId models.CustomerId, sessionId models.SessionId, keySelection models.KeySelection) (models.IdxInterface, error) {
_, err := n.Db.GetCustomer(customerId)
if err != nil {
@@ -107,7 +109,7 @@ func (n *NKodeAPI) SetNKode(customerId entities.CustomerId, sessionId entities.S
return confirmInterface, nil
}
func (n *NKodeAPI) ConfirmNKode(customerId entities.CustomerId, sessionId entities.SessionId, keySelection entities.KeySelection) error {
func (n *NKodeAPI) ConfirmNKode(customerId models.CustomerId, sessionId models.SessionId, keySelection models.KeySelection) error {
session, exists := n.SignupSessionCache.Get(sessionId.String())
if !exists {
log.Printf("session id does not exist %s", sessionId)
@@ -142,7 +144,7 @@ func (n *NKodeAPI) ConfirmNKode(customerId entities.CustomerId, sessionId entiti
return err
}
func (n *NKodeAPI) GetLoginInterface(userEmail entities.UserEmail, customerId entities.CustomerId) (*entities.LoginInterface, error) {
func (n *NKodeAPI) GetLoginInterface(userEmail models.UserEmail, customerId models.CustomerId) (*models.GetLoginInterfaceResp, error) {
user, err := n.Db.GetUser(userEmail, customerId)
if err != nil {
return nil, err
@@ -155,17 +157,17 @@ func (n *NKodeAPI) GetLoginInterface(userEmail entities.UserEmail, customerId en
if err != nil {
return nil, err
}
resp := entities.LoginInterface{
resp := models.GetLoginInterfaceResp{
UserIdxInterface: user.Interface.IdxInterface,
SvgInterface: svgInterface,
NumbOfKeys: user.Kp.NumbOfKeys,
AttrsPerKey: user.Kp.AttrsPerKey,
Colors: entities.SetColors,
Colors: models.SetColors,
}
return &resp, nil
}
func (n *NKodeAPI) Login(customerId entities.CustomerId, userEmail entities.UserEmail, keySelection entities.KeySelection) (*security.AuthenticationTokens, error) {
func (n *NKodeAPI) Login(customerId models.CustomerId, userEmail models.UserEmail, keySelection models.KeySelection) (*security.AuthenticationTokens, error) {
customer, err := n.Db.GetCustomer(customerId)
if err != nil {
return nil, err
@@ -205,7 +207,7 @@ func (n *NKodeAPI) Login(customerId entities.CustomerId, userEmail entities.User
return &jwtToken, nil
}
func (n *NKodeAPI) RenewAttributes(customerId entities.CustomerId) error {
func (n *NKodeAPI) RenewAttributes(customerId models.CustomerId) error {
return n.Db.Renew(customerId)
}
@@ -213,7 +215,7 @@ func (n *NKodeAPI) RandomSvgInterface() ([]string, error) {
return n.Db.RandomSvgInterface(entities.KeypadMax)
}
func (n *NKodeAPI) RefreshToken(userEmail entities.UserEmail, customerId entities.CustomerId, refreshToken string) (string, error) {
func (n *NKodeAPI) RefreshToken(userEmail models.UserEmail, customerId models.CustomerId, refreshToken string) (string, error) {
user, err := n.Db.GetUser(userEmail, customerId)
if err != nil {
return "", err
@@ -236,7 +238,7 @@ func (n *NKodeAPI) RefreshToken(userEmail entities.UserEmail, customerId entitie
return security.EncodeAndSignClaims(newAccessClaims)
}
func (n *NKodeAPI) ResetNKode(userEmail entities.UserEmail, customerId entities.CustomerId) error {
func (n *NKodeAPI) ResetNKode(userEmail models.UserEmail, customerId models.CustomerId) error {
user, err := n.Db.GetUser(userEmail, customerId)
if err != nil {
return fmt.Errorf("error getting user in rest nkode %v", err)

View File

@@ -1,14 +1,12 @@
package api
import (
"context"
"github.com/stretchr/testify/assert"
"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"
"go-nkode/internal/db"
"go-nkode/internal/email"
"go-nkode/internal/entities"
"go-nkode/internal/models"
"go-nkode/internal/security"
"os"
"testing"
)
@@ -17,31 +15,22 @@ func TestNKodeAPI(t *testing.T) {
//db1 := NewInMemoryDb()
//testNKodeAPI(t, &db1)
dbPath := os.Getenv("TEST_DB")
ctx := context.Background()
sqliteDb, err := sqlite_queue.OpenSqliteDb(dbPath)
assert.NoError(t, err)
dbFile := os.Getenv("TEST_DB")
queue, err := sqlite_queue.NewQueue(sqliteDb, ctx)
db2, err := db.NewSqliteDB(dbFile)
assert.NoError(t, err)
queue.Start()
defer func(queue *sqlite_queue.Queue) {
if err := queue.Stop(); err != nil {
log.Fatal(err)
}
}(queue)
sqlitedb := repository2.NewSqliteRepository(queue, ctx)
testNKodeAPI(t, &sqlitedb)
defer db2.Close()
testNKodeAPI(t, db2)
//if _, err := os.Stat(dbPath); err == nil {
// err = os.Remove(dbPath)
//if _, err := os.Stat(dbFile); err == nil {
// err = os.Remove(dbFile)
// assert.NoError(t, err)
//} else {
// assert.NoError(t, err)
//}
}
func testNKodeAPI(t *testing.T, db repository2.CustomerUserRepository) {
func testNKodeAPI(t *testing.T, db db.CustomerUserRepository) {
bufferSize := 100
emailsPerSec := 14
testClient := email.TestEmailClient{}
@@ -51,9 +40,9 @@ func testNKodeAPI(t *testing.T, db repository2.CustomerUserRepository) {
attrsPerKey := 5
numbOfKeys := 4
for idx := 0; idx < 1; idx++ {
userEmail := entities.UserEmail("test_username" + security.GenerateRandomString(12) + "@example.com")
userEmail := models.UserEmail("test_username" + security.GenerateRandomString(12) + "@example.com")
passcodeLen := 4
nkodePolicy := entities.NewDefaultNKodePolicy()
nkodePolicy := models.NewDefaultNKodePolicy()
keypadSize := entities.KeypadDimension{AttrsPerKey: attrsPerKey, NumbOfKeys: numbOfKeys}
nkodeApi := NewNKodeAPI(db, queue)
customerId, err := nkodeApi.CreateNewCustomer(nkodePolicy, nil)
@@ -62,7 +51,7 @@ func testNKodeAPI(t *testing.T, db repository2.CustomerUserRepository) {
assert.NoError(t, err)
setInterface := signupResponse.UserIdxInterface
sessionIdStr := signupResponse.SessionId
sessionId, err := entities.SessionIdFromString(sessionIdStr)
sessionId, err := models.SessionIdFromString(sessionIdStr)
assert.NoError(t, err)
keypadSize = entities.KeypadDimension{AttrsPerKey: numbOfKeys, NumbOfKeys: numbOfKeys}
userPasscode := setInterface[:passcodeLen]
@@ -99,7 +88,7 @@ func testNKodeAPI(t *testing.T, db repository2.CustomerUserRepository) {
assert.NoError(t, err)
setInterface = resetResponse.UserIdxInterface
sessionIdStr = resetResponse.SessionId
sessionId, err = entities.SessionIdFromString(sessionIdStr)
sessionId, err = models.SessionIdFromString(sessionIdStr)
assert.NoError(t, err)
keypadSize = entities.KeypadDimension{AttrsPerKey: numbOfKeys, NumbOfKeys: numbOfKeys}
userPasscode = setInterface[:passcodeLen]

View File

@@ -0,0 +1,21 @@
package db
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)
}

View File

@@ -1,26 +1,27 @@
package repository
package db
import (
"errors"
"fmt"
"go-nkode/pkg/nkode-core/entities"
"go-nkode/internal/entities"
"go-nkode/internal/models"
)
type InMemoryDb struct {
Customers map[entities.CustomerId]entities.Customer
Users map[entities.UserId]entities.User
userIdMap map[string]entities.UserId
Customers map[models.CustomerId]entities.Customer
Users map[models.UserId]entities.User
userIdMap map[string]models.UserId
}
func NewInMemoryDb() InMemoryDb {
return InMemoryDb{
Customers: make(map[entities.CustomerId]entities.Customer),
Users: make(map[entities.UserId]entities.User),
userIdMap: make(map[string]entities.UserId),
Customers: make(map[models.CustomerId]entities.Customer),
Users: make(map[models.UserId]entities.User),
userIdMap: make(map[string]models.UserId),
}
}
func (db *InMemoryDb) GetCustomer(id entities.CustomerId) (*entities.Customer, error) {
func (db *InMemoryDb) GetCustomer(id models.CustomerId) (*entities.Customer, error) {
customer, exists := db.Customers[id]
if !exists {
return nil, errors.New(fmt.Sprintf("customer %s dne", customer.Id))
@@ -28,7 +29,7 @@ func (db *InMemoryDb) GetCustomer(id entities.CustomerId) (*entities.Customer, e
return &customer, nil
}
func (db *InMemoryDb) GetUser(username entities.UserEmail, customerId entities.CustomerId) (*entities.User, error) {
func (db *InMemoryDb) GetUser(username models.UserEmail, customerId models.CustomerId) (*entities.User, error) {
key := userIdKey(customerId, username)
userId, exists := db.userIdMap[key]
if !exists {
@@ -71,7 +72,7 @@ func (db *InMemoryDb) UpdateUserNKode(user entities.User) error {
return errors.ErrUnsupported
}
func (db *InMemoryDb) UpdateUserInterface(userId entities.UserId, ui entities.UserInterface) error {
func (db *InMemoryDb) UpdateUserInterface(userId models.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))
@@ -81,11 +82,11 @@ func (db *InMemoryDb) UpdateUserInterface(userId entities.UserId, ui entities.Us
return nil
}
func (db *InMemoryDb) UpdateUserRefreshToken(userId entities.UserId, refreshToken string) error {
func (db *InMemoryDb) UpdateUserRefreshToken(userId models.UserId, refreshToken string) error {
return nil
}
func (db *InMemoryDb) Renew(id entities.CustomerId) error {
func (db *InMemoryDb) Renew(id models.CustomerId) error {
customer, exists := db.Customers[id]
if !exists {
return errors.New(fmt.Sprintf("customer %s does not exist", id))
@@ -120,19 +121,19 @@ func (db *InMemoryDb) RandomSvgInterface(kp entities.KeypadDimension) ([]string,
return make([]string, kp.TotalAttrs()), nil
}
func (db *InMemoryDb) RandomSvgIdxInterface(kp entities.KeypadDimension) (entities.SvgIdInterface, error) {
svgs := make(entities.SvgIdInterface, kp.TotalAttrs())
func (db *InMemoryDb) RandomSvgIdxInterface(kp entities.KeypadDimension) (models.SvgIdInterface, error) {
svgs := make(models.SvgIdInterface, kp.TotalAttrs())
for idx := range svgs {
svgs[idx] = idx
}
return svgs, nil
}
func (db *InMemoryDb) GetSvgStringInterface(idxs entities.SvgIdInterface) ([]string, error) {
func (db *InMemoryDb) GetSvgStringInterface(idxs models.SvgIdInterface) ([]string, error) {
return make([]string, len(idxs)), nil
}
func userIdKey(customerId entities.CustomerId, username entities.UserEmail) string {
func userIdKey(customerId models.CustomerId, username models.UserEmail) string {
key := fmt.Sprintf("%s:%s", customerId, username)
return key
}

View File

@@ -1,4 +1,4 @@
package repository
package db
import (
"context"
@@ -8,53 +8,115 @@ import (
"github.com/google/uuid"
_ "github.com/mattn/go-sqlite3" // Import the SQLite3 driver
"go-nkode/config"
"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"
"go-nkode/internal/entities"
"go-nkode/internal/models"
"go-nkode/internal/security"
"go-nkode/internal/sqlc"
"go-nkode/internal/utils"
"log"
"sync"
)
type SqliteRepository struct {
Queue *sqlc2.Queue
ctx context.Context
const writeBufferSize = 100
type sqlcGeneric func(*sqlc.Queries, context.Context, any) error
// WriteTx represents a write transaction
type WriteTx struct {
ErrChan chan error
Query sqlcGeneric
Args interface{}
}
func NewSqliteRepository(queue *sqlc2.Queue, ctx context.Context) SqliteRepository {
return SqliteRepository{
Queue: queue,
ctx: ctx,
// SqliteDB represents the SQLite database connection and write queue
type SqliteDB struct {
queries *sqlc.Queries
db *sql.DB
writeQueue chan WriteTx
wg sync.WaitGroup
ctx context.Context
cancel context.CancelFunc
}
// NewSqliteDB initializes a new SqliteDB instance
func NewSqliteDB(path string) (*SqliteDB, error) {
if path == "" {
return nil, errors.New("database path is required")
}
db, err := sql.Open("sqlite3", path)
if err != nil {
return nil, fmt.Errorf("failed to open database: %w", err)
}
if err := db.Ping(); err != nil {
return nil, fmt.Errorf("failed to connect to database: %w", err)
}
ctx, cancel := context.WithCancel(context.Background())
sqldb := &SqliteDB{
queries: sqlc.New(db),
db: db,
writeQueue: make(chan WriteTx, writeBufferSize),
ctx: ctx,
cancel: cancel,
}
sqldb.wg.Add(1)
go sqldb.processWriteQueue()
return sqldb, nil
}
// processWriteQueue handles write transactions from the queue
func (d *SqliteDB) processWriteQueue() {
defer d.wg.Done()
for {
select {
case <-d.ctx.Done():
return
case writeTx := <-d.writeQueue:
err := writeTx.Query(d.queries, d.ctx, writeTx.Args)
writeTx.ErrChan <- err
}
}
}
func (d *SqliteRepository) CreateCustomer(c entities.Customer) error {
queryFunc := func(q *sqlc2.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc2.CreateCustomerParams)
func (d *SqliteDB) Close() error {
d.cancel()
d.wg.Wait()
close(d.writeQueue)
return d.db.Close()
}
func (d *SqliteDB) CreateCustomer(c entities.Customer) error {
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc.CreateCustomerParams)
if !ok {
return fmt.Errorf("invalid argument type: expected CreateCustomerParams")
}
return q.CreateCustomer(ctx, params)
}
return d.Queue.EnqueueWriteTx(queryFunc, c.ToSqlcCreateCustomerParams())
return d.enqueueWriteTx(queryFunc, c.ToSqlcCreateCustomerParams())
}
func (d *SqliteRepository) WriteNewUser(u entities.User) error {
queryFunc := func(q *sqlc2.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc2.CreateUserParams)
func (d *SqliteDB) WriteNewUser(u entities.User) error {
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc.CreateUserParams)
if !ok {
return fmt.Errorf("invalid argument type: expected CreateUserParams")
}
return q.CreateUser(ctx, params)
}
// Use the wrapped function in EnqueueWriteTx
// Use the wrapped function in enqueueWriteTx
renew := 0
if u.Renew {
renew = 1
}
// Map entities.User to CreateUserParams
params := sqlc2.CreateUserParams{
params := sqlc.CreateUserParams{
ID: uuid.UUID(u.Id).String(),
Email: string(u.Email),
Renew: int64(renew),
@@ -74,23 +136,23 @@ func (d *SqliteRepository) WriteNewUser(u entities.User) error {
SvgIDInterface: security.IntArrToByteArr(u.Interface.SvgId),
CreatedAt: sql.NullString{String: utils.TimeStamp(), Valid: true},
}
return d.Queue.EnqueueWriteTx(queryFunc, params)
return d.enqueueWriteTx(queryFunc, params)
}
func (d *SqliteRepository) UpdateUserNKode(u entities.User) error {
queryFunc := func(q *sqlc2.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc2.UpdateUserParams)
func (d *SqliteDB) UpdateUserNKode(u entities.User) error {
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc.UpdateUserParams)
if !ok {
return fmt.Errorf("invalid argument type: expected UpdateUserParams")
}
return q.UpdateUser(ctx, params)
}
// Use the wrapped function in EnqueueWriteTx
// Use the wrapped function in enqueueWriteTx
renew := 0
if u.Renew {
renew = 1
}
params := sqlc2.UpdateUserParams{
params := sqlc.UpdateUserParams{
Email: string(u.Email),
Renew: int64(renew),
RefreshToken: sql.NullString{String: u.RefreshToken, Valid: u.RefreshToken != ""},
@@ -108,68 +170,68 @@ func (d *SqliteRepository) UpdateUserNKode(u entities.User) error {
IdxInterface: security.IntArrToByteArr(u.Interface.IdxInterface),
SvgIDInterface: security.IntArrToByteArr(u.Interface.SvgId),
}
return d.Queue.EnqueueWriteTx(queryFunc, params)
return d.enqueueWriteTx(queryFunc, params)
}
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)
func (d *SqliteDB) UpdateUserInterface(id models.UserId, ui entities.UserInterface) error {
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc.UpdateUserInterfaceParams)
if !ok {
return fmt.Errorf("invalid argument type: expected UpdateUserInterfaceParams")
}
return q.UpdateUserInterface(ctx, params)
}
params := sqlc2.UpdateUserInterfaceParams{
params := sqlc.UpdateUserInterfaceParams{
IdxInterface: security.IntArrToByteArr(ui.IdxInterface),
LastLogin: utils.TimeStamp(),
ID: uuid.UUID(id).String(),
}
return d.Queue.EnqueueWriteTx(queryFunc, params)
return d.enqueueWriteTx(queryFunc, params)
}
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)
func (d *SqliteDB) UpdateUserRefreshToken(id models.UserId, refreshToken string) error {
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc.UpdateUserRefreshTokenParams)
if !ok {
return fmt.Errorf("invalid argument type: expected UpdateUserRefreshToken")
}
return q.UpdateUserRefreshToken(ctx, params)
}
params := sqlc2.UpdateUserRefreshTokenParams{
params := sqlc.UpdateUserRefreshTokenParams{
RefreshToken: sql.NullString{
String: refreshToken,
Valid: true,
},
ID: uuid.UUID(id).String(),
}
return d.Queue.EnqueueWriteTx(queryFunc, params)
return d.enqueueWriteTx(queryFunc, params)
}
func (d *SqliteRepository) RenewCustomer(renewParams sqlc2.RenewCustomerParams) error {
queryFunc := func(q *sqlc2.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc2.RenewCustomerParams)
func (d *SqliteDB) RenewCustomer(renewParams sqlc.RenewCustomerParams) error {
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc.RenewCustomerParams)
if !ok {
}
return q.RenewCustomer(ctx, params)
}
return d.Queue.EnqueueWriteTx(queryFunc, renewParams)
return d.enqueueWriteTx(queryFunc, renewParams)
}
func (d *SqliteRepository) Renew(id entities.CustomerId) error {
func (d *SqliteDB) Renew(id models.CustomerId) error {
setXor, attrXor, err := d.renewCustomer(id)
if err != nil {
return err
}
customerId := entities.CustomerIdToString(id)
userRenewRows, err := d.Queue.Queries.GetUserRenew(d.ctx, customerId)
customerId := models.CustomerIdToString(id)
userRenewRows, err := d.queries.GetUserRenew(d.ctx, customerId)
if err != nil {
return err
}
queryFunc := func(q *sqlc2.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc2.RenewUserParams)
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc.RenewUserParams)
if !ok {
return fmt.Errorf("invalid argument type: expected RenewUserParams")
}
@@ -178,10 +240,10 @@ func (d *SqliteRepository) Renew(id entities.CustomerId) error {
for _, row := range userRenewRows {
user := entities.User{
Id: entities.UserIdFromString(row.ID),
CustomerId: entities.CustomerId{},
Id: models.UserIdFromString(row.ID),
CustomerId: models.CustomerId{},
Email: "",
EncipheredPasscode: entities.EncipheredNKode{},
EncipheredPasscode: models.EncipheredNKode{},
Kp: entities.KeypadDimension{
AttrsPerKey: int(row.AttributesPerKey),
NumbOfKeys: int(row.NumberOfKeys),
@@ -197,20 +259,20 @@ func (d *SqliteRepository) Renew(id entities.CustomerId) error {
if err = user.RenewKeys(setXor, attrXor); err != nil {
return err
}
params := sqlc2.RenewUserParams{
params := sqlc.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.enqueueWriteTx(queryFunc, params); err != nil {
return err
}
}
return nil
}
func (d *SqliteRepository) renewCustomer(id entities.CustomerId) ([]uint64, []uint64, error) {
func (d *SqliteDB) renewCustomer(id models.CustomerId) ([]uint64, []uint64, error) {
customer, err := d.GetCustomer(id)
if err != nil {
return nil, nil, err
@@ -220,37 +282,37 @@ func (d *SqliteRepository) renewCustomer(id entities.CustomerId) ([]uint64, []ui
return nil, nil, err
}
queryFunc := func(q *sqlc2.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc2.RenewCustomerParams)
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc.RenewCustomerParams)
if !ok {
return fmt.Errorf("invalid argument type: expected RenewCustomerParams")
}
return q.RenewCustomer(ctx, params)
}
params := sqlc2.RenewCustomerParams{
params := sqlc.RenewCustomerParams{
AttributeValues: security.Uint64ArrToByteArr(customer.Attributes.AttrVals),
SetValues: security.Uint64ArrToByteArr(customer.Attributes.SetVals),
ID: uuid.UUID(customer.Id).String(),
}
if err = d.Queue.EnqueueWriteTx(queryFunc, params); err != nil {
if err = d.enqueueWriteTx(queryFunc, params); err != nil {
return nil, nil, err
}
return setXor, attrXor, nil
}
func (d *SqliteRepository) RefreshUserPasscode(user entities.User, passcodeIdx []int, customerAttr entities.CustomerAttributes) error {
func (d *SqliteDB) RefreshUserPasscode(user entities.User, passcodeIdx []int, customerAttr entities.CustomerAttributes) error {
if err := user.RefreshPasscode(passcodeIdx, customerAttr); err != nil {
return err
}
queryFunc := func(q *sqlc2.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc2.RefreshUserPasscodeParams)
queryFunc := func(q *sqlc.Queries, ctx context.Context, args any) error {
params, ok := args.(sqlc.RefreshUserPasscodeParams)
if !ok {
return fmt.Errorf("invalid argument type: expected RefreshUserPasscodeParams")
}
return q.RefreshUserPasscode(ctx, params)
}
params := sqlc2.RefreshUserPasscodeParams{
params := sqlc.RefreshUserPasscodeParams{
Renew: 0,
Code: user.EncipheredPasscode.Code,
Mask: user.EncipheredPasscode.Mask,
@@ -261,18 +323,18 @@ func (d *SqliteRepository) RefreshUserPasscode(user entities.User, passcodeIdx [
Salt: user.CipherKeys.Salt,
ID: uuid.UUID(user.Id).String(),
}
return d.Queue.EnqueueWriteTx(queryFunc, params)
return d.enqueueWriteTx(queryFunc, params)
}
func (d *SqliteRepository) GetCustomer(id entities.CustomerId) (*entities.Customer, error) {
customer, err := d.Queue.Queries.GetCustomer(d.ctx, uuid.UUID(id).String())
func (d *SqliteDB) GetCustomer(id models.CustomerId) (*entities.Customer, error) {
customer, err := d.queries.GetCustomer(d.ctx, uuid.UUID(id).String())
if err != nil {
return nil, err
}
return &entities.Customer{
Id: id,
NKodePolicy: entities.NKodePolicy{
NKodePolicy: models.NKodePolicy{
MaxNkodeLen: int(customer.MaxNkodeLen),
MinNkodeLen: int(customer.MinNkodeLen),
DistinctSets: int(customer.DistinctSets),
@@ -284,8 +346,8 @@ func (d *SqliteRepository) GetCustomer(id entities.CustomerId) (*entities.Custom
}, nil
}
func (d *SqliteRepository) GetUser(email entities.UserEmail, customerId entities.CustomerId) (*entities.User, error) {
userRow, err := d.Queue.Queries.GetUser(d.ctx, sqlc2.GetUserParams{
func (d *SqliteDB) GetUser(email models.UserEmail, customerId models.CustomerId) (*entities.User, error) {
userRow, err := d.queries.GetUser(d.ctx, sqlc.GetUserParams{
Email: string(email),
CustomerID: uuid.UUID(customerId).String(),
})
@@ -306,10 +368,10 @@ func (d *SqliteRepository) GetUser(email entities.UserEmail, customerId entities
renew = true
}
user := entities.User{
Id: entities.UserIdFromString(userRow.ID),
Id: models.UserIdFromString(userRow.ID),
CustomerId: customerId,
Email: email,
EncipheredPasscode: entities.EncipheredNKode{
EncipheredPasscode: models.EncipheredNKode{
Code: userRow.Code,
Mask: userRow.Mask,
},
@@ -334,7 +396,7 @@ func (d *SqliteRepository) GetUser(email entities.UserEmail, customerId entities
return &user, nil
}
func (d *SqliteRepository) RandomSvgInterface(kp entities.KeypadDimension) ([]string, error) {
func (d *SqliteDB) RandomSvgInterface(kp entities.KeypadDimension) ([]string, error) {
ids, err := d.getRandomIds(kp.TotalAttrs())
if err != nil {
return nil, err
@@ -342,18 +404,18 @@ func (d *SqliteRepository) RandomSvgInterface(kp entities.KeypadDimension) ([]st
return d.getSvgsById(ids)
}
func (d *SqliteRepository) RandomSvgIdxInterface(kp entities.KeypadDimension) (entities.SvgIdInterface, error) {
func (d *SqliteDB) RandomSvgIdxInterface(kp entities.KeypadDimension) (models.SvgIdInterface, error) {
return d.getRandomIds(kp.TotalAttrs())
}
func (d *SqliteRepository) GetSvgStringInterface(idxs entities.SvgIdInterface) ([]string, error) {
func (d *SqliteDB) GetSvgStringInterface(idxs models.SvgIdInterface) ([]string, error) {
return d.getSvgsById(idxs)
}
func (d *SqliteRepository) getSvgsById(ids []int) ([]string, error) {
func (d *SqliteDB) getSvgsById(ids []int) ([]string, error) {
svgs := make([]string, len(ids))
for idx, id := range ids {
svg, err := d.Queue.Queries.GetSvgId(d.ctx, int64(id))
svg, err := d.queries.GetSvgId(d.ctx, int64(id))
if err != nil {
return nil, err
}
@@ -362,8 +424,25 @@ func (d *SqliteRepository) getSvgsById(ids []int) ([]string, error) {
return svgs, nil
}
func (d *SqliteRepository) getRandomIds(count int) ([]int, error) {
tx, err := d.Queue.Db.Begin()
func (d *SqliteDB) enqueueWriteTx(queryFunc sqlcGeneric, args any) error {
select {
case <-d.ctx.Done():
return errors.New("database is shutting down")
default:
}
errChan := make(chan error, 1)
writeTx := WriteTx{
Query: queryFunc,
Args: args,
ErrChan: errChan,
}
d.writeQueue <- writeTx
return <-errChan
}
func (d *SqliteDB) getRandomIds(count int) ([]int, error) {
tx, err := d.db.Begin()
if err != nil {
log.Print(err)
return nil, config.ErrSqliteTx

View File

@@ -1,34 +1,32 @@
package repository
package db
import (
"context"
"github.com/stretchr/testify/assert"
"go-nkode/pkg/nkode-core/entities"
sqlite_queue "go-nkode/pkg/nkode-core/sqlc"
"go-nkode/internal/entities"
"go-nkode/internal/models"
"os"
"testing"
)
func TestNewSqliteDB(t *testing.T) {
dbPath := os.Getenv("TEST_DB")
dbFile := os.Getenv("TEST_DB")
// sql_driver.MakeTables(dbFile)
ctx := context.Background()
sqliteDb, err := sqlite_queue.OpenSqliteDb(dbPath)
db, err := NewSqliteDB(dbFile)
assert.NoError(t, err)
defer db.Close()
queue, err := sqlite_queue.NewQueue(sqliteDb, ctx)
assert.NoError(t, err)
queue.Start()
defer queue.Stop()
db := NewSqliteRepository(queue, ctx)
assert.NoError(t, err)
testSignupLoginRenew(t, &db)
testSqliteDBRandomSvgInterface(t, &db)
testSignupLoginRenew(t, db)
testSqliteDBRandomSvgInterface(t, db)
// if _, err := os.Stat(dbFile); err == nil {
// err = os.Remove(dbFile)
// assert.NoError(t, err)
// } else {
// assert.NoError(t, err)
// }
}
func testSignupLoginRenew(t *testing.T, db CustomerUserRepository) {
nkodePolicy := entities.NewDefaultNKodePolicy()
nkodePolicy := models.NewDefaultNKodePolicy()
customerOrig, err := entities.NewCustomer(nkodePolicy)
assert.NoError(t, err)
err = db.CreateCustomer(*customerOrig)
@@ -39,14 +37,14 @@ func testSignupLoginRenew(t *testing.T, db CustomerUserRepository) {
username := "test_user@example.com"
kp := entities.KeypadDefault
passcodeIdx := []int{0, 1, 2, 3}
mockSvgInterface := make(entities.SvgIdInterface, kp.TotalAttrs())
mockSvgInterface := make(models.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(entities.UserEmail(username), customer.Id)
user, err := db.GetUser(models.UserEmail(username), customer.Id)
assert.NoError(t, err)
assert.Equal(t, userOrig, user)

View File

@@ -18,6 +18,7 @@ type Client interface {
SendEmail(Email) error
}
// Email represents a dummy email structure
type Email struct {
Sender string
Recipient string
@@ -27,7 +28,9 @@ 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
}
@@ -124,6 +127,7 @@ 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)
@@ -133,17 +137,20 @@ 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
<-q.rateLimit // Wait for the rate limiter to allow the next email
q.sendEmail(email)
q.wg.Done()
q.wg.Done() // Mark the email as processed
}
}()
}
// 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
@@ -151,8 +158,11 @@ 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()
// Close the email queue
close(q.emailQueue)
}

View File

@@ -22,7 +22,7 @@ func TestEmailQueue(t *testing.T) {
}
queue.AddEmail(email)
}
// Stop the queue after all emails are processed
// Close the queue after all emails are processed
queue.Stop()
assert.Equal(t, queue.FailedSendCount, 0)

View File

@@ -1,27 +1,27 @@
package entities
import (
"github.com/DonovanKelly/sugar-n-spice/set"
"github.com/google/uuid"
"go-nkode/config"
"go-nkode/pkg/nkode-core/security"
"go-nkode/pkg/nkode-core/sqlc"
"go-nkode/pkg/nkode-core/utils"
"go-nkode/internal/models"
"go-nkode/internal/security"
"go-nkode/internal/sqlc"
"go-nkode/internal/utils"
)
type Customer struct {
Id CustomerId
NKodePolicy NKodePolicy
Id models.CustomerId
NKodePolicy models.NKodePolicy
Attributes CustomerAttributes
}
func NewCustomer(nkodePolicy NKodePolicy) (*Customer, error) {
func NewCustomer(nkodePolicy models.NKodePolicy) (*Customer, error) {
customerAttrs, err := NewCustomerAttributes()
if err != nil {
return nil, err
}
customer := Customer{
Id: CustomerId(uuid.New()),
Id: models.CustomerId(uuid.New()),
NKodePolicy: nkodePolicy,
Attributes: *customerAttrs,
}
@@ -38,8 +38,8 @@ func (c *Customer) IsValidNKode(kp KeypadDimension, passcodeAttrIdx []int) error
if validIdx := kp.ValidateAttributeIndices(passcodeAttrIdx); !validIdx {
return config.ErrInvalidNKodeIdx
}
passcodeSetVals := make(set.Set[uint64])
passcodeAttrVals := make(set.Set[uint64])
passcodeSetVals := make(utils.Set[uint64])
passcodeAttrVals := make(utils.Set[uint64])
attrVals, err := c.Attributes.AttrValsForKp(kp)
if err != nil {
return err

View File

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

View File

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

View File

@@ -1,8 +1,8 @@
package entities
import (
"github.com/DonovanKelly/sugar-n-spice/all"
"go-nkode/config"
py "go-nkode/internal/utils"
)
type KeypadDimension struct {
@@ -26,13 +26,13 @@ func (kp *KeypadDimension) IsValidKeypadDimension() error {
}
func (kp *KeypadDimension) ValidKeySelections(selectedKeys []int) bool {
return all.All[int](selectedKeys, func(idx int) bool {
return py.All[int](selectedKeys, func(idx int) bool {
return 0 <= idx && idx < kp.NumbOfKeys
})
}
func (kp *KeypadDimension) ValidateAttributeIndices(attrIndicies []int) bool {
return all.All[int](attrIndicies, func(i int) bool {
return py.All[int](attrIndicies, func(i int) bool {
return i >= 0 && i < kp.TotalAttrs()
})
}

View File

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

View File

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

View File

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

View File

@@ -1,19 +1,20 @@
package entities
import (
"github.com/DonovanKelly/sugar-n-spice/set"
"go-nkode/config"
"go-nkode/pkg/nkode-core/security"
"go-nkode/internal/models"
"go-nkode/internal/security"
"go-nkode/internal/utils"
"log"
)
type UserInterface struct {
IdxInterface IdxInterface
SvgId SvgIdInterface
IdxInterface models.IdxInterface
SvgId models.SvgIdInterface
Kp *KeypadDimension
}
func NewUserInterface(kp *KeypadDimension, svgId SvgIdInterface) (*UserInterface, error) {
func NewUserInterface(kp *KeypadDimension, svgId models.SvgIdInterface) (*UserInterface, error) {
idxInterface := security.IdentityArray(kp.TotalAttrs())
userInterface := UserInterface{
IdxInterface: idxInterface,
@@ -121,15 +122,15 @@ func (u *UserInterface) randomAttributeRotation() error {
return nil
}
func (u *UserInterface) AttributeAdjacencyGraph() (map[int]set.Set[int], error) {
func (u *UserInterface) AttributeAdjacencyGraph() (map[int]utils.Set[int], error) {
interfaceKeypad, err := u.InterfaceMatrix()
if err != nil {
return nil, err
}
graph := make(map[int]set.Set[int])
graph := make(map[int]utils.Set[int])
for _, key := range interfaceKeypad {
keySet := set.NewSetFromSlice(key)
keySet := utils.NewSetFromSlice(key)
for _, attr := range key {
attrAdjacency := keySet.Copy()
attrAdjacency.Remove(attr)
@@ -153,7 +154,7 @@ func (u *UserInterface) LoginShuffle() error {
if err != nil {
return err
}
selectedSets := set.NewSetFromSlice[int](setIdxs[:numbOfSelectedSets])
selectedSets := utils.NewSetFromSlice[int](setIdxs[:numbOfSelectedSets])
for keyIdx, key := range keypadSet1 {
for idx := range key {

View File

@@ -1,30 +1,30 @@
package entities
import (
"github.com/DonovanKelly/sugar-n-spice/all"
"github.com/DonovanKelly/sugar-n-spice/set"
"github.com/google/uuid"
"go-nkode/config"
"go-nkode/pkg/nkode-core/security"
"go-nkode/internal/models"
"go-nkode/internal/security"
py "go-nkode/internal/utils"
"log"
"sort"
)
type UserSignSession struct {
Id SessionId
CustomerId CustomerId
Id models.SessionId
CustomerId models.CustomerId
LoginUserInterface UserInterface
Kp KeypadDimension
SetIdxInterface IdxInterface
ConfirmIdxInterface IdxInterface
SetKeySelection KeySelection
UserEmail UserEmail
SetIdxInterface models.IdxInterface
ConfirmIdxInterface models.IdxInterface
SetKeySelection models.KeySelection
UserEmail models.UserEmail
Reset bool
Expire int
Colors []RGBColor
Colors []models.RGBColor
}
func NewSignupResetSession(userEmail UserEmail, kp KeypadDimension, customerId CustomerId, svgInterface SvgIdInterface, reset bool) (*UserSignSession, error) {
func NewSignupResetSession(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
@@ -34,7 +34,7 @@ func NewSignupResetSession(userEmail UserEmail, kp KeypadDimension, customerId C
return nil, err
}
session := UserSignSession{
Id: SessionId(uuid.New()),
Id: models.SessionId(uuid.New()),
CustomerId: customerId,
LoginUserInterface: *loginInterface,
SetIdxInterface: signupInterface.IdxInterface,
@@ -49,8 +49,8 @@ func NewSignupResetSession(userEmail UserEmail, kp KeypadDimension, customerId C
return &session, nil
}
func (s *UserSignSession) DeducePasscode(confirmKeyEntry KeySelection) ([]int, error) {
validEntry := all.All[int](confirmKeyEntry, func(i int) bool {
func (s *UserSignSession) DeducePasscode(confirmKeyEntry models.KeySelection) ([]int, error) {
validEntry := py.All[int](confirmKeyEntry, func(i int) bool {
return 0 <= i && i < s.Kp.NumbOfKeys
})
@@ -93,8 +93,8 @@ func (s *UserSignSession) DeducePasscode(confirmKeyEntry KeySelection) ([]int, e
passcode := make([]int, passcodeLen)
for idx := 0; idx < passcodeLen; idx++ {
setKey := set.NewSetFromSlice[int](setKeyVals[idx])
confirmKey := set.NewSetFromSlice[int](confirmKeyVals[idx])
setKey := py.NewSetFromSlice[int](setKeyVals[idx])
confirmKey := py.NewSetFromSlice[int](confirmKeyVals[idx])
intersection := setKey.Intersect(confirmKey)
if intersection.Size() < 1 {
log.Printf("set and confirm do not intersect at index %d", idx)
@@ -110,8 +110,8 @@ func (s *UserSignSession) DeducePasscode(confirmKeyEntry KeySelection) ([]int, e
return passcode, nil
}
func (s *UserSignSession) SetUserNKode(keySelection KeySelection) (IdxInterface, error) {
validKeySelection := all.All[int](keySelection, func(i int) bool {
func (s *UserSignSession) SetUserNKode(keySelection models.KeySelection) (models.IdxInterface, error) {
validKeySelection := py.All[int](keySelection, func(i int) bool {
return 0 <= i && i < s.Kp.NumbOfKeys
})
if !validKeySelection {
@@ -130,7 +130,7 @@ func (s *UserSignSession) SetUserNKode(keySelection KeySelection) (IdxInterface,
return s.ConfirmIdxInterface, nil
}
func (s *UserSignSession) getSelectedKeyVals(keySelections KeySelection, userInterface []int) ([][]int, error) {
func (s *UserSignSession) getSelectedKeyVals(keySelections models.KeySelection, userInterface []int) ([][]int, error) {
signupKp := s.SignupKeypad()
keypadInterface, err := security.ListToMatrix(userInterface, signupKp.AttrsPerKey)
if err != nil {
@@ -144,7 +144,7 @@ func (s *UserSignSession) getSelectedKeyVals(keySelections KeySelection, userInt
return keyVals, nil
}
func signupInterface(baseUserInterface UserInterface, kp KeypadDimension) (*UserInterface, []RGBColor, error) {
func signupInterface(baseUserInterface UserInterface, kp KeypadDimension) (*UserInterface, []models.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
@@ -171,11 +171,11 @@ func signupInterface(baseUserInterface UserInterface, kp KeypadDimension) (*User
setIdxs = setIdxs[:kp.NumbOfKeys]
sort.Ints(setIdxs)
selectedSets := make([][]int, kp.NumbOfKeys)
selectedColors := make([]RGBColor, kp.NumbOfKeys)
selectedColors := make([]models.RGBColor, kp.NumbOfKeys)
for idx, setIdx := range setIdxs {
selectedSets[idx] = attrSetView[setIdx]
selectedColors[idx] = SetColors[setIdx]
selectedColors[idx] = models.SetColors[setIdx]
}
// convert set view back into key view
selectedSets, err = security.MatrixTranspose(selectedSets)

View File

@@ -1,8 +1,9 @@
package entities
import (
"github.com/DonovanKelly/sugar-n-spice/all"
"github.com/stretchr/testify/assert"
"go-nkode/internal/models"
py "go-nkode/internal/utils"
"testing"
)
@@ -64,7 +65,7 @@ func TestUserInterface_RandomShuffle(t *testing.T) {
AttrsPerKey: 10,
NumbOfKeys: 8,
}
mockSvgInterface := make(SvgIdInterface, kp.TotalAttrs())
mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
userInterface, err := NewUserInterface(&kp, mockSvgInterface)
assert.NoError(t, err)
userInterfaceCopy := make([]int, len(userInterface.IdxInterface))
@@ -87,7 +88,7 @@ func TestUserInterface_DisperseInterface(t *testing.T) {
for idx := 0; idx < 10000; idx++ {
kp := KeypadDimension{AttrsPerKey: 7, NumbOfKeys: 10}
mockSvgInterface := make(SvgIdInterface, kp.TotalAttrs())
mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
userInterface, err := NewUserInterface(&kp, mockSvgInterface)
assert.NoError(t, err)
preDispersion, err := userInterface.AttributeAdjacencyGraph()
@@ -106,7 +107,7 @@ func TestUserInterface_DisperseInterface(t *testing.T) {
func TestUserInterface_PartialInterfaceShuffle(t *testing.T) {
kp := KeypadDimension{AttrsPerKey: 7, NumbOfKeys: 10}
mockSvgInterface := make(SvgIdInterface, kp.TotalAttrs())
mockSvgInterface := make(models.SvgIdInterface, kp.TotalAttrs())
userInterface, err := NewUserInterface(&kp, mockSvgInterface)
assert.NoError(t, err)
preShuffle := userInterface.IdxInterface
@@ -119,12 +120,12 @@ func TestUserInterface_PartialInterfaceShuffle(t *testing.T) {
shuffleCompare[idx] = val == postShuffle[idx]
}
allTrue := all.All[bool](shuffleCompare, func(n bool) bool {
allTrue := py.All[bool](shuffleCompare, func(n bool) bool {
return n == true
})
assert.False(t, allTrue)
allFalse := all.All[bool](shuffleCompare, func(n bool) bool {
allFalse := py.All[bool](shuffleCompare, func(n bool) bool {
return n == false
})

View File

@@ -1,7 +1,10 @@
package models
import (
"go-nkode/pkg/nkode-core/entities"
"fmt"
"github.com/google/uuid"
"net/mail"
"strings"
)
type SetNKodeResp struct {
@@ -9,8 +12,8 @@ type SetNKodeResp struct {
}
type RandomSvgInterfaceResp struct {
Svgs []string `json:"svgs"`
Colors []entities.RGBColor `json:"colors"`
Svgs []string `json:"svgs"`
Colors []RGBColor `json:"colors"`
}
type RefreshTokenResp struct {
@@ -18,7 +21,7 @@ type RefreshTokenResp struct {
}
type NewCustomerPost struct {
NKodePolicy entities.NKodePolicy `json:"nkode_policy"`
NKodePolicy NKodePolicy `json:"nkode_policy"`
}
type GenerateSignupRestInterfacePost struct {
@@ -36,9 +39,9 @@ type SetNKodePost struct {
}
type ConfirmNKodePost struct {
CustomerId string `json:"customer_id"`
KeySelection entities.KeySelection `json:"key_selection"`
SessionId string `json:"session_id"`
CustomerId string `json:"customer_id"`
KeySelection KeySelection `json:"key_selection"`
SessionId string `json:"session_id"`
}
type GetLoginInterfacePost struct {
@@ -47,15 +50,20 @@ type GetLoginInterfacePost struct {
}
type LoginPost struct {
CustomerId string `json:"customer_id"`
UserEmail string `json:"email"`
KeySelection entities.KeySelection `json:"key_selection"`
CustomerId string `json:"customer_id"`
UserEmail string `json:"email"`
KeySelection 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"`
@@ -64,3 +72,96 @@ 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()
}
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,4 +1,4 @@
package entities
package models
import "go-nkode/config"

View File

@@ -6,7 +6,7 @@ import (
"encoding/binary"
"encoding/hex"
"errors"
"github.com/DonovanKelly/sugar-n-spice/set"
"go-nkode/internal/utils"
"log"
"math/big"
r "math/rand"
@@ -84,7 +84,7 @@ func GenerateRandomNonRepeatingUint64(listLen int) ([]uint64, error) {
if listLen > int(1)<<32 {
return nil, ErrRandNonRepeatingUint64
}
listSet := make(set.Set[uint64])
listSet := make(utils.Set[uint64])
for {
if listSet.Size() == listLen {
break
@@ -104,7 +104,7 @@ func GenerateRandomNonRepeatingInt(listLen int) ([]int, error) {
if listLen > int(1)<<31 {
return nil, ErrRandNonRepeatingInt
}
listSet := make(set.Set[int])
listSet := make(utils.Set[int])
for {
if listSet.Size() == listLen {
break

63
internal/utils/hashset.go Normal file
View File

@@ -0,0 +1,63 @@
package utils
type Set[T comparable] map[T]struct{}
func (s *Set[T]) Add(element T) {
(*s)[element] = struct{}{}
}
func (s *Set[T]) Remove(element T) {
delete(*s, element)
}
func (s *Set[T]) Contains(element T) bool {
_, exists := (*s)[element]
return exists
}
func (s *Set[T]) Size() int {
return len(*s)
}
func (s *Set[T]) ToSlice() []T {
list := make([]T, 0, len(*s))
for key := range *s {
list = append(list, key)
}
return list
}
func NewSetFromSlice[T comparable](slice []T) Set[T] {
set := make(Set[T])
for _, val := range slice {
set.Add(val)
}
return set
}
func (s *Set[T]) Copy() Set[T] {
newSet := make(Set[T])
for key, val := range *s {
newSet[key] = val
}
return newSet
}
func (s *Set[T]) IsDisjoint(otherSet Set[T]) bool {
for attr := range *s {
if otherSet.Contains(attr) {
return false
}
}
return true
}
func (s *Set[T]) Intersect(otherSet Set[T]) Set[T] {
intersect := make(Set[T])
for val := range *s {
if otherSet.Contains(val) {
intersect.Add(val)
}
}
return intersect
}

View File

@@ -0,0 +1,35 @@
package utils
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestSet(t *testing.T) {
intSet := make(Set[int])
intSet.Add(1)
intSet.Add(2)
assert.EqualValues(t, intSet.Size(), 2)
intSet.Add(3)
intSet.Add(3)
assert.EqualValues(t, intSet.Size(), 3)
intSet.Remove(2)
assert.EqualValues(t, intSet.Size(), 2)
assert.False(t, intSet.Contains(2))
assert.True(t, intSet.Contains(1))
list := intSet.ToSlice()
assert.Contains(t, list, 1)
assert.Contains(t, list, 3)
}
func TestSet_Copy(t *testing.T) {
intSet := NewSetFromSlice[int]([]int{1, 2, 3})
copySet := intSet.Copy()
intSet.Remove(1)
assert.Equal(t, intSet.Size(), 2)
assert.Equal(t, copySet.Size(), 3)
}

View File

@@ -0,0 +1,11 @@
package utils
func All[T comparable](slice []T, condition func(T) bool) bool {
for _, v := range slice {
if !condition(v) {
return false
}
}
return true
}

View File

@@ -1,101 +0,0 @@
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,20 +0,0 @@
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

@@ -1,93 +0,0 @@
package sqlc
import (
"context"
"database/sql"
"errors"
"fmt"
"sync"
)
const writeBufferSize = 100
type SqlcGeneric func(*Queries, context.Context, any) error
type WriteTx struct {
ErrChan chan error
Query SqlcGeneric
Args interface{}
}
type Queue struct {
Queries *Queries
Db *sql.DB
WriteQueue chan WriteTx
wg sync.WaitGroup
ctx context.Context
cancel context.CancelFunc
}
func NewQueue(sqlDb *sql.DB, ctx context.Context) (*Queue, error) {
ctx, cancel := context.WithCancel(context.Background())
sqldb := &Queue{
Queries: New(sqlDb),
Db: sqlDb,
WriteQueue: make(chan WriteTx, writeBufferSize),
ctx: ctx,
cancel: cancel,
}
return sqldb, nil
}
func (d *Queue) Start() {
d.wg.Add(1)
defer d.wg.Done()
go func() {
for {
select {
case <-d.ctx.Done():
return
case writeTx := <-d.WriteQueue:
err := writeTx.Query(d.Queries, d.ctx, writeTx.Args)
writeTx.ErrChan <- err
}
}
}()
}
func (d *Queue) Stop() error {
d.cancel()
d.wg.Wait()
close(d.WriteQueue)
return d.Db.Close()
}
func (d *Queue) EnqueueWriteTx(queryFunc SqlcGeneric, args any) error {
select {
case <-d.ctx.Done():
return errors.New("database is shutting down")
default:
}
errChan := make(chan error, 1)
writeTx := WriteTx{
Query: queryFunc,
Args: args,
ErrChan: errChan,
}
d.WriteQueue <- writeTx
return <-errChan
}
func OpenSqliteDb(dbPath string) (*sql.DB, error) {
sqliteDb, err := sql.Open("sqlite3", dbPath)
if err != nil {
return nil, fmt.Errorf("failed to open database: %w", err)
}
if err := sqliteDb.Ping(); err != nil {
return nil, fmt.Errorf("failed to connect to database: %w", err)
}
return sqliteDb, nil
}

View File

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