mirror of
https://github.com/Floriansylvain/RenewCMS.git
synced 2026-08-19 11:43:22 +02:00
@@ -1,11 +1,12 @@
|
|||||||
FROM node:latest as build-stage
|
FROM node:latest as build-stage
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package*.json ./
|
COPY ./web/admin-gui/package*.json ./
|
||||||
|
COPY ./.env ./
|
||||||
RUN npm install
|
RUN npm install
|
||||||
COPY ./ .
|
COPY ./web/admin-gui/ .
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
FROM nginx as production-stage
|
FROM nginx as production-stage
|
||||||
RUN mkdir /app
|
RUN mkdir /app
|
||||||
COPY --from=build-stage /app/dist /app
|
COPY --from=build-stage /app/dist /app
|
||||||
COPY nginx.conf /etc/nginx/nginx.conf
|
COPY ./web/admin-gui/nginx.conf /etc/nginx/nginx.conf
|
||||||
@@ -12,7 +12,9 @@ docker-compose up
|
|||||||
TODO
|
TODO
|
||||||
## Environment variables
|
## Environment variables
|
||||||
- ./env
|
- ./env
|
||||||
- API_PORT
|
- APP_HOST_ADDRESS
|
||||||
- FRONT_PORT
|
- APP_API_PORT
|
||||||
|
- APP_FRONT_PORT
|
||||||
|
- APP_JWT_SECRET
|
||||||
## Demo
|
## Demo
|
||||||
TODO
|
TODO
|
||||||
+48
-10
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -8,30 +9,67 @@ import (
|
|||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ginMode = os.Getenv("APP_GIN_MODE")
|
||||||
|
var apiPort = os.Getenv("APP_API_PORT")
|
||||||
|
var frontPort = os.Getenv("APP_FRONT_PORT")
|
||||||
|
var hostAddress = os.Getenv("APP_HOST_ADDRESS")
|
||||||
|
|
||||||
func initEnvVariables() {
|
func initEnvVariables() {
|
||||||
if godotenv.Load() != nil {
|
if godotenv.Load() != nil {
|
||||||
panic("Error loading .env file.")
|
panic("Error loading .env file.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initJWT() {
|
||||||
|
errInit := internal.AuthMiddleware.MiddlewareInit()
|
||||||
|
if errInit != nil {
|
||||||
|
fmt.Printf(errInit.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initBasicRoutes(r *gin.Engine) {
|
||||||
|
r.POST("/login/", internal.AuthMiddleware.LoginHandler)
|
||||||
|
r.GET("/ping/", internal.Ping)
|
||||||
|
}
|
||||||
|
|
||||||
|
func corsMiddleware(c *gin.Context) {
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Origin", fmt.Sprintf("http://%v:%v", hostAddress, frontPort))
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
|
||||||
|
if c.Request.Method == "OPTIONS" {
|
||||||
|
c.AbortWithStatus(204)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
func initArticlesRoutes(r *gin.Engine) {
|
||||||
|
articlesRouter := r.Group("/articles")
|
||||||
|
articlesRouter.Use(corsMiddleware, internal.AuthMiddleware.MiddlewareFunc())
|
||||||
|
|
||||||
|
articlesRouter.GET("/", internal.GetAllArticlesHandler)
|
||||||
|
articlesRouter.GET("/:id", internal.GetArticleHandler)
|
||||||
|
articlesRouter.POST("/:id", internal.AddArticleHandler)
|
||||||
|
articlesRouter.DELETE("/:id", internal.DeleteArticleHandler)
|
||||||
|
}
|
||||||
|
|
||||||
func initGin() {
|
func initGin() {
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
r.SetTrustedProxies([]string{"localhost"})
|
r.Use(corsMiddleware)
|
||||||
|
|
||||||
r.GET("/ping", internal.Ping)
|
if ginMode == "release" {
|
||||||
|
gin.SetMode(ginMode)
|
||||||
|
}
|
||||||
|
|
||||||
r.GET("/articles/", internal.AuthCheck, internal.GetAllArticles)
|
initBasicRoutes(r)
|
||||||
r.GET("/articles/:id", internal.AuthCheck, internal.GetArticle)
|
initArticlesRoutes(r)
|
||||||
r.POST("/articles/:id", internal.AuthCheck, internal.AddArticle)
|
|
||||||
r.DELETE("/articles/:id", internal.AuthCheck, internal.DeleteArticle)
|
|
||||||
|
|
||||||
r.POST("/login", internal.LoginUser)
|
r.Run(":" + apiPort)
|
||||||
r.POST("/logout", internal.LogoutUser)
|
|
||||||
|
|
||||||
r.Run(":" + os.Getenv("API_PORT"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
initEnvVariables()
|
initEnvVariables()
|
||||||
|
initJWT()
|
||||||
initGin()
|
initGin()
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-4
@@ -1,20 +1,24 @@
|
|||||||
services:
|
services:
|
||||||
backend:
|
backend:
|
||||||
build: .
|
build:
|
||||||
|
context: ./
|
||||||
|
dockerfile: ./Dockerfile.backend
|
||||||
image: gohcms-backend
|
image: gohcms-backend
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
ports:
|
ports:
|
||||||
- "${API_PORT}:8080"
|
- "${APP_API_PORT}:${APP_API_PORT}"
|
||||||
networks:
|
networks:
|
||||||
- back-net
|
- back-net
|
||||||
frontend:
|
frontend:
|
||||||
build: web/admin-gui/.
|
build:
|
||||||
|
context: ./
|
||||||
|
dockerfile: ./Dockerfile.frontend
|
||||||
image: gohcms-frontend
|
image: gohcms-frontend
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
ports:
|
ports:
|
||||||
- "${FRONT_PORT}:80"
|
- "${APP_FRONT_PORT}:80"
|
||||||
networks:
|
networks:
|
||||||
- front-net
|
- front-net
|
||||||
db:
|
db:
|
||||||
|
|||||||
@@ -2,37 +2,42 @@ module github.com/gotest
|
|||||||
|
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/appleboy/gin-jwt/v2 v2.9.1
|
||||||
|
github.com/gin-gonic/gin v1.8.1
|
||||||
|
github.com/joho/godotenv v1.4.0
|
||||||
|
go.mongodb.org/mongo-driver v1.11.1
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/gin-gonic/gin v1.8.1 // indirect
|
|
||||||
github.com/go-playground/locales v0.14.0 // indirect
|
github.com/go-playground/locales v0.14.0 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.10.0 // indirect
|
github.com/go-playground/validator/v10 v10.11.1 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||||
github.com/goccy/go-json v0.9.7 // indirect
|
github.com/goccy/go-json v0.10.0 // indirect
|
||||||
github.com/golang/snappy v0.0.1 // indirect
|
github.com/golang-jwt/jwt/v4 v4.4.3 // indirect
|
||||||
github.com/joho/godotenv v1.4.0 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.13.6 // indirect
|
github.com/klauspost/compress v1.15.13 // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.15 // indirect
|
github.com/mattn/go-sqlite3 v1.14.15 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
github.com/montanaflynn/stats v0.6.6 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.1.1 // indirect
|
github.com/xdg-go/scram v1.1.2 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.3 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
|
||||||
go.mongodb.org/mongo-driver v1.10.3 // indirect
|
golang.org/x/crypto v0.4.0 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
|
golang.org/x/net v0.4.0 // indirect
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
golang.org/x/sync v0.1.0 // indirect
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
golang.org/x/sys v0.3.0 // indirect
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect
|
golang.org/x/text v0.5.0 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
google.golang.org/protobuf v1.28.0 // indirect
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
github.com/appleboy/gin-jwt/v2 v2.9.1 h1:l29et8iLW6omcHltsOP6LLk4s3v4g2FbFs0koxGWVZs=
|
||||||
|
github.com/appleboy/gin-jwt/v2 v2.9.1/go.mod h1:jwcPZJ92uoC9nOUTOKWoN/f6JZOgMSKlFSHw5/FrRUk=
|
||||||
|
github.com/appleboy/gofight/v2 v2.1.2/go.mod h1:frW+U1QZEdDgixycTj4CygQ48yLTUhplt43+Wczp3rw=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -12,13 +15,21 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j
|
|||||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||||
github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
|
github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
|
||||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||||
|
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
|
||||||
|
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
|
github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
|
||||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
|
||||||
|
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
@@ -28,6 +39,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
|
|||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
|
github.com/klauspost/compress v1.15.13 h1:NFn1Wr8cfnenSJSA46lLq4wHCcBzKTSjnBIexDMMOV0=
|
||||||
|
github.com/klauspost/compress v1.15.13/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
@@ -38,16 +51,24 @@ github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
|||||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
|
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
|
||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
|
github.com/montanaflynn/stats v0.6.6 h1:Duep6KMIDpY4Yo11iFsvyqJDyfzLF9+sndUKT+v64GQ=
|
||||||
|
github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
|
github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@@ -55,11 +76,19 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
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.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/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
|
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
|
||||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||||
@@ -67,39 +96,84 @@ github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
|||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
|
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
|
||||||
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
|
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
|
||||||
|
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||||
|
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||||
github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=
|
github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=
|
||||||
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
|
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
|
||||||
|
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||||
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
go.mongodb.org/mongo-driver v1.10.3 h1:XDQEvmh6z1EUsXuIkXE9TaVeqHw6SwS1uf93jFs0HBA=
|
go.mongodb.org/mongo-driver v1.10.3 h1:XDQEvmh6z1EUsXuIkXE9TaVeqHw6SwS1uf93jFs0HBA=
|
||||||
go.mongodb.org/mongo-driver v1.10.3/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8=
|
go.mongodb.org/mongo-driver v1.10.3/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8=
|
||||||
|
go.mongodb.org/mongo-driver v1.11.1 h1:QP0znIRTuL0jf1oBQoAoM0C6ZJfBK4kx0Uumtv1A7w8=
|
||||||
|
go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
|
||||||
|
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||||
|
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
|
||||||
|
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU=
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU=
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||||
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
|
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||||
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||||
|
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
|||||||
@@ -1,119 +0,0 @@
|
|||||||
package internal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Article struct {
|
|
||||||
Id_name string `json:"id_name" bson:"id_name"`
|
|
||||||
Date int64 `json:"date" bson:"date"`
|
|
||||||
Content interface{} `json:"content" bson:"content"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DelArticle struct {
|
|
||||||
Id_name string `json:"id_name" bson:"id_name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var ARTICLES_LOCATION = Location{Database: "gohcms", Collection: "articles"}
|
|
||||||
|
|
||||||
// TODO Change to GetArticleList that will find filter json context
|
|
||||||
func GetAllArticles(c *gin.Context) {
|
|
||||||
var articles []Article
|
|
||||||
documents, err := getDocuments(ARTICLES_LOCATION, bson.D{})
|
|
||||||
if err != nil {
|
|
||||||
SendBadRequest(c, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(documents); i++ {
|
|
||||||
var newArticle Article
|
|
||||||
bson.Unmarshal(documents[i], &newArticle)
|
|
||||||
articles = append(articles, newArticle)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, articles)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetArticle(c *gin.Context) {
|
|
||||||
articleID := c.Params.ByName("id")
|
|
||||||
articles, err := getDocuments(ARTICLES_LOCATION,
|
|
||||||
bson.D{{Key: "id_name", Value: articleID}})
|
|
||||||
if err != nil {
|
|
||||||
SendBadRequest(c, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(articles) == 0 {
|
|
||||||
SendBadRequest(c, "The ID provided doesn't match any article.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var parsedArticle Article
|
|
||||||
bson.Unmarshal(articles[0], &parsedArticle)
|
|
||||||
c.JSON(http.StatusOK, parsedArticle)
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsArticleIdAlreadyUsed(id string) bool {
|
|
||||||
documents, _ := getDocuments(ARTICLES_LOCATION, bson.D{})
|
|
||||||
for i := 0; i < len(documents); i++ {
|
|
||||||
var newArticle Article
|
|
||||||
bson.Unmarshal(documents[i], &newArticle)
|
|
||||||
if newArticle.Id_name == id {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddArticle(c *gin.Context) {
|
|
||||||
var article Article
|
|
||||||
article.Id_name = c.Params.ByName("id")
|
|
||||||
if c.BindJSON(&article) != nil {
|
|
||||||
SendBadRequest(c, "Could not correctly parse the article.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
document, err := bson.Marshal(article)
|
|
||||||
if err != nil {
|
|
||||||
SendBadRequest(c, "Could not correctly marshal the article.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsArticleIdAlreadyUsed(article.Id_name) {
|
|
||||||
SendBadRequest(c, "Article ID already used.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = pushDocument(ARTICLES_LOCATION, document)
|
|
||||||
if err != nil {
|
|
||||||
SendBadRequest(c, "Could not insert document into DB.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
SendOk(c, "Article successfully added!")
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteArticle(c *gin.Context) {
|
|
||||||
var delArticle DelArticle
|
|
||||||
delArticle.Id_name = c.Params.ByName("id")
|
|
||||||
if c.BindJSON(&delArticle) != nil {
|
|
||||||
SendBadRequest(c, "Could not correctly parse the article ID.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
document, err := bson.Marshal(delArticle)
|
|
||||||
if err != nil {
|
|
||||||
SendBadRequest(c, "Could not correctly marshal the article ID.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteCount, err := deleteDocument(ARTICLES_LOCATION, document)
|
|
||||||
if err != nil {
|
|
||||||
SendBadRequest(c, "Could not insert document into DB.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
SendOk(c, fmt.Sprintf("%d articles were successfully deleted!", deleteCount))
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Article struct {
|
||||||
|
IdName string `json:"id_name" bson:"id_name"`
|
||||||
|
Date int64 `json:"date" bson:"date"`
|
||||||
|
Content interface{} `json:"content" bson:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DelArticle struct {
|
||||||
|
IdName string `json:"id_name" bson:"id_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var articlesLocation = Location{Database: "gohcms", Collection: "articles"}
|
||||||
|
|
||||||
|
func GetAllArticlesBusiness(documents [][]byte) []Article {
|
||||||
|
var articles = []Article{}
|
||||||
|
|
||||||
|
for i := 0; i < len(documents); i++ {
|
||||||
|
var newArticle Article
|
||||||
|
bson.Unmarshal(documents[i], &newArticle)
|
||||||
|
articles = append(articles, newArticle)
|
||||||
|
}
|
||||||
|
|
||||||
|
return articles
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsArticleIdAlreadyUsed(id string, documents [][]byte) bool {
|
||||||
|
for i := 0; i < len(documents); i++ {
|
||||||
|
var newArticle Article
|
||||||
|
bson.Unmarshal(documents[i], &newArticle)
|
||||||
|
if newArticle.IdName == id {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetAllArticlesHandler(c *gin.Context) {
|
||||||
|
documents, err := getDocuments(articlesLocation, bson.D{})
|
||||||
|
if err != nil {
|
||||||
|
SendBadRequest(c, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, GetAllArticlesBusiness(documents))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetArticleHandler(c *gin.Context) {
|
||||||
|
articleID := c.Params.ByName("id")
|
||||||
|
article, err := getUniqueDocument(articlesLocation,
|
||||||
|
bson.D{{Key: "id_name", Value: articleID}})
|
||||||
|
if err != nil {
|
||||||
|
SendBadRequest(c, "The ID provided doesn't match any article.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var parsedArticle Article
|
||||||
|
bson.Unmarshal(article, &parsedArticle)
|
||||||
|
c.JSON(http.StatusOK, parsedArticle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddArticleHandler(c *gin.Context) {
|
||||||
|
var article Article
|
||||||
|
article.IdName = c.Params.ByName("id")
|
||||||
|
if c.BindJSON(&article) != nil {
|
||||||
|
SendBadRequest(c, "Could not correctly parse the article.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
document, err := bson.Marshal(article)
|
||||||
|
if err != nil {
|
||||||
|
SendBadRequest(c, "Could not correctly marshal the article.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
documents, _ := getDocuments(articlesLocation, gin.H{})
|
||||||
|
if IsArticleIdAlreadyUsed(article.IdName, documents) {
|
||||||
|
SendBadRequest(c, "Article ID already used.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = pushDocument(articlesLocation, document)
|
||||||
|
if err != nil {
|
||||||
|
SendBadRequest(c, fmt.Sprintf(`Could not insert document into DB: %v`, err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
SendOk(c, "Article successfully added!")
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteArticleHandler(c *gin.Context) {
|
||||||
|
var delArticle DelArticle
|
||||||
|
delArticle.IdName = c.Params.ByName("id")
|
||||||
|
if c.BindJSON(&delArticle) != nil {
|
||||||
|
SendBadRequest(c, "Could not correctly parse the article ID.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
document, err := bson.Marshal(delArticle)
|
||||||
|
if err != nil {
|
||||||
|
SendBadRequest(c, "Could not correctly marshal the article ID.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteCount, err := deleteDocument(articlesLocation, document)
|
||||||
|
if err != nil {
|
||||||
|
SendBadRequest(c, "Could not insert document into DB.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
SendOk(c, fmt.Sprintf("%d articles were successfully deleted!", deleteCount))
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Article1, _ = bson.Marshal(Article{
|
||||||
|
IdName: "test_article_1",
|
||||||
|
Date: 1671482492,
|
||||||
|
Content: gin.H{},
|
||||||
|
})
|
||||||
|
|
||||||
|
var Article2, _ = bson.Marshal(Article{
|
||||||
|
IdName: "test_article_2",
|
||||||
|
Date: 1671899112,
|
||||||
|
Content: gin.H{},
|
||||||
|
})
|
||||||
|
|
||||||
|
var BSONConvertedArticles = [][]byte{Article1, Article2}
|
||||||
|
|
||||||
|
func TestGetAllArticlesBusiness(t *testing.T) {
|
||||||
|
documents := GetAllArticlesBusiness(BSONConvertedArticles)
|
||||||
|
article := documents[0]
|
||||||
|
|
||||||
|
if article.IdName != "test_article_1" {
|
||||||
|
log.Fatalf(`Excepted "test_article_1" as IdName, found "%v"`, article.IdName)
|
||||||
|
} else if article.Date != 1671482492 {
|
||||||
|
log.Fatalf(`Excepted 1671482492 as Date, found "%v"`, article.Date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsArticleIdAlreadyUsed(t *testing.T) {
|
||||||
|
if IsArticleIdAlreadyUsed("test_article_1", BSONConvertedArticles) == false {
|
||||||
|
log.Fatalf(`Excepted true, found false`)
|
||||||
|
} else if IsArticleIdAlreadyUsed("test_article_3", BSONConvertedArticles) == true {
|
||||||
|
log.Fatalf(`Excepted false, found true`)
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
-82
@@ -1,11 +1,13 @@
|
|||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
jwt "github.com/appleboy/gin-jwt/v2"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
@@ -13,92 +15,30 @@ type User struct {
|
|||||||
Password string `json:"password" bson:"password"`
|
Password string `json:"password" bson:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var USERS_LOCATION = Location{Database: "gohcms", Collection: "users"}
|
var UsersLocation = Location{Database: "gohcms", Collection: "users"}
|
||||||
|
|
||||||
func getUserHashedPassword(user User) string {
|
var AuthMiddleware, _ = jwt.New(&jwt.GinJWTMiddleware{
|
||||||
password := sha256.New()
|
Realm: "GohCMS",
|
||||||
password.Write([]byte(user.Password))
|
Key: []byte(os.Getenv("APP_JWT_SECRET")),
|
||||||
return fmt.Sprintf("%x", password.Sum(nil))
|
Timeout: time.Hour,
|
||||||
}
|
MaxRefresh: time.Hour,
|
||||||
|
Authenticator: JWTAuthenticator,
|
||||||
|
})
|
||||||
|
|
||||||
func isUserLoggedIn(user User) bool {
|
func JWTAuthenticator(c *gin.Context) (interface{}, error) {
|
||||||
for i := 0; i < len(SESSIONS); i++ {
|
var user = User{}
|
||||||
hash1 := fmt.Sprint(SESSIONS[i].Token.Sum(nil))
|
err := c.BindJSON(&user)
|
||||||
hash2 := fmt.Sprint(generateSessionToken(user).Sum(nil))
|
|
||||||
if hash1 != hash2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
sessionExpired := isSessionExpired(SESSIONS[i])
|
|
||||||
if sessionExpired {
|
|
||||||
removeSession(user)
|
|
||||||
}
|
|
||||||
return !sessionExpired
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isUserReal(user User) bool {
|
|
||||||
docUsers, _ := getDocuments(USERS_LOCATION, user)
|
|
||||||
return len(docUsers) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseUserFromContext(c *gin.Context) (User, error) {
|
|
||||||
var user User
|
|
||||||
if c.BindJSON(&user) != nil {
|
|
||||||
return User{}, errors.New("could not correctly parse user crendentials")
|
|
||||||
}
|
|
||||||
user.Password = getUserHashedPassword(user)
|
|
||||||
return user, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoginUser(c *gin.Context) {
|
|
||||||
user, err := parseUserFromContext(c)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SendBadRequest(c, err.Error())
|
return nil, errors.New("wrong credentials json format.")
|
||||||
return
|
|
||||||
}
|
|
||||||
if isUserLoggedIn(user) {
|
|
||||||
SendBadRequest(c, "User is already logged in!")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !isUserReal(user) {
|
|
||||||
SendBadRequest(c, "Unknown email or wrong password.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
addSession(user)
|
|
||||||
SendOk(c, "User successfully logged in.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func LogoutUser(c *gin.Context) {
|
_, err = getUniqueDocument(UsersLocation, bson.D{
|
||||||
user, err := parseUserFromContext(c)
|
{Key: "email", Value: user.Email},
|
||||||
|
{Key: "password", Value: user.Password},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SendBadRequest(c, err.Error())
|
return nil, errors.New("wrong email or password.")
|
||||||
return
|
|
||||||
}
|
|
||||||
if !isUserLoggedIn(user) {
|
|
||||||
SendBadRequest(c, "User is not logged in!")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
removeSession(user)
|
|
||||||
SendOk(c, "User successfully logged out.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func AuthCheck(c *gin.Context) {
|
return gin.H{"email": user.Email}, nil
|
||||||
var user User
|
|
||||||
username, password, isOk := c.Request.BasicAuth()
|
|
||||||
if !isOk {
|
|
||||||
SendBadRequest(c, "Incorrect or missing user credentials.")
|
|
||||||
c.Abort()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user.Email = username
|
|
||||||
user.Password = password
|
|
||||||
user.Password = getUserHashedPassword(user)
|
|
||||||
|
|
||||||
if !isUserLoggedIn(user) {
|
|
||||||
SendForbidden(c, "Authentification failed, credentials could be wrong, user may not be logged in, session may have expired.")
|
|
||||||
c.Abort()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,18 @@ func getDocuments(location Location, filter interface{}) ([][]byte, error) {
|
|||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getUniqueDocument(location Location, filter interface{}) ([]byte, error) {
|
||||||
|
client := getNewClient()
|
||||||
|
collection := client.Database(location.Database).Collection(location.Collection)
|
||||||
|
defer client.Disconnect(context.TODO())
|
||||||
|
|
||||||
|
singleResult := collection.FindOne(context.TODO(), filter)
|
||||||
|
if singleResult.Err() != nil {
|
||||||
|
return nil, errors.New("no document was found, check the filter or the location.")
|
||||||
|
}
|
||||||
|
return singleResult.DecodeBytes()
|
||||||
|
}
|
||||||
|
|
||||||
func deleteDocument(location Location, filter interface{}) (int64, error) {
|
func deleteDocument(location Location, filter interface{}) (int64, error) {
|
||||||
client := getNewClient()
|
client := getNewClient()
|
||||||
collection := client.Database(location.Database).Collection(location.Collection)
|
collection := client.Database(location.Database).Collection(location.Collection)
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
package internal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"fmt"
|
|
||||||
"hash"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Session struct {
|
|
||||||
Token hash.Hash `json:"token"`
|
|
||||||
ExpirationTimestamp int64 `json:"expirationTimestamp"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var SESSIONS []Session
|
|
||||||
|
|
||||||
const SESSION_DURATION int64 = 3600
|
|
||||||
|
|
||||||
func generateSessionToken(user User) hash.Hash {
|
|
||||||
token := crypto.SHA256.New()
|
|
||||||
stringToHash := fmt.Sprintf(user.Email + user.Password)
|
|
||||||
token.Write([]byte(stringToHash))
|
|
||||||
return token
|
|
||||||
}
|
|
||||||
|
|
||||||
func isSessionExpired(session Session) bool {
|
|
||||||
return session.ExpirationTimestamp < time.Now().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
func addSession(user User) {
|
|
||||||
SESSIONS = append(SESSIONS, Session{
|
|
||||||
Token: generateSessionToken(user),
|
|
||||||
ExpirationTimestamp: time.Now().Unix() + SESSION_DURATION,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeSession(user User) {
|
|
||||||
index := 0
|
|
||||||
sessLen := len(SESSIONS)
|
|
||||||
for i := 0; i < sessLen; i++ {
|
|
||||||
if SESSIONS[i].Token == generateSessionToken(user) {
|
|
||||||
index = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SESSIONS[index] = SESSIONS[sessLen-1]
|
|
||||||
SESSIONS[sessLen-1] = Session{}
|
|
||||||
SESSIONS = SESSIONS[:sessLen-1]
|
|
||||||
}
|
|
||||||
@@ -42,3 +42,8 @@ db.createCollection('users', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
db.users.insertOne({
|
||||||
|
email: "root",
|
||||||
|
password: "root"
|
||||||
|
})
|
||||||
Vendored
+1
@@ -1 +1,2 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
declare const __APP_ENV__: Record<string, string>
|
||||||
Generated
+565
-509
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@
|
|||||||
"type-check": "vue-tsc --noEmit"
|
"type-check": "vue-tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pinia": "^2.0.21",
|
"pinia": "^2.0.28",
|
||||||
"vue": "^3.2.38",
|
"vue": "^3.2.38",
|
||||||
"vue-editor-js": "^2.0.2",
|
"vue-editor-js": "^2.0.2",
|
||||||
"vue-router": "^4.1.5"
|
"vue-router": "^4.1.5"
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { RouterView } from 'vue-router'
|
import { RouterView, useRoute } from 'vue-router'
|
||||||
|
import Navbar from './components/Navbar.vue';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<Navbar v-if="useRoute().name !== 'login'"></Navbar>
|
||||||
<RouterView />
|
<RouterView />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css2?family=Frank+Ruhl+Libre:wght@500&family=Hind:wght@400;500;600;700&family=Nunito:wght@500;600;700&display=swap');
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--regular-weight: 400;
|
||||||
|
--medium-weight: 500;
|
||||||
|
--semibold-weight: 600;
|
||||||
|
--bold-weight: 700;
|
||||||
|
|
||||||
|
--radius: 15px;
|
||||||
|
|
||||||
|
--font-body: 16px;
|
||||||
|
--font-small: 14px;
|
||||||
|
--font-verysmall: 12px;
|
||||||
|
|
||||||
|
--primary: #2CD358;
|
||||||
|
--primary-dark: #00BA2A;
|
||||||
|
--primary-light: #97E6A2;
|
||||||
|
--secondary: #D32CA6;
|
||||||
|
--secondary-dark: #BA0094;
|
||||||
|
--secondary-light: #E88DCA;
|
||||||
|
--neutral-dark: #121212;
|
||||||
|
--neutral-medium: #707070;
|
||||||
|
--neutral-light: #aaaaaa;
|
||||||
|
--neutral-verylight: #e7e7e7;
|
||||||
|
--brand-blue: #00ACD7;
|
||||||
|
--error: #E80000;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: 'Frank Ruhl Libre', serif;
|
||||||
|
font-size: 40px;
|
||||||
|
}
|
||||||
|
h2 { font-size: 32px; }
|
||||||
|
h3 { font-size: 24px; }
|
||||||
|
h4 { font-size: 18px; }
|
||||||
|
h2, h3, h4, h5, h6 {
|
||||||
|
font-family: 'Nunito', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-family: 'Hind', sans-serif;
|
||||||
|
color: var(--neutral-dark);
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-input,
|
||||||
|
.label-input-error {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-input label,
|
||||||
|
.label-input-error label {
|
||||||
|
font-size: var(--font-body);
|
||||||
|
font-weight: var(--semibold-weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-input p,
|
||||||
|
.label-input-error p {
|
||||||
|
color: var(--neutral-medium);
|
||||||
|
font-size: var(--font-body);
|
||||||
|
font-weight: var(--medium-weight);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-input input,
|
||||||
|
.label-input-error input {
|
||||||
|
padding: 3px 16px;
|
||||||
|
font-size: var(--font-body);
|
||||||
|
|
||||||
|
border: solid 1.25px var(--neutral-light);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-input-error input {
|
||||||
|
border: solid 1.75px var(--error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-input-error p {
|
||||||
|
color: var(--error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-primary,
|
||||||
|
.button-disabled {
|
||||||
|
border-radius: var(--radius);
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
padding: 3px 16px;
|
||||||
|
font-size: var(--font-body);
|
||||||
|
font-weight: var(--medium-weight);
|
||||||
|
|
||||||
|
background-color: var(--primary);
|
||||||
|
|
||||||
|
transition: background-color 150ms;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-primary:hover {
|
||||||
|
background-color: var(--primary-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-primary:active {
|
||||||
|
background-color: var(--primary-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-disabled {
|
||||||
|
color: var(--neutral-dark);
|
||||||
|
background-color: var(--neutral-light);
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useAuthStore } from '@/stores/AuthStore';
|
||||||
|
import { deleteCookie } from '@/utils/cookies';
|
||||||
|
import { RouterLink, useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
deleteCookie('JWTtoken')
|
||||||
|
deleteCookie('JWTexpire')
|
||||||
|
useAuthStore().clearAll()
|
||||||
|
router.push('/')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<header>
|
||||||
|
<h2><span style="color:var(--brand-blue)">Go</span>hCMS</h2>
|
||||||
|
<nav>
|
||||||
|
<RouterLink to="/home">Accueil</RouterLink>
|
||||||
|
<RouterLink to="/articles">Articles</RouterLink>
|
||||||
|
<a class="disconnect-link" @click="logout()">Se déconnecter</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 24px;
|
||||||
|
|
||||||
|
box-shadow: #0005 0 0 10px;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
header h2 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px 0;
|
||||||
|
|
||||||
|
width: fit-content;
|
||||||
|
|
||||||
|
color: var(--neutral-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
header h2 span {
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav a {
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 4px 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 150ms background-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav a:hover {
|
||||||
|
background-color: var(--neutral-verylight);
|
||||||
|
}
|
||||||
|
|
||||||
|
.disconnect-link {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disconnect-link:hover {
|
||||||
|
background-color: #fff;
|
||||||
|
color: var(--error);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
|
|
||||||
|
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
|
|
||||||
import './assets/main.css'
|
import '@/assets/base.css'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,68 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory, type NavigationGuard } from 'vue-router'
|
||||||
|
import { useAuthStore } from '@/stores/AuthStore'
|
||||||
|
import { nextTick } from 'vue'
|
||||||
|
import Debug from '@/views/Debug.vue'
|
||||||
|
import Login from '@/views/Login.vue'
|
||||||
import Home from '@/views/Home.vue'
|
import Home from '@/views/Home.vue'
|
||||||
|
import Articles from '@/views/Articles.vue'
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
|
name: 'login',
|
||||||
|
component: Login,
|
||||||
|
meta: {
|
||||||
|
title: 'GohCMS - Connexion'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/debug',
|
||||||
|
name: 'debug',
|
||||||
|
component: Debug,
|
||||||
|
meta: {
|
||||||
|
title: 'GohCMS - Debug'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/home',
|
||||||
name: 'home',
|
name: 'home',
|
||||||
component: Home
|
component: Home,
|
||||||
|
meta: {
|
||||||
|
title: 'GohCMS - Accueil'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/articles',
|
||||||
|
name: 'articles',
|
||||||
|
component: Articles,
|
||||||
|
meta: {
|
||||||
|
title: 'GohCMS - Articles'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
router.beforeEach(async (to, from) => {
|
||||||
|
const isTokenValid = useAuthStore().isValid()
|
||||||
|
if (to.name === 'login') {
|
||||||
|
if (isTokenValid) return {
|
||||||
|
name: 'home'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!isTokenValid) {
|
||||||
|
return {
|
||||||
|
name: 'login',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
router.afterEach((to, from) => {
|
||||||
|
nextTick(() => {
|
||||||
|
document.title = to.meta.title as string ?? 'GohCMS'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { getCookie } from "@/utils/cookies";
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useErrorsStore } from "./ErrorsStore";
|
||||||
|
|
||||||
|
export interface jwtFormat {
|
||||||
|
code: number,
|
||||||
|
expire: string,
|
||||||
|
token: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAuthStore = defineStore("AuthStore", () => {
|
||||||
|
const expire = ref('')
|
||||||
|
const token = ref('')
|
||||||
|
|
||||||
|
function clearAll(): void {
|
||||||
|
expire.value = ''
|
||||||
|
token.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSet(): boolean {
|
||||||
|
return token.value !== undefined && token.value !== ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function isExpired(): boolean {
|
||||||
|
const tokenDate = new Date(expire.value)
|
||||||
|
const currentDate = new Date()
|
||||||
|
|
||||||
|
if (currentDate.getTime() > tokenDate.getTime()) {
|
||||||
|
useErrorsStore().sessionExpired = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValid(): boolean {
|
||||||
|
return isSet() && !isExpired()
|
||||||
|
}
|
||||||
|
|
||||||
|
function initStore(): void {
|
||||||
|
const JWTtoken = getCookie('JWTtoken')
|
||||||
|
const JWTexpire = getCookie('JWTexpire')
|
||||||
|
if (JWTtoken !== "" && JWTexpire !== "") {
|
||||||
|
token.value = JWTtoken
|
||||||
|
expire.value = JWTexpire
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initStore()
|
||||||
|
|
||||||
|
return { expire, token, isValid, clearAll }
|
||||||
|
})
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { ref, type Ref } from "vue";
|
||||||
|
|
||||||
|
interface storeErrors {
|
||||||
|
sessionExpired: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useErrorsStore = defineStore("ErrorsStore", () => {
|
||||||
|
const errors: Ref<storeErrors> = ref({
|
||||||
|
sessionExpired: false
|
||||||
|
})
|
||||||
|
|
||||||
|
return errors
|
||||||
|
})
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
export interface cookie {
|
||||||
|
key: string,
|
||||||
|
value: string,
|
||||||
|
expire: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setCookie(cookieData: cookie): void {
|
||||||
|
document.cookie = `${cookieData.key}=${cookieData.value}; SameSite=Strict; Expires=${cookieData.expire};Secure`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCookie(cookieName: string): string {
|
||||||
|
return document.cookie.match('(^|;)\\s*' + cookieName + '\\s*=\\s*([^;]+)')?.pop() || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteCookie(cookieName: string): void {
|
||||||
|
setCookie({
|
||||||
|
key: cookieName,
|
||||||
|
value: '',
|
||||||
|
expire: new Date(0).toString()
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
export interface Pong {
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function pingApi(): Promise<String> {
|
|
||||||
let result: String = ''
|
|
||||||
await fetch('http://localhost:8080/ping')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(jsonResponse => result = jsonResponse.message)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<p>articles page</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// import EditorJS from '@editorjs/editorjs';
|
||||||
|
import { useAuthStore, type jwtFormat } from '@/stores/AuthStore';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
const email = ref('')
|
||||||
|
const password = ref('')
|
||||||
|
|
||||||
|
const baseURL = `http://${__APP_ENV__.APP_HOST_ADDRESS}:${__APP_ENV__.APP_API_PORT}`
|
||||||
|
|
||||||
|
function getArticles() {
|
||||||
|
fetch(`${baseURL}/articles/`, {
|
||||||
|
headers: { "Authorization": `Bearer ${useAuthStore().token}` }
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(result => console.log(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
function ping() {
|
||||||
|
fetch(`${baseURL}/ping/`, {
|
||||||
|
headers: { "Authorization": `Bearer ${useAuthStore().token}` }
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(result => console.log(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
function jwtHandler(apiResponse: jwtFormat): void {
|
||||||
|
if (apiResponse.code !== 200) {
|
||||||
|
console.log(apiResponse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
useAuthStore().token = apiResponse.token
|
||||||
|
useAuthStore().expire = apiResponse.expire
|
||||||
|
console.log('success! logged in.')
|
||||||
|
}
|
||||||
|
|
||||||
|
function login(email: string, password: string): void {
|
||||||
|
fetch(`${baseURL}/login/`, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
email: email,
|
||||||
|
password: password
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(result => jwtHandler(result))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>Connexion</h2>
|
||||||
|
|
||||||
|
<form @submit.prevent="login(email, password)">
|
||||||
|
<div>
|
||||||
|
<label for="email">Email address</label>
|
||||||
|
<input id="email" name="email" placeholder="email" type="text" v-model="email">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input id="password" name="password" placeholder="password" type="password" v-model="password">
|
||||||
|
</div>
|
||||||
|
<button type="submit">Se connecter</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<button class="button-primary" @click="getArticles()">get all articles</button>
|
||||||
|
<button class="button-primary" @click="ping()">ping</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,77 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// import EditorJS from '@editorjs/editorjs';
|
|
||||||
import { pingApi } from '@/utils/ping';
|
|
||||||
import { onMounted, ref } from 'vue';
|
|
||||||
|
|
||||||
const inputArticleID = ref('')
|
|
||||||
const username = ref('')
|
|
||||||
const password = ref('')
|
|
||||||
|
|
||||||
onMounted(async function() {
|
|
||||||
console.log('ping...')
|
|
||||||
const pong = await pingApi()
|
|
||||||
console.log(pong)
|
|
||||||
})
|
|
||||||
|
|
||||||
function addArticle() {
|
|
||||||
fetch(`http://localhost:8080/articles/${Math.random() * 100}`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {"Authorization" : `Basic ${btoa(`${username.value}:${password.value}`)}`},
|
|
||||||
body: JSON.stringify({
|
|
||||||
content: {},
|
|
||||||
date: new Date().getTime()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(result => console.log(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteArticle(articleID: String) {
|
|
||||||
fetch(`http://localhost:8080/articles/${articleID}`, {
|
|
||||||
method: "DELETE",
|
|
||||||
headers: {"Authorization" : `Basic ${btoa(`${username.value}:${password.value}`)}`},
|
|
||||||
body: JSON.stringify({
|
|
||||||
id_name: articleID
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(result => console.log(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
function getArticle(articleID: String) {
|
|
||||||
fetch(`http://localhost:8080/articles/${articleID}`, {
|
|
||||||
headers: {"Authorization" : `Basic ${btoa(`${username.value}:${password.value}`)}`},
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(result => console.log(result))
|
|
||||||
}
|
|
||||||
|
|
||||||
function auth(type: string, username: string, password: string) {
|
|
||||||
fetch(`http://localhost:8080/${type}`, {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({
|
|
||||||
email: username,
|
|
||||||
password: password
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(result => console.log(result))
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<p>Salut à tous</p>
|
<p>home page</p>
|
||||||
<button @click="addArticle">add rand article</button>
|
|
||||||
<button @click="getArticle('')">display articles in console</button> <br>
|
|
||||||
<label for="articleIDinput"></label>
|
|
||||||
<input id="articleIDinput" name="articleIDinput" type="text" placeholder="article ID" v-model="inputArticleID">
|
|
||||||
<button @click="deleteArticle(inputArticleID)">delete this article</button>
|
|
||||||
<button @click="getArticle(inputArticleID)">get this article</button> <br>
|
|
||||||
|
|
||||||
<input type="text" name="username" id="username" v-model="username" placeholder="username">
|
|
||||||
<input type="text" name="password" id="password" v-model="password" placeholder="password">
|
|
||||||
<button @click="auth('login', username, password)">login</button>
|
|
||||||
<button @click="auth('logout', username, password)">logout</button>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useAuthStore, type jwtFormat } from '@/stores/AuthStore';
|
||||||
|
import { useErrorsStore } from '@/stores/ErrorsStore';
|
||||||
|
import { setCookie } from '@/utils/cookies';
|
||||||
|
import { ref, type Ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const isTokenOK: Ref<boolean|undefined> = ref(undefined)
|
||||||
|
const router = useRouter()
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
const email = ref('')
|
||||||
|
const password = ref('')
|
||||||
|
|
||||||
|
const isFormEmpty: ()=>boolean = () => {
|
||||||
|
return email.value === '' || password.value === ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateJWTcookies(JWTdata: jwtFormat): void {
|
||||||
|
const cookieExpire = new Date(JWTdata.expire)
|
||||||
|
cookieExpire.setDate(cookieExpire.getDate() + 1)
|
||||||
|
|
||||||
|
setCookie({
|
||||||
|
key: 'JWTtoken',
|
||||||
|
value: JWTdata.token,
|
||||||
|
expire: cookieExpire.toString()
|
||||||
|
})
|
||||||
|
setCookie({
|
||||||
|
key: 'JWTexpire',
|
||||||
|
value: JWTdata.expire,
|
||||||
|
expire: cookieExpire.toString()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableErrors(): void {
|
||||||
|
useErrorsStore().sessionExpired = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function jwtHandler(apiResponse: jwtFormat): void {
|
||||||
|
if (apiResponse.code !== 200) {
|
||||||
|
console.error(apiResponse)
|
||||||
|
isTokenOK.value = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updateJWTcookies(apiResponse)
|
||||||
|
disableErrors()
|
||||||
|
authStore.token = apiResponse.token
|
||||||
|
authStore.expire = apiResponse.expire
|
||||||
|
isTokenOK.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function login(email: string, password: string): void {
|
||||||
|
fetch(`http://${__APP_ENV__.APP_HOST_ADDRESS}:${__APP_ENV__.APP_API_PORT}/login/`, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
email: email,
|
||||||
|
password: password
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(result => {
|
||||||
|
jwtHandler(result)
|
||||||
|
if (isTokenOK.value === true) {
|
||||||
|
router.push('/home')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="login-page">
|
||||||
|
<div class="login-form">
|
||||||
|
<h2>Connexion à <span style="color:var(--brand-blue)">Go</span>hCMS</h2>
|
||||||
|
|
||||||
|
<form @submit.prevent="login(email, password)">
|
||||||
|
<div class="inputs-group">
|
||||||
|
<div :class="`label-input${isTokenOK === false ? '-error' : ''}`">
|
||||||
|
<label for="email">Adresse mail</label>
|
||||||
|
<input id="email" name="email" placeholder="E-mail" type="text" v-model="email">
|
||||||
|
</div>
|
||||||
|
<div :class="`label-input${isTokenOK === false ? '-error' : ''}`">
|
||||||
|
<label for="password">Mot de passe</label>
|
||||||
|
<input id="password" name="password" placeholder="Mot de passe" type="password" v-model="password">
|
||||||
|
<p v-if="isTokenOK === false">❌ E-mail et/ou mot de passe incorrect(s).</p>
|
||||||
|
<p v-else-if="useErrorsStore().sessionExpired">⌛ Votre session a expiré.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button :class="`button-${isFormEmpty() ? 'disabled' : 'primary'}`" type="submit" :disabled="isFormEmpty()">
|
||||||
|
Se connecter
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
h2 {
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 span {
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-page {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form {
|
||||||
|
width: fit-content;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
|
padding: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputs-group,
|
||||||
|
.login-form form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form form {
|
||||||
|
gap: 32px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,14 +1,27 @@
|
|||||||
import { fileURLToPath, URL } from 'node:url'
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
import { defineConfig, loadEnv } from 'vite'
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig(({ command, mode }) => {
|
||||||
|
const env1 = loadEnv(mode, "./../../", "")
|
||||||
|
const env2 = loadEnv(mode, process.cwd(), "")
|
||||||
|
|
||||||
|
return {
|
||||||
plugins: [vue()],
|
plugins: [vue()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
define: {
|
||||||
|
__APP_ENV__: {
|
||||||
|
...env1,
|
||||||
|
...env2,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
port: env1.APP_FRONT_PORT,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user