2023-03-28 02:35:42 -04:00
/ *
Copyright © 2023 AGECEM
* /
package cmd
2023-02-17 17:28:47 -05:00
import (
2023-04-21 18:51:20 -04:00
"context"
2023-04-21 21:34:18 -04:00
"crypto/subtle"
2023-04-26 16:51:13 -04:00
"encoding/json"
2023-03-21 20:29:06 -04:00
"fmt"
2023-04-21 21:34:18 -04:00
"log"
2023-04-28 19:30:31 -04:00
"regexp"
2023-03-28 02:35:42 -04:00
"embed"
2023-03-21 18:37:51 -04:00
"html/template"
"io"
2023-02-17 17:28:47 -05:00
"net/http"
2023-03-21 18:48:23 -04:00
"sort"
2023-02-17 17:28:47 -05:00
2023-04-21 18:51:20 -04:00
"github.com/minio/minio-go/v7"
2023-03-28 02:35:42 -04:00
"github.com/spf13/cobra"
2023-04-21 16:41:50 -04:00
"github.com/spf13/viper"
2023-03-28 02:35:42 -04:00
2023-04-26 16:51:13 -04:00
"git.agecem.com/agecem/agecem-org/api"
2023-07-04 16:05:23 -04:00
"git.agecem.com/agecem/agecem-org/config"
2023-07-04 16:47:03 -04:00
"git.agecem.com/agecem/agecem-org/media"
2023-03-21 18:37:51 -04:00
"git.agecem.com/agecem/agecem-org/public"
2023-07-04 21:57:13 -04:00
"git.agecem.com/agecem/agecem-org/templates"
2023-02-17 17:28:47 -05:00
"github.com/labstack/echo/v4"
2023-03-21 18:37:51 -04:00
"github.com/labstack/echo/v4/middleware"
2023-02-17 17:28:47 -05:00
)
2023-03-21 18:37:51 -04:00
type Template struct {
templates * template . Template
}
2023-07-04 16:05:23 -04:00
var cfg config . Config
2023-07-04 21:57:13 -04:00
var (
publicFS embed . FS
templatesFS embed . FS
)
2023-03-21 18:37:51 -04:00
2023-03-28 02:35:42 -04:00
// serverCmd represents the server command
var serverCmd = & cobra . Command {
Use : "server" ,
Short : "Démarrer le serveur web" ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
2023-07-04 16:05:23 -04:00
if err := viper . Unmarshal ( & cfg ) ; err != nil {
log . Fatal ( err )
}
2023-07-04 20:35:13 -04:00
mediaClient , err := media . NewMediaClientFromViper ( )
if err != nil {
log . Fatal ( err )
}
new_buckets , err := mediaClient . Seed ( )
if err != nil {
log . Fatal ( err )
}
log . Printf ( "Seeded %d buckets.\n" , len ( new_buckets ) )
2023-03-28 02:35:42 -04:00
RunServer ( )
} ,
}
2023-02-23 04:24:04 -05:00
func init ( ) {
2023-03-28 02:35:42 -04:00
rootCmd . AddCommand ( serverCmd )
2023-07-04 21:57:13 -04:00
publicFS = public . GetPublicFS ( )
templatesFS = templates . GetTemplatesFS ( )
2023-04-21 16:41:50 -04:00
// server.port - --server-port
serverCmd . Flags ( ) . Int ( "server-port" , 8080 , "Port to run the webserver on (config: server.port)" )
viper . BindPFlag ( "server.port" , serverCmd . Flags ( ) . Lookup ( "server-port" ) )
2023-04-28 20:00:54 -04:00
// Not currently used
/ *
// server.documents.location - --server-documents-location
serverCmd . Flags ( ) . String ( "server-documents-location" , "us-east" , "Storage bucket location (config: server.documents.location)" )
viper . BindPFlag ( "server.documents.location" , serverCmd . Flags ( ) . Lookup ( "server-documents-location" ) )
* /
2023-04-21 16:41:50 -04:00
// server.documents.endpoint - --server-documents-endpoint
serverCmd . Flags ( ) . String ( "server-documents-endpoint" , "minio:9000" , "Storage server endpoint (config: server.documents.endpoint)" )
viper . BindPFlag ( "server.documents.endpoint" , serverCmd . Flags ( ) . Lookup ( "server-documents-endpoint" ) )
// server.documents.access_key_id - --server-documents-access-key-id
2023-07-04 20:50:41 -04:00
serverCmd . Flags ( ) . String ( "server-documents-access-key-id" , "agecem-org" , "Storage server access key id (config: server.documents.access_key_id)" )
2023-07-04 16:05:23 -04:00
viper . BindPFlag ( "server.documents.access_key_id" , serverCmd . Flags ( ) . Lookup ( "server-documents-access-key-id" ) )
2023-04-21 16:41:50 -04:00
// server.documents.secret_access_key - --server-documents-secret-access-key
2023-07-04 20:50:41 -04:00
serverCmd . Flags ( ) . String ( "server-documents-secret-access-key" , "agecem-org" , "Storage server secret access key (config: server.documents.secret_access_key)" )
2023-04-21 16:41:50 -04:00
viper . BindPFlag ( "server.documents.secret_access_key" , serverCmd . Flags ( ) . Lookup ( "server-documents-secret-access-key" ) )
// server.documents.use_ssl - --server-documents-use-ssl
2023-07-04 20:50:41 -04:00
serverCmd . Flags ( ) . Bool ( "server-documents-use-ssl" , false , "Storage server SSL status (config: server.documents.use_ssl)" )
2023-04-21 16:41:50 -04:00
viper . BindPFlag ( "server.documents.use_ssl" , serverCmd . Flags ( ) . Lookup ( "server-documents-use-ssl" ) )
// server.documents.buckets - --server-documents-buckets
2023-07-04 20:50:41 -04:00
serverCmd . Flags ( ) . StringSlice ( "server-documents-buckets" , [ ] string { "proces-verbaux" , "politiques-et-reglements" } , "Buckets that are allowed to be accessed by the API (config: server.documents.buckets)" )
2023-04-21 16:41:50 -04:00
viper . BindPFlag ( "server.documents.buckets" , serverCmd . Flags ( ) . Lookup ( "server-documents-buckets" ) )
2023-04-21 21:34:18 -04:00
// server.api.auth - --server-api-auth
2023-07-04 20:50:41 -04:00
serverCmd . Flags ( ) . Bool ( "server-api-auth" , true , "Enable to allow key authentication for /v1 routes (config: server.api.auth)" )
2023-04-21 21:34:18 -04:00
viper . BindPFlag ( "server.api.auth" , serverCmd . Flags ( ) . Lookup ( "server-api-auth" ) )
// server.api.key - --server-api-key
2023-07-04 20:50:41 -04:00
serverCmd . Flags ( ) . String ( "server-api-key" , "agecem-org" , "Key to use for authenticating to /v1 routes" )
2023-04-21 21:34:18 -04:00
viper . BindPFlag ( "server.api.key" , serverCmd . Flags ( ) . Lookup ( "server-api-key" ) )
2023-04-26 19:15:22 -04:00
// server.admin.auth - --server-admin-auth
2023-07-04 20:50:41 -04:00
serverCmd . Flags ( ) . Bool ( "server-admin-auth" , true , "Enable to allow basic authentication for /admin routes (config: server.admin.auth)" )
2023-04-26 19:15:22 -04:00
viper . BindPFlag ( "server.admin.auth" , serverCmd . Flags ( ) . Lookup ( "server-admin-auth" ) )
2023-04-28 20:00:54 -04:00
// server.admin.username - --server-admin-username
2023-07-04 20:50:41 -04:00
serverCmd . Flags ( ) . String ( "server-admin-username" , "agecem-org" , "Username for basic authentication for /admin routes (config: server.admin.username)" )
2023-04-26 19:15:22 -04:00
viper . BindPFlag ( "server.admin.username" , serverCmd . Flags ( ) . Lookup ( "server-admin-username" ) )
2023-04-28 20:00:54 -04:00
// server.admin.password - --server-admin-password
2023-07-04 20:50:41 -04:00
serverCmd . Flags ( ) . String ( "server-admin-password" , "agecem-org" , "Password for basic authentication for /admin routes (config: server.admin.password)" )
2023-04-26 19:15:22 -04:00
viper . BindPFlag ( "server.admin.password" , serverCmd . Flags ( ) . Lookup ( "server-admin-password" ) )
2023-02-23 04:24:04 -05:00
}
2023-03-28 02:35:42 -04:00
func RunServer ( ) {
2023-02-17 17:28:47 -05:00
e := echo . New ( )
2023-02-23 04:24:04 -05:00
2023-03-21 18:37:51 -04:00
t := & Template {
2023-07-04 21:57:13 -04:00
templates : template . Must ( template . ParseFS ( templatesFS , "html/*.gohtml" ) ) ,
2023-03-21 18:37:51 -04:00
}
e . Renderer = t
e . Pre ( middleware . RemoveTrailingSlash ( ) )
2023-04-21 21:34:18 -04:00
groupV1 := e . Group ( "/v1" )
groupV1 . Use ( middleware . AddTrailingSlash ( ) )
2023-07-04 16:05:23 -04:00
if cfg . Server . Api . Auth {
if len ( cfg . Server . Api . Key ) < 10 {
2023-04-21 21:34:18 -04:00
log . Fatal ( "server.api.auth is enabled, but server.api.key is too small (needs at least 10 characters)" )
}
groupV1 . Use ( middleware . KeyAuth ( func ( key string , c echo . Context ) ( bool , error ) {
2023-07-04 16:05:23 -04:00
return subtle . ConstantTimeCompare ( [ ] byte ( key ) , [ ] byte ( cfg . Server . Api . Key ) ) == 1 , nil
2023-04-21 21:34:18 -04:00
} ) )
2023-04-26 19:15:22 -04:00
log . Println ( "Key auth for /v1 activated" )
}
groupAdmin := e . Group ( "/admin" )
groupAdmin . Use ( middleware . AddTrailingSlash ( ) )
2023-07-04 16:05:23 -04:00
if cfg . Server . Admin . Auth {
if len ( cfg . Server . Admin . Username ) < 5 {
2023-04-26 19:15:22 -04:00
log . Fatal ( "server.admin.auth is enabled, but server.admin.username is too small (needs at least 5 characters)" )
}
2023-07-04 16:05:23 -04:00
if len ( cfg . Server . Admin . Password ) < 10 {
2023-04-26 19:15:22 -04:00
log . Fatal ( "server.admin.auth is enabled, but server.admin.password is too small (needs at least 10 characters)" )
}
groupAdmin . Use ( middleware . BasicAuth ( func ( username_entered , password_entered string , c echo . Context ) ( bool , error ) {
// Be careful to use constant time comparison to prevent timing attacks
2023-07-04 16:05:23 -04:00
if subtle . ConstantTimeCompare ( [ ] byte ( username_entered ) , [ ] byte ( cfg . Server . Admin . Username ) ) == 1 &&
subtle . ConstantTimeCompare ( [ ] byte ( password_entered ) , [ ] byte ( cfg . Server . Admin . Password ) ) == 1 {
2023-04-26 19:15:22 -04:00
return true , nil
}
return false , nil
} ) )
log . Println ( "Basic auth for /admin activated" )
2023-04-21 21:34:18 -04:00
}
2023-03-21 18:48:23 -04:00
// API Routes
2023-04-21 21:34:18 -04:00
groupV1 . GET ( "" , handleV1 )
groupV1 . POST ( "/seed" , handleV1Seed )
2023-03-21 18:48:23 -04:00
2023-04-21 21:34:18 -04:00
groupV1 . GET ( "/bucket" , handleV1BucketList )
groupV1 . GET ( "/bucket/:bucket" , handleV1BucketRead )
2023-04-21 16:41:50 -04:00
2023-04-24 18:29:05 -04:00
groupV1 . POST ( "/bucket/:bucket" , handleV1DocumentCreate )
2023-04-24 17:19:52 -04:00
groupV1 . GET ( "/bucket/:bucket/:document" , handleV1DocumentRead )
groupV1 . PUT ( "/bucket/:bucket/:document" , handleV1DocumentUpdate )
groupV1 . DELETE ( "/bucket/:bucket/:document" , handleV1DocumentDelete )
2023-03-21 20:26:39 -04:00
// Static Routes
2023-03-24 20:02:46 -04:00
e . GET ( "/static/general.css" , handleStaticCSSGeneral )
2023-03-21 20:26:39 -04:00
e . GET ( "/static/index.css" , handleStaticCSSIndex )
2023-03-21 18:48:23 -04:00
// HTML Routes
2023-03-21 18:37:51 -04:00
e . GET ( "/" , handleIndex )
2023-03-21 20:29:06 -04:00
e . GET ( "/a-propos" , handleAPropos )
e . GET ( "/actualite" , handleActualite )
e . GET ( "/actualite/:article" , handleActualiteArticle )
e . GET ( "/vie-etudiante" , handleVieEtudiante )
e . GET ( "/vie-etudiante/:organisme" , handleVieEtudianteOrganisme )
e . GET ( "/documentation" , handleDocumentation )
2023-03-24 18:21:22 -04:00
e . GET ( "/formulaires" , handleFormulaires )
2023-03-21 20:29:06 -04:00
2023-04-26 18:27:58 -04:00
// Public Routes
e . GET ( "/public/documentation/:bucket/:document" , handlePublicDocumentation )
2023-04-26 19:15:22 -04:00
// Admin Routes
groupAdmin . GET ( "" , handleAdmin )
2023-04-26 19:28:20 -04:00
groupAdmin . GET ( "/documents/upload" , handleAdminDocumentsUpload )
2023-04-26 19:43:43 -04:00
groupAdmin . POST ( "/documents/upload" , handleAdminDocumentsUploadPOST )
2023-04-21 16:41:50 -04:00
e . Logger . Fatal ( e . Start (
2023-07-04 16:05:23 -04:00
fmt . Sprintf ( ":%d" , cfg . Server . Port ) ) )
2023-02-17 17:28:47 -05:00
}
2023-03-21 18:37:51 -04:00
func ( t * Template ) Render ( w io . Writer , name string , data interface { } , c echo . Context ) error {
return t . templates . ExecuteTemplate ( w , name , data )
}
// API Handlers
2023-03-21 18:48:23 -04:00
// handleV1 affiche les routes accessibles.
// Les routes sont triées selon .Path, pour les rendre plus facilement navigables.
func handleV1 ( c echo . Context ) error {
routes := c . Echo ( ) . Routes ( )
sort . Slice ( routes , func ( i , j int ) bool { return routes [ i ] . Path < routes [ j ] . Path } )
return c . JSON ( http . StatusOK , routes )
}
2023-04-21 21:34:18 -04:00
// handleV1Seed créé des buckets dans minio selon la liste de buckets dans server.documents.buckets
// Les buckets sont créés avec paramètres par défaut, et sont ensuite visible dans /v1/bucket.
func handleV1Seed ( c echo . Context ) error {
2023-07-04 16:47:03 -04:00
mediaClient , err := media . NewMediaClientFromViper ( )
2023-04-21 21:34:18 -04:00
if err != nil {
return c . JSON ( http . StatusInternalServerError , map [ string ] string {
2023-07-04 16:47:03 -04:00
"message" : "Error during media.NewMediaClientFromViper()" ,
2023-07-04 16:05:23 -04:00
"error" : err . Error ( ) ,
2023-04-21 21:34:18 -04:00
} )
}
2023-07-04 20:35:35 -04:00
new_buckets , err := mediaClient . Seed ( )
if err != nil {
return c . JSON ( http . StatusInternalServerError , map [ string ] string {
"message" : "Error during mediaClient.Seed()" ,
"error" : err . Error ( ) ,
} )
2023-04-21 21:34:18 -04:00
}
2023-07-04 20:35:35 -04:00
2023-04-24 16:30:06 -04:00
var message string
if len ( new_buckets ) == 0 {
message = "All buckets already exist"
} else {
message = "Buckets successfully created"
}
2023-04-21 21:34:18 -04:00
return c . JSON ( http . StatusOK , map [ string ] interface { } {
2023-04-24 16:30:06 -04:00
"message" : message ,
2023-04-21 21:34:18 -04:00
"buckets" : new_buckets ,
} )
}
// handleV1BucketList affiche les buckets permis par server.documents.buckets, qui existent.
func handleV1BucketList ( c echo . Context ) error {
2023-07-04 16:55:45 -04:00
mediaClient , err := media . NewMediaClientFromViper ( )
2023-04-21 16:41:50 -04:00
if err != nil {
2023-04-21 18:51:20 -04:00
return c . JSON ( http . StatusInternalServerError , map [ string ] string {
2023-07-04 16:55:45 -04:00
"message" : "Error during media.NewMediaClientFromViper()" ,
"error" : err . Error ( ) ,
2023-04-21 18:51:20 -04:00
} )
2023-04-21 16:41:50 -04:00
}
2023-04-21 18:36:20 -04:00
var buckets [ ] string
2023-04-21 16:41:50 -04:00
2023-07-04 16:05:23 -04:00
for _ , bucket_name := range cfg . Server . Documents . Buckets {
2023-07-04 16:55:45 -04:00
exists , err := mediaClient . MinioClient . BucketExists ( context . Background ( ) , bucket_name )
2023-04-21 16:41:50 -04:00
if err != nil {
2023-04-21 18:51:20 -04:00
return c . JSON ( http . StatusInternalServerError , "Error during minio#BucketExists" )
2023-04-21 16:41:50 -04:00
}
2023-04-21 18:36:20 -04:00
if exists {
buckets = append ( buckets , bucket_name )
}
2023-04-21 16:41:50 -04:00
}
2023-04-21 18:36:20 -04:00
return c . JSON ( http . StatusOK , buckets )
2023-04-21 16:41:50 -04:00
}
2023-04-21 21:34:18 -04:00
func handleV1BucketRead ( c echo . Context ) error {
bucket := c . Param ( "bucket" )
2023-04-26 15:00:17 -04:00
allowed := false
2023-07-04 16:05:23 -04:00
for _ , bucket_allowed := range cfg . Server . Documents . Buckets {
2023-04-26 15:00:17 -04:00
if bucket == bucket_allowed {
allowed = true
}
}
if ! allowed {
/ *
return c . JSON ( http . StatusBadRequest , map [ string ] string {
"message" : "Bucket is not allowed in server.documents.buckets" ,
} )
* /
return c . JSON ( http . StatusNotFound , map [ string ] string { "message" : "Not Found" } )
}
2023-04-21 21:34:18 -04:00
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
2023-07-04 16:55:45 -04:00
mediaClient , err := media . NewMediaClientFromViper ( )
2023-04-21 21:34:18 -04:00
if err != nil {
return c . JSON ( http . StatusInternalServerError , map [ string ] string {
2023-07-04 16:55:45 -04:00
"message" : "Error during media.NewMediaClientFromViper()" ,
"error" : err . Error ( ) ,
2023-04-21 21:34:18 -04:00
} )
}
2023-07-04 16:55:45 -04:00
exists , err := mediaClient . MinioClient . BucketExists ( ctx , bucket )
2023-04-21 21:34:18 -04:00
if err != nil {
return c . JSON ( http . StatusInternalServerError , "Error during minio#BucketExists" )
}
if ! exists {
2023-04-24 17:04:02 -04:00
return c . JSON ( http . StatusNotFound , map [ string ] string { "message" : "Not Found" } )
2023-04-21 21:34:18 -04:00
}
var keys [ ] string
2023-07-04 16:55:45 -04:00
objectCh := mediaClient . MinioClient . ListObjects ( ctx , bucket , minio . ListObjectsOptions { } )
2023-04-21 21:34:18 -04:00
for object := range objectCh {
if object . Err != nil {
return c . JSON ( http . StatusInternalServerError , map [ string ] string {
"message" : "Error during minio#ListObjects" ,
} )
}
keys = append ( keys , object . Key )
}
return c . JSON ( http . StatusOK , keys )
}
2023-04-24 18:40:17 -04:00
// handleV1DocumentCreate permet d'ajouter un object dans un bucket, par multipart/form-data
2023-04-24 17:05:18 -04:00
func handleV1DocumentCreate ( c echo . Context ) error {
2023-04-24 18:29:05 -04:00
bucket := c . Param ( "bucket" )
2023-04-28 16:52:16 -04:00
form_file , err := c . FormFile ( "document" )
2023-04-24 18:29:05 -04:00
if err != nil {
2023-04-28 15:57:09 -04:00
return c . JSON ( http . StatusBadRequest , map [ string ] interface { } {
"message" : "Error during handleV1DocumentCreate's echo#Context.FormFile" ,
"error" : err ,
} )
2023-04-24 18:29:05 -04:00
}
2023-04-26 15:00:17 -04:00
allowed := false
2023-07-04 16:05:23 -04:00
for _ , bucket_allowed := range cfg . Server . Documents . Buckets {
2023-04-26 15:00:17 -04:00
if bucket == bucket_allowed {
allowed = true
}
}
if ! allowed {
return c . JSON ( http . StatusNotFound , map [ string ] string { "message" : "Not Found" } )
}
2023-04-24 18:29:05 -04:00
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
2023-07-04 16:55:45 -04:00
mediaClient , err := media . NewMediaClientFromViper ( )
2023-04-24 18:29:05 -04:00
if err != nil {
return c . JSON ( http . StatusInternalServerError , map [ string ] string {
2023-07-04 16:55:45 -04:00
"message" : "Error during media.NewMediaClientFromViper()" ,
"error" : err . Error ( ) ,
2023-04-24 18:29:05 -04:00
} )
}
src , err := form_file . Open ( )
if err != nil {
return err
}
defer src . Close ( )
2023-04-28 19:30:31 -04:00
reg , err := regexp . Compile ( "[^.a-zA-Z0-9_-]+" )
if err != nil {
return c . Render ( http . StatusInternalServerError , "documentation-html" , nil )
}
filename_processed := reg . ReplaceAllString ( form_file . Filename , "" )
2023-07-04 16:55:45 -04:00
info , err := mediaClient . MinioClient . PutObject ( ctx , bucket , filename_processed , src , form_file . Size , minio . PutObjectOptions {
2023-04-24 18:29:05 -04:00
ContentType : form_file . Header . Get ( "Content-Type" ) ,
} )
if err != nil {
return c . JSON ( http . StatusInternalServerError , map [ string ] string {
"message" : "Error during minio#PutObject" ,
} )
}
return c . JSON ( http . StatusOK , map [ string ] interface { } {
"message" : "ok" ,
"info" : map [ string ] interface { } {
"bucket" : info . Bucket ,
"key" : info . Key ,
"size" : info . Size ,
} ,
2023-04-24 17:05:18 -04:00
} )
}
2023-04-24 18:40:17 -04:00
// handleV1DocumentRead permet de lire le contenu d'un fichier et protentiellement de le télécharger
2023-04-24 17:05:18 -04:00
func handleV1DocumentRead ( c echo . Context ) error {
2023-04-26 13:23:53 -04:00
bucket := c . Param ( "bucket" )
document := c . Param ( "document" )
2023-04-26 15:00:17 -04:00
allowed := false
2023-07-04 16:05:23 -04:00
for _ , bucket_allowed := range cfg . Server . Documents . Buckets {
2023-04-26 15:00:17 -04:00
if bucket == bucket_allowed {
allowed = true
}
}
if ! allowed {
/ *
return c . JSON ( http . StatusBadRequest , map [ string ] string {
"message" : "Bucket is not allowed in server.documents.buckets" ,
} )
* /
return c . JSON ( http . StatusNotFound , map [ string ] string { "message" : "Not Found" } )
}
2023-04-26 13:23:53 -04:00
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
2023-07-04 16:55:45 -04:00
mediaClient , err := media . NewMediaClientFromViper ( )
2023-04-26 13:23:53 -04:00
if err != nil {
return c . JSON ( http . StatusInternalServerError , map [ string ] string {
2023-07-04 16:55:45 -04:00
"message" : "Error during media.NewMediaClientFromViper()" ,
"error" : err . Error ( ) ,
2023-04-26 13:23:53 -04:00
} )
}
2023-07-04 16:55:45 -04:00
bucket_exists , err := mediaClient . MinioClient . BucketExists ( ctx , bucket )
2023-04-26 13:23:53 -04:00
if err != nil {
return c . JSON ( http . StatusInternalServerError , "Error during minio#BucketExists" )
}
if ! bucket_exists {
return c . JSON ( http . StatusNotFound , map [ string ] string { "message" : "Not Found" } )
}
2023-07-04 16:55:45 -04:00
document_info , err := mediaClient . MinioClient . StatObject ( ctx , bucket , document , minio . StatObjectOptions { } )
2023-04-26 15:00:17 -04:00
2023-04-26 13:23:53 -04:00
if err != nil {
2023-04-26 15:00:17 -04:00
if err . Error ( ) == "The specified key does not exist." {
return c . JSON ( http . StatusNotFound , map [ string ] string { "message" : "Not Found" } )
}
return c . JSON ( http . StatusInternalServerError , map [ string ] interface { } {
2023-04-26 13:23:53 -04:00
"message" : "Error during minio#StatObject" ,
} )
}
_ = document_info
2023-07-04 16:55:45 -04:00
document_object , err := mediaClient . MinioClient . GetObject ( ctx , bucket , document , minio . GetObjectOptions { } )
2023-04-26 13:23:53 -04:00
if err != nil {
return c . JSON ( http . StatusInternalServerError , map [ string ] string {
"message" : "Error during minio#GetObject" ,
} )
}
defer document_object . Close ( )
return c . Stream ( http . StatusOK , document_info . ContentType , document_object )
2023-04-24 17:05:18 -04:00
}
2023-04-24 18:40:17 -04:00
// handleV1DocumentUpdate permet de mettre à jour certains champs d'un object, comme le Content-Type ou le Filename
2023-04-24 17:05:18 -04:00
func handleV1DocumentUpdate ( c echo . Context ) error {
return c . JSON ( http . StatusNotImplemented , map [ string ] string {
"message" : "Not Implemented" ,
} )
}
2023-04-24 18:40:17 -04:00
// handleV1DocumentDelete permet de supprimer un object
2023-04-24 17:05:18 -04:00
func handleV1DocumentDelete ( c echo . Context ) error {
2023-04-24 22:34:57 -04:00
bucket := c . Param ( "bucket" )
document := c . Param ( "document" )
2023-04-26 15:00:17 -04:00
allowed := false
2023-07-04 16:05:23 -04:00
for _ , bucket_allowed := range cfg . Server . Documents . Buckets {
2023-04-26 15:00:17 -04:00
if bucket == bucket_allowed {
allowed = true
}
}
if ! allowed {
/ *
return c . JSON ( http . StatusBadRequest , map [ string ] string {
"message" : "Bucket is not allowed in server.documents.buckets" ,
} )
* /
return c . JSON ( http . StatusNotFound , map [ string ] string { "message" : "Not Found" } )
}
2023-04-24 22:34:57 -04:00
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
2023-07-04 16:55:45 -04:00
mediaClient , err := media . NewMediaClientFromViper ( )
2023-04-24 22:34:57 -04:00
if err != nil {
return c . JSON ( http . StatusInternalServerError , map [ string ] string {
2023-07-04 16:55:45 -04:00
"message" : "Error during media.NewMediaClientFromViper()" ,
"error" : err . Error ( ) ,
2023-04-24 22:34:57 -04:00
} )
}
2023-07-04 16:55:45 -04:00
bucket_exists , err := mediaClient . MinioClient . BucketExists ( ctx , bucket )
2023-04-24 22:34:57 -04:00
if err != nil {
return c . JSON ( http . StatusInternalServerError , "Error during minio#BucketExists" )
}
if ! bucket_exists {
return c . JSON ( http . StatusNotFound , map [ string ] string { "message" : "Not Found" } )
}
2023-07-04 16:55:45 -04:00
document_info , err := mediaClient . MinioClient . StatObject ( ctx , bucket , document , minio . StatObjectOptions { } )
2023-04-24 22:34:57 -04:00
if err != nil {
2023-04-26 15:00:17 -04:00
if err . Error ( ) == "The specified key does not exist." {
return c . JSON ( http . StatusNotFound , map [ string ] string { "message" : "Not Found" } )
}
return c . JSON ( http . StatusInternalServerError , map [ string ] interface { } {
2023-04-24 22:34:57 -04:00
"message" : "Error during minio#StatObject" ,
} )
}
//TODO Add error validation
_ = document_info
2023-07-04 16:55:45 -04:00
err = mediaClient . MinioClient . RemoveObject ( ctx , bucket , document , minio . RemoveObjectOptions { } )
2023-04-24 22:34:57 -04:00
if err != nil {
return c . JSON ( http . StatusInternalServerError , map [ string ] string {
"message" : "Error during minio#RemoveObject" ,
} )
}
return c . JSON ( http . StatusOK , map [ string ] string {
2023-04-28 20:55:36 -04:00
"message" : "Document deleted" ,
2023-04-24 17:05:18 -04:00
} )
}
2023-03-21 18:37:51 -04:00
// HTML Handlers
func handleIndex ( c echo . Context ) error {
return c . Render ( http . StatusOK , "index-html" , nil )
}
2023-03-21 19:59:41 -04:00
2023-03-21 20:29:06 -04:00
func handleAPropos ( c echo . Context ) error {
2023-03-24 20:05:52 -04:00
return c . Render ( http . StatusOK , "a-propos-html" , nil )
2023-03-21 20:29:06 -04:00
}
func handleActualite ( c echo . Context ) error {
2023-03-24 20:05:52 -04:00
return c . Render ( http . StatusOK , "actualite-html" , nil )
2023-03-21 20:29:06 -04:00
}
func handleActualiteArticle ( c echo . Context ) error {
article := c . Param ( "article" )
return c . String ( http . StatusOK , fmt . Sprintf ( "Article: %s" , article ) )
}
2023-04-26 15:15:43 -04:00
2023-03-21 20:29:06 -04:00
func handleVieEtudiante ( c echo . Context ) error {
2023-03-24 20:05:52 -04:00
return c . Render ( http . StatusOK , "vie-etudiante-html" , nil )
2023-03-21 20:29:06 -04:00
}
2023-04-26 15:15:43 -04:00
2023-03-21 20:29:06 -04:00
func handleVieEtudianteOrganisme ( c echo . Context ) error {
organisme := c . Param ( "organisme" )
return c . String ( http . StatusOK , fmt . Sprintf ( "Organisme: %s" , organisme ) )
}
2023-04-26 15:15:43 -04:00
2023-03-21 20:29:06 -04:00
func handleDocumentation ( c echo . Context ) error {
2023-07-04 16:05:23 -04:00
client , err := api . New ( "http" , "localhost" , cfg . Server . Port , api . APIOptions {
KeyAuth : cfg . Server . Api . Auth ,
Key : cfg . Server . Api . Key ,
2023-04-26 16:51:13 -04:00
} )
if err != nil {
return c . Render ( http . StatusInternalServerError , "documentation-html" , nil )
}
result , err := client . Call ( http . MethodGet , "/v1/bucket" )
if err != nil {
return c . Render ( http . StatusInternalServerError , "documentation-html" , nil )
}
var buckets [ ] string
err = json . Unmarshal ( result , & buckets )
if err != nil {
return c . Render ( http . StatusInternalServerError , "documentation-html" , nil )
}
type Bucket struct {
Name string
Documents [ ] string
}
var data [ ] Bucket
for _ , bucket := range buckets {
2023-04-28 19:30:31 -04:00
content , err := client . Call ( http . MethodGet , fmt . Sprintf ( "/v1/bucket/%s" , bucket ) )
2023-04-26 16:51:13 -04:00
if err != nil {
return c . Render ( http . StatusInternalServerError , "documentation-html" , nil )
}
var documents [ ] string
2023-04-28 19:30:31 -04:00
err = json . Unmarshal ( content , & documents )
2023-04-26 16:51:13 -04:00
if err != nil {
return c . Render ( http . StatusInternalServerError , "documentation-html" , nil )
}
2023-04-28 19:30:31 -04:00
// Ce bloc retire tous les caractères spéciaux d'une string
// N'est pas présentement activé, car les fichiers sont processed
// à la création de toute façon.
/ *
reg , err := regexp . Compile ( "[^.a-zA-Z0-9_-]+" )
if err != nil {
return c . Render ( http . StatusInternalServerError , "documentation-html" , nil )
}
var documents_processed [ ] string
for _ , document := range documents {
document_processed := reg . ReplaceAllString ( document , "" )
documents_processed = append ( documents_processed , document_processed )
}
* /
documents_processed := documents
2023-04-26 16:51:13 -04:00
data = append ( data , Bucket {
Name : bucket ,
2023-04-28 19:30:31 -04:00
Documents : documents_processed ,
2023-04-26 16:51:13 -04:00
} )
}
return c . Render ( http . StatusOK , "documentation-html" , data )
2023-03-21 20:29:06 -04:00
}
2023-04-26 15:15:43 -04:00
2023-03-24 18:21:22 -04:00
func handleFormulaires ( c echo . Context ) error {
2023-03-24 20:05:52 -04:00
return c . Render ( http . StatusOK , "formulaires-html" , nil )
2023-03-21 20:29:06 -04:00
}
2023-04-26 18:27:58 -04:00
func handlePublicDocumentation ( c echo . Context ) error {
2023-07-04 16:05:23 -04:00
client , err := api . New ( "http" , "localhost" , cfg . Server . Port , api . APIOptions {
KeyAuth : cfg . Server . Api . Auth ,
Key : cfg . Server . Api . Key ,
2023-04-26 18:27:58 -04:00
} )
if err != nil {
return c . JSON ( http . StatusNotFound , map [ string ] string { "message" : "Not Found" } )
}
bucket := c . Param ( "bucket" )
document := c . Param ( "document" )
result , err := client . Call ( http . MethodGet , fmt . Sprintf ( "/v1/bucket/%s/%s" , bucket , document ) )
if err != nil {
return c . JSON ( http . StatusNotFound , map [ string ] string { "message" : "Not Found" } )
}
// Check if result can fit inside a map containing a message
var result_map map [ string ] string
err = json . Unmarshal ( result , & result_map )
if err == nil {
return c . JSON ( http . StatusBadRequest , result_map )
}
return c . Blob ( http . StatusOK , "application/octet-stream" , result )
}
2023-04-26 19:15:22 -04:00
func handleAdmin ( c echo . Context ) error {
2023-04-26 19:28:20 -04:00
return c . Render ( http . StatusOK , "admin-html" , nil )
}
func handleAdminDocumentsUpload ( c echo . Context ) error {
return c . Render ( http . StatusOK , "admin-upload-html" , nil )
2023-04-26 19:15:22 -04:00
}
2023-04-26 19:43:43 -04:00
func handleAdminDocumentsUploadPOST ( c echo . Context ) error {
2023-07-04 16:05:23 -04:00
client , err := api . New ( "http" , "localhost" , cfg . Server . Port , api . APIOptions {
KeyAuth : cfg . Server . Api . Auth ,
Key : cfg . Server . Api . Key ,
BasicAuth : cfg . Server . Admin . Auth ,
Username : cfg . Server . Admin . Username ,
Password : cfg . Server . Admin . Password ,
2023-04-28 15:57:09 -04:00
} )
if err != nil {
2023-04-28 17:30:08 -04:00
return c . Render ( http . StatusInternalServerError , "admin-upload-html" , struct { Message string } { Message : fmt . Sprintf ( "handleAdminDocumentsUploadPOST#api.New: %s" , err ) } )
2023-04-28 15:57:09 -04:00
}
bucket := c . FormValue ( "bucket" )
document , err := c . FormFile ( "document" )
if err != nil {
2023-04-28 17:30:08 -04:00
return c . Render ( http . StatusBadRequest , "admin-upload-html" , struct { Message string } { Message : fmt . Sprintf ( "handleAdminDocumentsUploadPOST#c.FormFile: %s" , err ) } )
2023-04-28 15:57:09 -04:00
}
response , err := client . UploadDocument ( bucket , document )
if err != nil {
2023-04-28 17:30:08 -04:00
return c . Render ( http . StatusInternalServerError , "admin-upload-html" , struct { Message string } { Message : fmt . Sprintf ( "handleAdminDocumentsUploadPOST#client.UploadDocument: %s" , err ) } )
}
// Format response
var message , info , status string
2023-04-28 19:30:31 -04:00
info = fmt . Sprintf ( "[%.0f] /public/documentation/%s/%s" , response . Info . Size , response . Info . Bucket , response . Info . Object )
status = response . Message
2023-04-26 19:43:43 -04:00
2023-04-28 17:30:08 -04:00
message = fmt . Sprintf ( "%s - %s" , status , info )
return c . Render ( http . StatusOK , "admin-upload-html" , struct { Message string } { Message : message } )
2023-04-26 19:43:43 -04:00
}
2023-03-21 19:59:41 -04:00
// CSS Handlers
func handleStaticCSSIndex ( c echo . Context ) error {
// TODO Ajouter gestion d'erreurs
2023-07-04 21:57:13 -04:00
data , _ := publicFS . ReadFile ( "css/index.css" )
2023-03-21 19:59:41 -04:00
return c . Blob ( http . StatusOK , "text/css" , data )
}
2023-03-24 20:02:46 -04:00
func handleStaticCSSGeneral ( c echo . Context ) error {
// TODO Ajouter gestion d'erreurs
2023-07-04 21:57:13 -04:00
data , _ := publicFS . ReadFile ( "css/general.css" )
2023-03-24 20:02:46 -04:00
return c . Blob ( http . StatusOK , "text/css" , data )
}