diff --git a/.gitignore b/.gitignore index 2eea525d885d5148108f6f3a9a8613863f783d36..8c09811d196e402bd273d828868eacd5ae8e855d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.env \ No newline at end of file +.env +backoffice.db \ No newline at end of file diff --git a/go.mod b/go.mod index 2dd264bb9de099efcccaa667f4818af2de39830f..4c494f0f08e45b6cd8c98314131f99f2e6a2ed11 100644 --- a/go.mod +++ b/go.mod @@ -7,5 +7,6 @@ require ( golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 gorm.io/driver/mysql v1.1.1 + gorm.io/driver/sqlite v1.1.4 gorm.io/gorm v1.21.11 ) diff --git a/go.sum b/go.sum index bd99c0a6a6660d48b7d14b3cfd06992f7f02d3e8..6dbdd2a401fefe57f72c4ba809bd26bdda3a7f0f 100644 --- a/go.sum +++ b/go.sum @@ -105,6 +105,7 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -113,6 +114,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ= +github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= github.com/nicolaspernoud/vestibule v0.0.0-20210626100803-e2554e116746 h1:mMpAetOOm54X87qjKq+RiSNutdULgFWp1knqhUeYf4s= github.com/nicolaspernoud/vestibule v0.0.0-20210626100803-e2554e116746/go.mod h1:zQIZ4A7ZYJBcS/DBZpMadr5N8WrATlj7267VlvKSX88= github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis= @@ -386,6 +389,9 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.1.1 h1:yr1bpyqiwuSPJ4aGGUX9nu46RHXlF8RASQVb1QQNcvo= gorm.io/driver/mysql v1.1.1/go.mod h1:KdrTanmfLPPyAOeYGyG+UpDys7/7eeWT1zCq+oekYnU= +gorm.io/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM= +gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw= +gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.21.9/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= gorm.io/gorm v1.21.11 h1:CxkXW6Cc+VIBlL8yJEHq+Co4RYXdSLiMKNvgoZPjLK4= gorm.io/gorm v1.21.11/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= diff --git a/internal/backoffice/backoffice.go b/internal/backoffice/backoffice.go deleted file mode 100644 index 135175db29963e4ae75c9707bf1b96ebd7d572d5..0000000000000000000000000000000000000000 --- a/internal/backoffice/backoffice.go +++ /dev/null @@ -1,30 +0,0 @@ -package backoffice - -import ( - "io/ioutil" - "log" - "net/http" - - "forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server/internal/post" -) - -func HandleCreatePost(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - w.WriteHeader(http.StatusMethodNotAllowed) - return - } - - body, err := ioutil.ReadAll(r.Body) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - - err = post.Create(string(body)) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - - log.Printf("| New post | %v", r.RemoteAddr) -} diff --git a/internal/database/database.go b/internal/database/database.go new file mode 100644 index 0000000000000000000000000000000000000000..7113933d37f7e262cea072d17415a2c7aa8e2351 --- /dev/null +++ b/internal/database/database.go @@ -0,0 +1,58 @@ +package database + +import ( + "fmt" + + "forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server/internal/common" + "gorm.io/driver/mysql" + "gorm.io/driver/sqlite" + "gorm.io/gorm" +) + +type MonthlyReport struct { + gorm.Model + Month int + Year int + ContentType string + Content string +} + +var ( + db *gorm.DB + dbUser = common.StringValueFromEnv("DATABASE_USER", "") + dbPassword = common.StringValueFromEnv("DATABASE_PASSWORD", "") + dbName = common.StringValueFromEnv("DATABASE_NAME", "") +) + +func init() { + var err error + if dbUser == "" || dbPassword == "" || dbName == "" { + db, err = gorm.Open(sqlite.Open("backoffice.db"), &gorm.Config{}) + if err != nil { + panic("failed to connect database") + } + } else { + dsn := fmt.Sprintf("%v:%v@tcp(127.0.0.1:3306)/%v?charset=utf8mb4&parseTime=True&loc=Local", dbUser, dbPassword, dbName) + db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) + if err != nil { + panic("failed to connect database") + } + } + // Migrate the schema + db.AutoMigrate(&MonthlyReport{}) +} + +func Exists(month int, year int, contentType string, content string) bool { + var monthlyReport MonthlyReport + if err := db.Where("month = ? AND year = ? AND content_type = ? AND content = ?", content).First(&monthlyReport).Error; err != nil { + return false + } + return true +} + +func Create(month int, year int, contentType string, content string) error { + if err := db.Create(&MonthlyReport{Month: month, Year: year, ContentType: contentType, Content: content}).Error; err != nil { + return err + } + return nil +} diff --git a/internal/monthlyNews/monthlyNews.go b/internal/monthlyNews/monthlyNews.go new file mode 100644 index 0000000000000000000000000000000000000000..d934a4cb1566a90c7407a20b7297e0a450c04a4f --- /dev/null +++ b/internal/monthlyNews/monthlyNews.go @@ -0,0 +1,71 @@ +package monthlyNews + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + + "forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server/internal/database" +) + +func ProcessMonthlyNews(w http.ResponseWriter, r *http.Request) { + switch method := r.Method; method { + case "GET": + SendMonthlyNews(w, r) + case "POST": + AddMonthlyNews(w, r) + case "DELETE": + DeleteMonthlyNews(w, r) + case "PUT": + UpdateMonthlyNews(w, r) + default: + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + } +} + +func AddMonthlyNews(w http.ResponseWriter, r *http.Request) { + + if r.Body == http.NoBody { + http.Error(w, "request body is empty", http.StatusBadRequest) + return + } + + type MonthlyNews struct { + Month int `json:"month"` + Year int `json:"year"` + Header string `json:"header"` + Quote string `json:"quote"` + } + + decoder := json.NewDecoder(r.Body) + var monthlyNews MonthlyNews + err := decoder.Decode(&monthlyNews) + if err != nil { + fmt.Println(err) + } + + err = database.Create(monthlyNews.Month, monthlyNews.Year, "header", monthlyNews.Header) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + err = database.Create(monthlyNews.Month, monthlyNews.Year, "quote", monthlyNews.Quote) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + log.Printf("| new monthly news | %v", r.RemoteAddr) +} + +func SendMonthlyNews(w http.ResponseWriter, r *http.Request) {} + +// TODO: +func UpdateMonthlyNews(w http.ResponseWriter, r *http.Request) {} + +// TODO: +func DeleteMonthlyNews(w http.ResponseWriter, r *http.Request) {} + +// TODO: diff --git a/internal/monthlyReport/monthlyReport.go b/internal/monthlyReport/monthlyReport.go new file mode 100644 index 0000000000000000000000000000000000000000..65696c6df6ce895b47dafe302bcaf63062583226 --- /dev/null +++ b/internal/monthlyReport/monthlyReport.go @@ -0,0 +1 @@ +package monthlyReport diff --git a/internal/poll/poll.go b/internal/poll/poll.go new file mode 100644 index 0000000000000000000000000000000000000000..92424266f61d7275605b234508342938127e3e58 --- /dev/null +++ b/internal/poll/poll.go @@ -0,0 +1 @@ +package poll diff --git a/internal/post/post.go b/internal/post/post.go deleted file mode 100644 index 79a0d7adbb628dea14da062767f152ba5bf30b37..0000000000000000000000000000000000000000 --- a/internal/post/post.go +++ /dev/null @@ -1,47 +0,0 @@ -package post - -import ( - "fmt" - - "forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server/internal/common" - "gorm.io/driver/mysql" - "gorm.io/gorm" -) - -type Post struct { - gorm.Model - Content string -} - -var ( - db *gorm.DB - dbUser = common.StringValueFromEnv("DATABASE_USER", "") - dbPassword = common.StringValueFromEnv("DATABASE_PASSWORD", "") - dbName = common.StringValueFromEnv("DATABASE_NAME", "") -) - -func init() { - var err error - dsn := fmt.Sprintf("%v:%v@tcp(127.0.0.1:3306)/%v?charset=utf8mb4&parseTime=True&loc=Local", dbUser, dbPassword, dbName) - db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) - if err != nil { - panic("failed to connect database") - } - // Migrate the schema - db.AutoMigrate(&Post{}) -} - -func Exists(content string) bool { - var post Post - if err := db.Where("content = ?", content).First(&post).Error; err != nil { - return false - } - return true -} - -func Create(content string) error { - if err := db.Create(&Post{Content: content}).Error; err != nil { - return err - } - return nil -} diff --git a/internal/rootmux/rootmux.go b/internal/rootmux/rootmux.go index e628d182e03ac38d576e485fde08429676196955..40618d32e64e0bd97f132123d254a0c12dba649d 100644 --- a/internal/rootmux/rootmux.go +++ b/internal/rootmux/rootmux.go @@ -5,8 +5,8 @@ import ( "os" "forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server/internal/auth" - "forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server/internal/backoffice" "forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server/internal/common" + "forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server/internal/monthlyNews" "github.com/nicolaspernoud/vestibule/pkg/middlewares" ) @@ -26,7 +26,7 @@ func CreateRootMux(staticDir string) RootMux { mainMux.Handle("/api/common/WhoAmI", auth.ValidateAuthMiddleware(auth.WhoAmI(), []string{"*"}, false)) adminMux := http.NewServeMux() - adminMux.HandleFunc("/newPost", backoffice.HandleCreatePost) + adminMux.HandleFunc("/monthlyNews/", monthlyNews.ProcessMonthlyNews) mainMux.Handle("/api/admin/", http.StripPrefix("/api/admin", auth.ValidateAuthMiddleware(adminMux, []string{os.Getenv("ADMIN_ROLE")}, true))) // Serve static files falling back to serving index.html diff --git a/internal/rootmux/rootmux_test.go b/internal/rootmux/rootmux_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0b57990f613bb17c37d2ad6832f87c762147ff3b --- /dev/null +++ b/internal/rootmux/rootmux_test.go @@ -0,0 +1,115 @@ +package rootmux + +import ( + "encoding/json" + "net/http" + "net/http/cookiejar" + "net/http/httptest" + "net/url" + "os" + "testing" + + "forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server/internal/auth" + "forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server/internal/mocks" + "forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server/internal/tester" + "forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server/internal/tokens" +) + +var ( + newMonthlyNews = `{"month": 1,"year": 2000,"header":"newsHeader","quote":"newsQuote"}` + noH map[string]string +) + +func init() { + tokens.Init("testdata/tokenskey.json", true) +} + +func TestAll(t *testing.T) { + // Create the mock OAuth2 server + oAuth2Server := httptest.NewServer(mocks.CreateMockOAuth2()) + defer oAuth2Server.Close() + // Create the mock API server + go http.ListenAndServe(":8091", mocks.CreateMockAPI()) + // Set the constants with environment variables + os.Setenv("HOSTNAME", "localhost") + os.Setenv("ADMIN_ROLE", "ADMINS") + os.Setenv("CLIENT_ID", "foo") + os.Setenv("CLIENT_SECRET", "bar") + os.Setenv("TOKEN_URL", oAuth2Server.URL+"/token") + os.Setenv("USERINFO_URL", oAuth2Server.URL+"/userinfo") + os.Setenv("LOGOUT_URL", oAuth2Server.URL+"/logout") + // Set up testers + 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) + + os.Setenv("USERINFO_URL", oAuth2Server.URL+"/admininfo") + adminTests(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) + defer ts.Close() // Close the tester + // Try to login (must fail) + 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) + defer ts.Close() // Close the tester + + // Try to create a monthlyNews (must fail) + do("POST", "/api/admin/monthlyNews/", noH, newMonthlyNews, http.StatusUnauthorized, "error extracting token") +} + +/** +ADMIN TESTS (this tests are to check that an administrator can alter the apps) +**/ +func adminTests(t *testing.T) { + // Create the tester + ts, do, _ := createTester(t) + defer ts.Close() // Close the tester + tests := func() { + // Get the XSRF Token + response := do("GET", "/api/common/WhoAmI", noH, "", http.StatusOK, "") + token := auth.TokenData{} + json.Unmarshal([]byte(response), &token) + xsrfHeader := map[string]string{"XSRF-TOKEN": token.XSRFToken} + // Try to create a monthly news without the XSRF-TOKEN (must fail) + do("POST", "/api/admin/monthlyNews/", noH, newMonthlyNews, http.StatusUnauthorized, "XSRF") + // Try to create an app (must pass) + do("POST", "/api/admin/monthlyNews/", xsrfHeader, newMonthlyNews, http.StatusOK, "") + } + // Try to login (must pass) + do("GET", "/OAuth2Login", noH, "", http.StatusOK, "<!DOCTYPE html>") + // Run the tests + tests() + // Try to logout (must pass) + do("GET", "/Logout", noH, "", http.StatusOK, "Logout OK") + // Try to create a monthly news again (must fail) + do("GET", "/api/admin/monthlyNews/", noH, "", http.StatusUnauthorized, "error extracting token") +} + +func createTester(t *testing.T) (*httptest.Server, tester.DoFn, tester.DoFn) { + // Create the server + mux := CreateRootMux("../../web") + ts := httptest.NewServer(mux.Mux) + url, _ := url.Parse(ts.URL) + port := url.Port() + mux.Manager.Config.RedirectURL = "http://" + os.Getenv("HOSTNAME") + ":" + port + "/OAuth2Callback" + mux.Manager.Hostname = "http://" + os.Getenv("HOSTNAME") + ":" + port + // Create the cookie jar + jar, _ := cookiejar.New(nil) + // wrap the testing function + return ts, tester.CreateServerTester(t, port, os.Getenv("HOSTNAME"), jar), tester.CreateServerTester(t, port, os.Getenv("HOSTNAME"), nil) +} diff --git a/internal/tester/tester.go b/internal/tester/tester.go new file mode 100644 index 0000000000000000000000000000000000000000..d7c6a6739062d632843205b3a7de13ef13795618 --- /dev/null +++ b/internal/tester/tester.go @@ -0,0 +1,92 @@ +package tester + +import ( + "context" + "io/ioutil" + "net" + "net/http" + "net/http/cookiejar" + "net/http/httptest" + "net/url" + "strings" + "testing" + "time" +) + +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{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + } + // or create your own transport, there's an example on godoc. + http.DefaultTransport.(*http.Transport).DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { + addrAndPort := strings.Split(addr, ":") + if strings.HasSuffix(addrAndPort[0], "vestibule.io") { + addr = "127.0.0.1:" + addrAndPort[1] + } + return dialer.DialContext(ctx, network, addr) + } + if strings.HasPrefix(testURL, "/") { + testURL = "http://" + hostname + ":" + port + testURL + } else { + u, _ := url.Parse("http://" + testURL) + testURL = "http://" + u.Host + ":" + port + u.Path + "?" + u.RawQuery + } + req, err := http.NewRequest(method, testURL, strings.NewReader(payload)) + if err != nil { + t.Fatal(err) + } + for i, v := range headers { + req.Header.Set(i, v) + } + var client *http.Client + if jar != nil { + client = &http.Client{Jar: jar} + } else { + client = &http.Client{} + } + res, err := client.Do(req) + if err != nil { + t.Fatal(err) + } + body, _ := ioutil.ReadAll(res.Body) + bodyString := string(body) + if status := res.StatusCode; status != expectedStatus { + t.Errorf("Tested %v %v %v ; handler returned wrong status code: got %v want %v", method, testURL, payload, status, expectedStatus) + } + if !strings.HasPrefix(bodyString, expectedBody) { + t.Errorf("Tested %v %v %v ; handler returned unexpected body: got %v want %v", method, testURL, payload, bodyString, expectedBody) + } + return bodyString +} + +// CreateServerTester wraps DoRequestOnServer to factorize t, port and jar +func CreateServerTester(t *testing.T, hostname string, port string, jar *cookiejar.Jar) DoFn { + return func(method string, url string, headers map[string]string, payload string, expectedStatus int, expectedBody string) string { + return DoRequestOnServer(t, port, hostname, jar, method, url, headers, payload, expectedStatus, expectedBody) + } +} diff --git a/web/components/post/post.js b/web/components/post/post.js index b99a1faa08938153530a04ec1de82abfb9dc108b..748a948353d047e3b674557eba19bcc726cf7317 100644 --- a/web/components/post/post.js +++ b/web/components/post/post.js @@ -13,25 +13,22 @@ class Post { } // DOM elements - post_textarea; new_post_button; async mount(mountpoint) { document.getElementById(mountpoint).innerHTML = /* HTML */ ` <div class="container is-fluid" > - <div id="textarea-control" class="control my-2"> - <textarea - id="post-textarea" - class="textarea is-info is-medium has-fixed-size" - placeholder="Contenu du post" - ></textarea> - </div> - <button id="post-submit" class="button is-success"> - Créer nouveau post - </button> + <label for="month" class="label">Month</label> + <input id="month" name="month" type="text" /> + <label for="year" class="label">Year</label> + <input id="year" name="year" type="text" /> + <label for="header" class="label">Header</label> + <input id="header" name="header" type="text" /> + <label for="quote" class="label">Quote</label> + <input id="quote" name="quote" type="text" /> + <input id="post-submit" type="button" value="Submit form" /> </div>`; - this.post_textarea = document.getElementById("post-textarea"); this.new_post_button = document.getElementById("post-submit"); this.new_post_button.addEventListener("click", async () => { await this.submitPost(); @@ -39,15 +36,19 @@ class Post { } async submitPost() { - const control = document.getElementById("textarea-control"); - control.classList.add("is-loading"); + try { - const response = await fetch("/api/admin/newPost", { + const response = await fetch("/api/admin/monthlyNews/", { method: "POST", headers: new Headers({ "XSRF-Token": this.current_user.xsrftoken, }), - body: document.getElementById("post-textarea").value + body: JSON.stringify({ + month: parseInt(document.getElementById("month").value), + year: parseInt(document.getElementById("year").value), + header: document.getElementById("header").value, + quote: document.getElementById("quote").value, + }) }); if (response.status !== 200) { throw new Error( @@ -55,11 +56,8 @@ class Post { ); } Messages.Show("is-success", "Le post a été créé avec succès"); - } catch (e) { HandleError(e); - this.post_textarea.value = "Une erreur s'est produite !"; } - control.classList.remove("is-loading"); } }