Skip to content
Snippets Groups Projects
Commit 5f403c85 authored by Bastien DUMONT's avatar Bastien DUMONT :angel:
Browse files

Merge branch 'feat/US827-custom-alert' into 'dev'

Feat/us827 custom alert

See merge request web-et-numerique/llle_project/backoffice-server!38
parents 4b28ec98 1a7961ec
Branches
Tags v1.2.4
4 merge requests!73Deploy Openshift v2,!65MEP: removed Meilisearch,!52back-office SGE before canary release,!38Feat/us827 custom alert
Pipeline #42638 passed
# Common settings
HOSTNAME=localhost
ADMIN_ROLE
DEBUG_MODE
MOCK_OAUTH2
HTTPS_PORT
IMAGE_FOLDER
# Needed to user OAuth2 authentication :
REDIRECT_URL
CLIENT_ID
CLIENT_SECRET
AUTH_URL
TOKEN_URL
USERINFO_URL
# Access to the database
DATABASE_USER
DATABASE_PASSWORD
DATABASE_NAME
SGE_API_TOKEN
MEILI_MASTER_KEY
MEILI_HOST
\ No newline at end of file
......@@ -12,19 +12,19 @@
"program": "${workspaceFolder}/main.go",
"env": {
"HOSTNAME": "localhost",
"ADMIN_ROLE" : "ADMINS",
"ADMIN_ROLE": "ADMINS",
"DEBUG_MODE": "true",
"MOCK_OAUTH2": "true",
"HTTPS_PORT": "1443",
"IMAGE_FOLDER": "image-lib",
"REDIRECT_URL": "http://localhost:1443/OAuth2Callback",
"REDIRECT_URL": "https://localhost:1443/OAuth2Callback",
"CLIENT_ID": "foo",
"CLIENT_SECRET": "bar",
"AUTH_URL": "http://localhost:8090/auth",
"TOKEN_URL": "http://localhost:8090/token",
"USERINFO_URL": "http://localhost:8090/admininfo",
"DATABASE_USER": "root",
"DATABASE_PASSWORD": "password",
"DATABASE_NAME": "backoffice",
......@@ -58,4 +58,4 @@
"showLog": true
}
]
}
\ No newline at end of file
}
......@@ -14,13 +14,13 @@ This repository contains the backend part of this backoffice.
# How to setup :
This backend should be deployed with the frontend from [this repo](https://forge.grandlyon.com/web-et-numerique/llle_project/backoffice-client)
This backend should be deployed with the frontend from [this repo](https://forge.grandlyon.com/web-et-numerique/llle_project/backoffice-client)
However this backend can be run in standalone :
- Clone the repository
- Set a .env file at the root and add all variables declared in the [template.env](https://forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server/-/blob/dev/template.env) file
- Set a .env file at the root and add all variables declared in the [template.env](https://forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server/-/blob/dev/.env.template)
- (**Optionnal**) you can create new certificates by running `cd dev_certificates && ./generate-certificates.sh`
......@@ -30,7 +30,8 @@ Once deployed, you can access to a Swagger documentation of the API on https://$
## Launch locally
To launch it locally :
To launch it locally :
- Run `docker-compose up -d`
In local you can access phpmyadmin interface to manage the database : http://localhost:8008
......@@ -39,11 +40,16 @@ In local you can access phpmyadmin interface to manage the database : http://loc
To launch the backend for development goal:
- edit *main.go* file, uncomment the last line "log.Fatal(http.ListenAndServe(":"+strconv.Itoa(httpsPort), rootMux.Router))" and comment the line above "log.Fatal(http.ListenAndServeTLS(":"+strconv.Itoa(httpsPort), "./dev_certificates/localhost.crt", "./dev_certificates/localhost.key", rootMux.Router))"
- edit _main.go_ file, uncomment the last line "log.Fatal(http.ListenAndServe(":"+strconv.Itoa(httpsPort), rootMux.Router))" and comment the line above "log.Fatal(http.ListenAndServeTLS(":"+strconv.Itoa(httpsPort), "./dev_certificates/localhost.crt", "./dev_certificates/localhost.key", rootMux.Router))"
- This way you disable https so make sure your requests goes on http adresses
- in *vscode/launch.json* set "REDIRECT_URL" to "http://localhost:1443/OAuth2Callback",
- in _vscode/launch.json_ set "REDIRECT_URL" to "http://localhost:1443/OAuth2Callback",
- also comment the port and host values
- if you comment the DATABASE_USER line, it will launches with a sqlite database instead of mysql
- launch the app by pressing F5 on vscode, you will see the logs on the debug console.
- this way you won't have the login every time you relaunch the backend for development
## Build image for local
```
docker build . -t backoffice-server
```
......@@ -47,7 +47,7 @@ services:
PMA_HOST: database
backoffice-container:
image: backoffice
image: backoffice
depends_on:
database:
condition: service_healthy
......
package models
import (
"encoding/json"
"errors"
"log"
"net/http"
"gorm.io/gorm"
)
type CustomPopup struct {
ID uint `gorm:"<-:create"`
PopupEnabled bool `json:"popupEnabled"`
Title string `json:"title"`
Description string `json:"description"`
}
// GetCustomPopup godoc
// @Summary Give status of custom popup
// @Description Give status of custom poup
// @Tags customPopup
// @Produce json
// @Success 200 {object} CustomPopup
// @Failure 404 {string} string "Not found"
// @Router /api/common/customPopup [get]
func (dh *DataHandler) GetCustomPopup(w http.ResponseWriter, r *http.Request) {
var popupInfo CustomPopup
err := dh.sqlClient.First(&popupInfo).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
http.Error(w, "custom popup status not found", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(popupInfo)
log.Printf("| get customPopup | %v", r.RemoteAddr)
}
// SaveCustomPopup godoc
// @Summary Update custom popup content
// @Description Update custom popup content
// @Tags customPopup
// @Accept json
// @Produce json
// @Success 200 {object} CustomPopup "Updated successfully"
// @Failure 400 {string} string "Bad Request"
// @Failure 500 {string} string "Internal server error"
// @Param customPopup body CustomPopup true "CustomPopup to create/update with new content"
// @Router /api/admin/customPopup [put]
func (dh *DataHandler) SaveCustomPopup(w http.ResponseWriter, r *http.Request) {
if r.Body == http.NoBody {
http.Error(w, "request body is empty", http.StatusBadRequest)
return
}
decoder := json.NewDecoder(r.Body)
var customPopup CustomPopup
err := decoder.Decode(&customPopup)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
var updatedCustomPopup CustomPopup
err = dh.sqlClient.First(&updatedCustomPopup).Error
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
updatedCustomPopup.PopupEnabled = customPopup.PopupEnabled
updatedCustomPopup.Title = customPopup.Title
updatedCustomPopup.Description = customPopup.Description
dh.sqlClient.Save(&updatedCustomPopup)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(customPopup)
log.Printf("| updated customPopup | %v", r.RemoteAddr)
}
......@@ -46,9 +46,11 @@ func NewDataHandler() *DataHandler {
sqlClient.AutoMigrate(&MonthlyNews{})
sqlClient.AutoMigrate(&Poll{})
sqlClient.AutoMigrate(&PartnersInfo{})
sqlClient.AutoMigrate(&CustomPopup{})
sqlClient.AutoMigrate(&Price{})
sqlClient.AutoMigrate(&Consent{})
//TODO fix recreate on server init
// Create default partner status
sqlClient.Create(&PartnersInfo{
GRDFFailure: false,
......@@ -57,6 +59,13 @@ func NewDataHandler() *DataHandler {
NotificationActivated: false,
})
// Create default custom popup
sqlClient.Create(&CustomPopup{
PopupEnabled: false,
Title: "",
Description: "",
})
// Meilisearch setup
meiliClient := meilisearch.NewClient(meilisearch.ClientConfig{
Host: common.StringValueFromEnv("MEILI_HOST", ""),
......
......@@ -43,6 +43,7 @@ func CreateRootMux() RootMux {
r.HandleFunc("/api/common/monthlyReport", dh.GetMonthlyReport).Methods(http.MethodGet)
r.HandleFunc("/api/common/monthlyReport/{year}/{month}", dh.GetMonthlyReport).Methods(http.MethodGet)
r.HandleFunc("/api/common/partnersInfo", dh.GetPartnersInfo).Methods(http.MethodGet)
r.HandleFunc("/api/common/customPopup", dh.GetCustomPopup).Methods(http.MethodGet)
r.HandleFunc("/api/common/prices", dh.GetAllPrices).Methods(http.MethodGet)
r.HandleFunc("/api/common/prices/{fluidtype}", dh.GetPricesByFluid).Methods(http.MethodGet)
......@@ -69,6 +70,7 @@ func CreateRootMux() RootMux {
apiAdmin.HandleFunc("/poll/{year}/{month}", dh.DeletePoll).Methods(http.MethodDelete)
apiAdmin.HandleFunc("/partnersInfo", dh.SavePartnersInfo).Methods(http.MethodPut)
apiAdmin.HandleFunc("/customPopup", dh.SaveCustomPopup).Methods(http.MethodPut)
apiAdmin.HandleFunc("/imageNames", file.GetEcogestureImages).Methods(http.MethodGet)
......
......@@ -29,6 +29,8 @@ var (
newPollStr string
partnersInfo = models.PartnersInfo{ID: 1, GRDFFailure: false, EnedisFailure: false, EGLFailure: true, NotificationActivated: true}
partnersInfoStr string
customPopupInfo = models.CustomPopup{ID: 1, PopupEnabled: false, Title: "Alerte personnalisée", Description: "Ecolyo 4ever"}
customPopupStr string
consent = models.Consent{Firstname: "Foo", Lastname: "Bar", PointID: 123456}
consentStr string
noH map[string]string
......@@ -65,6 +67,8 @@ func TestMain(m *testing.M) {
monthlyInfoStr = string(monthlyInfoBytes)
partnersInfoBytes, _ := json.Marshal(partnersInfo)
partnersInfoStr = string(partnersInfoBytes)
customPopupBytes, _ := json.Marshal(customPopupInfo)
customPopupStr = string(customPopupBytes)
newPollBytes, _ := json.Marshal(newPoll)
newPollStr = string(newPollBytes)
consentBytes, _ := json.Marshal(consent)
......@@ -90,9 +94,11 @@ func TestAll(t *testing.T) {
sgeTests(t)
}
/**
/*
*
SECURITY TESTS (this tests are to check that the security protections works)
**/
*
*/
func oauth2Tests(t *testing.T) {
// Create the tester
ts, do, _ := createTester(t)
......@@ -101,9 +107,11 @@ func oauth2Tests(t *testing.T) {
do("GET", "/OAuth2Login", noH, "", http.StatusInternalServerError, "invalid oauth state")
}
/**
/*
*
UNLOGGED USER TESTS (this tests are to check that the security protections works)
**/
*
*/
func unloggedTests(t *testing.T) {
// Create the tester
ts, do, _ := createTester(t)
......@@ -119,9 +127,11 @@ func unloggedTests(t *testing.T) {
do("GET", "/api/common/partnersInfo", noH, "", http.StatusOK, `{"ID":1,"grdf_failure":false,"enedis_failure":false,"egl_failure":false,"notification_activated":false}`)
}
/**
/*
*
ADMIN TESTS (this tests are to check that an administrator can edit a newsletter's content)
**/
*
*/
func adminTests(t *testing.T) {
// Create the tester
ts, do, _ := createTester(t)
......@@ -179,13 +189,13 @@ func adminTests(t *testing.T) {
// Try to update the partnersInfo (must pass)
do("PUT", "/api/admin/partnersInfo", xsrfHeader, partnersInfoStr, http.StatusOK, partnersInfoStr)
// Try to get the monthlyInfo created (must pass)
// Try to get the partnersInfo created (must pass)
do("GET", "/api/common/partnersInfo", xsrfHeader, "", http.StatusOK, partnersInfoStr)
// Try to update the partnersInfo (must pass)
do("PUT", "/api/admin/partnersInfo", xsrfHeader, partnersInfoStr, http.StatusOK, partnersInfoStr)
// Try to get the monthlyInfo created (must pass)
do("GET", "/api/common/partnersInfo", xsrfHeader, "", http.StatusOK, partnersInfoStr)
// Try to update the customPopup (must pass)
do("PUT", "/api/admin/customPopup", xsrfHeader, customPopupStr, http.StatusOK, customPopupStr)
// Try to get the partnersInfo created (must pass)
do("GET", "/api/common/customPopup", xsrfHeader, "", http.StatusOK, customPopupStr)
// Try to delete the monthlyNews created (must pass)
do("DELETE", "/api/admin/monthlyNews/2021/1", xsrfHeader, "", http.StatusOK, "successful delete")
......
# Common settings
HOSTNAME=
ADMIN_ROLE=
DEBUG_MODE=
MOCK_OAUTH2=
HTTPS_PORT=
IMAGE_FOLDER=
# Needed to user OAuth2 authentication :
REDIRECT_URL=
CLIENT_ID=
CLIENT_SECRET=
AUTH_URL=
TOKEN_URL=
USERINFO_URL=
# Access to the database
DATABASE_USER=
DATABASE_PASSWORD=
DATABASE_NAME=
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment