wip
This commit is contained in:
32
mangezmieux-backend/.github/workflows/build.yaml
vendored
Normal file
32
mangezmieux-backend/.github/workflows/build.yaml
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '**'
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GOPRIVATE: github.com/kratisto
|
||||||
|
GONOSUMDB: "*github.com/kratisto/mangezmieux-backend"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Configure git for private modules
|
||||||
|
env:
|
||||||
|
TOKEN: ${{ secrets.GHA_TOKEN_PAT }}
|
||||||
|
run: git config --global url."https://YOUR_GITHUB_USERNAME:${TOKEN}@github.com".insteadOf "https://github.com"
|
||||||
|
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: '1.21'
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: go build -v ./...
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: go test -v ./...
|
||||||
21
mangezmieux-backend/.github/workflows/release.yaml
vendored
Normal file
21
mangezmieux-backend/.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'main'
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Semantic Release
|
||||||
|
id: semantic # Need an `id` for output variables
|
||||||
|
uses: cycjimmy/semantic-release-action@v4
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- uses: rickstaa/action-create-tag@v1
|
||||||
|
id: "tag_create"
|
||||||
|
with:
|
||||||
|
tag: "pkg/client/v${{ steps.semantic.outputs.new_release_version }}"
|
||||||
5
mangezmieux-backend/.gitignore
vendored
Normal file
5
mangezmieux-backend/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
mangezmieux-backend
|
||||||
|
vendor/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
*.iml
|
||||||
38
mangezmieux-backend/.releaserc
Normal file
38
mangezmieux-backend/.releaserc
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"branches": ["main"],
|
||||||
|
"tagFormat":"v${version}",
|
||||||
|
"plugins": [
|
||||||
|
[
|
||||||
|
"@semantic-release/commit-analyzer",
|
||||||
|
{
|
||||||
|
"preset": "angular",
|
||||||
|
"releaseRules": [
|
||||||
|
{"type": "chore", "release": "patch"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@semantic-release/release-notes-generator",
|
||||||
|
[
|
||||||
|
"@semantic-release/changelog",
|
||||||
|
{
|
||||||
|
"changelogFile": "CHANGELOG.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"@semantic-release/git",
|
||||||
|
{
|
||||||
|
"assets": ["CHANGELOG.md"],
|
||||||
|
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@semantic-release/github",
|
||||||
|
[
|
||||||
|
"@semantic-release/git",
|
||||||
|
{
|
||||||
|
"assets": ["CHANGELOG.md"],
|
||||||
|
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
21
mangezmieux-backend/LICENSE
Normal file
21
mangezmieux-backend/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Jeffrey Duroyon
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
82
mangezmieux-backend/Makefile
Normal file
82
mangezmieux-backend/Makefile
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# Set an output prefix, which is the local directory if not specified
|
||||||
|
PREFIX?=$(shell pwd)
|
||||||
|
.DEFAULT_GOAL := build
|
||||||
|
|
||||||
|
DOCKER_IMAGE_NAME := mangezmieux-backend
|
||||||
|
# Setup name variables for the package/tool
|
||||||
|
NAME := mangezmieux-backend
|
||||||
|
BIN_NAME := mangezmieux-backend
|
||||||
|
PKG := github.com/kratisto/mangezmieux-backend
|
||||||
|
|
||||||
|
# GO env vars
|
||||||
|
ifeq ($(GOPATH),)
|
||||||
|
GOPATH:=~/go
|
||||||
|
endif
|
||||||
|
GO=$(firstword $(subst :, ,$(GOPATH)))
|
||||||
|
|
||||||
|
.PHONY: ensure-vendor
|
||||||
|
ensure-vendor: ## Get all vendor dependencies
|
||||||
|
@echo "+ $@"
|
||||||
|
dep ensure
|
||||||
|
|
||||||
|
.PHONY: update-vendor
|
||||||
|
update-vendor: ## Get all vendor dependencies
|
||||||
|
@echo "+ $@"
|
||||||
|
dep ensure -update
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean: local-clean ## Clean your generated files
|
||||||
|
@echo "+ $@"
|
||||||
|
docker image rm mangezmieux:snapshot || true
|
||||||
|
|
||||||
|
.PHONY: local-build
|
||||||
|
local-build: local-clean local-format ## Build locally the binary
|
||||||
|
@echo "+ $@"
|
||||||
|
go build -o $(GO)/bin/$(BIN_NAME) .
|
||||||
|
|
||||||
|
.PHONY: local-clean
|
||||||
|
local-clean: ## Cleanup locally any build binaries or packages
|
||||||
|
@echo "+ $@"
|
||||||
|
@$(RM) $(BIN_NAME)
|
||||||
|
|
||||||
|
.PHONY: local-format
|
||||||
|
local-format: ## format locally all files
|
||||||
|
@echo "+ $@"
|
||||||
|
gofmt -s -l -w .
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build: sources-image ## Build the docker image with application binary
|
||||||
|
@echo "+ $@"
|
||||||
|
docker build --no-cache \
|
||||||
|
-f containers/Dockerfile \
|
||||||
|
--build-arg SOURCES_IMAGE=mangezmieux-sources:snapshot \
|
||||||
|
-t mangezmieux:snapshot .
|
||||||
|
|
||||||
|
GIT_CREDENTIALS?=$(shell cat ~/.git-credentials 2> /dev/null)
|
||||||
|
.PHONY: sources-image
|
||||||
|
sources-image: ## Generate a Docker image with only the sources
|
||||||
|
@echo "+ $@"
|
||||||
|
docker build -t mangezmieux-sources:snapshot -f containers/Dockerfile.sources .
|
||||||
|
|
||||||
|
.PHONY: liquibase
|
||||||
|
liquibase: ## Run the dependencies of the server (launch the containers/docker-compose.local.yml)
|
||||||
|
@echo "+ $@"
|
||||||
|
@docker run --network host --rm -v ./liquibase/changelogs:/liquibase/changelog liquibase/liquibase --defaults-file=/liquibase/changelog/liquibase.properties --changelog-file=changelog-master.xml --classpath=changelog update
|
||||||
|
|
||||||
|
.PHONY: local-run-golang
|
||||||
|
local-run-golang: ## Build the server and run it
|
||||||
|
@echo "+ $@"
|
||||||
|
BUILD_GOLANG_CMD=local-build \
|
||||||
|
LAUNCH_GOLANG_CMD="$(GO)/bin/$(BIN_NAME)" \
|
||||||
|
$(MAKE) local-launch-golang
|
||||||
|
|
||||||
|
.PHONY: local-launch-golang
|
||||||
|
local-launch-golang: ## Build the server and run it
|
||||||
|
@echo "+ $@"
|
||||||
|
PID=`ps -ax | egrep "\b$(BIN_NAME)"| cut -d " " -f 1`; \
|
||||||
|
kill $$PID || true
|
||||||
|
$(MAKE) $(BUILD_GOLANG_CMD)
|
||||||
|
DB_PORT=`` $(LAUNCH_GOLANG_CMD) serve --loglevel debug --logformat text --postgreshost localhost:$$DB_PORT
|
||||||
|
|
||||||
|
.PHONY: local-run
|
||||||
|
local-run: local-run-golang ## Run the server with its dependencies
|
||||||
1
mangezmieux-backend/README.md
Normal file
1
mangezmieux-backend/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# mangezmieux-backend
|
||||||
106
mangezmieux-backend/cmd/root.go
Normal file
106
mangezmieux-backend/cmd/root.go
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"mangezmieux-backend/configuration"
|
||||||
|
"mangezmieux-backend/internal/logger"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Log.
|
||||||
|
parameterLogLevel = "loglevel"
|
||||||
|
parameterLogFormat = "logformat"
|
||||||
|
|
||||||
|
defaultLogLevel = "debug"
|
||||||
|
defaultLogFormat = "text"
|
||||||
|
|
||||||
|
// Mock.
|
||||||
|
parameterMock = "mock"
|
||||||
|
|
||||||
|
defaultMock = true
|
||||||
|
|
||||||
|
// Router.
|
||||||
|
parameterPort = "port"
|
||||||
|
defaultPort = "8080"
|
||||||
|
|
||||||
|
// DATABASE.
|
||||||
|
parameterPostgresDBName = "postgresdbname"
|
||||||
|
defaultPostgresDBName = "mangezmieux"
|
||||||
|
parameterPostgresDBSchema = "postgresdbschema"
|
||||||
|
defaultPostgresDBSchema = "mangezmieux"
|
||||||
|
parameterPostgresHost = "postgreshost"
|
||||||
|
defaultPostgresHost = "localhost"
|
||||||
|
parameterPostgresUser = "postgresuser"
|
||||||
|
defaultPostgresUser = "postgres"
|
||||||
|
parameterPostgresPwd = "postgrespwd"
|
||||||
|
defaultPostgresPwd = "mysecretpassword"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
config = &configuration.Config{}
|
||||||
|
|
||||||
|
cfgFile string
|
||||||
|
// GITHASH : Stores the git revision to be displayed.
|
||||||
|
GITHASH string
|
||||||
|
// VERSION : Stores the binary version to be displayed.
|
||||||
|
VERSION string
|
||||||
|
|
||||||
|
// rootCmd represents the base command when called without any subcommands.
|
||||||
|
rootCmd = &cobra.Command{
|
||||||
|
Use: "mangezmieux",
|
||||||
|
Short: "mangezmieux",
|
||||||
|
Version: fmt.Sprintf("%s (%s)", VERSION, GITHASH),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
|
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||||
|
func Execute() {
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
logger.GetLogger().Error(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(serveCmd)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
// Cobra supports persistent flags, which, if defined here,
|
||||||
|
// will be global for your application.
|
||||||
|
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.mangezmieux.yaml)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// initConfig reads in config file and ENV variables if set.
|
||||||
|
func initConfig() {
|
||||||
|
if cfgFile != "" {
|
||||||
|
// Use config file from the flag.
|
||||||
|
viper.SetConfigFile(cfgFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
viper.AutomaticEnv() // read in environment variables that match
|
||||||
|
|
||||||
|
// If a config file is found, read it in.
|
||||||
|
if err := viper.ReadInConfig(); err == nil {
|
||||||
|
logger.GetLogger().Info("Using config file:", viper.ConfigFileUsed())
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Mock = viper.GetBool(parameterMock)
|
||||||
|
|
||||||
|
config.Port = viper.GetString(parameterPort)
|
||||||
|
|
||||||
|
config.LogLevel = viper.GetString(parameterLogLevel)
|
||||||
|
config.LogFormat = viper.GetString(parameterLogFormat)
|
||||||
|
|
||||||
|
config.PostgresDBName = viper.GetString(parameterPostgresDBName)
|
||||||
|
config.PostgresDBSchema = viper.GetString(parameterPostgresDBSchema)
|
||||||
|
config.PostgresHost = viper.GetString(parameterPostgresHost)
|
||||||
|
config.PostgresUser = viper.GetString(parameterPostgresUser)
|
||||||
|
config.PostgresPwd = viper.GetString(parameterPostgresPwd)
|
||||||
|
|
||||||
|
config.Version = VERSION
|
||||||
|
}
|
||||||
91
mangezmieux-backend/cmd/serve.go
Normal file
91
mangezmieux-backend/cmd/serve.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"mangezmieux-backend/internal/acl"
|
||||||
|
aclKey "mangezmieux-backend/internal/acl/key"
|
||||||
|
"mangezmieux-backend/internal/acl/service"
|
||||||
|
|
||||||
|
"mangezmieux-backend/internal/ginserver"
|
||||||
|
"mangezmieux-backend/internal/health"
|
||||||
|
"mangezmieux-backend/internal/injector"
|
||||||
|
"mangezmieux-backend/internal/jwt"
|
||||||
|
"mangezmieux-backend/internal/logger"
|
||||||
|
"mangezmieux-backend/internal/postgres"
|
||||||
|
"mangezmieux-backend/internal/users"
|
||||||
|
service2 "mangezmieux-backend/internal/users/service"
|
||||||
|
coreValidator "mangezmieux-backend/internal/validator"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
// serveCmd represents the serve command.
|
||||||
|
var serveCmd = &cobra.Command{
|
||||||
|
Use: "serve",
|
||||||
|
Short: "Serve endpoints",
|
||||||
|
PreRun: initServeBindingsFlags,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
serve()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func serve() {
|
||||||
|
initConfig()
|
||||||
|
logger.InitLogger(config.LogLevel, config.LogFormat)
|
||||||
|
logrus.
|
||||||
|
WithField(parameterLogLevel, config.LogLevel).
|
||||||
|
WithField(parameterLogFormat, config.LogFormat).
|
||||||
|
WithField(parameterPort, config.Port).
|
||||||
|
WithField(parameterPostgresHost, config.PostgresHost).
|
||||||
|
Warn("Configuration")
|
||||||
|
inj := &injector.Injector{}
|
||||||
|
authMiddleware := users.AuthMiddleware // <= its not configured yet
|
||||||
|
|
||||||
|
jwt.Setup(inj)
|
||||||
|
jwtService := injector.Get[*jwt.Service](inj, jwt.JWTKey)
|
||||||
|
inj.Set("AuthenticationMiddleware", authMiddleware.GinMiddleware(jwtService))
|
||||||
|
|
||||||
|
ginserver.Setup(inj)
|
||||||
|
coreValidator.Setup(inj)
|
||||||
|
|
||||||
|
psqlInfo := fmt.Sprintf("host=localhost port=5432 user=%s "+
|
||||||
|
"password=mysecretpassword dbname=%s sslmode=disable",
|
||||||
|
config.PostgresUser, config.PostgresDBName)
|
||||||
|
postgres.Setup(inj, psqlInfo)
|
||||||
|
|
||||||
|
health.Setup(inj)
|
||||||
|
acl.SetupDao(inj)
|
||||||
|
users.Setup(inj)
|
||||||
|
|
||||||
|
acl.Setup(inj)
|
||||||
|
|
||||||
|
authMiddleware.Service = injector.Get[*service2.Service](inj, users.ServiceKey)
|
||||||
|
authMiddleware.RoleService = injector.Get[service.Service](inj, aclKey.ServiceKey)
|
||||||
|
ginserver.Start(inj, config.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
serveCmd.Flags().String(parameterLogLevel, defaultLogLevel, "Use this flag to set the logging level")
|
||||||
|
serveCmd.Flags().String(parameterLogFormat, defaultLogFormat, "Use this flag to set the logging format")
|
||||||
|
serveCmd.Flags().Bool(parameterMock, defaultMock, "Use this flag to mock external services")
|
||||||
|
serveCmd.Flags().String(parameterPort, defaultPort, "Use this flag to set the listening port of the api")
|
||||||
|
serveCmd.Flags().String(parameterPostgresDBName, defaultPostgresDBName, "Use this flag to set database name")
|
||||||
|
serveCmd.Flags().String(parameterPostgresDBSchema, defaultPostgresDBSchema, "Use this flag to set database schema name")
|
||||||
|
serveCmd.Flags().String(parameterPostgresHost, defaultPostgresHost, "Use this flag to set database host")
|
||||||
|
serveCmd.Flags().String(parameterPostgresUser, defaultPostgresUser, "Use this flag to set database user name")
|
||||||
|
serveCmd.Flags().String(parameterPostgresPwd, defaultPostgresPwd, "Use this flag to set database user password")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initServeBindingsFlags(cmd *cobra.Command, args []string) {
|
||||||
|
_ = viper.BindPFlag(parameterLogLevel, cmd.Flags().Lookup(parameterLogLevel))
|
||||||
|
_ = viper.BindPFlag(parameterLogFormat, cmd.Flags().Lookup(parameterLogFormat))
|
||||||
|
_ = viper.BindPFlag(parameterMock, cmd.Flags().Lookup(parameterMock))
|
||||||
|
_ = viper.BindPFlag(parameterPort, cmd.Flags().Lookup(parameterPort))
|
||||||
|
_ = viper.BindPFlag(parameterPostgresDBName, cmd.Flags().Lookup(parameterPostgresDBName))
|
||||||
|
_ = viper.BindPFlag(parameterPostgresDBSchema, cmd.Flags().Lookup(parameterPostgresDBSchema))
|
||||||
|
_ = viper.BindPFlag(parameterPostgresHost, cmd.Flags().Lookup(parameterPostgresHost))
|
||||||
|
_ = viper.BindPFlag(parameterPostgresUser, cmd.Flags().Lookup(parameterPostgresUser))
|
||||||
|
_ = viper.BindPFlag(parameterPostgresPwd, cmd.Flags().Lookup(parameterPostgresPwd))
|
||||||
|
}
|
||||||
19
mangezmieux-backend/configuration/config.go
Normal file
19
mangezmieux-backend/configuration/config.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
// Config holds all the configuration of the application.
|
||||||
|
type Config struct {
|
||||||
|
Mock bool
|
||||||
|
|
||||||
|
Port string
|
||||||
|
|
||||||
|
LogLevel string
|
||||||
|
LogFormat string
|
||||||
|
|
||||||
|
PostgresDBName string
|
||||||
|
PostgresDBSchema string
|
||||||
|
PostgresHost string
|
||||||
|
PostgresUser string
|
||||||
|
PostgresPwd string
|
||||||
|
|
||||||
|
Version string
|
||||||
|
}
|
||||||
67
mangezmieux-backend/go.mod
Normal file
67
mangezmieux-backend/go.mod
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
module mangezmieux-backend
|
||||||
|
|
||||||
|
go 1.22
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/cucumber/godog v0.14.1
|
||||||
|
github.com/gin-contrib/cors v1.7.2
|
||||||
|
github.com/gin-gonic/gin v1.10.0
|
||||||
|
github.com/go-playground/validator/v10 v10.22.0
|
||||||
|
github.com/gofrs/uuid v4.3.1+incompatible
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/lib/pq v1.10.9
|
||||||
|
github.com/ohler55/ojg v1.22.0
|
||||||
|
github.com/sirupsen/logrus v1.9.3
|
||||||
|
github.com/spf13/cobra v1.8.1
|
||||||
|
github.com/spf13/viper v1.19.0
|
||||||
|
golang.org/x/crypto v0.24.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/bytedance/sonic v1.11.6 // indirect
|
||||||
|
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||||
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
|
github.com/cucumber/gherkin/go/v26 v26.2.0 // indirect
|
||||||
|
github.com/cucumber/messages/go/v21 v21.0.1 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||||
|
github.com/hashicorp/go-memdb v1.3.4 // indirect
|
||||||
|
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||||
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // 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.2.2 // indirect
|
||||||
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||||
|
golang.org/x/net v0.25.0 // indirect
|
||||||
|
golang.org/x/sys v0.21.0 // indirect
|
||||||
|
golang.org/x/text v0.16.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.34.1 // indirect
|
||||||
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
176
mangezmieux-backend/go.sum
Normal file
176
mangezmieux-backend/go.sum
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||||
|
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||||
|
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||||
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
|
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||||
|
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
|
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||||
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI=
|
||||||
|
github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0=
|
||||||
|
github.com/cucumber/godog v0.14.1 h1:HGZhcOyyfaKclHjJ+r/q93iaTJZLKYW6Tv3HkmUE6+M=
|
||||||
|
github.com/cucumber/godog v0.14.1/go.mod h1:FX3rzIDybWABU4kuIXLZ/qtqEe1Ac5RdXmqvACJOces=
|
||||||
|
github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM+RuOwHZiMI=
|
||||||
|
github.com/cucumber/messages/go/v21 v21.0.1/go.mod h1:zheH/2HS9JLVFukdrsPWoPdmUtmYQAQPLk7w5vWsk5s=
|
||||||
|
github.com/cucumber/messages/go/v22 v22.0.0/go.mod h1:aZipXTKc0JnjCsXrJnuZpWhtay93k7Rn3Dee7iyPJjs=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||||
|
github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw=
|
||||||
|
github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E=
|
||||||
|
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.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||||
|
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
|
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.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||||
|
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
|
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/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI=
|
||||||
|
github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
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.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/go-cmp v0.5.9/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/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
|
github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c=
|
||||||
|
github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
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.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
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.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
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/ohler55/ojg v1.22.0 h1:McZObj3cD/Zz/ojzk5Pi5VvgQcagxmT1bVKNzhE5ihI=
|
||||||
|
github.com/ohler55/ojg v1.22.0/go.mod h1:gQhDVpQLqrmnd2eqGAvJtn+NfKoYJbe/A4Sj3/Vro4o=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||||
|
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||||
|
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||||
|
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
|
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||||
|
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||||
|
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||||
|
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
|
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||||
|
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||||
|
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||||
|
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
|
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.4/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/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
|
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.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
|
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
|
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||||
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||||
|
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||||
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
|
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||||
|
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
|
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||||
|
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||||
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||||
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||||
|
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||||
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||||
|
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
|
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||||
|
google.golang.org/protobuf v1.34.1/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/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
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=
|
||||||
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
71
mangezmieux-backend/internal/acl/handler.go
Normal file
71
mangezmieux-backend/internal/acl/handler.go
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package acl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
"mangezmieux-backend/internal/acl/service"
|
||||||
|
"mangezmieux-backend/internal/logger"
|
||||||
|
"mangezmieux-backend/internal/middleware"
|
||||||
|
"mangezmieux-backend/internal/responses"
|
||||||
|
"mangezmieux-backend/internal/users/model"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handler struct {
|
||||||
|
service service.Service
|
||||||
|
Validator *validator.Validate
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
rolePathParam = "roleName"
|
||||||
|
roleIdPathParam = "roleId"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewHandler(service service.Service, validator *validator.Validate) *Handler {
|
||||||
|
return &Handler{
|
||||||
|
service: service,
|
||||||
|
Validator: validator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) GetAllRole(context *gin.Context) {
|
||||||
|
roles, err := h.service.GetAllRole()
|
||||||
|
if err != nil {
|
||||||
|
logger.GetLogger().Error(err)
|
||||||
|
var apiError *responses.APIError
|
||||||
|
if errors.As(err, &apiError) {
|
||||||
|
responses.JSONError(context.Writer, *apiError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
responses.JSONErrorWithMessage(context.Writer, responses.ErrInternalServer, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
responses.JSON(context.Writer, http.StatusOK, roles)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) GetMyRoles(context *gin.Context) {
|
||||||
|
user, exists := context.Get(middleware.CtxUser)
|
||||||
|
if !exists {
|
||||||
|
responses.JSONErrorWithMessage(context.Writer, responses.ErrInternalServer, "User not found in context")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userModel := user.(*model.User)
|
||||||
|
|
||||||
|
userRight, err := h.service.GetRoleForCurrentUser(userModel)
|
||||||
|
if err != nil {
|
||||||
|
logger.GetLogger().Error(err)
|
||||||
|
|
||||||
|
var apiError *responses.APIError
|
||||||
|
if errors.As(err, &apiError) {
|
||||||
|
responses.JSONError(context.Writer, *apiError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
responses.JSONErrorWithMessage(context.Writer, responses.ErrInternalServer, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
responses.JSON(context.Writer, http.StatusOK, userRight)
|
||||||
|
|
||||||
|
}
|
||||||
6
mangezmieux-backend/internal/acl/key/key.go
Normal file
6
mangezmieux-backend/internal/acl/key/key.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package key
|
||||||
|
|
||||||
|
const (
|
||||||
|
ServiceKey = "RoleService"
|
||||||
|
DaoKey = "DaoService"
|
||||||
|
)
|
||||||
48
mangezmieux-backend/internal/acl/model/model.go
Normal file
48
mangezmieux-backend/internal/acl/model/model.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"mangezmieux-backend/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserRight struct {
|
||||||
|
UserRole []*UserRole `json:"userRole"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Resource struct {
|
||||||
|
Id uuid.UUID `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
model.Metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoleVerbResource struct {
|
||||||
|
Id uuid.UUID `json:"id"`
|
||||||
|
Verb string `json:"verb"`
|
||||||
|
RoleId uuid.UUID `json:"role"`
|
||||||
|
ResourceId uuid.UUID `json:"resource"`
|
||||||
|
model.Metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoleEditable struct {
|
||||||
|
Id uuid.UUID `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
model.Metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
type Role struct {
|
||||||
|
RoleEditable
|
||||||
|
ResourceVerb map[string][]*Verb
|
||||||
|
}
|
||||||
|
|
||||||
|
type Verb struct {
|
||||||
|
Id uuid.UUID `json:"id"`
|
||||||
|
Verb string `json:"verb"`
|
||||||
|
model.Metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserRole struct {
|
||||||
|
Id uuid.UUID `json:"id"`
|
||||||
|
RoleId uuid.UUID `json:"role"`
|
||||||
|
UserId uuid.UUID `json:"user"`
|
||||||
|
model.Metadata
|
||||||
|
}
|
||||||
60
mangezmieux-backend/internal/acl/service/service.go
Normal file
60
mangezmieux-backend/internal/acl/service/service.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"mangezmieux-backend/internal/acl/model"
|
||||||
|
"mangezmieux-backend/internal/acl/sql"
|
||||||
|
model2 "mangezmieux-backend/internal/users/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type service struct {
|
||||||
|
dao sql.Dao
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s service) GetRoleForCurrentUser(user *model2.User) (*model.UserRight, error) {
|
||||||
|
userId, err := uuid.Parse(user.ID.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userRole, err := s.GetUserRoleByUser(userId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userRight := &model.UserRight{
|
||||||
|
UserRole: userRole,
|
||||||
|
}
|
||||||
|
return userRight, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s service) GetAllRole() ([]*model.Role, error) {
|
||||||
|
roles, err := s.dao.GetAllRole()
|
||||||
|
return roles, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s service) GetUserRoleByUser(id uuid.UUID) ([]*model.UserRole, error) {
|
||||||
|
userRoles, err := s.dao.GetUserRoleByUser(id)
|
||||||
|
return userRoles, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s service) GetRole(id uuid.UUID) (*model.Role, error) {
|
||||||
|
role, err := s.dao.GetRole(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return role, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service interface {
|
||||||
|
GetAllRole() ([]*model.Role, error)
|
||||||
|
GetRoleForCurrentUser(user *model2.User) (*model.UserRight, error)
|
||||||
|
GetUserRoleByUser(id uuid.UUID) ([]*model.UserRole, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(dao sql.Dao) Service {
|
||||||
|
return &service{
|
||||||
|
dao: dao,
|
||||||
|
}
|
||||||
|
}
|
||||||
39
mangezmieux-backend/internal/acl/setup.go
Normal file
39
mangezmieux-backend/internal/acl/setup.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package acl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"mangezmieux-backend/internal/acl/key"
|
||||||
|
"mangezmieux-backend/internal/acl/service"
|
||||||
|
aclSql "mangezmieux-backend/internal/acl/sql"
|
||||||
|
"mangezmieux-backend/internal/ginserver"
|
||||||
|
"mangezmieux-backend/internal/injector"
|
||||||
|
"mangezmieux-backend/internal/postgres"
|
||||||
|
"mangezmieux-backend/internal/validator"
|
||||||
|
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
validatorv10 "github.com/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupDao(inj *injector.Injector) {
|
||||||
|
client := injector.Get[*sql.DB](inj, postgres.DatabaseKey)
|
||||||
|
dao := aclSql.NewDao(client)
|
||||||
|
inj.Set(key.DaoKey, dao)
|
||||||
|
|
||||||
|
}
|
||||||
|
func Setup(inj *injector.Injector) {
|
||||||
|
securedRoute := injector.Get[*gin.RouterGroup](inj, ginserver.SecuredRouterInjectorKey)
|
||||||
|
validatorCli := injector.Get[*validatorv10.Validate](inj, validator.ValidatorInjectorKey)
|
||||||
|
dao := injector.Get[aclSql.Dao](inj, key.DaoKey)
|
||||||
|
aclService := service.NewService(dao)
|
||||||
|
handler := NewHandler(aclService, validatorCli)
|
||||||
|
|
||||||
|
inj.Set(key.ServiceKey, aclService)
|
||||||
|
|
||||||
|
aclRoute := securedRoute.Group("/roles")
|
||||||
|
aclRoute.Handle(http.MethodGet, "/", handler.GetAllRole)
|
||||||
|
|
||||||
|
securedRoute.Handle(http.MethodGet, "/users/me/roles", handler.GetMyRoles)
|
||||||
|
|
||||||
|
}
|
||||||
39
mangezmieux-backend/internal/acl/sql/dao.go
Normal file
39
mangezmieux-backend/internal/acl/sql/dao.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package sql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"mangezmieux-backend/internal/acl/model"
|
||||||
|
model2 "mangezmieux-backend/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Dao interface {
|
||||||
|
//Role
|
||||||
|
GetRole(id uuid.UUID) (*model.Role, error)
|
||||||
|
GetRoleByName(name string) (*model.Role, error)
|
||||||
|
AddRole(roleName string, metadata model2.Metadata) (*model.Role, error)
|
||||||
|
DeleteRole(id uuid.UUID) error
|
||||||
|
GetAllRole() ([]*model.Role, error)
|
||||||
|
|
||||||
|
//Resource
|
||||||
|
AddResource(resourceName string, metadata model2.Metadata) (*model.Resource, error)
|
||||||
|
DeleteResource(id uuid.UUID) error
|
||||||
|
GetResource(id uuid.UUID) (*model.Resource, error)
|
||||||
|
GetResourceByName(name string) (*model.Resource, error)
|
||||||
|
GetAllResource() ([]*model.Resource, error)
|
||||||
|
|
||||||
|
//RoleVerbResource
|
||||||
|
GetRoleVerbResource(id uuid.UUID) (*model.RoleVerbResource, error)
|
||||||
|
GetRoleVerbResourceByRoleResourceAndVerb(roleId, resourceId uuid.UUID, verb string) (*model.RoleVerbResource, error)
|
||||||
|
GetRoleVerbResourceByRoleResource(roleId, resourceId uuid.UUID) ([]*model.RoleVerbResource, error)
|
||||||
|
GetRoleVerbResourceByRole(roleId uuid.UUID) ([]*model.RoleVerbResource, error)
|
||||||
|
AddRoleVerbResource(roleId, resourceId uuid.UUID, verb string, metadata model2.Metadata) (*model.RoleVerbResource, error)
|
||||||
|
DeleteRoleVerbResource(id uuid.UUID) error
|
||||||
|
|
||||||
|
//UserRole
|
||||||
|
GetUserRole(id uuid.UUID) (*model.UserRole, error)
|
||||||
|
GetUserRoleByUserAndRole(userId, roleId uuid.UUID) (*model.UserRole, error)
|
||||||
|
GetUserRoleByUser(userId uuid.UUID) ([]*model.UserRole, error)
|
||||||
|
GetUserRoleByRole(role uuid.UUID) ([]*model.UserRole, error)
|
||||||
|
AddUserRole(userId, roleId uuid.UUID, metadata model2.Metadata) (*model.UserRole, error)
|
||||||
|
DeleteUserRole(id uuid.UUID) error
|
||||||
|
}
|
||||||
13
mangezmieux-backend/internal/acl/sql/sql.go
Normal file
13
mangezmieux-backend/internal/acl/sql/sql.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package sql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dao struct {
|
||||||
|
client *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDao(client *sql.DB) Dao {
|
||||||
|
return &dao{client: client}
|
||||||
|
}
|
||||||
121
mangezmieux-backend/internal/acl/sql/sql_resource.go
Normal file
121
mangezmieux-backend/internal/acl/sql/sql_resource.go
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
package sql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/lib/pq"
|
||||||
|
"mangezmieux-backend/internal/acl/model"
|
||||||
|
model2 "mangezmieux-backend/internal/model"
|
||||||
|
"mangezmieux-backend/internal/postgres"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (sqlDAO dao) AddResource(resourceName string, metadata model2.Metadata) (*model.Resource, error) {
|
||||||
|
var Id uuid.UUID
|
||||||
|
q := `
|
||||||
|
INSERT INTO mangezmieux.resource
|
||||||
|
(name, creation_date, creation_user)
|
||||||
|
VALUES
|
||||||
|
($1, $2, $3)
|
||||||
|
RETURNING
|
||||||
|
Id`
|
||||||
|
|
||||||
|
err := sqlDAO.client.QueryRow(q, resourceName, metadata.CreationDate, metadata.CreationUser).Scan(&Id)
|
||||||
|
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
|
||||||
|
resource, err := sqlDAO.GetResource(Id)
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) DeleteResource(Id uuid.UUID) error {
|
||||||
|
q := `
|
||||||
|
DELETE FROM mangezmieux.resource
|
||||||
|
WHERE Id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
_, err := sqlDAO.client.Exec(q, Id.String())
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) GetResource(Id uuid.UUID) (*model.Resource, error) {
|
||||||
|
q := `
|
||||||
|
SELECT Id, name, creation_date, last_update_date
|
||||||
|
FROM mangezmieux.resource r
|
||||||
|
WHERE r.Id = $1
|
||||||
|
`
|
||||||
|
row := sqlDAO.client.QueryRow(q, Id.String())
|
||||||
|
|
||||||
|
resource := &model.Resource{}
|
||||||
|
err := row.Scan(&resource.Id, &resource.Name, &resource.CreationDate, &resource.LastUpdateDate)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
return resource, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) GetResourceByName(name string) (*model.Resource, error) {
|
||||||
|
q := `
|
||||||
|
SELECT Id, name, creation_date, last_update_date
|
||||||
|
FROM mangezmieux.resource r
|
||||||
|
WHERE r.name = $1
|
||||||
|
`
|
||||||
|
row := sqlDAO.client.QueryRow(q, name)
|
||||||
|
|
||||||
|
resource := &model.Resource{}
|
||||||
|
err := row.Scan(&resource.Id, &resource.Name, &resource.CreationDate, &resource.LastUpdateDate)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
return resource, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) GetAllResource() ([]*model.Resource, error) {
|
||||||
|
q := `
|
||||||
|
SELECT Id, name, creation_date, last_update_date
|
||||||
|
FROM mangezmieux.resource r
|
||||||
|
`
|
||||||
|
rows, err := sqlDAO.client.Query(q)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if rows.Err() != nil {
|
||||||
|
return nil, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
resources := make([]*model.Resource, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
resource := &model.Resource{}
|
||||||
|
err := rows.Scan(&resource.Id, &resource.Name, &resource.CreationDate, &resource.LastUpdateDate)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
resources = append(resources, resource)
|
||||||
|
}
|
||||||
|
return resources, nil
|
||||||
|
}
|
||||||
121
mangezmieux-backend/internal/acl/sql/sql_role.go
Normal file
121
mangezmieux-backend/internal/acl/sql/sql_role.go
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
package sql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/lib/pq"
|
||||||
|
"mangezmieux-backend/internal/acl/model"
|
||||||
|
model2 "mangezmieux-backend/internal/model"
|
||||||
|
"mangezmieux-backend/internal/postgres"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (sqlDAO dao) GetRole(id uuid.UUID) (*model.Role, error) {
|
||||||
|
q := `
|
||||||
|
SELECT id, name, creation_date, last_update_date
|
||||||
|
FROM mangezmieux.role r
|
||||||
|
WHERE r.id = $1
|
||||||
|
`
|
||||||
|
row := sqlDAO.client.QueryRow(q, id.String())
|
||||||
|
|
||||||
|
role := &model.Role{}
|
||||||
|
err := row.Scan(&role.Id, &role.Name, &role.CreationDate, &role.LastUpdateDate)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
return role, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) GetRoleByName(name string) (*model.Role, error) {
|
||||||
|
q := `
|
||||||
|
SELECT id, name, creation_date, last_update_date
|
||||||
|
FROM mangezmieux.role r
|
||||||
|
WHERE r.name = $1
|
||||||
|
`
|
||||||
|
row := sqlDAO.client.QueryRow(q, name)
|
||||||
|
|
||||||
|
role := &model.Role{}
|
||||||
|
err := row.Scan(&role.Id, &role.Name, &role.CreationDate, &role.LastUpdateDate)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
return role, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) AddRole(roleName string, metadata model2.Metadata) (*model.Role, error) {
|
||||||
|
var id uuid.UUID
|
||||||
|
q := `
|
||||||
|
INSERT INTO mangezmieux.role
|
||||||
|
(name, creation_date, creation_user)
|
||||||
|
VALUES
|
||||||
|
($1,$2,$3)
|
||||||
|
RETURNING
|
||||||
|
id`
|
||||||
|
|
||||||
|
err := sqlDAO.client.QueryRow(q, roleName, metadata.CreationDate, metadata.CreationUser).Scan(&id)
|
||||||
|
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
|
||||||
|
role, err := sqlDAO.GetRole(id)
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
|
||||||
|
return role, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) DeleteRole(id uuid.UUID) error {
|
||||||
|
q := `
|
||||||
|
DELETE FROM mangezmieux.role
|
||||||
|
WHERE id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
_, err := sqlDAO.client.Exec(q, id.String())
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) GetAllRole() ([]*model.Role, error) {
|
||||||
|
q := `
|
||||||
|
SELECT id, name, creation_date, last_update_date
|
||||||
|
FROM mangezmieux.role r
|
||||||
|
`
|
||||||
|
rows, err := sqlDAO.client.Query(q)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if rows.Err() != nil {
|
||||||
|
return nil, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
roles := make([]*model.Role, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
role := &model.Role{}
|
||||||
|
err := rows.Scan(&role.Id, &role.Name, &role.CreationDate, &role.LastUpdateDate)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
roles = append(roles, role)
|
||||||
|
}
|
||||||
|
return roles, nil
|
||||||
|
}
|
||||||
191
mangezmieux-backend/internal/acl/sql/sql_role_verb_resource.go
Normal file
191
mangezmieux-backend/internal/acl/sql/sql_role_verb_resource.go
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
package sql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"mangezmieux-backend/internal/acl/model"
|
||||||
|
model2 "mangezmieux-backend/internal/model"
|
||||||
|
"mangezmieux-backend/internal/postgres"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (sqlDAO dao) GetRoleVerbResource(id uuid.UUID) (*model.RoleVerbResource, error) {
|
||||||
|
q := `
|
||||||
|
SELECT id, role_id, verb, resource_id, creation_date, last_update_date
|
||||||
|
FROM mangezmieux.role_verb_resource r
|
||||||
|
WHERE r.id = $1
|
||||||
|
`
|
||||||
|
row := sqlDAO.client.QueryRow(q, id.String())
|
||||||
|
|
||||||
|
roleVerbResource := &model.RoleVerbResource{}
|
||||||
|
err := row.Scan(&roleVerbResource.Id, &roleVerbResource.RoleId, &roleVerbResource.Verb, &roleVerbResource.ResourceId, &roleVerbResource.CreationDate, &roleVerbResource.LastUpdateDate)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
return roleVerbResource, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) GetRoleVerbResourceByRoleResourceAndVerb(roleId, resourceId uuid.UUID, verb string) (*model.RoleVerbResource, error) {
|
||||||
|
q := `
|
||||||
|
SELECT id, role_id, verb, resource_id, creation_date, last_update_date
|
||||||
|
FROM mangezmieux.role_verb_resource r
|
||||||
|
WHERE r.role_id = $1
|
||||||
|
AND r.resource_id = $2
|
||||||
|
AND r.verb = $3
|
||||||
|
`
|
||||||
|
row := sqlDAO.client.QueryRow(q, roleId.String(), resourceId.String(), verb)
|
||||||
|
|
||||||
|
roleVerbResource := &model.RoleVerbResource{}
|
||||||
|
err := row.Scan(&roleVerbResource.Id, &roleVerbResource.RoleId, &roleVerbResource.Verb, &roleVerbResource.ResourceId, &roleVerbResource.CreationDate, &roleVerbResource.LastUpdateDate)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
return roleVerbResource, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) GetRoleVerbResourceByResourceAndVerb(resourceId uuid.UUID, verb string) ([]*model.RoleVerbResource, error) {
|
||||||
|
q := `
|
||||||
|
SELECT id, role_id, verb, resource_id, creation_date, last_update_date
|
||||||
|
FROM mangezmieux.role_verb_resource r
|
||||||
|
WHERE r.resource_id = $1
|
||||||
|
AND r.verb = $2
|
||||||
|
`
|
||||||
|
rows, err := sqlDAO.client.Query(q, resourceId.String(), verb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if rows.Err() != nil {
|
||||||
|
return nil, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
roleVerResources := make([]*model.RoleVerbResource, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
roleVerbResource := &model.RoleVerbResource{}
|
||||||
|
err := rows.Scan(&roleVerbResource.Id, &roleVerbResource.RoleId, &roleVerbResource.Verb, &roleVerbResource.ResourceId, &roleVerbResource.CreationDate, &roleVerbResource.LastUpdateDate)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
roleVerResources = append(roleVerResources, roleVerbResource)
|
||||||
|
}
|
||||||
|
return roleVerResources, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) GetRoleVerbResourceByRoleResource(roleId, resourceId uuid.UUID) ([]*model.RoleVerbResource, error) {
|
||||||
|
q := `
|
||||||
|
SELECT id, role_id, verb, resource_id, creation_date, last_update_date
|
||||||
|
FROM mangezmieux.role_verb_resource r
|
||||||
|
WHERE r.role_id = $1
|
||||||
|
AND r.resource_id = $2
|
||||||
|
`
|
||||||
|
rows, err := sqlDAO.client.Query(q, roleId.String(), resourceId.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if rows.Err() != nil {
|
||||||
|
return nil, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
roleVerResources := make([]*model.RoleVerbResource, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
roleVerbResource := &model.RoleVerbResource{}
|
||||||
|
err := rows.Scan(&roleVerbResource.Id, &roleVerbResource.RoleId, &roleVerbResource.Verb, &roleVerbResource.ResourceId, &roleVerbResource.CreationDate, &roleVerbResource.LastUpdateDate)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
roleVerResources = append(roleVerResources, roleVerbResource)
|
||||||
|
}
|
||||||
|
return roleVerResources, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) GetRoleVerbResourceByRole(roleId uuid.UUID) ([]*model.RoleVerbResource, error) {
|
||||||
|
q := `
|
||||||
|
SELECT id, role_id, verb, resource_id, creation_date, last_update_date
|
||||||
|
FROM mangezmieux.role_verb_resource r
|
||||||
|
WHERE r.role_id = $1
|
||||||
|
`
|
||||||
|
rows, err := sqlDAO.client.Query(q, roleId.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if rows.Err() != nil {
|
||||||
|
return nil, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
roleVerResources := make([]*model.RoleVerbResource, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
roleVerbResource := &model.RoleVerbResource{}
|
||||||
|
err := rows.Scan(&roleVerbResource.Id, &roleVerbResource.RoleId, &roleVerbResource.Verb, &roleVerbResource.ResourceId, &roleVerbResource.CreationDate, &roleVerbResource.LastUpdateDate)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
roleVerResources = append(roleVerResources, roleVerbResource)
|
||||||
|
}
|
||||||
|
return roleVerResources, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) AddRoleVerbResource(roleId, resourceId uuid.UUID, verb string, metadata model2.Metadata) (*model.RoleVerbResource, error) {
|
||||||
|
var Id uuid.UUID
|
||||||
|
q := `
|
||||||
|
INSERT INTO mangezmieux.role_verb_resource
|
||||||
|
(role_id, verb, resource_id, creation_date, creation_user)
|
||||||
|
VALUES
|
||||||
|
($1,$2,$3,$4,$5)
|
||||||
|
RETURNING
|
||||||
|
Id`
|
||||||
|
|
||||||
|
err := sqlDAO.client.QueryRow(q, roleId.String(), verb, resourceId.String(), metadata.CreationDate, metadata.CreationUser).Scan(&Id)
|
||||||
|
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
|
||||||
|
roleVerbResource, err := sqlDAO.GetRoleVerbResource(Id)
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
|
||||||
|
return roleVerbResource, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) DeleteRoleVerbResource(id uuid.UUID) error {
|
||||||
|
q := `
|
||||||
|
DELETE FROM mangezmieux.role_verb_resource
|
||||||
|
WHERE Id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
_, err := sqlDAO.client.Exec(q, id.String())
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
161
mangezmieux-backend/internal/acl/sql/sql_user_role.go
Normal file
161
mangezmieux-backend/internal/acl/sql/sql_user_role.go
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
package sql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"mangezmieux-backend/internal/acl/model"
|
||||||
|
model2 "mangezmieux-backend/internal/model"
|
||||||
|
"mangezmieux-backend/internal/postgres"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (sqlDAO dao) GetUserRole(id uuid.UUID) (*model.UserRole, error) {
|
||||||
|
q := `
|
||||||
|
SELECT id, role_id, user_id, creation_date, last_update_date
|
||||||
|
FROM mangezmieux.user_role r
|
||||||
|
WHERE r.id = $1
|
||||||
|
`
|
||||||
|
row := sqlDAO.client.QueryRow(q, id.String())
|
||||||
|
|
||||||
|
userRole := &model.UserRole{}
|
||||||
|
err := row.Scan(&userRole.Id, &userRole.RoleId, &userRole.UserId, &userRole.CreationDate, &userRole.LastUpdateDate)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
return userRole, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) GetUserRoleByUserAndRole(userId, roleId uuid.UUID) (*model.UserRole, error) {
|
||||||
|
q := `
|
||||||
|
SELECT id, role_id, user_id, creation_date, last_update_date
|
||||||
|
FROM mangezmieux.user_role r
|
||||||
|
WHERE r.role_id = $1
|
||||||
|
AND r.user_id = $2
|
||||||
|
`
|
||||||
|
row := sqlDAO.client.QueryRow(q, roleId.String(), userId.String())
|
||||||
|
|
||||||
|
userRole := &model.UserRole{}
|
||||||
|
err := row.Scan(&userRole.Id, &userRole.RoleId, &userRole.UserId, &userRole.CreationDate, &userRole.LastUpdateDate)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
return userRole, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) GetUserRoleByUser(userId uuid.UUID) ([]*model.UserRole, error) {
|
||||||
|
q := `
|
||||||
|
SELECT id, role_id, user_id, creation_date, last_update_date
|
||||||
|
FROM mangezmieux.user_role r
|
||||||
|
WHERE r.user_id = $1
|
||||||
|
`
|
||||||
|
rows, err := sqlDAO.client.Query(q, userId.String())
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return make([]*model.UserRole, 0), nil
|
||||||
|
}
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rows.Err() != nil {
|
||||||
|
return nil, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
userRoles := make([]*model.UserRole, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
userRole := &model.UserRole{}
|
||||||
|
err := rows.Scan(&userRole.Id, &userRole.RoleId, &userRole.UserId, &userRole.CreationDate, &userRole.LastUpdateDate)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
userRoles = append(userRoles, userRole)
|
||||||
|
}
|
||||||
|
return userRoles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) GetUserRoleByRole(role uuid.UUID) ([]*model.UserRole, error) {
|
||||||
|
q := `
|
||||||
|
SELECT id, role_id, user_id, creation_date, last_update_date
|
||||||
|
FROM mangezmieux.user_role r
|
||||||
|
WHERE r.role_id = $1
|
||||||
|
`
|
||||||
|
rows, err := sqlDAO.client.Query(q, role.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if rows.Err() != nil {
|
||||||
|
return nil, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
userRoles := make([]*model.UserRole, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
userRole := &model.UserRole{}
|
||||||
|
err := rows.Scan(&userRole.Id, &userRole.RoleId, &userRole.UserId, &userRole.CreationDate, &userRole.LastUpdateDate)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
userRoles = append(userRoles, userRole)
|
||||||
|
}
|
||||||
|
return userRoles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) AddUserRole(userId, roleId uuid.UUID, metadata model2.Metadata) (*model.UserRole, error) {
|
||||||
|
var Id uuid.UUID
|
||||||
|
q := `
|
||||||
|
INSERT INTO mangezmieux.user_role
|
||||||
|
(user_id, role_id, creation_date, creation_user)
|
||||||
|
VALUES
|
||||||
|
($1,$2,$3,$4)
|
||||||
|
RETURNING
|
||||||
|
Id`
|
||||||
|
|
||||||
|
err := sqlDAO.client.QueryRow(q, userId.String(), roleId.String(), metadata.CreationDate, metadata.CreationUser).Scan(&Id)
|
||||||
|
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
|
||||||
|
userRole, err := sqlDAO.GetUserRole(Id)
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
|
||||||
|
return userRole, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO dao) DeleteUserRole(id uuid.UUID) error {
|
||||||
|
q := `
|
||||||
|
DELETE FROM mangezmieux.user_role
|
||||||
|
WHERE Id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
_, err := sqlDAO.client.Exec(q, id.String())
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
8
mangezmieux-backend/internal/ginserver/headers.go
Executable file
8
mangezmieux-backend/internal/ginserver/headers.go
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
package ginserver
|
||||||
|
|
||||||
|
const (
|
||||||
|
HeaderNameContentType = "content-type"
|
||||||
|
HeaderNameCorrelationID = "correlationID"
|
||||||
|
|
||||||
|
HeaderValueApplicationJSONUTF8 = "application/json; charset=UTF-8"
|
||||||
|
)
|
||||||
70
mangezmieux-backend/internal/ginserver/logger.go
Normal file
70
mangezmieux-backend/internal/ginserver/logger.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package ginserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mangezmieux-backend/internal/logger"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
letterIdxBits = 6 // 6 bits to represent a letter index
|
||||||
|
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
|
||||||
|
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
|
||||||
|
)
|
||||||
|
|
||||||
|
var src = rand.NewSource(time.Now().UnixNano())
|
||||||
|
|
||||||
|
func randStringBytesMaskImprSrc(n int) string {
|
||||||
|
b := make([]byte, n)
|
||||||
|
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
|
||||||
|
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
|
||||||
|
if remain == 0 {
|
||||||
|
cache, remain = src.Int63(), letterIdxMax
|
||||||
|
}
|
||||||
|
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
|
||||||
|
b[i] = letterBytes[idx]
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
cache >>= letterIdxBits
|
||||||
|
remain--
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLoggerMiddleware() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
correlationID := c.Request.Header.Get(HeaderNameCorrelationID)
|
||||||
|
if correlationID == "" {
|
||||||
|
correlationID = randStringBytesMaskImprSrc(30)
|
||||||
|
c.Writer.Header().Set(HeaderNameCorrelationID, correlationID)
|
||||||
|
}
|
||||||
|
|
||||||
|
logEntry := logger.GetLogger().WithField(HeaderNameCorrelationID, correlationID)
|
||||||
|
|
||||||
|
c.Set(logger.ContextKeyLogger, logEntry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHTTPLoggerMiddleware() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
logger.GetLoggerFromCtx(c).
|
||||||
|
WithField("method", c.Request.Method).
|
||||||
|
WithField("url", c.Request.RequestURI).
|
||||||
|
WithField("from", c.ClientIP()).
|
||||||
|
Info("start handling HTTP request")
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
d := time.Since(start)
|
||||||
|
|
||||||
|
logger.GetLoggerFromCtx(c).
|
||||||
|
WithField("status", c.Writer.Status()).
|
||||||
|
WithField("duration", d.String()).
|
||||||
|
Info("end handling HTTP request")
|
||||||
|
}
|
||||||
|
}
|
||||||
45
mangezmieux-backend/internal/ginserver/setup.go
Normal file
45
mangezmieux-backend/internal/ginserver/setup.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package ginserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mangezmieux-backend/internal/injector"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/cors"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
routerInjectorKey = "ROUTER"
|
||||||
|
|
||||||
|
SecuredRouterInjectorKey = "SECURED_ROUTER"
|
||||||
|
UnsecuredRouterInjectorKey = "UNSECURED_ROUTER"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Setup(inj *injector.Injector) {
|
||||||
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
|
||||||
|
router := gin.New()
|
||||||
|
router.HandleMethodNotAllowed = true
|
||||||
|
|
||||||
|
router.Use(cors.New(cors.Config{
|
||||||
|
AllowOrigins: []string{"http://localhost:3000/", "http://localhost:3000"},
|
||||||
|
AllowMethods: []string{"*"},
|
||||||
|
AllowHeaders: []string{"*"},
|
||||||
|
ExposeHeaders: []string{"*"},
|
||||||
|
AllowCredentials: true,
|
||||||
|
MaxAge: 12 * time.Hour,
|
||||||
|
}))
|
||||||
|
router.Use(gin.Recovery())
|
||||||
|
router.Use(GetLoggerMiddleware())
|
||||||
|
router.Use(GetHTTPLoggerMiddleware())
|
||||||
|
public := router.Group("")
|
||||||
|
inj.Set(UnsecuredRouterInjectorKey, public)
|
||||||
|
|
||||||
|
authMiddleware := injector.Get[gin.HandlerFunc](inj, "AuthenticationMiddleware")
|
||||||
|
|
||||||
|
securedUserRoute := public.Group("/api/v1")
|
||||||
|
securedUserRoute.Use(authMiddleware)
|
||||||
|
|
||||||
|
inj.Set(SecuredRouterInjectorKey, securedUserRoute)
|
||||||
|
inj.Set(routerInjectorKey, router)
|
||||||
|
}
|
||||||
41
mangezmieux-backend/internal/ginserver/start.go
Normal file
41
mangezmieux-backend/internal/ginserver/start.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package ginserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"mangezmieux-backend/internal/injector"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Start(inj *injector.Injector, port string) {
|
||||||
|
router := injector.Get[*gin.Engine](inj, routerInjectorKey)
|
||||||
|
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: ":" + port,
|
||||||
|
Handler: router,
|
||||||
|
ReadHeaderTimeout: 4 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// service connections
|
||||||
|
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
log.Fatalf("listen: %s\n", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for interrupt signal to gracefully shutdown the server with
|
||||||
|
// a timeout of 5 seconds.
|
||||||
|
quit := make(chan os.Signal, 1)
|
||||||
|
// kill (no param) default send syscanll.SIGTERM
|
||||||
|
// kill -2 is syscall.SIGINT
|
||||||
|
// kill -9 is syscall. SIGKILL but can"t be catch, so don't need add it
|
||||||
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
<-quit
|
||||||
|
log.Println("Shutdown Server ...")
|
||||||
|
}
|
||||||
18
mangezmieux-backend/internal/godog/model.go
Normal file
18
mangezmieux-backend/internal/godog/model.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package godog
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
type TestCenter interface {
|
||||||
|
GetFunctionalityContext() any
|
||||||
|
}
|
||||||
|
type genericTestCenter struct {
|
||||||
|
testCenter TestCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResourceHandler interface {
|
||||||
|
Create(ctx context.Context, val string) (context.Context, error)
|
||||||
|
Read(ctx context.Context, val string) (context.Context, error)
|
||||||
|
Update(ctx context.Context, val string) (context.Context, error)
|
||||||
|
Patch(ctx context.Context, val string) (context.Context, error)
|
||||||
|
Delete(ctx context.Context, val string) (context.Context, error)
|
||||||
|
}
|
||||||
23
mangezmieux-backend/internal/godog/resources.go
Normal file
23
mangezmieux-backend/internal/godog/resources.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package godog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var resourcesHandler = map[string]ResourceHandler{}
|
||||||
|
|
||||||
|
func RegisterResourceHandler(resourceName string, handler ResourceHandler) {
|
||||||
|
if _, ok := resourcesHandler[resourceName]; ok {
|
||||||
|
log.Fatalf("Can't add an already existing handler for resource %s", resourceName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resourcesHandler[resourceName] = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetResourceHandler(resourceName string) ResourceHandler {
|
||||||
|
if val, ok := resourcesHandler[resourceName]; ok {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("no handler found for resource type %s", resourceName))
|
||||||
|
}
|
||||||
92
mangezmieux-backend/internal/godog/sentence.go
Normal file
92
mangezmieux-backend/internal/godog/sentence.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package godog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/cucumber/godog"
|
||||||
|
"github.com/ohler55/ojg/jp"
|
||||||
|
"net/http/httptest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (gtc *genericTestCenter) theResourceIsCreated(ctx context.Context) (context.Context, error) {
|
||||||
|
return gtc.theResponseHasStatus(ctx, 201)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gtc *genericTestCenter) theResourceAlreadyExists(ctx context.Context) (context.Context, error) {
|
||||||
|
return gtc.theResponseHasStatus(ctx, 409)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gtc *genericTestCenter) theUserMissRight(ctx context.Context) (context.Context, error) {
|
||||||
|
return gtc.theResponseHasStatus(ctx, 403)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gtc *genericTestCenter) badRequest(ctx context.Context) (context.Context, error) {
|
||||||
|
return gtc.theResponseHasStatus(ctx, 400)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gtc *genericTestCenter) forbiddenRequest(ctx context.Context) (context.Context, error) {
|
||||||
|
return gtc.theResponseHasStatus(ctx, 403)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gtc *genericTestCenter) theResponseHasStatus(ctx context.Context, status int) (context.Context, error) {
|
||||||
|
httpRecorder := ctx.Value("recorder").(*httptest.ResponseRecorder)
|
||||||
|
if httpRecorder.Code != status {
|
||||||
|
return ctx, fmt.Errorf("got code %d with body %s", httpRecorder.Code, httpRecorder.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, "OBJECT_RESPONSE", httpRecorder.Body.String())
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gtc *genericTestCenter) theFieldHasValue(ctx context.Context, fieldName, value string) (context.Context, error) {
|
||||||
|
compiledPath, err := jp.ParseString(fieldName)
|
||||||
|
body := ctx.Value("OBJECT_RESPONSE").(string)
|
||||||
|
if err != nil {
|
||||||
|
return ctx, err
|
||||||
|
}
|
||||||
|
var resourceAsMap interface{}
|
||||||
|
err = json.Unmarshal([]byte(body), &resourceAsMap)
|
||||||
|
if err != nil {
|
||||||
|
return ctx, err
|
||||||
|
}
|
||||||
|
|
||||||
|
datas := compiledPath.Get(resourceAsMap)
|
||||||
|
if len(datas) != 1 {
|
||||||
|
return ctx, fmt.Errorf("Found %v data. Expected only one", len(datas))
|
||||||
|
}
|
||||||
|
if datas[0] != value {
|
||||||
|
return ctx, fmt.Errorf("The field %s has value %s . Expected %s", fieldName, datas[0], value)
|
||||||
|
}
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gtc *genericTestCenter) resourceDoesntExist(ctx context.Context, resourceType string, resourceName string) (context.Context, error) {
|
||||||
|
handler := GetResourceHandler(resourceType)
|
||||||
|
ctx, _ = handler.Delete(ctx, resourceName)
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gtc *genericTestCenter) theUserCreatesAResourcesWithTheFollowingData(ctx context.Context, resourceType string, content *godog.DocString) (context.Context, error) {
|
||||||
|
handler := GetResourceHandler(resourceType)
|
||||||
|
ctx, _ = handler.Create(ctx, content.Content)
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gtc *genericTestCenter) theUserPatchesAResourcesWithTheFollowingData(ctx context.Context, resourceType string, content *godog.DocString) (context.Context, error) {
|
||||||
|
handler := GetResourceHandler(resourceType)
|
||||||
|
ctx, _ = handler.Patch(ctx, content.Content)
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gtc *genericTestCenter) theUserUpdatesAResourcesWithTheFollowingData(ctx context.Context, resourceType string, content *godog.DocString) (context.Context, error) {
|
||||||
|
handler := GetResourceHandler(resourceType)
|
||||||
|
ctx, _ = handler.Update(ctx, content.Content)
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gtc *genericTestCenter) theResourceExistWithTheFollowingData(ctx context.Context, resourceType string, content *godog.DocString) (context.Context, error) {
|
||||||
|
handler := GetResourceHandler(resourceType)
|
||||||
|
ctx, _ = handler.Create(ctx, content.Content)
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
20
mangezmieux-backend/internal/godog/setup.go
Normal file
20
mangezmieux-backend/internal/godog/setup.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package godog
|
||||||
|
|
||||||
|
import cucumber "github.com/cucumber/godog"
|
||||||
|
|
||||||
|
func Setup(ctx *cucumber.ScenarioContext, testCenter TestCenter) {
|
||||||
|
gtc := genericTestCenter{testCenter: testCenter}
|
||||||
|
ctx.Step(`^the resource is created`, gtc.theResourceIsCreated)
|
||||||
|
ctx.Step(`^the response indicates that the ([^\s]+) already exists`, gtc.theResourceAlreadyExists)
|
||||||
|
ctx.Step(`^the response indicates that the user doesn't have right`, gtc.theUserMissRight)
|
||||||
|
ctx.Step(`^the response indicates that this a bad request`, gtc.badRequest)
|
||||||
|
ctx.Step(`^the response indicates that this a forbidden request`, gtc.forbiddenRequest)
|
||||||
|
ctx.Step(`^the response has status (\d+)$`, gtc.theResponseHasStatus)
|
||||||
|
ctx.Step(`^the ([^\s]+) ([^\s]+) doesn\'t exist yet`, gtc.resourceDoesntExist)
|
||||||
|
ctx.Step(`^the field ([^\s]+) is (\d+)$`, gtc.theFieldHasValue)
|
||||||
|
ctx.Step(`^the field ([^\s]+) has value "([^"]*)"$`, gtc.theFieldHasValue)
|
||||||
|
ctx.Step(`^the user create a[n]* ([^\s]+) with the following data:$`, gtc.theUserCreatesAResourcesWithTheFollowingData)
|
||||||
|
ctx.Step(`^the user update a[n]* ([^\s]+) with the following data:$`, gtc.theUserUpdatesAResourcesWithTheFollowingData)
|
||||||
|
ctx.Step(`^the user patch a[n]* ([^\s]+) with the following data:$`, gtc.theUserUpdatesAResourcesWithTheFollowingData)
|
||||||
|
ctx.Step(`^the ([^\s]+) exists with the following data:$`, gtc.theResourceExistWithTheFollowingData)
|
||||||
|
}
|
||||||
27
mangezmieux-backend/internal/health/handler.go
Normal file
27
mangezmieux-backend/internal/health/handler.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package health
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @openapi:path
|
||||||
|
// /_health:
|
||||||
|
//
|
||||||
|
// get:
|
||||||
|
// tags:
|
||||||
|
// - "Monitoring"
|
||||||
|
// summary: Health check
|
||||||
|
// description: Health check
|
||||||
|
// responses:
|
||||||
|
// 200:
|
||||||
|
// description: "Health response"
|
||||||
|
// content:
|
||||||
|
// application/json:
|
||||||
|
// schema:
|
||||||
|
// $ref: "#/components/schemas/Health"
|
||||||
|
func GetHealth(c *gin.Context) {
|
||||||
|
health := &Health{Alive: true}
|
||||||
|
c.JSON(http.StatusOK, health)
|
||||||
|
}
|
||||||
8
mangezmieux-backend/internal/health/model.go
Normal file
8
mangezmieux-backend/internal/health/model.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package health
|
||||||
|
|
||||||
|
// Health struct
|
||||||
|
// @openapi:schema.
|
||||||
|
type Health struct {
|
||||||
|
Alive bool `json:"alive"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
15
mangezmieux-backend/internal/health/setup.go
Normal file
15
mangezmieux-backend/internal/health/setup.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package health
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mangezmieux-backend/internal/ginserver"
|
||||||
|
"mangezmieux-backend/internal/injector"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Setup(inj *injector.Injector) {
|
||||||
|
publicRoute := injector.Get[*gin.RouterGroup](inj, ginserver.UnsecuredRouterInjectorKey)
|
||||||
|
|
||||||
|
publicRoute.Handle(http.MethodGet, "/health", GetHealth)
|
||||||
|
}
|
||||||
42
mangezmieux-backend/internal/injector/injector.go
Normal file
42
mangezmieux-backend/internal/injector/injector.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package injector
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Injector struct {
|
||||||
|
content map[string]any
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Injector) Get(key string) any {
|
||||||
|
val, ok := i.content[key]
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("Can't get key %s from injector", key))
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Injector) GetWithDefault(key string, defaultValue any) any {
|
||||||
|
val, ok := i.content[key]
|
||||||
|
if !ok {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
func Get[T any](i *Injector, key string) T {
|
||||||
|
return i.Get(key).(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWithDefault[T any](i *Injector, key string, defaultValue any) T {
|
||||||
|
return i.GetWithDefault(key, defaultValue).(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Injector) Set(key string, content any) {
|
||||||
|
if i.content == nil {
|
||||||
|
i.content = map[string]any{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := i.content[key]; ok {
|
||||||
|
panic(fmt.Sprintf("Key %s already have content", key))
|
||||||
|
}
|
||||||
|
i.content[key] = content
|
||||||
|
}
|
||||||
78
mangezmieux-backend/internal/jwt/service.go
Normal file
78
mangezmieux-backend/internal/jwt/service.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package jwt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mangezmieux-backend/internal/responses"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
jwtLib "github.com/golang-jwt/jwt/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
SecretKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService() *Service {
|
||||||
|
return &Service{SecretKey: "hard-coded-temp"}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Claims struct {
|
||||||
|
ID string `json:"username"`
|
||||||
|
jwtLib.RegisteredClaims
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ValidateToken(token string) (*Claims, error) {
|
||||||
|
claims := &Claims{}
|
||||||
|
tkn, err := jwtLib.ParseWithClaims(token, claims, func(token *jwtLib.Token) (any, error) {
|
||||||
|
return []byte(s.SecretKey), nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tkn.Valid {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return claims, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GenerateJWTToken(userId string) (string, error) {
|
||||||
|
expirationTime := time.Now().Add(10 * time.Minute)
|
||||||
|
|
||||||
|
claims := &Claims{
|
||||||
|
ID: userId,
|
||||||
|
RegisteredClaims: jwtLib.RegisteredClaims{
|
||||||
|
// In JWT, the expiry time is expressed as unix milliseconds
|
||||||
|
ExpiresAt: jwtLib.NewNumericDate(expirationTime),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
token := jwtLib.NewWithClaims(jwtLib.SigningMethodHS256, claims)
|
||||||
|
// Create the JWT string
|
||||||
|
tokenString, err := token.SignedString([]byte(s.SecretKey))
|
||||||
|
if err != nil {
|
||||||
|
return "", &responses.ErrInternalServer
|
||||||
|
}
|
||||||
|
return tokenString, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Refresh(oldToken string) (string, error) {
|
||||||
|
claims := &Claims{}
|
||||||
|
tkn, err := jwtLib.ParseWithClaims(oldToken, claims, func(token *jwtLib.Token) (any, error) {
|
||||||
|
return []byte(s.SecretKey), nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tkn.Valid {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, create a new token for the current use, with a renewed expiration time
|
||||||
|
expirationTime := time.Now().Add(10 * time.Minute)
|
||||||
|
claims.ExpiresAt = jwtLib.NewNumericDate(expirationTime)
|
||||||
|
token := jwtLib.NewWithClaims(jwtLib.SigningMethodHS256, claims)
|
||||||
|
tokenString, err := token.SignedString([]byte(s.SecretKey))
|
||||||
|
return tokenString, nil
|
||||||
|
|
||||||
|
}
|
||||||
16
mangezmieux-backend/internal/jwt/setup.go
Normal file
16
mangezmieux-backend/internal/jwt/setup.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package jwt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mangezmieux-backend/internal/injector"
|
||||||
|
)
|
||||||
|
|
||||||
|
const JWTKey = "JWT"
|
||||||
|
|
||||||
|
func Setup(inj *injector.Injector) {
|
||||||
|
|
||||||
|
// build components
|
||||||
|
service := NewService()
|
||||||
|
|
||||||
|
// register provided components
|
||||||
|
inj.Set(JWTKey, service)
|
||||||
|
}
|
||||||
74
mangezmieux-backend/internal/logger/logger.go
Executable file
74
mangezmieux-backend/internal/logger/logger.go
Executable file
@@ -0,0 +1,74 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LogFormatText = "text"
|
||||||
|
LogFormatJSON = "json"
|
||||||
|
|
||||||
|
ContextKeyLogger = "logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
logLevel = logrus.DebugLevel
|
||||||
|
logFormat logrus.Formatter = &logrus.TextFormatter{}
|
||||||
|
logOut io.Writer
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitLogger(ll, lf string) {
|
||||||
|
logLevel = parseLogrusLevel(ll)
|
||||||
|
logrus.SetLevel(logLevel)
|
||||||
|
|
||||||
|
logFormat = parseLogrusFormat(lf)
|
||||||
|
logrus.SetFormatter(logFormat)
|
||||||
|
|
||||||
|
logOut = os.Stdout
|
||||||
|
logrus.SetOutput(logOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLoggerFromCtx(c *gin.Context) *logrus.Entry {
|
||||||
|
if logger, ok := c.Get(ContextKeyLogger); ok {
|
||||||
|
logEntry, assertionOk := logger.(*logrus.Entry)
|
||||||
|
if assertionOk {
|
||||||
|
return logEntry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return logrus.NewEntry(GetLogger())
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLogger() *logrus.Logger {
|
||||||
|
logger := logrus.New()
|
||||||
|
logger.Formatter = logFormat
|
||||||
|
logger.Level = logLevel
|
||||||
|
logger.Out = logOut
|
||||||
|
return logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLogrusLevel(logLevelStr string) logrus.Level {
|
||||||
|
logLevel, err := logrus.ParseLevel(logLevelStr)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Errorf("error while parsing log level. %v is set as default.", logLevel)
|
||||||
|
logLevel = logrus.DebugLevel
|
||||||
|
}
|
||||||
|
return logLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLogrusFormat(logFormatStr string) logrus.Formatter {
|
||||||
|
var formatter logrus.Formatter
|
||||||
|
switch logFormatStr {
|
||||||
|
case LogFormatText:
|
||||||
|
formatter = &logrus.TextFormatter{ForceColors: true, FullTimestamp: true}
|
||||||
|
case LogFormatJSON:
|
||||||
|
formatter = &logrus.JSONFormatter{}
|
||||||
|
default:
|
||||||
|
logrus.Errorf("error while parsing log format. %v is set as default.", formatter)
|
||||||
|
formatter = &logrus.TextFormatter{ForceColors: true, FullTimestamp: true}
|
||||||
|
}
|
||||||
|
return formatter
|
||||||
|
}
|
||||||
102
mangezmieux-backend/internal/middleware/introspect.go
Normal file
102
mangezmieux-backend/internal/middleware/introspect.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
model2 "mangezmieux-backend/internal/acl/model"
|
||||||
|
"mangezmieux-backend/internal/jwt"
|
||||||
|
"mangezmieux-backend/internal/logger"
|
||||||
|
"mangezmieux-backend/internal/responses"
|
||||||
|
"mangezmieux-backend/internal/users/model"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
const CtxUser = "user"
|
||||||
|
const CtxUserRight = "userRight"
|
||||||
|
const CtxRole = "role"
|
||||||
|
const CtxToken = "token"
|
||||||
|
|
||||||
|
type IntrospectService interface {
|
||||||
|
Introspect(token string) (user *model.User, err error)
|
||||||
|
GetRole(ctx context.Context, user *model.User) (userRight *model2.UserRight, err error)
|
||||||
|
GetAllRole(ctx context.Context) ([]*model2.Role, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAuthenticationMiddleware(introspectService IntrospectService, jwtService *jwt.Service) gin.HandlerFunc {
|
||||||
|
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
token, err := getTokenFromGinCtx(c)
|
||||||
|
c.Set(CtxToken, token)
|
||||||
|
|
||||||
|
ctx := c.Request.Context()
|
||||||
|
ctx = context.WithValue(ctx, CtxToken, token)
|
||||||
|
if err != nil {
|
||||||
|
logger.GetLogger().WithError(err).Debug("no token found")
|
||||||
|
responses.JSONErrorWithMessage(c.Writer, responses.ErrBadRequestFormat, err.Error())
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = jwtService.ValidateToken(token)
|
||||||
|
if err != nil {
|
||||||
|
logger.GetLogger().WithError(err).Debug("error during token validation")
|
||||||
|
responses.JSONErrorWithMessage(c.Writer, responses.ErrBadRequestFormat, err.Error())
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user, err := introspectService.Introspect(token)
|
||||||
|
if err != nil {
|
||||||
|
logger.GetLogger().WithError(err).Debug("error during introspect")
|
||||||
|
responses.JSONErrorWithMessage(c.Writer, responses.ErrBadRequestFormat, err.Error())
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Set(CtxUser, user)
|
||||||
|
|
||||||
|
userRight, err := introspectService.GetRole(ctx, user)
|
||||||
|
if err != nil {
|
||||||
|
logger.GetLogger().WithError(err).Debug("error during getting role for user")
|
||||||
|
responses.JSONErrorWithMessage(c.Writer, responses.ErrBadRequestFormat, err.Error())
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Set(CtxUserRight, userRight)
|
||||||
|
|
||||||
|
roles, err := introspectService.GetAllRole(ctx)
|
||||||
|
if err != nil {
|
||||||
|
logger.GetLogger().WithError(err).Debug("error during getting role map")
|
||||||
|
responses.JSONErrorWithMessage(c.Writer, responses.ErrBadRequestFormat, err.Error())
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Set(CtxRole, roles)
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTokenFromGinCtx allow to get the access token of the request in the Authorization request header.
|
||||||
|
// It will split the header and remove the Bearer part to extract only the token.
|
||||||
|
func getTokenFromGinCtx(c *gin.Context) (string, error) {
|
||||||
|
auth := c.GetHeader("Authorization")
|
||||||
|
if auth != "" {
|
||||||
|
authSplitted := strings.SplitN(auth, " ", 2)
|
||||||
|
if len(authSplitted) != 2 {
|
||||||
|
return "", errors.New("malformed authorization header")
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.ToUpper(authSplitted[0]) != strings.ToUpper("Bearer") && strings.ToUpper(authSplitted[0]) != strings.ToUpper("JWT") {
|
||||||
|
return "", errors.New("unsupported authentication scheme")
|
||||||
|
}
|
||||||
|
|
||||||
|
return authSplitted[1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if cookie, err := c.Cookie("token"); err == nil {
|
||||||
|
return cookie, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("no token found in the request")
|
||||||
|
}
|
||||||
16
mangezmieux-backend/internal/middleware/setup.go
Normal file
16
mangezmieux-backend/internal/middleware/setup.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mangezmieux-backend/internal/injector"
|
||||||
|
"mangezmieux-backend/internal/jwt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const AuthenticationMiddlewareKey = "AuthenticationMiddleware"
|
||||||
|
const IntrospectServiceKey = "AuthCli"
|
||||||
|
|
||||||
|
func Setup(inj *injector.Injector) {
|
||||||
|
|
||||||
|
jwtService := injector.Get[*jwt.Service](inj, jwt.JWTKey)
|
||||||
|
introspectService := injector.Get[IntrospectService](inj, IntrospectServiceKey)
|
||||||
|
inj.Set(AuthenticationMiddlewareKey, GetAuthenticationMiddleware(introspectService, jwtService))
|
||||||
|
}
|
||||||
10
mangezmieux-backend/internal/model/metadata.go
Normal file
10
mangezmieux-backend/internal/model/metadata.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Metadata struct {
|
||||||
|
CreationDate time.Time `json:"creation_date"`
|
||||||
|
LastUpdateDate time.Time `json:"last_update_date"`
|
||||||
|
CreationUser string `json:"creation_user"`
|
||||||
|
LastUpdateUser string `json:"last_update_user"`
|
||||||
|
}
|
||||||
32
mangezmieux-backend/internal/postgres/database_error.go
Executable file
32
mangezmieux-backend/internal/postgres/database_error.go
Executable file
@@ -0,0 +1,32 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Type int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrTypeNotFound Type = iota
|
||||||
|
ErrTypeDuplicate
|
||||||
|
ErrTypeForeignKeyViolation
|
||||||
|
)
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
Cause error
|
||||||
|
Type Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDAOError(t Type, cause error) error {
|
||||||
|
return &Error{
|
||||||
|
Type: t,
|
||||||
|
Cause: cause,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
if e.Cause != nil {
|
||||||
|
return fmt.Sprintf("Type %d: %s", e.Type, e.Cause.Error())
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Type %d: no cause given", e.Type)
|
||||||
|
}
|
||||||
35
mangezmieux-backend/internal/postgres/database_postgresql.go
Normal file
35
mangezmieux-backend/internal/postgres/database_postgresql.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"github.com/lib/pq"
|
||||||
|
"mangezmieux-backend/internal/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
pgCodeUniqueViolation = "23505"
|
||||||
|
pgCodeForeingKeyViolation = "23503"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandlePgError(e *pq.Error) error {
|
||||||
|
if e.Code == pgCodeUniqueViolation {
|
||||||
|
return NewDAOError(ErrTypeDuplicate, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Code == pgCodeForeingKeyViolation {
|
||||||
|
return NewDAOError(ErrTypeForeignKeyViolation, e)
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDatabasePostgreSQL(connectionURI string) *sql.DB {
|
||||||
|
db, err := sql.Open("postgres", connectionURI)
|
||||||
|
if err != nil {
|
||||||
|
logger.GetLogger().WithError(err).Fatal("Unable to get a connection to the postgres db")
|
||||||
|
}
|
||||||
|
err = db.Ping()
|
||||||
|
if err != nil {
|
||||||
|
logger.GetLogger().WithError(err).Fatal("Unable to ping the postgres db")
|
||||||
|
}
|
||||||
|
return db
|
||||||
|
}
|
||||||
11
mangezmieux-backend/internal/postgres/setup.go
Normal file
11
mangezmieux-backend/internal/postgres/setup.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import "mangezmieux-backend/internal/injector"
|
||||||
|
|
||||||
|
const DatabaseKey = "POSTGRES"
|
||||||
|
|
||||||
|
func Setup(inj *injector.Injector, connectionURI string) {
|
||||||
|
client := NewDatabasePostgreSQL(connectionURI)
|
||||||
|
|
||||||
|
inj.Set(DatabaseKey, client)
|
||||||
|
}
|
||||||
62
mangezmieux-backend/internal/responses/error.go
Normal file
62
mangezmieux-backend/internal/responses/error.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package responses
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrBadRequestFormat = APIError{
|
||||||
|
Type: "bad_format",
|
||||||
|
HTTPCode: http.StatusBadRequest,
|
||||||
|
Description: "unable to read request body, please check that the json is valid",
|
||||||
|
}
|
||||||
|
ErrDataValidation = APIError{
|
||||||
|
Type: "data_validation",
|
||||||
|
HTTPCode: http.StatusBadRequest,
|
||||||
|
Description: "the data are not valid",
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrNotFound = APIError{
|
||||||
|
Type: "not_found",
|
||||||
|
HTTPCode: http.StatusNotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrAlreadyExists = APIError{
|
||||||
|
Type: "already_exists",
|
||||||
|
HTTPCode: http.StatusConflict,
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrUnauthorized = APIError{
|
||||||
|
Type: "unauthorized",
|
||||||
|
HTTPCode: http.StatusUnauthorized,
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrForbidden = APIError{
|
||||||
|
Type: "forbidden",
|
||||||
|
HTTPCode: http.StatusForbidden,
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrInternalServer = APIError{
|
||||||
|
Type: "internal_server_error",
|
||||||
|
HTTPCode: http.StatusInternalServerError,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type APIError struct {
|
||||||
|
HTTPCode int `json:"-"`
|
||||||
|
Type string `json:"error"`
|
||||||
|
Description string `json:"errorDescription"`
|
||||||
|
Details []FieldError `json:"errorDetails,omitempty"`
|
||||||
|
Headers map[string][]string `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FieldError struct {
|
||||||
|
Field string `json:"field"`
|
||||||
|
Constraint string `json:"constraint"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *APIError) Error() string {
|
||||||
|
return fmt.Sprintf("error : %d, %s, %s, %v", e.HTTPCode, e.Type, e.Description, e.Details)
|
||||||
|
}
|
||||||
34
mangezmieux-backend/internal/responses/responses.go
Normal file
34
mangezmieux-backend/internal/responses/responses.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package responses
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"mangezmieux-backend/internal/ginserver"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func JSON(w http.ResponseWriter, status int, data interface{}) {
|
||||||
|
w.Header().Set(ginserver.HeaderNameContentType, ginserver.HeaderValueApplicationJSONUTF8)
|
||||||
|
w.WriteHeader(status)
|
||||||
|
if data != nil {
|
||||||
|
err := json.NewEncoder(w).Encode(data)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func JSONError(w http.ResponseWriter, e APIError) {
|
||||||
|
if e.Headers != nil {
|
||||||
|
for k, headers := range e.Headers {
|
||||||
|
for _, headerValue := range headers {
|
||||||
|
w.Header().Add(k, headerValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON(w, e.HTTPCode, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func JSONErrorWithMessage(w http.ResponseWriter, e APIError, message string) {
|
||||||
|
e.Description = message
|
||||||
|
JSONError(w, e)
|
||||||
|
}
|
||||||
123
mangezmieux-backend/internal/users/handler.go
Normal file
123
mangezmieux-backend/internal/users/handler.go
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package users
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
"mangezmieux-backend/internal/logger"
|
||||||
|
"mangezmieux-backend/internal/middleware"
|
||||||
|
"mangezmieux-backend/internal/responses"
|
||||||
|
"mangezmieux-backend/internal/users/model"
|
||||||
|
"mangezmieux-backend/internal/users/service"
|
||||||
|
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
coreValidator "mangezmieux-backend/internal/validator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handler struct {
|
||||||
|
Service *service.Service
|
||||||
|
Validator *validator.Validate
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandler(service *service.Service, validator *validator.Validate) *Handler {
|
||||||
|
return &Handler{
|
||||||
|
Service: service,
|
||||||
|
Validator: validator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) CreateUser(context *gin.Context) {
|
||||||
|
userEditable := model.UserEditable{}
|
||||||
|
if err := context.BindJSON(&userEditable); err != nil {
|
||||||
|
responses.JSONError(context.Writer, coreValidator.NewDataValidationAPIError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user, err := h.Service.CreateUser(&userEditable)
|
||||||
|
if err != nil {
|
||||||
|
logger.GetLogger().Error(err)
|
||||||
|
var apiError *responses.APIError
|
||||||
|
if errors.As(err, &apiError) {
|
||||||
|
responses.JSONError(context.Writer, *apiError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
responses.JSONErrorWithMessage(context.Writer, responses.ErrInternalServer, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
responses.JSON(context.Writer, http.StatusCreated, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) Login(context *gin.Context) {
|
||||||
|
userLoginRequest := model.UserLoginRequest{}
|
||||||
|
if err := context.BindJSON(&userLoginRequest); err != nil {
|
||||||
|
responses.JSONError(context.Writer, coreValidator.NewDataValidationAPIError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
token, err := h.Service.Login(userLoginRequest)
|
||||||
|
if err != nil {
|
||||||
|
logger.GetLogger().Error(err)
|
||||||
|
var apiError *responses.APIError
|
||||||
|
if errors.As(err, &apiError) {
|
||||||
|
responses.JSONError(context.Writer, *apiError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
responses.JSONErrorWithMessage(context.Writer, responses.ErrInternalServer, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
context.SetCookie("token", token, 10, "/", "localhost", true, false)
|
||||||
|
userLoginResponse := model.UserLoginResponse{
|
||||||
|
AccessToken: token,
|
||||||
|
TokenType: "Bearer",
|
||||||
|
}
|
||||||
|
responses.JSON(context.Writer, http.StatusOK, userLoginResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) IntrospectToken(context *gin.Context) {
|
||||||
|
authorization := context.Request.Header.Get("Authorization")
|
||||||
|
splitToken := strings.Split(authorization, "Bearer ")
|
||||||
|
reqToken := splitToken[1]
|
||||||
|
user, err := h.Service.Introspect(reqToken)
|
||||||
|
if err != nil {
|
||||||
|
logger.GetLogger().Error(err)
|
||||||
|
var apiError *responses.APIError
|
||||||
|
if errors.As(err, &apiError) {
|
||||||
|
responses.JSONError(context.Writer, *apiError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
responses.JSONErrorWithMessage(context.Writer, responses.ErrInternalServer, err.Error())
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
responses.JSON(context.Writer, http.StatusOK, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) RefreshToken(context *gin.Context) {
|
||||||
|
authorization := context.Request.Header.Get("Authorization")
|
||||||
|
splitToken := strings.Split(authorization, "Bearer ")
|
||||||
|
reqToken := splitToken[1]
|
||||||
|
|
||||||
|
refreshedToken, err := h.Service.Refresh(reqToken)
|
||||||
|
if err != nil {
|
||||||
|
logger.GetLogger().Error(err)
|
||||||
|
var apiError *responses.APIError
|
||||||
|
if errors.As(err, &apiError) {
|
||||||
|
responses.JSONError(context.Writer, *apiError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
responses.JSONErrorWithMessage(context.Writer, responses.ErrInternalServer, err.Error())
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
context.SetCookie("token", refreshedToken, 10, "/", "localhost", true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) GetMe(context *gin.Context) {
|
||||||
|
usr, exists := context.Get(middleware.CtxUser)
|
||||||
|
if !exists {
|
||||||
|
responses.JSONErrorWithMessage(context.Writer, responses.ErrInternalServer, "User not found in context")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
responses.JSON(context.Writer, http.StatusOK, usr)
|
||||||
|
|
||||||
|
}
|
||||||
44
mangezmieux-backend/internal/users/middleware.go
Normal file
44
mangezmieux-backend/internal/users/middleware.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package users
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
model2 "mangezmieux-backend/internal/acl/model"
|
||||||
|
"mangezmieux-backend/internal/acl/service"
|
||||||
|
"mangezmieux-backend/internal/jwt"
|
||||||
|
"mangezmieux-backend/internal/middleware"
|
||||||
|
"mangezmieux-backend/internal/users/model"
|
||||||
|
service2 "mangezmieux-backend/internal/users/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
var AuthMiddleware = newMiddleware()
|
||||||
|
|
||||||
|
type internalAuthMiddleware struct {
|
||||||
|
Service *service2.Service
|
||||||
|
RoleService service.Service
|
||||||
|
UserService service.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMiddleware() *internalAuthMiddleware {
|
||||||
|
return &internalAuthMiddleware{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *internalAuthMiddleware) GinMiddleware(jwtService *jwt.Service) gin.HandlerFunc {
|
||||||
|
return middleware.GetAuthenticationMiddleware(m, jwtService)
|
||||||
|
}
|
||||||
|
|
||||||
|
// delegate useful for deferred binding (when the middleware is installed, GinMiddleware() is called, the service m.Service is not yet created :-( )
|
||||||
|
// see cmd/app.go for deferred binding at the end.
|
||||||
|
func (m *internalAuthMiddleware) Introspect(token string) (*model.User, error) {
|
||||||
|
return m.Service.Introspect(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// delegate useful for deferred binding (when the middleware is installed, GinMiddleware() is called, the service m.Service is not yet created :-( )
|
||||||
|
// see cmd/app.go for deferred binding at the end.
|
||||||
|
func (m *internalAuthMiddleware) GetRole(ctx context.Context, user *model.User) (*model2.UserRight, error) {
|
||||||
|
return m.RoleService.GetRoleForCurrentUser(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *internalAuthMiddleware) GetAllRole(ctx context.Context) ([]*model2.Role, error) {
|
||||||
|
return m.RoleService.GetAllRole()
|
||||||
|
}
|
||||||
31
mangezmieux-backend/internal/users/model/model.go
Normal file
31
mangezmieux-backend/internal/users/model/model.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gofrs/uuid"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID *uuid.UUID `json:"ID"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
UpdatedAt *time.Time `json:"updatedAt"`
|
||||||
|
UserEditable
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserEditable struct {
|
||||||
|
Firstname string `json:"first_name" binding:"required"`
|
||||||
|
Lastname string `json:"last_name" binding:"required"`
|
||||||
|
Email string `json:"email" binding:"required"`
|
||||||
|
Password string `json:"password" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserLoginRequest struct {
|
||||||
|
Email string `json:"email" binding:"required"`
|
||||||
|
Password string `json:"password" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserLoginResponse struct {
|
||||||
|
AccessToken string `json:"accessToken"`
|
||||||
|
TokenType string `json:"tokenType"`
|
||||||
|
ExpiresIn string `json:"expiresIn"`
|
||||||
|
}
|
||||||
98
mangezmieux-backend/internal/users/service/service.go
Normal file
98
mangezmieux-backend/internal/users/service/service.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mangezmieux-backend/internal/jwt"
|
||||||
|
"mangezmieux-backend/internal/responses"
|
||||||
|
"mangezmieux-backend/internal/users/model"
|
||||||
|
"mangezmieux-backend/internal/users/sql"
|
||||||
|
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
dao sql.Dao
|
||||||
|
jwt *jwt.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(dao sql.Dao, jwt *jwt.Service) *Service {
|
||||||
|
return &Service{dao: dao, jwt: jwt}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) CreateUser(userEditable *model.UserEditable) (*model.User, error) {
|
||||||
|
now := time.Now()
|
||||||
|
user := &model.User{
|
||||||
|
ID: nil,
|
||||||
|
CreatedAt: now,
|
||||||
|
UpdatedAt: &now,
|
||||||
|
UserEditable: model.UserEditable{
|
||||||
|
Firstname: userEditable.Firstname,
|
||||||
|
Lastname: userEditable.Lastname,
|
||||||
|
Email: userEditable.Email,
|
||||||
|
Password: userEditable.Password,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(userEditable.Password), 8)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &responses.ErrInternalServer
|
||||||
|
}
|
||||||
|
user.Password = string(hashedPassword)
|
||||||
|
err = s.dao.Create(user)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
user.Password = ""
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Login(request model.UserLoginRequest) (string, error) {
|
||||||
|
user, err := s.dao.FindByMail(request.Email)
|
||||||
|
if err != nil {
|
||||||
|
return "", &responses.ErrUnauthorized
|
||||||
|
}
|
||||||
|
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(request.Password))
|
||||||
|
if err != nil {
|
||||||
|
return "", &responses.ErrUnauthorized
|
||||||
|
}
|
||||||
|
return s.jwt.GenerateJWTToken(user.ID.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Introspect(token string) (*model.User, error) {
|
||||||
|
claims, err := s.jwt.ValidateToken(token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
usr, err := s.dao.FindByID(claims.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userModel := s.transformEntityToResponse(usr, false)
|
||||||
|
|
||||||
|
return userModel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Refresh(oldToken string) (string, error) {
|
||||||
|
return s.jwt.Refresh(oldToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) transformEntityToResponse(user *model.User, withPassword bool) *model.User {
|
||||||
|
password := ""
|
||||||
|
if withPassword {
|
||||||
|
password = user.Password
|
||||||
|
}
|
||||||
|
|
||||||
|
return &model.User{
|
||||||
|
ID: user.ID,
|
||||||
|
CreatedAt: user.CreatedAt,
|
||||||
|
UpdatedAt: user.UpdatedAt,
|
||||||
|
UserEditable: model.UserEditable{
|
||||||
|
Firstname: user.Firstname,
|
||||||
|
Lastname: user.Lastname,
|
||||||
|
Email: user.Email,
|
||||||
|
Password: password,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
42
mangezmieux-backend/internal/users/setup.go
Normal file
42
mangezmieux-backend/internal/users/setup.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package users
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"mangezmieux-backend/internal/ginserver"
|
||||||
|
"mangezmieux-backend/internal/injector"
|
||||||
|
"mangezmieux-backend/internal/jwt"
|
||||||
|
"mangezmieux-backend/internal/postgres"
|
||||||
|
service2 "mangezmieux-backend/internal/users/service"
|
||||||
|
sql2 "mangezmieux-backend/internal/users/sql"
|
||||||
|
"mangezmieux-backend/internal/validator"
|
||||||
|
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
validatorv10 "github.com/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ServiceKey = "UsersService"
|
||||||
|
|
||||||
|
func Setup(inj *injector.Injector) {
|
||||||
|
publicRoute := injector.Get[*gin.RouterGroup](inj, ginserver.UnsecuredRouterInjectorKey)
|
||||||
|
validatorCli := injector.Get[*validatorv10.Validate](inj, validator.ValidatorInjectorKey)
|
||||||
|
jwtService := injector.Get[*jwt.Service](inj, jwt.JWTKey)
|
||||||
|
|
||||||
|
client := injector.Get[*sql.DB](inj, postgres.DatabaseKey)
|
||||||
|
dao := sql2.NewDao(client)
|
||||||
|
|
||||||
|
service := service2.NewService(dao, jwtService)
|
||||||
|
handler := NewHandler(service, validatorCli)
|
||||||
|
|
||||||
|
inj.Set(ServiceKey, service)
|
||||||
|
|
||||||
|
publicRoute.Handle(http.MethodPost, "/api/v1/users", handler.CreateUser)
|
||||||
|
publicRoute.Handle(http.MethodPost, "/oauth2/token", handler.Login)
|
||||||
|
publicRoute.Handle(http.MethodPost, "/oauth2/introspect", handler.IntrospectToken)
|
||||||
|
publicRoute.Handle(http.MethodPost, "/oauth2/refresh", handler.RefreshToken)
|
||||||
|
|
||||||
|
securedRoute := injector.Get[*gin.RouterGroup](inj, ginserver.SecuredRouterInjectorKey)
|
||||||
|
securedRoute.Handle(http.MethodGet, "/users/me", handler.GetMe)
|
||||||
|
|
||||||
|
}
|
||||||
11
mangezmieux-backend/internal/users/sql/dao.go
Normal file
11
mangezmieux-backend/internal/users/sql/dao.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package sql
|
||||||
|
|
||||||
|
import "mangezmieux-backend/internal/users/model"
|
||||||
|
|
||||||
|
type Dao interface {
|
||||||
|
FindByMail(mail string) (*model.User, error)
|
||||||
|
Create(user *model.User) error
|
||||||
|
Delete(mail string) error
|
||||||
|
FindByMailAndPassword(mail string, password string) (*model.User, error)
|
||||||
|
FindByID(id string) (*model.User, error)
|
||||||
|
}
|
||||||
111
mangezmieux-backend/internal/users/sql/sql.go
Normal file
111
mangezmieux-backend/internal/users/sql/sql.go
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
package sql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"mangezmieux-backend/internal/postgres"
|
||||||
|
"mangezmieux-backend/internal/users/model"
|
||||||
|
|
||||||
|
"github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SQLDao struct {
|
||||||
|
client *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDao(client *sql.DB) Dao {
|
||||||
|
return &SQLDao{client: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO *SQLDao) FindByMailAndPassword(mail string, password string) (*model.User, error) {
|
||||||
|
q := `
|
||||||
|
SELECT u.ID, u.first_name, u.last_name, u.creation_date, u.last_update_date
|
||||||
|
FROM mangezmieux.user u
|
||||||
|
WHERE u.email = $1 AND u.password = $2
|
||||||
|
`
|
||||||
|
row := sqlDAO.client.QueryRow(q, mail, password)
|
||||||
|
|
||||||
|
u := model.User{}
|
||||||
|
err := row.Scan(&u.Email, &u.Firstname, &u.Lastname, &u.CreatedAt, &u.UpdatedAt)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
return &u, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO *SQLDao) FindByMail(mail string) (*model.User, error) {
|
||||||
|
q := `
|
||||||
|
SELECT u.ID, u.email, u.first_name, u.last_name, u.creation_date, u.last_update_date, u.password
|
||||||
|
FROM mangezmieux.user u
|
||||||
|
WHERE u.email = $1
|
||||||
|
`
|
||||||
|
row := sqlDAO.client.QueryRow(q, mail)
|
||||||
|
|
||||||
|
u := model.User{}
|
||||||
|
err := row.Scan(&u.ID, &u.Email, &u.Firstname, &u.Lastname, &u.CreatedAt, &u.UpdatedAt, &u.Password)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
return &u, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO *SQLDao) FindByID(id string) (*model.User, error) {
|
||||||
|
q := `
|
||||||
|
SELECT u.ID, u.email, u.first_name, u.last_name, u.creation_date, u.last_update_date
|
||||||
|
FROM mangezmieux.user u
|
||||||
|
WHERE u.id = $1
|
||||||
|
`
|
||||||
|
row := sqlDAO.client.QueryRow(q, id)
|
||||||
|
|
||||||
|
u := model.User{}
|
||||||
|
err := row.Scan(&u.ID, &u.Email, &u.Firstname, &u.Lastname, &u.CreatedAt, &u.UpdatedAt)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return nil, postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, postgres.NewDAOError(postgres.ErrTypeNotFound, err)
|
||||||
|
}
|
||||||
|
return &u, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO *SQLDao) Create(user *model.User) error {
|
||||||
|
q := `
|
||||||
|
INSERT INTO mangezmieux.user
|
||||||
|
(email, password, first_name, last_name, creation_date, last_update_date)
|
||||||
|
VALUES
|
||||||
|
($1, $2, $3, $4, $5, $6)
|
||||||
|
RETURNING id, creation_date
|
||||||
|
`
|
||||||
|
|
||||||
|
err := sqlDAO.client.
|
||||||
|
QueryRow(q, user.Email, user.Password, user.Firstname, user.Lastname, user.CreatedAt, user.UpdatedAt).
|
||||||
|
Scan(&user.ID, &user.CreatedAt)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sqlDAO *SQLDao) Delete(id string) error {
|
||||||
|
q := `
|
||||||
|
DELETE FROM mangezmieux.user
|
||||||
|
WHERE id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
_, err := sqlDAO.client.Exec(q, id)
|
||||||
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
|
return postgres.HandlePgError(errPq)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
51
mangezmieux-backend/internal/validator/error.go
Normal file
51
mangezmieux-backend/internal/validator/error.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package validator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"mangezmieux-backend/internal/logger"
|
||||||
|
"mangezmieux-backend/internal/responses"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
validatorLib "github.com/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
var regexpValidatorNamespacePrefix = regexp.MustCompile(`^\w+\.`)
|
||||||
|
|
||||||
|
func NewDataValidationAPIError(err error) responses.APIError {
|
||||||
|
apiErr := responses.ErrDataValidation
|
||||||
|
if err != nil {
|
||||||
|
var invalidValidationErrror *validatorLib.InvalidValidationError
|
||||||
|
if errors.As(err, &invalidValidationErrror) {
|
||||||
|
logger.GetLogger().WithError(invalidValidationErrror).WithField("templateAPIErr", apiErr).Error("InvalidValidationError")
|
||||||
|
} else {
|
||||||
|
var validationErrors validatorLib.ValidationErrors
|
||||||
|
if errors.As(err, &validationErrors) {
|
||||||
|
for _, e := range validationErrors {
|
||||||
|
reason := e.Tag()
|
||||||
|
if _, ok := CustomValidators[e.Tag()]; ok {
|
||||||
|
reason = truncatingSprintf(CustomValidators[e.Tag()].Message, e.Param())
|
||||||
|
}
|
||||||
|
|
||||||
|
namespaceWithoutStructName := regexpValidatorNamespacePrefix.ReplaceAllString(e.Namespace(), "")
|
||||||
|
fe := responses.FieldError{
|
||||||
|
Field: namespaceWithoutStructName,
|
||||||
|
Constraint: e.Tag(),
|
||||||
|
Description: reason,
|
||||||
|
}
|
||||||
|
apiErr.Details = append(apiErr.Details, fe)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
apiErr.Description = err.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return apiErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// truncatingSprintf is used as fmt.Sprintf but allow to truncate the additional parameters given when there is more parameters than %v in str.
|
||||||
|
func truncatingSprintf(str string, args ...interface{}) string {
|
||||||
|
n := strings.Count(str, "%v")
|
||||||
|
return fmt.Sprintf(str, args[:n]...)
|
||||||
|
}
|
||||||
9
mangezmieux-backend/internal/validator/setup.go
Normal file
9
mangezmieux-backend/internal/validator/setup.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package validator
|
||||||
|
|
||||||
|
import "mangezmieux-backend/internal/injector"
|
||||||
|
|
||||||
|
const ValidatorInjectorKey = "VALIDATOR"
|
||||||
|
|
||||||
|
func Setup(inj *injector.Injector) {
|
||||||
|
inj.Set(ValidatorInjectorKey, newValidator())
|
||||||
|
}
|
||||||
56
mangezmieux-backend/internal/validator/validator.go
Normal file
56
mangezmieux-backend/internal/validator/validator.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package validator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
validatorLib "github.com/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
var CustomValidators = map[string]customValidator{
|
||||||
|
"enum": {
|
||||||
|
Message: "This field should be in: %v",
|
||||||
|
Validator: validateEnum,
|
||||||
|
},
|
||||||
|
"required": {
|
||||||
|
Message: "This field is required and cannot be empty",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type customValidator struct {
|
||||||
|
Message string
|
||||||
|
Validator validatorLib.FuncCtx
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateEnum(ctx context.Context, fl validatorLib.FieldLevel) bool {
|
||||||
|
for _, v := range strings.Split(fl.Param(), " ") {
|
||||||
|
if v == fl.Field().String() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func newValidator() *validatorLib.Validate {
|
||||||
|
va := validatorLib.New()
|
||||||
|
|
||||||
|
va.RegisterTagNameFunc(func(fld reflect.StructField) string {
|
||||||
|
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)
|
||||||
|
if len(name) < 1 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return name[0]
|
||||||
|
})
|
||||||
|
|
||||||
|
for k, v := range CustomValidators {
|
||||||
|
if v.Validator != nil {
|
||||||
|
err := va.RegisterValidationCtx(k, v.Validator)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return va
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
|
||||||
|
|
||||||
|
<!-- Define default schema name used by changesets-->
|
||||||
|
<property name="schemaName" value="mangezmieux"/>
|
||||||
|
|
||||||
|
<include file="prepare-database.sql" relativeToChangelogFile="true"/>
|
||||||
|
|
||||||
|
<include file="changesets/create-user-table.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="changesets/create-role-table.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="changesets/create-resource-table.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="changesets/create-role-verb-table.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="changesets/create-user-role-table.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="changesets/create-ingredient-table.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="changesets/create-recipe-table.xml" relativeToChangelogFile="true" />
|
||||||
|
|
||||||
|
<include file="changesets/insert-resource.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="changesets/insert-admin-role.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="changesets/insert-admin-user.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="changesets/insert-mangezmieux-admin-role.xml" relativeToChangelogFile="true" />
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
|
||||||
|
<changeSet id="create-user-table" author="Jeffrey Duroyon">
|
||||||
|
<createTable tableName="user" schemaName="${schemaName}">
|
||||||
|
<column name="id" type="uuid" defaultValueComputed="gen_random_uuid()">
|
||||||
|
<constraints primaryKey="true" nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="email" type="varchar(50)">
|
||||||
|
<constraints unique="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="password" type="varchar(255)">
|
||||||
|
</column>
|
||||||
|
<column name="first_name" type="varchar(45)">
|
||||||
|
</column>
|
||||||
|
<column name="last_name" type="varchar(45)">
|
||||||
|
</column>
|
||||||
|
<column name="creation_date" type="timestamp" defaultValueComputed="now()">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_update_date" type="timestamp" defaultValueComputed="now()">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_update_user" type="varchar(100)">
|
||||||
|
</column>
|
||||||
|
<column name="creation_user" type="varchar(100)">
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
|
||||||
|
<changeSet id="create-ingredient-table" author="Jeffrey Duroyon">
|
||||||
|
<createTable tableName="ingredient" schemaName="${schemaName}">
|
||||||
|
<column name="id" type="uuid" defaultValueComputed="gen_random_uuid()">
|
||||||
|
<constraints primaryKey="true" nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="name" type="varchar(255)">
|
||||||
|
<constraints unique="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="rarity" type="varchar(255)">
|
||||||
|
</column>
|
||||||
|
|
||||||
|
<column name="creation_date" type="timestamp" defaultValueComputed="now()">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_update_date" type="timestamp" defaultValueComputed="now()">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_update_user" type="varchar(100)">
|
||||||
|
</column>
|
||||||
|
<column name="creation_user" type="varchar(100)">
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"
|
||||||
|
>
|
||||||
|
<changeSet id="create-resource-table" author="Jeffrey Duroyon">
|
||||||
|
<createTable tableName="resource" schemaName="${schemaName}">
|
||||||
|
<column name="id" type="uuid" defaultValueComputed="gen_random_uuid()">
|
||||||
|
<constraints primaryKey="true" nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="name" type="varchar(40)">
|
||||||
|
<constraints unique="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="creation_date" type="timestamp" defaultValueComputed="now()">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_update_date" type="timestamp">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_update_user" type="varchar(100)">
|
||||||
|
</column>
|
||||||
|
<column name="creation_user" type="varchar(100)">
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"
|
||||||
|
>
|
||||||
|
<changeSet id="create-role-table" author="Jeffrey Duroyon">
|
||||||
|
<createTable tableName="role" schemaName="${schemaName}">
|
||||||
|
<column name="id" type="uuid" defaultValueComputed="gen_random_uuid()">
|
||||||
|
<constraints primaryKey="true" nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="name" type="varchar(40)">
|
||||||
|
<constraints unique="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="creation_date" type="timestamp" defaultValueComputed="now()">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_update_date" type="timestamp">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_update_user" type="varchar(100)">
|
||||||
|
</column>
|
||||||
|
<column name="creation_user" type="varchar(100)">
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"
|
||||||
|
>
|
||||||
|
<changeSet id="create-role-verb-table" author="Jeffrey Duroyon">
|
||||||
|
<createTable tableName="role_verb_resource" schemaName="${schemaName}">
|
||||||
|
<column name="id" type="uuid" defaultValueComputed="gen_random_uuid()">
|
||||||
|
<constraints primaryKey="true" nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="role_id" type="uuid">
|
||||||
|
<constraints nullable="false" foreignKeyName="fk_role_verb_resource_role" references="${schemaName}.role(id)"/>
|
||||||
|
</column>
|
||||||
|
<column name="verb" type="varchar(40)">
|
||||||
|
</column>
|
||||||
|
<column name="resource_id" type="uuid">
|
||||||
|
<constraints nullable="false" foreignKeyName="fk_role_verb_resource_resource" references="${schemaName}.resource(id)"/>
|
||||||
|
</column>
|
||||||
|
<column name="creation_date" type="timestamp" defaultValueComputed="now()">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_update_date" type="timestamp">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_update_user" type="varchar(100)">
|
||||||
|
</column>
|
||||||
|
<column name="creation_user" type="varchar(100)">
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
<addUniqueConstraint
|
||||||
|
columnNames="role_id, verb, resource_id"
|
||||||
|
constraintName="unique_role_verb_resource"
|
||||||
|
schemaName="${schemaName}"
|
||||||
|
tableName="role_verb_resource"
|
||||||
|
/>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"
|
||||||
|
>
|
||||||
|
<changeSet id="create-user-role-table" author="Jeffrey Duroyon">
|
||||||
|
<createTable tableName="user_role" schemaName="${schemaName}">
|
||||||
|
<column name="id" type="uuid" defaultValueComputed="gen_random_uuid()">
|
||||||
|
<constraints primaryKey="true" nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="user_id" type="uuid">
|
||||||
|
<constraints nullable="false" foreignKeyName="fk_user_role_box_user" references="${schemaName}.user(id)"/>
|
||||||
|
</column>
|
||||||
|
<column name="role_id" type="uuid">
|
||||||
|
<constraints nullable="false" foreignKeyName="fk_user_role_box_role" references="${schemaName}.role(id)"/>
|
||||||
|
</column>
|
||||||
|
<column name="creation_date" type="timestamp" defaultValueComputed="now()">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_update_date" type="timestamp">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_update_user" type="varchar(100)">
|
||||||
|
</column>
|
||||||
|
<column name="creation_user" type="varchar(100)">
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
<addUniqueConstraint
|
||||||
|
columnNames="role_id, user_id"
|
||||||
|
constraintName="unique_user_role"
|
||||||
|
schemaName="${schemaName}"
|
||||||
|
tableName="user_role"
|
||||||
|
/>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
|
||||||
|
<changeSet id="create-user-table" author="Jeffrey Duroyon">
|
||||||
|
<createTable tableName="user" schemaName="${schemaName}">
|
||||||
|
<column name="id" type="uuid" defaultValueComputed="gen_random_uuid()">
|
||||||
|
<constraints primaryKey="true" nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="email" type="varchar(50)">
|
||||||
|
<constraints unique="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="password" type="varchar(255)">
|
||||||
|
</column>
|
||||||
|
<column name="first_name" type="varchar(45)">
|
||||||
|
</column>
|
||||||
|
<column name="last_name" type="varchar(45)">
|
||||||
|
</column>
|
||||||
|
<column name="creation_date" type="timestamp" defaultValueComputed="now()">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_update_date" type="timestamp" defaultValueComputed="now()">
|
||||||
|
<constraints nullable="true"/>
|
||||||
|
</column>
|
||||||
|
<column name="last_update_user" type="varchar(100)">
|
||||||
|
</column>
|
||||||
|
<column name="creation_user" type="varchar(100)">
|
||||||
|
</column>
|
||||||
|
</createTable>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"
|
||||||
|
>
|
||||||
|
<changeSet id="insert-role-and-verb" author="Jeffrey Duroyon">
|
||||||
|
<insert tableName="role" schemaName="${schemaName}">
|
||||||
|
<column name="name" value="ADMIN"/>
|
||||||
|
</insert>
|
||||||
|
<insert tableName="role_verb_resource" schemaName="${schemaName}">
|
||||||
|
<column name="verb" value="ADMIN"/>
|
||||||
|
<column name="role_id" valueComputed="(SELECT id from ${schemaName}.role where name='ADMIN')"/>
|
||||||
|
<column name="resource_id" valueComputed="(SELECT id from ${schemaName}.resource where name='ADMIN')"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"
|
||||||
|
>
|
||||||
|
<changeSet id="insert-admin-user" author="Jeffrey Duroyon">
|
||||||
|
<insert tableName="user" schemaName="${schemaName}">
|
||||||
|
<column name="email" value="admin@mangezmieux.com"/>
|
||||||
|
<column name="password" value="$2a$10$zLk6vcP7dKQ3KZ1c5icu3OiT68AeGyW.rxGL54AqOoUhoId2hmrrK"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"
|
||||||
|
>
|
||||||
|
<changeSet id="insert-mangezmieux-admin-role" author="Jeffrey Duroyon">
|
||||||
|
<insert tableName="role" schemaName="${schemaName}">
|
||||||
|
<column name="name" value="MANGEZMIEUX_ADMIN"/>
|
||||||
|
</insert>
|
||||||
|
<insert tableName="role_verb_resource" schemaName="${schemaName}">
|
||||||
|
<column name="verb" value="ADMIN"/>
|
||||||
|
<column name="role_id" valueComputed="(SELECT id from ${schemaName}.role where name='MANGEZMIEUX_ADMIN')"/>
|
||||||
|
<column name="resource_id" valueComputed="(SELECT id from ${schemaName}.resource where name='ADMIN')"/>
|
||||||
|
</insert>
|
||||||
|
<insert tableName="user_role" schemaName="${schemaName}">
|
||||||
|
<column name="user_id" valueComputed="(SELECT id from ${schemaName}.user where email='admin@mangezmieux.com')"/>
|
||||||
|
<column name="role_id" valueComputed="(SELECT id from ${schemaName}.role where name='MANGEZMIEUX_ADMIN')"/>
|
||||||
|
</insert>
|
||||||
|
<!-- <column name="studentId" valueComputed="(SELECT sId from StudentMaster where studentName='Jay Parikh')"/>-->
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"
|
||||||
|
>
|
||||||
|
<changeSet id="insert-resource" author="Jeffrey Duroyon">
|
||||||
|
<insert tableName="resource" schemaName="${schemaName}">
|
||||||
|
<column name="name" value="ADMIN"/>
|
||||||
|
</insert>
|
||||||
|
<insert tableName="resource" schemaName="${schemaName}">
|
||||||
|
<column name="name" value="USER"/>
|
||||||
|
</insert>
|
||||||
|
<insert tableName="resource" schemaName="${schemaName}">
|
||||||
|
<column name="name" value="RECIPE"/>
|
||||||
|
</insert>
|
||||||
|
<insert tableName="resource" schemaName="${schemaName}">
|
||||||
|
<column name="name" value="ROLE"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
url: jdbc:postgresql://127.0.0.1:5432/mangezmieux
|
||||||
|
username: postgres
|
||||||
|
password: mysecretpassword
|
||||||
|
logLevel: info
|
||||||
2
mangezmieux-backend/liquibase/changelogs/prepare-database.sql
Executable file
2
mangezmieux-backend/liquibase/changelogs/prepare-database.sql
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
CREATE SCHEMA IF NOT EXISTS mangezmieux;
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||||
7
mangezmieux-backend/main.go
Normal file
7
mangezmieux-backend/main.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "mangezmieux-backend/cmd"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cmd.Execute()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user