diff --git a/backend/handlers/projects.go b/backend/handlers/projects.go index 2c67c70..94f0eed 100644 --- a/backend/handlers/projects.go +++ b/backend/handlers/projects.go @@ -23,29 +23,27 @@ func projectsRouter(router fiber.Router) error { return nil } -var projectsLastEdit time.Time; +var projectsLastEdit time.Time = time.Now().Truncate(time.Second); func GetAllProjects(c *fiber.Ctx) error { - isCached, err := utils.Cache(c, projectsLastEdit); + isCached, err := utils.Cache(c, &projectsLastEdit); if err == nil && isCached { return nil; } projects, err := db.GetAllProjects() - currentTime := time.Now(); if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "Cannot retrieve projects", "trace": fmt.Sprint(err), }) } - + err = c.Status(fiber.StatusOK).JSON(projects) if err != nil { return err; } - projectsLastEdit = currentTime; return nil; } @@ -83,6 +81,8 @@ func CreateProject(c *fiber.Ctx) error { }) } + projectsLastEdit = time.Now().Truncate(time.Second); + return c.Status(fiber.StatusCreated).JSON(fiber.Map{ "id": id, }) @@ -111,6 +111,8 @@ func UpdateProject(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusNotFound) } + projectsLastEdit = time.Now().Truncate(time.Second); + return c.SendStatus(fiber.StatusNoContent) } @@ -132,6 +134,8 @@ func DeleteProject(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusNotFound) } + projectsLastEdit = time.Now().Truncate(time.Second); + return c.SendStatus(fiber.StatusNoContent) } diff --git a/backend/main.go b/backend/main.go index a8a9727..b0223a3 100644 --- a/backend/main.go +++ b/backend/main.go @@ -33,7 +33,7 @@ func main() { app.Use(cors.New(cors.Config{ AllowOrigins: origins, AllowMethods: "GET,POST,PUT,DELETE", - AllowHeaders: "Origin, Content-Type, Accept", + AllowHeaders: "Origin, Content-Type, Accept, Cache-Control, Pragma,Expires, If-Modified-Since", })) // app.Use(cache.New()) diff --git a/backend/utils/cache.go b/backend/utils/cache.go index 280cb64..f083470 100644 --- a/backend/utils/cache.go +++ b/backend/utils/cache.go @@ -6,16 +6,22 @@ import ( "github.com/gofiber/fiber/v2" ) -func Cache(c *fiber.Ctx, lastEdit time.Time) (bool, error) { - clientLast, err := time.Parse(time.RFC1123, c.Get("If-Modified-Since")) +func Cache(c *fiber.Ctx, lastEdit *time.Time) (bool, error) { + ifModifiedSince := c.Get("If-Modified-Since") + if ifModifiedSince == "" { + return false, nil + } + + clientLast, err := time.Parse(time.RFC1123, ifModifiedSince) if err != nil { return false, err } - - if clientLast.Before(lastEdit) { + + if !lastEdit.After(clientLast) { c.SendStatus(fiber.StatusNotModified) return true, nil } + c.Set("Last-Modified", lastEdit.Format(time.RFC1123)) return false, nil } \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c623709..e9b537a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,7 +8,8 @@ "name": "frontend", "version": "0.0.1", "dependencies": { - "axios": "^1.6.3", + "axios": "^1.6.4", + "axios-cache-interceptor": "^1.4.1", "less": "^4.2.0", "svelte-multiselect": "^10.2.0" }, @@ -1251,15 +1252,34 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.3.tgz", - "integrity": "sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.4.tgz", + "integrity": "sha512-heJnIs6N4aa1eSthhN9M5ioILu8Wi8vmQW9iHQ9NUvfkJb0lEEDUiIdQNAuBtfUt3FxReaKdpQA5DbmMOqzF/A==", "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.4", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, + "node_modules/axios-cache-interceptor": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/axios-cache-interceptor/-/axios-cache-interceptor-1.4.1.tgz", + "integrity": "sha512-Ax4+PiGfNxpQvyF00t55nFzWoVnqW7slKCg9va6dbqiuAGIxRE8r1uMzunw8TKJ5iwLivFqAb0EeiLeUCxuZIw==", + "dependencies": { + "cache-parser": "1.2.4", + "fast-defer": "1.1.8", + "object-code": "1.3.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/arthurfiorette/axios-cache-interceptor?sponsor=1" + }, + "peerDependencies": { + "axios": "^1" + } + }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -1313,6 +1333,11 @@ "node": "*" } }, + "node_modules/cache-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/cache-parser/-/cache-parser-1.2.4.tgz", + "integrity": "sha512-O0KwuHuJnbHUrghHi2kGp0SxnWSIBXTYt7M8WVhW0kbPRUNUKoE/Of6e1rRD6AAxmfxFunKnt90yEK09D+sc5g==" + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1880,6 +1905,11 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-defer": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/fast-defer/-/fast-defer-1.1.8.tgz", + "integrity": "sha512-lEJeOH5VL5R09j6AA0D4Uvq7AgsHw0dAImQQ+F3iSyHZuAxyQfWobsagGpTcOPvJr3urmKRHrs+Gs9hV+/Qm/Q==" + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -1990,9 +2020,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", "funding": [ { "type": "individual", @@ -2665,6 +2695,11 @@ "node": ">=0.10.0" } }, + "node_modules/object-code": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/object-code/-/object-code-1.3.2.tgz", + "integrity": "sha512-3CVDmQiru7YYVr+4OpCGrqkVE7GogmWinDcgfno1fXlKgIhtW9FhgHTiV98gMPUjQwWuWvijQDCY8sGnqKrhTw==" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 9a1e0e7..aa983f3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -35,7 +35,8 @@ }, "type": "module", "dependencies": { - "axios": "^1.6.3", + "axios": "^1.6.4", + "axios-cache-interceptor": "^1.4.1", "less": "^4.2.0", "svelte-multiselect": "^10.2.0" } diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index 1a6f48b..570a70c 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -19,9 +19,9 @@ projects = response.data || []; } catch (e: any) { toastAlert('Failed to fetch projects', e); - setTimeout(() => { - window.location.reload(); - }, 11000); + // setTimeout(() => { + // window.location.reload(); + // }, 11000); } }); diff --git a/frontend/src/utils/api.ts b/frontend/src/utils/api.ts index 1d07c4c..bb87744 100644 --- a/frontend/src/utils/api.ts +++ b/frontend/src/utils/api.ts @@ -1,27 +1,34 @@ -import axios, { Axios, type AxiosResponse } from "axios"; -import { backend } from "../stores/config"; -import { toastAlert } from "./toasts"; +import axios, { Axios, type AxiosResponse } from 'axios'; +import { backend } from '../stores/config'; +import { toastAlert } from './toasts'; +import { setupCache } from 'axios-cache-interceptor'; -export default new Axios({ - ...axios.defaults, - baseURL: backend + '/api', - validateStatus: () => true, - headers: { - 'Content-Type': 'application/json' - }, -}); +export default setupCache( + new Axios({ + ...axios.defaults, + baseURL: backend + '/api', + validateStatus: () => true, + headers: { + 'Content-Type': 'application/json' + } + }), + { + interpretHeader: true, + modifiedSince: true + } +); -export function processError (response: AxiosResponse, message: string = '') { - let title = `${response.status} ${response.statusText}`; - let subtitle = message; +export function processError(response: AxiosResponse, message: string = '') { + let title = `${response.status} ${response.statusText}`; + let subtitle = message; - if(response.headers["content-type"] === "application/json") { - const parsed = response.data; - subtitle = parsed.error; - if(response.data.trace) { - subtitle += '

' + parsed.trace; - } - } + if (response.headers['content-type'] === 'application/json') { + const parsed = response.data; + subtitle = parsed.error; + if (response.data.trace) { + subtitle += '

' + parsed.trace; + } + } - toastAlert(title, subtitle); -} \ No newline at end of file + toastAlert(title, subtitle); +}