mobility-accounts/storage/postgresql.go

329 lines
11 KiB
Go

package storage
import (
"database/sql"
"encoding/json"
"fmt"
_ "github.com/lib/pq"
"github.com/spf13/viper"
"regexp"
"strconv"
"strings"
)
type PostgresqlStorage struct {
DbConnection *sql.DB
}
func NewPostgresqlStorage(cfg *viper.Viper) (PostgresqlStorage, error) {
var (
host = cfg.GetString("storage.db.psql.host")
port = cfg.GetString("storage.db.psql.port")
user = cfg.GetString("storage.db.psql.user")
password = cfg.GetString("storage.db.psql.password")
dbname = cfg.GetString("storage.db.psql.dbname")
)
portInt, _ := strconv.Atoi(port)
psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, portInt,
user, password, dbname)
db, err := sql.Open("postgres", psqlconn)
if err != nil {
fmt.Println("error", err)
return PostgresqlStorage{}, fmt.Errorf("connection to postgresql failed")
}
err = db.Ping()
if err != nil {
return PostgresqlStorage{}, fmt.Errorf("connection to postgresql database failed")
}
return PostgresqlStorage{
DbConnection: db,
}, nil
}
func (psql PostgresqlStorage) GetAccount(id string) (*Account, error) {
var (
data, metadata, emailValidation, phoneValidation []byte
)
account := &Account{}
if isUUIDv4(id) {
stmtAccounts, err := psql.DbConnection.Prepare("SELECT id, namespace, data, metadata FROM accounts WHERE id = $1")
if err != nil {
return nil, fmt.Errorf("psql connection failed")
}
defer stmtAccounts.Close()
err = stmtAccounts.QueryRow(id).Scan(&account.ID, &account.Namespace, &data, &metadata)
if err != nil {
return nil, fmt.Errorf("psql select account query failed")
}
err = json.Unmarshal(data, &account.Data)
if err != nil {
return nil, err
}
err = json.Unmarshal(metadata, &account.Metadata)
if err != nil {
return nil, err
}
stmtAccount_auth, err := psql.DbConnection.Prepare("SELECT username, password, email, email_validation, phone_number, phone_number_validation FROM account_auth WHERE account_id= $1")
if err != nil {
return nil, fmt.Errorf("psql connection failed")
}
defer stmtAccount_auth.Close()
err = stmtAccount_auth.QueryRow(id).Scan(&account.Authentication.Local.Username,
&account.Authentication.Local.Password,
&account.Authentication.Local.Email,
&emailValidation,
&account.Authentication.Local.PhoneNumber,
&phoneValidation)
if err != nil {
return nil, fmt.Errorf("psql select account auth query failed")
}
err = json.Unmarshal(emailValidation, &account.Authentication.Local.EmailValidation)
if err != nil {
return nil, err
}
err = json.Unmarshal(phoneValidation, &account.Authentication.Local.PhoneNumberValidation)
if err != nil {
return nil, err
}
}
return account, nil
}
func (psql PostgresqlStorage) LocalAuthentication(namespace string, username string, email string, phone_number string) (*Account, error) {
account := &Account{}
var (
data, metadata, emailValidation, phoneValidation []byte
)
if username != "" {
usernameStmt, err := psql.DbConnection.Prepare("SELECT id, namespace, data, metadata, username, password, email, email_validation, phone_number, phone_number_validation FROM accounts INNER JOIN account_auth ON accounts.id = account_auth.account_id WHERE namespace = $1 AND username = $2;")
if err != nil {
return nil, err
}
err = usernameStmt.QueryRow(namespace, username).Scan(&account.ID, &account.Namespace, &data, &metadata, &account.Authentication.Local.Username,
&account.Authentication.Local.Password, &account.Authentication.Local.Email, &emailValidation, &account.Authentication.Local.PhoneNumber, &phoneValidation)
if err != nil {
return nil, err
}
err = json.Unmarshal(metadata, &account.Metadata)
if err != nil {
return nil, err
}
err = json.Unmarshal(data, &account.Data)
if err != nil {
return nil, err
}
err = json.Unmarshal(emailValidation, &account.Authentication.Local.EmailValidation)
if err != nil {
return nil, err
}
err = json.Unmarshal(phoneValidation, &account.Authentication.Local.PhoneNumberValidation)
if err != nil {
return nil, err
}
return account, nil
} else if email != "" {
account.Authentication.Local.Email = email
emailStmt, err := psql.DbConnection.Prepare("SELECT id, namespace, data, metadata, username, password, email_validation, phone_number, phone_number_validation FROM accounts INNER JOIN account_auth ON accounts.id = account_auth.account_id WHERE namespace = $1 AND email = $2;")
if err != nil {
return nil, err
}
err = emailStmt.QueryRow(namespace, email).Scan(&account.ID, &account.Namespace, &data, &metadata, &account.Authentication.Local.Username,
&account.Authentication.Local.Password, &emailValidation, &account.Authentication.Local.PhoneNumber, &phoneValidation)
if err != nil {
return nil, err
}
err = json.Unmarshal(metadata, &account.Metadata)
if err != nil {
return nil, err
}
err = json.Unmarshal(data, &account.Data)
if err != nil {
return nil, err
}
err = json.Unmarshal(emailValidation, &account.Authentication.Local.EmailValidation)
if err != nil {
return nil, err
}
err = json.Unmarshal(phoneValidation, &account.Authentication.Local.PhoneNumberValidation)
if err != nil {
return nil, err
}
return account, nil
} else if phone_number != "" {
account.Authentication.Local.PhoneNumber = phone_number
phoneStmt, err := psql.DbConnection.Prepare("SELECT id, namespace, data, metadata, username, password, email, email_validation, phone_number_validation FROM accounts INNER JOIN account_auth ON accounts.id = account_auth.account_id WHERE namespace = $1 AND phone_number = $2;")
if err != nil {
return nil, err
}
err = phoneStmt.QueryRow(namespace, phone_number).Scan(&account.ID, &account.Namespace, &data, &metadata, &account.Authentication.Local.Username,
&account.Authentication.Local.Password, &account.Authentication.Local.Email, &emailValidation, &phoneValidation)
if err != nil {
return nil, err
}
err = json.Unmarshal(metadata, &account.Metadata)
if err != nil {
return nil, err
}
err = json.Unmarshal(data, &account.Data)
if err != nil {
return nil, err
}
err = json.Unmarshal(emailValidation, &account.Authentication.Local.EmailValidation)
if err != nil {
return nil, err
}
err = json.Unmarshal(phoneValidation, &account.Authentication.Local.PhoneNumberValidation)
if err != nil {
return nil, err
}
return account, nil
}
return account, nil
}
func (psql PostgresqlStorage) GetAccounts(namespaces []string) ([]Account, error) {
var accounts []Account
namespacesStr := "'" + strings.Join(namespaces, "', '") + "'"
query := `
SELECT accounts.id, accounts.namespace, accounts.data, accounts.metadata,
account_auth.username, account_auth.password,
account_auth.email, account_auth.email_validation,
account_auth.phone_number, account_auth.phone_number_validation
FROM accounts
INNER JOIN account_auth ON accounts.id = account_auth.account_id
WHERE accounts.namespace IN (%s)
`
query = fmt.Sprintf(query, namespacesStr)
rows, err := psql.DbConnection.Query(query)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var account Account
var dataBytes []byte
var metadataBytes []byte
var emailValidationBytes []byte
var phoneNumberValidationBytes []byte
err := rows.Scan(
&account.ID,
&account.Namespace,
&dataBytes,
&metadataBytes,
&account.Authentication.Local.Username,
&account.Authentication.Local.Password,
&account.Authentication.Local.Email,
&emailValidationBytes,
&account.Authentication.Local.PhoneNumber,
&phoneNumberValidationBytes,
)
if err != nil {
return nil, err
}
err = json.Unmarshal(dataBytes, &account.Data)
if err != nil {
return nil, err
}
err = json.Unmarshal(metadataBytes, &account.Metadata)
if err != nil {
return nil, err
}
err = json.Unmarshal(emailValidationBytes, &account.Authentication.Local.EmailValidation)
if err != nil {
return nil, err
}
err = json.Unmarshal(phoneNumberValidationBytes, &account.Authentication.Local.PhoneNumberValidation)
if err != nil {
return nil, err
}
accounts = append(accounts, account)
}
return accounts, nil
}
func (PostgresqlStorage) GetAccountsByIds(accountids []string) ([]Account, error) {
return nil, fmt.Errorf("")
}
func (psql PostgresqlStorage) CreateAccount(account Account) error {
insertAccountStmt, err := psql.DbConnection.Prepare("INSERT INTO accounts (id, namespace, data, metadata) VALUES ($1, $2, $3, $4)")
if err != nil {
return err
}
dataAccountJson, err := json.Marshal(account.Data)
if err != nil {
return err
}
metadataAccountJson, err := json.Marshal(account.Metadata)
if err != nil {
return err
}
_, err = insertAccountStmt.Exec(account.ID, account.Namespace, dataAccountJson, metadataAccountJson)
if err != nil {
return err
}
insertAccountAuthStmt, err := psql.DbConnection.Prepare("INSERT INTO account_auth (account_id, username, password, email, email_validation,phone_number,phone_number_validation) values($1, $2, $3, $4, $5, $6, $7)")
if err != nil {
return err
}
emailValidationJson, err := json.Marshal(account.Authentication.Local.EmailValidation)
if err != nil {
return err
}
phoneValidationJson, err := json.Marshal(account.Authentication.Local.PhoneNumberValidation)
if err != nil {
return err
}
_, err = insertAccountAuthStmt.Exec(account.ID, account.Authentication.Local.Username, account.Authentication.Local.Password,
account.Authentication.Local.Email, emailValidationJson, account.Authentication.Local.PhoneNumber, phoneValidationJson)
if err != nil {
return err
}
return nil
}
func (psql PostgresqlStorage) UpdateAccount(account Account) error {
updateAccountStmt, err := psql.DbConnection.Prepare("update accounts set namespace=$1, data=$2, metadata=$3 where id= $4")
if err != nil {
return err
}
dataAccountJson, err := json.Marshal(account.Data)
if err != nil {
return err
}
metadataAccountJson, err := json.Marshal(account.Metadata)
if err != nil {
return err
}
_, err = updateAccountStmt.Exec(account.Namespace, dataAccountJson, metadataAccountJson, account.ID)
if err != nil {
return err
}
updateAccountAuthStmt, err := psql.DbConnection.Prepare("update account_auth set username = $1, password = $2, email = $3, email_validation = $4 ,phone_number = $5,phone_number_validation = $6 where account_id = $7")
if err != nil {
return err
}
emailValidationJson, err := json.Marshal(account.Authentication.Local.EmailValidation)
if err != nil {
return err
}
phoneValidationJson, err := json.Marshal(account.Authentication.Local.PhoneNumberValidation)
if err != nil {
return err
}
_, err = updateAccountAuthStmt.Exec(account.Authentication.Local.Username, account.Authentication.Local.Password, account.Authentication.Local.Email,
emailValidationJson, account.Authentication.Local.PhoneNumber, phoneValidationJson, account.ID)
if err != nil {
return err
}
return nil
}
func isUUIDv4(str string) bool {
pattern := regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89aAbB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$`)
return pattern.MatchString(str)
}