diff --git a/.env.template b/.env.template index 1771b0ddd7123700a539a873be9ca5d6e5eb230c..6eff757694efae4006cac09ac11dbea8d7642627 100644 --- a/.env.template +++ b/.env.template @@ -5,7 +5,7 @@ ADMIN_ROLE=ADMINS DEBUG_MODE MOCK_OAUTH2 HTTPS_PORT -IMAGE_FOLDER +IMAGE_FOLDER=mnt/image-lib # Needed to user OAuth2 authentication : REDIRECT_URL diff --git a/.gitignore b/.gitignore index 8d832a6f90912a8b03b6949ed96ad8a6630c91a4..d2cb364414f9cf2390d2ef842be9fe3dfc97adcb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ .env backoffice.db -db_data/ \ No newline at end of file +db_data/ +mnt/ +__debug_bin* + +# Swagger documentation +docs/swagger.json +docs/swagger.yaml diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000000000000000000000000000000000..3ab34ffd7cbd31da6326bc575535ca07d690eaff --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Backoffice Server", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "${workspaceFolder}/main.go", + "envFile": "${workspaceFolder}/.env", + "showLog": true + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 11a1978e8b65f62fa6d8ce4f9b7fba81a8f0623b..729c384ee9f3e02854db8881d8b48b0d28c353f7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -25,18 +25,26 @@ "backoffice", "ecogesture", "ecolyo", + "Enedis", "firstname", "fluidtype", "godoc", "gorm", "grandlyon", + "GRDF", + "imagebase", "insee", "lastname", "llle", "mysqldump", "numerique", + "redir", "rootmux", "selfdata", - "uroot" + "Sharedfor", + "Successfull", + "uroot", + "userinfo", + "xsrf" ] } \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 7a0ec0fec112bd1e7456912e17c7a0be00c81377..f4efa81e01e91845d2e5aa036ec955ad556f9561 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ # STEP 1 build executable binary # ################################## -FROM golang as builder +FROM golang AS builder # Install git + SSL ca certificates. # Git is required for fetching the dependencies. @@ -28,11 +28,6 @@ WORKDIR /app ADD . . -# Build Swagger documentation -RUN go install github.com/swaggo/swag/cmd/swag@latest -RUN go get github.com/swaggo/swag -RUN swag init -g ./internal/rootmux/rootmux.go - # Get dependencies and run tests RUN go version RUN go get -d -v @@ -43,6 +38,8 @@ RUN CGO_ENABLED=0 go build \ -ldflags='-w -s -extldflags "-static"' -a \ -o /app/backoffice-server . +RUN mkdir -p /app/mnt /app/mnt/configs + RUN chown -Rf "${UID}" ./* # Allow running on ports < 1000 @@ -67,9 +64,6 @@ COPY --from=builder /app/backoffice-server /app/backoffice-server COPY --from=builder /app/dev_certificates /app/dev_certificates COPY --from=builder /app/mnt/configs /app/mnt/configs -# Copy swagger documentation folder -COPY --from=builder /app/docs /app/docs - # Use an unprivileged user. USER appuser:appuser diff --git a/docker-compose.yml b/docker-compose.yml index e4a8d5a610e4fa1c361ea082835329b6ca9defba..374ce59b43dd396bdb72ce1b4dbd08fa215d5f17 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,36 +1,36 @@ -version: '3.1' +version: "3.1" -services: - database: - image: mysql:8 - healthcheck: - test: mysqladmin ping -h 127.0.0.1 -u root --password=$$MYSQL_ROOT_PASSWORD - interval: 5s - timeout: 10s - retries: 60 - volumes: - - ./db_data:/var/lib/mysql - networks: - - ecolyo-agent-network - ports: - - 3306:3306 - environment: - MYSQL_ROOT_PASSWORD: ${DATABASE_PASSWORD} - MYSQL_DATABASE: ${DATABASE_NAME} +services: + database: + image: mysql:8 + healthcheck: + test: mysqladmin ping -h 127.0.0.1 -u root --password=$$MYSQL_ROOT_PASSWORD + interval: 5s + timeout: 10s + retries: 60 + volumes: + - ./db_data:/var/lib/mysql + networks: + - ecolyo-agent-network + ports: + - 3306:3306 + environment: + MYSQL_ROOT_PASSWORD: ${DATABASE_PASSWORD} + MYSQL_DATABASE: ${DATABASE_NAME} - phpmyadmin: - image: phpmyadmin/phpmyadmin:latest - depends_on: - - database - networks: - - ecolyo-agent-network - ports: - - 8008:80 - environment: - PMA_HOST: database + phpmyadmin: + image: phpmyadmin/phpmyadmin:latest + depends_on: + - database + networks: + - ecolyo-agent-network + ports: + - 8008:80 + environment: + PMA_HOST: database volumes: - db_data: + db_data: networks: - ecolyo-agent-network: \ No newline at end of file + ecolyo-agent-network: diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 2670382e45bcb5ab9aad6b68a1e57b798888e20b..ed83d4e2e732c62a91b125e10ab66c2a2e875e91 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -10,6 +10,7 @@ import ( "strings" "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/common" + "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/constants" "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/tokens" ) @@ -76,7 +77,7 @@ func ValidateAuthMiddleware(next http.Handler, allowedRoles []string, checkXSRF if perr == nil { redirectTo += ":" + port } - w.Header().Set("Content-Type", "text/html") + w.Header().Set(constants.ContentType, "text/html") w.WriteHeader(http.StatusUnauthorized) responseContent := fmt.Sprintf("error extracting token: %v<meta http-equiv=\"Refresh\" content=\"0; url=https://%v#login\"/>", err.Error(), redirectTo) fmt.Fprint(w, responseContent) diff --git a/internal/common/common.go b/internal/common/common.go index 3514b4531a5c3e5cb649868e6540efecd34cd913..c2bd4ed727a0cc09aa9b3ee07dbbf230f34665f8 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -18,6 +18,8 @@ import ( "github.com/go-chi/chi/v5" ) +const missingQueryElement = "missing query element" + var ( disableLogFatal = false lock sync.Mutex // Mutex used to lock file writing @@ -156,7 +158,7 @@ func YearMonthFromRequest(r *http.Request) (year int, month int, err error) { monthStr := chi.URLParam(r, "month") if yearStr == "" || monthStr == "" { - return 0, 0, errors.New("missing query element") + return 0, 0, errors.New(missingQueryElement) } year, err = strconv.Atoi(yearStr) @@ -191,7 +193,7 @@ func PageLimitFromRequest(r *http.Request) (page int, limit int, err error) { limitStr := r.URL.Query().Get("limit") if pageStr == "" || limitStr == "" { - return 0, 0, errors.New("missing query element") + return 0, 0, errors.New(missingQueryElement) } page, err = strconv.Atoi(pageStr) @@ -215,12 +217,12 @@ func PageLimitFromRequest(r *http.Request) (page int, limit int, err error) { return page, limit, nil } -//Get fluidType from Request +// Get fluidType from Request func FluidTypeFromRequest(r *http.Request) (fluidtype int, err error) { fluidTypeStr := chi.URLParam(r, "fluidtype") if fluidTypeStr == "" { - return 100, errors.New("missing query element") + return 100, errors.New(missingQueryElement) } fluidtype, err = strconv.Atoi(fluidTypeStr) diff --git a/internal/constants/constants.go b/internal/constants/constants.go new file mode 100644 index 0000000000000000000000000000000000000000..f56f127f6ec8e5325d9bb7990bcddc6660b67c38 --- /dev/null +++ b/internal/constants/constants.go @@ -0,0 +1,7 @@ +package constants + +const ( + ContentType = "Content-Type" + Json = "application/json" + FormUrlEncoded = "application/x-www-form-urlencoded" +) diff --git a/internal/file/file.go b/internal/file/file.go index 53e515b3cc87b4d5722ff2119e4b085862fb962a..050cc01192ded776e9304a53a9847d7107fec9c3 100644 --- a/internal/file/file.go +++ b/internal/file/file.go @@ -8,6 +8,7 @@ import ( "net/http" "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/common" + "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/constants" ) var ( @@ -22,7 +23,7 @@ func GetEcogestureImages(w http.ResponseWriter, r *http.Request) { return } - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) w.Write(jsondata) log.Printf("| get image names | %v", r.RemoteAddr) } diff --git a/internal/file/file_test.go b/internal/file/file_test.go index d8dcc99b3b5b767933c9633adea7139ece1fab46..28a3feb530eb63fc6e14afd8a578347f7f943c9b 100644 --- a/internal/file/file_test.go +++ b/internal/file/file_test.go @@ -25,13 +25,13 @@ func TestFilenamesFromFolder(t *testing.T) { t.Errorf(`error: %s`, err) return } - jsondata, err := json.Marshal(filenames) + jsonData, err := json.Marshal(filenames) if err != nil { t.Errorf(`error: %s`, err) return } - if string(jsondata) != expected { - t.Errorf(`unexpected answer: got %s want %s`, string(jsondata), expected) + if string(jsonData) != expected { + t.Errorf(`unexpected answer: got %s want %s`, string(jsonData), expected) return } os.RemoveAll("test") diff --git a/internal/mocks/mocks.go b/internal/mocks/mocks.go index 724552d295444b5bbc6b24e095144deb69d79088..41c0663619f46451c9d6e3c43feee50c37dc6c34 100644 --- a/internal/mocks/mocks.go +++ b/internal/mocks/mocks.go @@ -4,6 +4,8 @@ package mocks import ( "fmt" "net/http" + + "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/constants" ) var ( @@ -33,7 +35,7 @@ func CreateMockOAuth2() *http.ServeMux { // Returns access token back to the user mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/x-www-form-urlencoded") + w.Header().Set(constants.ContentType, constants.FormUrlEncoded) w.Write([]byte("access_token=mocktoken&scope=user&token_type=bearer")) }) // Returns userinfo back to the user (with an animator user) @@ -51,7 +53,7 @@ func CreateMockOAuth2() *http.ServeMux { }) // Returns userinfo back to the user (with an admin user) mux.HandleFunc("/admininfo", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) w.Write([]byte(`{ "displayName": "Ad MIN", "memberOf": [ diff --git a/internal/models/consent.go b/internal/models/consent.go index 6a79ba09751d117a8421d4b14ab9155e21a3ea4b..88e6a1771e08b6c474226b425181b4608e74f86e 100644 --- a/internal/models/consent.go +++ b/internal/models/consent.go @@ -8,6 +8,7 @@ import ( "time" "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/common" + "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/constants" "gorm.io/gorm" ) @@ -48,6 +49,7 @@ type UpdateConsentBody struct { } // GetConsentById godoc +// // @Summary Get details of a specific consent // @Description Get details of a specific consent // @Tags consent @@ -74,13 +76,14 @@ func (dh *DataHandler) GetConsentById(w http.ResponseWriter, r *http.Request) { return } - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) json.NewEncoder(w).Encode(consent) log.Printf("| get consent | name : %v | %v", consent.Lastname, r.RemoteAddr) } // PostConsent godoc +// // @Summary Create a new consent // @Description Create a new consent // @Tags consent @@ -114,7 +117,7 @@ func (dh *DataHandler) PostConsent(w http.ResponseWriter, r *http.Request) { return } - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(consent) @@ -122,6 +125,7 @@ func (dh *DataHandler) PostConsent(w http.ResponseWriter, r *http.Request) { } // UpdateConsent godoc +// // @Summary Update a consent, giving it a serviceID // @Description Update a consent, giving it a serviceID // @Tags consent @@ -173,13 +177,14 @@ func (dh *DataHandler) UpdateConsent(w http.ResponseWriter, r *http.Request) { return } - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) json.NewEncoder(w).Encode(consent) log.Printf("| updated consent | name : %v | serviceID : %v | %v", consent.Lastname, consent.ServiceID, r.RemoteAddr) } // DeleteConsentById godoc +// // @Summary Delete a specific consent // @Description Delete a specific consent // @Tags consent @@ -219,6 +224,7 @@ func (dh *DataHandler) DeleteConsentById(w http.ResponseWriter, r *http.Request) } // SearchConsent godoc +// // @Summary Search for consents // @Description Search for consents based on the pointID // @Tags consent @@ -250,7 +256,7 @@ func (dh *DataHandler) SearchConsent(w http.ResponseWriter, r *http.Request) { pagination.TotalRows = totalRows pagination.Rows = consents - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) json.NewEncoder(w).Encode(pagination) log.Printf("| get all consents | limit : %d | page : %d | %v", limit, page, r.RemoteAddr) diff --git a/internal/models/customPopup.go b/internal/models/customPopup.go index 6e3dd2732ead6106a651fa1d2061967ac7d69b37..79b8f6cd6e5dd4ae3c70c1244384a49bb3d2b095 100644 --- a/internal/models/customPopup.go +++ b/internal/models/customPopup.go @@ -7,6 +7,7 @@ import ( "net/http" "time" + "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/constants" "gorm.io/gorm" ) @@ -19,6 +20,7 @@ type CustomPopup struct { } // GetCustomPopup godoc +// // @Summary Give status of custom popup // @Description Give status of custom poup // @Tags customPopup @@ -35,12 +37,13 @@ func (dh *DataHandler) GetCustomPopup(w http.ResponseWriter, r *http.Request) { return } - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.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 @@ -80,7 +83,7 @@ func (dh *DataHandler) SaveCustomPopup(w http.ResponseWriter, r *http.Request) { dh.sqlClient.Save(&updatedCustomPopup) - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) json.NewEncoder(w).Encode(customPopup) log.Printf("| updated customPopup | %v", r.RemoteAddr) } diff --git a/internal/models/mailSubject.go b/internal/models/mailSubject.go index c3005a2f841a27b0c0166caddbd13e3f9eb4277d..b0c5f6cf7370a7190e861fb7051bff622495fee5 100644 --- a/internal/models/mailSubject.go +++ b/internal/models/mailSubject.go @@ -6,6 +6,7 @@ import ( "net/http" "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/common" + "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/constants" ) type MailSubject struct { @@ -15,6 +16,7 @@ type MailSubject struct { } // GetSingleMailSubject godoc +// // @Summary Get details of a specific mailSubject // @Description Get details of a specific mailSubject // @Tags mailSubject @@ -31,7 +33,7 @@ func (dh *DataHandler) GetSingleMailSubject(w http.ResponseWriter, r *http.Reque return } - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) var mailSubject MailSubject err = dh.sqlClient.Where("year = ? AND month = ?", year, month).First(&mailSubject).Error @@ -44,6 +46,7 @@ func (dh *DataHandler) GetSingleMailSubject(w http.ResponseWriter, r *http.Reque } // SaveMailSubject godoc +// // @Summary Create/update a specific mailSubject' content // @Description Create/update a specific mailSubject' content // @Tags mailSubject @@ -103,6 +106,7 @@ func (dh *DataHandler) SaveMailSubject(w http.ResponseWriter, r *http.Request) { } // DeleteMailSubject godoc +// // @Summary Delete a specific mailSubject // @Description Delete a specific mailSubject // @Tags mailSubject diff --git a/internal/models/monthlyInfo.go b/internal/models/monthlyInfo.go index 971c9c5f1817b2390b20f350f4b93a2d912607b5..3d98125e950a7a6729f1ea819dfe3ec64152ccc7 100644 --- a/internal/models/monthlyInfo.go +++ b/internal/models/monthlyInfo.go @@ -6,6 +6,7 @@ import ( "net/http" "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/common" + "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/constants" ) type MonthlyInfo struct { @@ -16,6 +17,7 @@ type MonthlyInfo struct { } // GetAllMonthlyInfo godoc +// // @Summary List all monthlyInfo // @Description Get details of all monthlyInfo // @Tags monthlyInfo @@ -26,12 +28,13 @@ func (dh *DataHandler) GetAllMonthlyInfo(w http.ResponseWriter, r *http.Request) var monthlyInfo []MonthlyInfo dh.sqlClient.Find(&monthlyInfo) - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) json.NewEncoder(w).Encode(monthlyInfo) log.Printf("| get all monthlyInfo | %v", r.RemoteAddr) } // GetSingleMonthlyInfo godoc +// // @Summary Get details of a specific monthlyInfo // @Description Get details of a specific monthlyInfo // @Tags monthlyInfo @@ -48,7 +51,7 @@ func (dh *DataHandler) GetSingleMonthlyInfo(w http.ResponseWriter, r *http.Reque return } - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) var monthlyInfo MonthlyInfo err = dh.sqlClient.Where("year = ? AND month = ?", year, month).First(&monthlyInfo).Error @@ -61,6 +64,7 @@ func (dh *DataHandler) GetSingleMonthlyInfo(w http.ResponseWriter, r *http.Reque } // SaveMonthlyInfo godoc +// // @Summary Create/update a specific monthlyInfo' content // @Description Create/update a specific monthlyInfo' content // @Tags monthlyInfo @@ -128,6 +132,7 @@ func (dh *DataHandler) SaveMonthlyInfo(w http.ResponseWriter, r *http.Request) { } // DeleteMonthlyInfo godoc +// // @Summary Delete a specific monthlyInfo // @Description Delete a specific monthlyInfo // @Tags monthlyInfo diff --git a/internal/models/monthlyNews.go b/internal/models/monthlyNews.go index 9bdfdb8dccc79d8d987a558dca771e9463af2be6..9b687f144e860877ef966191ae554b32f198d4a2 100644 --- a/internal/models/monthlyNews.go +++ b/internal/models/monthlyNews.go @@ -6,6 +6,7 @@ import ( "net/http" "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/common" + "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/constants" ) type MonthlyNews struct { @@ -16,6 +17,7 @@ type MonthlyNews struct { } // GetAllMonthlyNews godoc +// // @Summary List all monthlyNews // @Description Get details of all monthlyNews // @Tags monthlyNews @@ -26,12 +28,13 @@ func (dh *DataHandler) GetAllMonthlyNews(w http.ResponseWriter, r *http.Request) var monthlyNews []MonthlyNews dh.sqlClient.Find(&monthlyNews) - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) json.NewEncoder(w).Encode(monthlyNews) log.Printf("| get all monthlyNews | %v", r.RemoteAddr) } // GetSingleMonthlyNews godoc +// // @Summary Get details of a specific monthlyNews // @Description Get details of a specific monthlyNews // @Tags monthlyNews @@ -48,7 +51,7 @@ func (dh *DataHandler) GetSingleMonthlyNews(w http.ResponseWriter, r *http.Reque return } - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) var monthlyNews MonthlyNews err = dh.sqlClient.Where("year = ? AND month = ?", year, month).First(&monthlyNews).Error @@ -61,6 +64,7 @@ func (dh *DataHandler) GetSingleMonthlyNews(w http.ResponseWriter, r *http.Reque } // SaveMonthlyNews godoc +// // @Summary Create/update a specific monthlyNews' content // @Description Create/update a specific monthlyNews' content // @Tags monthlyNews @@ -128,6 +132,7 @@ func (dh *DataHandler) SaveMonthlyNews(w http.ResponseWriter, r *http.Request) { } // DeleteMonthlyNews godoc +// // @Summary Delete a specific monthlyNews // @Description Delete a specific monthlyNews // @Tags monthlyNews diff --git a/internal/models/monthlyReport.go b/internal/models/monthlyReport.go index c5df9c1a349ddee50174f3e04391a3e5db0e5860..136a9b463d25908a708f82a0686e63512bea6716 100644 --- a/internal/models/monthlyReport.go +++ b/internal/models/monthlyReport.go @@ -5,6 +5,8 @@ import ( "fmt" "net/http" "strconv" + + "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/constants" ) type MonthlyReport struct { @@ -20,6 +22,7 @@ type MonthlyReport struct { } // GetMonthlyReport godoc +// // @Summary Get details of a specific monthlyReport // @Description Get details of a specific monthlyReport // @Tags monthlyReport @@ -48,7 +51,7 @@ func (dh *DataHandler) GetMonthlyReport(w http.ResponseWriter, r *http.Request) return } - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) json.NewEncoder(w).Encode(monthlyReport) } diff --git a/internal/models/partnersInfo.go b/internal/models/partnersInfo.go index cdea10aec1770bbba1051ccb1bfd934d8560a007..7d0b58c4515e85ab588f354170fc2d396fcc4739 100644 --- a/internal/models/partnersInfo.go +++ b/internal/models/partnersInfo.go @@ -6,6 +6,7 @@ import ( "log" "net/http" + "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/constants" "gorm.io/gorm" ) @@ -18,6 +19,7 @@ type PartnersInfo struct { } // GetPartnersInfo godoc +// // @Summary Give status of partners' services // @Description Give status of partners' services // @Tags partnersInfo @@ -34,12 +36,13 @@ func (dh *DataHandler) GetPartnersInfo(w http.ResponseWriter, r *http.Request) { return } - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) json.NewEncoder(w).Encode(partnersInfo) log.Printf("| get partnersInfo | %v", r.RemoteAddr) } // SavePartnersInfo godoc +// // @Summary Update partnersInfo' content // @Description Update partnersInfo' content // @Tags partnersInfo @@ -79,7 +82,7 @@ func (dh *DataHandler) SavePartnersInfo(w http.ResponseWriter, r *http.Request) dh.sqlClient.Save(&updatedPartnersInfo) - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) json.NewEncoder(w).Encode(partnersInfo) log.Printf("| updated partnersInfo | %v", r.RemoteAddr) diff --git a/internal/models/poll.go b/internal/models/poll.go index 76683a20660ebd17f3b2ae1259062c5aefd636b4..e046ee5839e031633ad46c941ec801e51b4c952b 100644 --- a/internal/models/poll.go +++ b/internal/models/poll.go @@ -6,6 +6,7 @@ import ( "net/http" "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/common" + "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/constants" ) type Poll struct { @@ -16,6 +17,7 @@ type Poll struct { } // GetAllPolls godoc +// // @Summary List all polls // @Description Get details of all polls // @Tags poll @@ -30,12 +32,13 @@ func (dh *DataHandler) GetAllPolls(w http.ResponseWriter, r *http.Request) { return } - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) json.NewEncoder(w).Encode(polls) log.Printf("| get all polls | %v", r.RemoteAddr) } // GetSinglePoll godoc +// // @Summary Get details of a specific poll // @Description Get details of a specific poll // @Tags poll @@ -52,7 +55,7 @@ func (dh *DataHandler) GetSinglePoll(w http.ResponseWriter, r *http.Request) { return } - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) var poll Poll err = dh.sqlClient.Where("year = ? AND month = ?", year, month).First(&poll).Error @@ -65,6 +68,7 @@ func (dh *DataHandler) GetSinglePoll(w http.ResponseWriter, r *http.Request) { } // SavePoll godoc +// // @Summary Update a specific poll' content // @Description Update a specific poll' content // @Tags poll @@ -125,6 +129,7 @@ func (dh *DataHandler) SavePoll(w http.ResponseWriter, r *http.Request) { } // DeletePoll godoc +// // @Summary Delete a specific poll // @Description Delete a specific poll // @Tags poll diff --git a/internal/models/price.go b/internal/models/price.go index 5503bf185fe55ec3d83bfad7709ba529fb7b9a3c..c0c59d127d9263587d2eb20bf7170f027c99f143 100644 --- a/internal/models/price.go +++ b/internal/models/price.go @@ -7,6 +7,7 @@ import ( "time" "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/common" + "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/constants" "gorm.io/gorm" ) @@ -19,6 +20,7 @@ type Price struct { } // GetPrices godoc +// // @Summary Get all prices // @Description Get all prices // @Tags prices @@ -30,12 +32,13 @@ func (dh *DataHandler) GetAllPrices(w http.ResponseWriter, r *http.Request) { var prices []Price dh.sqlClient.Find(&prices) - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) json.NewEncoder(w).Encode(prices) log.Printf("| Get all prices | %v", r.RemoteAddr) } // GetPricesByFluid godoc +// // @Summary Get all prices for a given fluid // @Description Get all prices for a given fluid // @Tags prices @@ -51,7 +54,7 @@ func (dh *DataHandler) GetPricesByFluid(w http.ResponseWriter, r *http.Request) return } var price []Price - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) err = dh.sqlClient.Where("fluid_type = ? ", fluidtype).Order("start_date desc").Find(&price).Error if err != nil { @@ -63,6 +66,7 @@ func (dh *DataHandler) GetPricesByFluid(w http.ResponseWriter, r *http.Request) } // SavePrice godoc +// // @Summary Add a new price entry // @Description Add a new price entry // @Tags prices @@ -143,9 +147,9 @@ func (dh *DataHandler) SavePrice(w http.ResponseWriter, r *http.Request) { } } - if isEditable == false { + if !isEditable { w.WriteHeader(http.StatusForbidden) - log.Printf("Unallowed to edit price because is not included in the the last 3 editable prices") + log.Printf("Unauthorized to edit price because is not included in the the last 3 editable prices") return } // Update existing price @@ -155,7 +159,7 @@ func (dh *DataHandler) SavePrice(w http.ResponseWriter, r *http.Request) { return } - w.Header().Set("Content-Type", "application/json") + w.Header().Set(constants.ContentType, constants.Json) json.NewEncoder(w).Encode(priceToUpdate) log.Printf("| Updated price | fluidType : %d price : %v | startDate : %v | %v", priceToUpdate.FluidType, priceToUpdate.Price, priceToUpdate.StartDate, r.RemoteAddr) } diff --git a/internal/rootmux/rootmux.go b/internal/rootmux/rootmux.go index 04e185c7d855b939f47234e24a144650da8a3d59..1d572e9fcab8fbaf61a1b4959c7632aee3848436 100644 --- a/internal/rootmux/rootmux.go +++ b/internal/rootmux/rootmux.go @@ -91,7 +91,7 @@ func CreateRootMux() RootMux { r.Delete("/consent/{id}", dh.DeleteConsentById) }) - r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {}) + r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { /** handles Oauth2 redirection */ }) swaggerFs := http.FileServer(http.Dir("./docs/")) r.Handle("/doc/*", http.StripPrefix("/doc/", swaggerFs)) diff --git a/internal/rootmux/rootmux_test.go b/internal/rootmux/rootmux_test.go index 9d86529c154af8225060079782a35c89b82b3433..5370dbaf074e88240ccfc543911ea28e0962a083 100644 --- a/internal/rootmux/rootmux_test.go +++ b/internal/rootmux/rootmux_test.go @@ -16,6 +16,14 @@ import ( "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/tokens" ) +const ( + SuccessfullDelete = "successful delete" + ErrorExtractingToken = "error extracting token" + ErrorInvalidToken = "invalid token" + ErrorEmptyBody = "request body is empty" + ErrorXSRF = "XSRF protection triggered" +) + var ( oAuth2Server *httptest.Server mailSubject = models.MailSubject{Year: 2021, Month: 1, Subject: "[Ecolyo] Newsletter"} @@ -90,7 +98,7 @@ func TestAll(t *testing.T) { os.Setenv("AUTH_URL", oAuth2Server.URL+"/auth-wrong-state") // Set the server to access failing OAuth2 endpoints oauth2Tests(t) os.Setenv("AUTH_URL", oAuth2Server.URL+"/auth") // Set the server to access the correct OAuth2Endpoint - unloggedTests(t) + noAuthTests(t) os.Setenv("USERINFO_URL", oAuth2Server.URL+"/animatorinfo") animatorTests(t) os.Setenv("USERINFO_URL", oAuth2Server.URL+"/admininfo") @@ -112,16 +120,16 @@ func oauth2Tests(t *testing.T) { /* * -UNLOGGED USER TESTS (this tests are to check that the security protections works) +NO AUTH USER TESTS (this tests are to check that the security protections works) * */ -func unloggedTests(t *testing.T) { +func noAuthTests(t *testing.T) { // Create the tester ts, do, _ := createTester(t) defer ts.Close() // Close the tester // Try to create a monthlyNews (must fail) - do("PUT", "/api/animator/monthlyNews", noH, monthlyNewsStr, http.StatusUnauthorized, "error extracting token") + do("PUT", "/api/animator/monthlyNews", noH, monthlyNewsStr, http.StatusUnauthorized, ErrorExtractingToken) // Try to get a monthlyReport (must fail because no parameters are given) do("GET", "/api/common/monthlyReport", noH, "", http.StatusBadRequest, "") // Try to get a monthlyReport (must pass empty) @@ -146,9 +154,9 @@ func animatorTests(t *testing.T) { json.Unmarshal([]byte(response), &token) xsrfHeader := map[string]string{"XSRF-TOKEN": token.XSRFToken} // Try to create a monthlyNews without the XSRF-TOKEN (must fail) - do("PUT", "/api/animator/monthlyNews", noH, monthlyNewsStr, http.StatusUnauthorized, "XSRF protection triggered") + do("PUT", "/api/animator/monthlyNews", noH, monthlyNewsStr, http.StatusUnauthorized, ErrorXSRF) // Try to create a monthlyNews without body (must fail) - do("PUT", "/api/animator/monthlyNews", xsrfHeader, "", http.StatusBadRequest, "request body is empty") + do("PUT", "/api/animator/monthlyNews", xsrfHeader, "", http.StatusBadRequest, ErrorEmptyBody) // Try to get a monthlyNews before it is created (must fail because not found) do("GET", "/api/animator/monthlyNews/2021/1", xsrfHeader, "", http.StatusNotFound, "") // Try to create a monthlyNews (must pass) @@ -161,9 +169,9 @@ func animatorTests(t *testing.T) { do("GET", "/api/common/monthlyReport?year=2021&month=1", noH, "", http.StatusOK, `{"year":2021,"month":1,"subject":"[Ecolyo] Votre bilan de décembre 2020","info":"","image":"","newsTitle":"Les nouveautés du service","newsContent":"Nouvelles fonctionnalités","question":"","link":""}`) // Try to create a poll without the XSRF-TOKEN (must fail) - do("PUT", "/api/animator/poll", noH, newPollStr, http.StatusUnauthorized, "XSRF protection triggered") + do("PUT", "/api/animator/poll", noH, newPollStr, http.StatusUnauthorized, ErrorXSRF) // Try to create a poll without body (must fail) - do("PUT", "/api/animator/poll", xsrfHeader, "", http.StatusBadRequest, "request body is empty") + do("PUT", "/api/animator/poll", xsrfHeader, "", http.StatusBadRequest, ErrorEmptyBody) // Try to get a poll before it is created (must fail because not found') do("GET", "/api/animator/poll/2021/1", xsrfHeader, "", http.StatusNotFound, "") // Try to create a poll (must pass) @@ -176,9 +184,9 @@ func animatorTests(t *testing.T) { do("GET", "/api/common/monthlyReport?year=2021&month=1", noH, "", http.StatusOK, `{"year":2021,"month":1,"subject":"[Ecolyo] Votre bilan de décembre 2020","info":"","image":"","newsTitle":"Les nouveautés du service","newsContent":"Nouvelles fonctionnalités","question":"pollQuestion","link":"pollLink"}`) // Try to create a monthlyInfo without the XSRF-TOKEN (must fail) - do("PUT", "/api/animator/monthlyInfo", noH, monthlyInfoStr, http.StatusUnauthorized, "XSRF protection triggered") + do("PUT", "/api/animator/monthlyInfo", noH, monthlyInfoStr, http.StatusUnauthorized, ErrorXSRF) // Try to create a monthlyInfo without body (must fail) - do("PUT", "/api/animator/monthlyInfo", xsrfHeader, "", http.StatusBadRequest, "request body is empty") + do("PUT", "/api/animator/monthlyInfo", xsrfHeader, "", http.StatusBadRequest, ErrorEmptyBody) // Try to get a monthlyInfo before it is created (must fail because not found) do("GET", "/api/animator/monthlyInfo/2021/1", xsrfHeader, "", http.StatusNotFound, "") // Try to create a monthlyInfo (must pass) @@ -201,7 +209,7 @@ func animatorTests(t *testing.T) { do("GET", "/api/common/customPopup", xsrfHeader, "", http.StatusOK, customPopupStr) // Try to delete the monthlyNews created (must pass) - do("DELETE", "/api/animator/monthlyNews/2021/1", xsrfHeader, "", http.StatusOK, "successful delete") + do("DELETE", "/api/animator/monthlyNews/2021/1", xsrfHeader, "", http.StatusOK, SuccessfullDelete) // Try to get a monthlyNews after it is deleted (must fail because not found) do("GET", "/api/animator/monthlyNews/2021/1", xsrfHeader, "", http.StatusNotFound, "") @@ -210,14 +218,14 @@ func animatorTests(t *testing.T) { // Try to get the monthlyReport (must pass) do("GET", "/api/common/monthlyReport?year=2021&month=1", noH, "", http.StatusOK, `{"year":2021,"month":1,"subject":"[Ecolyo] Newsletter","info":"Informations du mois","image":"imagebase64","newsTitle":"","newsContent":"","question":"pollQuestion","link":"pollLink"`) // Try to delete the poll created (must pass) - do("DELETE", "/api/animator/poll/2021/1", xsrfHeader, "", http.StatusOK, "successful delete") + do("DELETE", "/api/animator/poll/2021/1", xsrfHeader, "", http.StatusOK, SuccessfullDelete) // Try to get a poll after it is deleted (must fail because not found) do("GET", "/api/animator/poll/2021/1", xsrfHeader, "", http.StatusNotFound, "") // Try to get the monthlyReport (must pass) do("GET", "/api/common/monthlyReport?year=2021&month=1", noH, "", http.StatusOK, `{"year":2021,"month":1,"subject":"[Ecolyo] Newsletter","info":"Informations du mois","image":"imagebase64","newsTitle":"","newsContent":"","question":"","link":""`) // Try to delete the mail subject created (must pass) - do("DELETE", "/api/animator/mailSubject/2021/1", xsrfHeader, "", http.StatusOK, "successful delete") + do("DELETE", "/api/animator/mailSubject/2021/1", xsrfHeader, "", http.StatusOK, SuccessfullDelete) // Try to get a mail subject after it is deleted (must fail because not found) do("GET", "/api/animator/mailSubject/2021/1", xsrfHeader, "", http.StatusNotFound, "") // Try to get the monthlyReport (must pass) @@ -233,9 +241,9 @@ func animatorTests(t *testing.T) { // Try to logout (must pass) do("GET", "/Logout", noH, "", http.StatusOK, "") // Try to get a monthlyNews again (must fail) - do("GET", "/api/animator/monthlyNews", noH, "", http.StatusUnauthorized, "error extracting token") + do("GET", "/api/animator/monthlyNews", noH, "", http.StatusUnauthorized, ErrorExtractingToken) // Try to get a poll again (must fail) - do("GET", "/api/animator/poll", noH, "", http.StatusUnauthorized, "error extracting token") + do("GET", "/api/animator/poll", noH, "", http.StatusUnauthorized, ErrorExtractingToken) } @@ -257,7 +265,7 @@ func adminTests(t *testing.T) { // Try to logout (must pass) do("GET", "/Logout", noH, "", http.StatusOK, "") // Try to get SGE consents again (must fail) - do("GET", "/api/admin/consent?limit=50&page=0", xsrfHeader, "", http.StatusUnauthorized, "error extracting token") + do("GET", "/api/admin/consent?limit=50&page=0", xsrfHeader, "", http.StatusUnauthorized, ErrorExtractingToken) } func sgeTests(t *testing.T) { @@ -265,13 +273,13 @@ func sgeTests(t *testing.T) { ts, do, _ := createTester(t) defer ts.Close() // Close the tester // Try to create a consent (must fail) - do("POST", "/api/sge/consent", noH, consentStr, http.StatusUnauthorized, "invalid token") + do("POST", "/api/sge/consent", noH, consentStr, http.StatusUnauthorized, ErrorInvalidToken) // Try to get a consent (must fail) - do("GET", "/api/sge/consent/1", noH, "", http.StatusUnauthorized, "invalid token") + do("GET", "/api/sge/consent/1", noH, "", http.StatusUnauthorized, ErrorInvalidToken) // Try to update a consent (must fail) - do("PUT", "/api/sge/consent/1", noH, "", http.StatusUnauthorized, "invalid token") + do("PUT", "/api/sge/consent/1", noH, "", http.StatusUnauthorized, ErrorInvalidToken) // Try to delete a consent (must fail) - do("DELETE", "/api/sge/consent/1", noH, "", http.StatusUnauthorized, "invalid token") + do("DELETE", "/api/sge/consent/1", noH, "", http.StatusUnauthorized, ErrorInvalidToken) // Create correct authorization header sgeApiHeader := map[string]string{"Authorization": "Bearer " + auth.SGEApiToken} diff --git a/internal/tester/tester.go b/internal/tester/tester.go index d7c6a6739062d632843205b3a7de13ef13795618..51a9e80cc59c76c733728d6df4c14a648142a266 100644 --- a/internal/tester/tester.go +++ b/internal/tester/tester.go @@ -6,7 +6,6 @@ import ( "net" "net/http" "net/http/cookiejar" - "net/http/httptest" "net/url" "strings" "testing" @@ -15,26 +14,6 @@ import ( type DoFn func(method string, url string, headers map[string]string, payload string, expectedStatus int, expectedBody string) string -// DoRequestOnHandler does a request on a router (or handler) and check the response -func DoRequestOnHandler(t *testing.T, router http.Handler, method string, route string, headers map[string]string, payload string, expectedStatus int, expectedBody string) string { - req, err := http.NewRequest(method, route, strings.NewReader(payload)) - if err != nil { - t.Fatal(err) - } - for i, v := range headers { - req.Header.Set(i, v) - } - rr := httptest.NewRecorder() - router.ServeHTTP(rr, req) - if status := rr.Code; status != expectedStatus { - t.Errorf("Tested %v %v %v ; handler returned wrong status code: got %v want %v", method, route, payload, status, expectedStatus) - } - if !strings.HasPrefix(rr.Body.String(), expectedBody) { - t.Errorf("Tested %v %v %v ; handler returned unexpected body: got %v want %v", method, route, payload, rr.Body.String(), expectedBody) - } - return string(rr.Body.String()) -} - // DoRequestOnServer does a request on listening server func DoRequestOnServer(t *testing.T, hostname string, port string, jar *cookiejar.Jar, method string, testURL string, headers map[string]string, payload string, expectedStatus int, expectedBody string) string { dialer := &net.Dialer{ diff --git a/mnt/configs/tokenskey.json b/mnt/configs/tokenskey.json deleted file mode 100644 index 3920d9ac878d69bc836ee66729cb1d027ed5b498..0000000000000000000000000000000000000000 --- a/mnt/configs/tokenskey.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Key": "2ioa6+gmlILIQcsG/HmBqDSsszPCe4GWKjCfyrtgLek=" -} \ No newline at end of file diff --git a/scripts/import-convert-assets.sh b/scripts/import-convert-assets.sh new file mode 100755 index 0000000000000000000000000000000000000000..6ebfa51ea3dde5594bff81715fd89bd8abfdb4af --- /dev/null +++ b/scripts/import-convert-assets.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# See https://stackoverflow.com/questions/24112727/relative-paths-based-on-file-location-instead-of-current-working-directory +WD=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) +cd "$WD" + +# Get env variables +source ../.env + +REGISTRY_ID=409 +EMAIL_ASSETS_PATH="src/assets/icons/email" +ECOGESTURE_ASSETS_PATH="src/assets/icons/visu/ecogesture" + +# Clear folder +rm -rf ../$IMAGE_FOLDER + +# Create folder +mkdir -p ../$IMAGE_FOLDER ../$IMAGE_FOLDER/ecogesture + +# Fetch and convert email assets +curl "https://forge.grandlyon.com/api/v4/projects/${REGISTRY_ID}/repository/archive?path=${EMAIL_ASSETS_PATH}" --output email.tar.gz +tar -xf email.tar.gz +find *-email/$EMAIL_ASSETS_PATH -type f -name "*.svg" | while read -r svg_file; do + filename="$(basename "$svg_file" .svg)" + inkscape -h 200 -o ../$IMAGE_FOLDER/$filename.png *-email/$EMAIL_ASSETS_PATH/$filename.svg +done +rm *-email/$EMAIL_ASSETS_PATH/*.svg + +# Fetch and convert ecogesture assets +curl "https://forge.grandlyon.com/api/v4/projects/${REGISTRY_ID}/repository/archive?path=${ECOGESTURE_ASSETS_PATH}" --output ecogesture.tar.gz +tar -xf ecogesture.tar.gz +find *-ecogesture/$ECOGESTURE_ASSETS_PATH -type f -name "*.svg" | while read -r svg_file; do + filename="$(basename "$svg_file" .svg)" + inkscape -h 200 -o ../$IMAGE_FOLDER/ecogesture/$filename.png *-ecogesture/$ECOGESTURE_ASSETS_PATH/$filename.svg +done +rm *-ecogesture/$ECOGESTURE_ASSETS_PATH/*.svg + +# Cleanup +rm -rf email.tar.gz ecogesture.tar.gz *-email *-ecogesture diff --git a/scripts/init-fluidprices.sh b/scripts/init-fluidprices.sh new file mode 100755 index 0000000000000000000000000000000000000000..89e01f52f8026f64ae3117b8b2963c72477b8595 --- /dev/null +++ b/scripts/init-fluidprices.sh @@ -0,0 +1,112 @@ +#!/bin/bash + +# See https://stackoverflow.com/questions/24112727/relative-paths-based-on-file-location-instead-of-current-working-directory +WD=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) +cd "$WD/.." + +source .env + +# REMOVE PRICES TABLE +docker compose exec database mysql -u${DATABASE_USER} -p${DATABASE_PASSWORD} -e "use $DATABASE_NAME; DROP TABLE prices;" +echo "Prices database dropped" + +# CREATE PRICES TABLE +docker compose exec database mysql -u${DATABASE_USER} -p${DATABASE_PASSWORD} -e "use $DATABASE_NAME; CREATE TABLE prices ( + id int(11) NOT NULL, + fluid_type bigint(20) DEFAULT NULL, + price float DEFAULT NULL, + start_date longtext, + end_date longtext, + created_at datetime(3) DEFAULT NULL, + updated_at datetime(3) DEFAULT NULL, + deleted_at datetime(3) DEFAULT NULL +);" +echo "Prices database created" + +# POPULATE PRICES TABLE +docker compose exec database mysql -u${DATABASE_USER} -p${DATABASE_PASSWORD} -e "use $DATABASE_NAME; INSERT INTO prices (id, fluid_type, price, start_date, end_date) VALUES + (1,0,0.1256,'2012-07-23 00:00:00','2013-07-31 23:59:59'), + (2,0,0.1329,'2013-08-01 00:00:00','2014-10-31 23:59:59'), + (3,0,0.1401,'2014-01-11 00:00:00','2015-07-31 23:59:59'), + (4,0,0.1437,'2015-08-01 00:00:00','2016-07-31 23:59:59'), + (5,0,0.1503,'2016-08-01 00:00:00','2017-07-31 23:59:59'), + (6,0,0.1546,'2017-08-01 00:00:00','2018-01-31 23:59:59'), + (7,0,0.1555,'2018-02-01 00:00:00','2018-07-31 23:59:59'), + (8,0,0.145,'2018-08-01 00:00:00','2019-05-31 23:59:59'), + (9,0,0.1531,'2019-02-01 00:00:00','2019-07-31 23:59:59'), + (10,0,0.1524,'2019-08-01 00:00:00','2020-01-31 23:59:59'), + (11,0,0.1546,'2020-02-01 00:00:00','2020-07-31 23:59:59'), + (12,0,0.1557,'2020-08-01 00:00:00','2021-01-31 23:59:59'), + (13,0,0.1582,'2021-02-01 00:00:00','2021-07-31 23:59:59'), + (14,0,0.1558,'2021-08-01 00:00:00','2022-01-31 23:59:59'), + (15,0,0.174,'2022-02-01 00:00:00',NULL), + (16,1,0.0030735,'2012-01-01 00:00:00','2012-12-31 23:59:59'), + (17,1,0.0031483,'2013-01-01 00:00:00','2013-12-31 23:59:59'), + (18,1,0.0031381,'2014-01-01 00:00:00','2014-12-31 23:59:59'), + (19,1,0.00307,'2015-01-01 00:00:00','2015-12-31 23:59:59'), + (20,1,0.0031,'2016-01-01 00:00:00','2016-12-31 23:59:59'), + (21,1,0.00311,'2017-01-01 00:00:00','2017-12-31 23:59:59'), + (22,1,0.00313,'2018-01-01 00:00:00','2018-12-31 23:59:59'), + (23,1,0.00313,'2019-01-01 00:00:00','2019-12-31 23:59:59'), + (24,1,0.00315,'2020-01-01 00:00:00','2020-12-31 23:59:59'), + (25,1,0.00319,'2021-01-01 00:00:00',NULL), + (26,2,0.0919,'2017-01-01 00:00:00','2017-01-31 23:59:59'), + (27,2,0.0915,'2017-02-01 00:00:00','2017-02-28 23:59:59'), + (28,2,0.0932,'2017-03-01 00:00:00','2017-03-31 23:59:59'), + (29,2,0.0927,'2017-04-01 00:00:00','2017-04-30 23:59:59'), + (30,2,0.0906,'2017-05-01 00:00:00','2017-05-31 23:59:59'), + (31,2,0.0906,'2017-06-01 00:00:00','2017-06-30 23:59:59'), + (32,2,0.0788,'2017-07-01 00:00:00','2017-07-31 23:59:59'), + (33,2,0.0783,'2017-08-01 00:00:00','2017-08-31 23:59:59'), + (34,2,0.0783,'2017-09-01 00:00:00','2017-09-30 23:59:59'), + (35,2,0.0791,'2017-10-01 00:00:00','2017-10-31 23:59:59'), + (36,2,0.0806,'2017-11-01 00:00:00','2017-11-30 23:59:59'), + (37,2,0.0812,'2017-12-01 00:00:00','2017-12-31 23:59:59'), + (38,2,0.0857,'2018-01-01 00:00:00','2018-01-31 23:59:59'), + (39,2,0.0866,'2018-02-01 00:00:00','2018-02-28 23:59:59'), + (40,2,0.0847,'2018-03-01 00:00:00','2018-03-31 23:59:59'), + (41,2,0.0839,'2018-04-01 00:00:00','2018-04-30 23:59:59'), + (42,2,0.0842,'2018-05-01 00:00:00','2018-05-31 23:59:59'), + (43,2,0.0855,'2018-06-01 00:00:00','2018-06-30 23:59:59'), + (44,2,0.0959,'2018-07-01 00:00:00','2018-07-31 23:59:59'), + (45,2,0.0961,'2018-08-01 00:00:00','2018-08-31 23:59:59'), + (46,2,0.0967,'2018-09-01 00:00:00','2018-09-30 23:59:59'), + (47,2,0.0989,'2018-10-01 00:00:00','2018-10-31 23:59:59'), + (48,2,0.1031,'2018-11-01 00:00:00','2018-11-30 23:59:59'), + (49,2,0.1013,'2018-12-01 00:00:00','2018-12-31 23:59:59'), + (50,2,0.0999,'2019-01-01 00:00:00','2019-01-31 23:59:59'), + (51,2,0.0993,'2019-02-01 00:00:00','2019-02-28 23:59:59'), + (52,2,0.0993,'2019-03-01 00:00:00','2019-03-31 23:59:59'), + (53,2,0.0977,'2019-04-01 00:00:00','2019-04-30 23:59:59'), + (54,2,0.0973,'2019-05-01 00:00:00','2019-05-31 23:59:59'), + (55,2,0.0969,'2019-06-01 00:00:00','2019-06-30 23:59:59'), + (56,2,0.0795,'2019-07-01 00:00:00','2019-07-31 23:59:59'), + (57,2,0.0791,'2019-08-01 00:00:00','2019-08-31 23:59:59'), + (58,2,0.0785,'2019-09-01 00:00:00','2019-09-30 23:59:59'), + (59,2,0.077,'2019-10-01 00:00:00','2019-10-31 23:59:59'), + (60,2,0.0789,'2019-11-01 00:00:00','2019-11-30 23:59:59'), + (61,2,0.0793,'2019-12-01 00:00:00','2019-12-31 23:59:59'), + (62,2,0.0787,'2020-01-01 00:00:00','2020-01-31 23:59:59'), + (63,2,0.0765,'2020-02-01 00:00:00','2020-02-29 23:59:59'), + (64,2,0.0736,'2020-03-01 00:00:00','2020-03-31 23:59:59'), + (65,2,0.071,'2020-04-01 00:00:00','2020-04-30 23:59:59'), + (66,2,0.0703,'2020-05-01 00:00:00','2020-05-31 23:59:59'), + (67,2,0.0687,'2020-06-01 00:00:00','2020-06-30 23:59:59'), + (68,2,0.0698,'2020-07-01 00:00:00','2020-07-31 23:59:59'), + (69,2,0.0705,'2020-08-01 00:00:00','2020-08-31 23:59:59'), + (70,2,0.0709,'2020-09-01 00:00:00','2020-09-30 23:59:59'), + (71,2,0.0735,'2020-10-01 00:00:00','2020-10-31 23:59:59'), + (72,2,0.0745,'2020-11-01 00:00:00','2020-11-30 23:59:59'), + (73,2,0.0759,'2020-12-01 00:00:00','2020-12-31 23:59:59'), + (74,2,0.076,'2021-01-01 00:00:00','2021-01-31 23:59:59'), + (75,2,0.0782,'2021-02-01 00:00:00','2021-02-28 23:59:59'), + (76,2,0.0818,'2021-03-01 00:00:00','2021-03-31 23:59:59'), + (77,2,0.079,'2021-04-01 00:00:00','2021-04-30 23:59:59'), + (78,2,0.0797,'2021-05-01 00:00:00','2021-05-31 23:59:59'), + (79,2,0.0826,'2021-06-01 00:00:00','2021-06-30 23:59:59'), + (80,2,0.0895,'2021-07-01 00:00:00','2021-07-31 23:59:59'), + (81,2,0.0934,'2021-08-01 00:00:00','2021-08-31 23:59:59'), + (82,2,0.1002,'2021-09-01 00:00:00','2021-09-30 23:59:59'), + (83,2,0.1121,'2021-10-01 00:00:00',NULL) +;" +echo "Prices database populated" diff --git a/scripts/init-swagger-doc.sh b/scripts/init-swagger-doc.sh new file mode 100755 index 0000000000000000000000000000000000000000..e8f11ada3741d307066dd4062202aa204a58dc16 --- /dev/null +++ b/scripts/init-swagger-doc.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# See https://stackoverflow.com/questions/24112727/relative-paths-based-on-file-location-instead-of-current-working-directory +WD=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) +cd "$WD/.." + +source .env + +# Build documentation +swag init -g ./internal/rootmux/rootmux.go + +# Keep only swagger.json and swagger.yaml +rm ./docs/docs.go