From 2b4795055626c5d680cd69fba5387369face2a85 Mon Sep 17 00:00:00 2001 From: Antonio De Lucreziis Date: Thu, 2 Feb 2023 01:25:12 +0100 Subject: [PATCH] feat: ora funge tutto benissimo per qualche motivo --- .npmrc | 2 + Makefile | 2 +- cmd/build/main.go | 7 +- frontend/pages/articles/article.html | 17 + frontend/pages/articles/index.html | 16 + frontend/pages/articles/typography.scss | 3 + frontend/styles/main.scss | 5 + meta/routes.js | 18 + package.json | 6 +- pnpm-lock.yaml | 417 ++++++++++++++++++ server.js | 76 ++++ services/server/articles/articles.go | 57 +++ services/server/dev/dev.go | 140 +++++- .../lista-utenti.go | 14 +- .../lista-utenti_test.go | 2 +- services/server/router/router.go | 72 +++ services/server/routes/routes.go | 1 - services/server/server.go | 15 +- sl/sl.go | 8 +- vite.config.js | 98 ++-- 20 files changed, 879 insertions(+), 97 deletions(-) create mode 100644 .npmrc create mode 100644 frontend/pages/articles/article.html create mode 100644 frontend/pages/articles/index.html create mode 100644 frontend/pages/articles/typography.scss create mode 100644 frontend/styles/main.scss create mode 100644 meta/routes.js create mode 100644 server.js create mode 100644 services/server/articles/articles.go rename services/server/{lista-utenti => listaUtenti}/lista-utenti.go (62%) rename services/server/{lista-utenti => listaUtenti}/lista-utenti_test.go (98%) create mode 100644 services/server/router/router.go diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..84c929e --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +# Needed by pnpm to work with "@preact/preset-vite" +shamefully-hoist=true diff --git a/Makefile b/Makefile index fc3daf3..2b308bc 100644 --- a/Makefile +++ b/Makefile @@ -6,5 +6,5 @@ test: .PHONY: build build: go run -v ./cmd/build - pnpm run build --emptyOutDir + pnpm run build go build -o ./out/bin/server ./cmd/server diff --git a/cmd/build/main.go b/cmd/build/main.go index 7742d8f..60b45cd 100644 --- a/cmd/build/main.go +++ b/cmd/build/main.go @@ -29,11 +29,6 @@ func main() { log.Fatal(err) } - dev, err := sl.Use(l, dev.Slot) - if err != nil { - log.Fatal(err) - } - f, err := os.Create("out/routes.json") if err != nil { log.Fatal(err) @@ -41,7 +36,7 @@ func main() { enc := json.NewEncoder(f) enc.SetIndent("", " ") - if err := enc.Encode(dev.HtmlRouteBindings); err != nil { + if err := enc.Encode(dev.UseRoutesMetadata(l)); err != nil { log.Fatal(err) } diff --git a/frontend/pages/articles/article.html b/frontend/pages/articles/article.html new file mode 100644 index 0000000..6280483 --- /dev/null +++ b/frontend/pages/articles/article.html @@ -0,0 +1,17 @@ + + + + + + + + {{ .Title }} • Articoli • PHC + + + + + +

Articolo "{{ .Example }}"

+

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Neque, quasi...

+ + diff --git a/frontend/pages/articles/index.html b/frontend/pages/articles/index.html new file mode 100644 index 0000000..d533431 --- /dev/null +++ b/frontend/pages/articles/index.html @@ -0,0 +1,16 @@ + + + + + + + + Articoli • PHC + + + + +

Articoli

+ {{ .Example }} + + diff --git a/frontend/pages/articles/typography.scss b/frontend/pages/articles/typography.scss new file mode 100644 index 0000000..d224431 --- /dev/null +++ b/frontend/pages/articles/typography.scss @@ -0,0 +1,3 @@ +h1 { + color: red; +} diff --git a/frontend/styles/main.scss b/frontend/styles/main.scss new file mode 100644 index 0000000..4cd982c --- /dev/null +++ b/frontend/styles/main.scss @@ -0,0 +1,5 @@ +*, +*::before, +*::after { + box-sizing: border-box; +} diff --git a/meta/routes.js b/meta/routes.js new file mode 100644 index 0000000..89f69fe --- /dev/null +++ b/meta/routes.js @@ -0,0 +1,18 @@ +import fetch from 'node-fetch' +import { readFile } from 'fs/promises' + +export async function getBuildRoutesMetadata(file) { + console.log('Loading routes from disk...') + + const routesRaw = await readFile(file, 'utf8') + return JSON.parse(routesRaw) +} + +export async function getDevRoutesMetadata(url) { + console.log('Loading routes from go server...') + + const routesReq = await fetch(url) + const routes = await routesReq.json() + + return routes +} diff --git a/package.json b/package.json index a92aade..872ff4a 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,14 @@ "version": "1.0.0", "type": "module", "scripts": { - "dev": "vite --clearScreen false", - "build": "vite build" + "dev": "node server.js", + "build": "vite build --emptyOutDir" }, "devDependencies": { "@preact/preset-vite": "^2.5.0", "axios": "^1.2.6", + "express": "^4.18.2", + "morgan": "^1.10.0", "node-fetch": "^3.3.0", "sass": "^1.57.1", "vite": "^4.0.4" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5bd7478..3116d62 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3,6 +3,8 @@ lockfileVersion: 5.4 specifiers: '@preact/preset-vite': ^2.5.0 axios: ^1.2.6 + express: ^4.18.2 + morgan: ^1.10.0 node-fetch: ^3.3.0 preact: ^10.11.3 sass: ^1.57.1 @@ -14,6 +16,8 @@ dependencies: devDependencies: '@preact/preset-vite': 2.5.0_preact@10.11.3+vite@4.0.4 axios: 1.2.6 + express: 4.18.2 + morgan: 1.10.0 node-fetch: 3.3.0 sass: 1.57.1 vite: 4.0.4_sass@1.57.1 @@ -562,6 +566,14 @@ packages: picomatch: 2.3.1 dev: true + /accepts/1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + dev: true + /ansi-styles/3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -577,6 +589,10 @@ packages: picomatch: 2.3.1 dev: true + /array-flatten/1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + dev: true + /asynckit/0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} dev: true @@ -597,11 +613,38 @@ packages: '@babel/core': ^7.12.10 dev: true + /basic-auth/2.0.1: + resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} + engines: {node: '>= 0.8'} + dependencies: + safe-buffer: 5.1.2 + dev: true + /binary-extensions/2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} dev: true + /body-parser/1.20.1: + resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.1 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /braces/3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} @@ -620,6 +663,18 @@ packages: update-browserslist-db: 1.0.10_browserslist@4.21.5 dev: true + /bytes/3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + dev: true + + /call-bind/1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.2.0 + dev: true + /caniuse-lite/1.0.30001449: resolution: {integrity: sha512-CPB+UL9XMT/Av+pJxCKGhdx+yg1hzplvFJQlJ2n68PyQGMz9L/E2zCyLdOL8uasbouTUgnPl+y0tccI/se+BEw==} dev: true @@ -665,15 +720,47 @@ packages: delayed-stream: 1.0.0 dev: true + /content-disposition/0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /content-type/1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + dev: true + /convert-source-map/1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} dev: true + /cookie-signature/1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + dev: true + + /cookie/0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + dev: true + /data-uri-to-buffer/4.0.1: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} dev: true + /debug/2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: true + /debug/4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -691,10 +778,29 @@ packages: engines: {node: '>=0.4.0'} dev: true + /depd/2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dev: true + + /destroy/1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dev: true + + /ee-first/1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + dev: true + /electron-to-chromium/1.4.284: resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} dev: true + /encodeurl/1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + dev: true + /esbuild/0.16.17: resolution: {integrity: sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==} engines: {node: '>=12'} @@ -730,6 +836,10 @@ packages: engines: {node: '>=6'} dev: true + /escape-html/1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + dev: true + /escape-string-regexp/1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -739,6 +849,50 @@ packages: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} dev: true + /etag/1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + dev: true + + /express/4.18.2: + resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} + engines: {node: '>= 0.10.0'} + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.1 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.5.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.2.0 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.7 + qs: 6.11.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.18.0 + serve-static: 1.15.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: true + /fetch-blob/3.2.0: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} @@ -754,6 +908,21 @@ packages: to-regex-range: 5.0.1 dev: true + /finalhandler/1.2.0: + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /follow-redirects/1.15.2: resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} engines: {node: '>=4.0'} @@ -780,6 +949,16 @@ packages: fetch-blob: 3.2.0 dev: true + /forwarded/0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + dev: true + + /fresh/0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + dev: true + /fsevents/2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -797,6 +976,14 @@ packages: engines: {node: '>=6.9.0'} dev: true + /get-intrinsic/1.2.0: + resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.3 + dev: true + /glob-parent/5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -814,6 +1001,11 @@ packages: engines: {node: '>=4'} dev: true + /has-symbols/1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + /has/1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} @@ -821,10 +1013,37 @@ packages: function-bind: 1.1.1 dev: true + /http-errors/2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + dev: true + + /iconv-lite/0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + /immutable/4.2.2: resolution: {integrity: sha512-fTMKDwtbvO5tldky9QZ2fMX7slR0mYpY5nbnFWYp0fOzDhHqhgIw9KoYgxLWsoNTS9ZHGauHj18DTyEw6BK3Og==} dev: true + /inherits/2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /ipaddr.js/1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + dev: true + /is-binary-path/2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -881,6 +1100,20 @@ packages: yallist: 3.1.1 dev: true + /media-typer/0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + dev: true + + /merge-descriptors/1.0.1: + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + dev: true + + /methods/1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + dev: true + /mime-db/1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -893,16 +1126,48 @@ packages: mime-db: 1.52.0 dev: true + /mime/1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /morgan/1.10.0: + resolution: {integrity: sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==} + engines: {node: '>= 0.8.0'} + dependencies: + basic-auth: 2.0.1 + debug: 2.6.9 + depd: 2.0.0 + on-finished: 2.3.0 + on-headers: 1.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /ms/2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: true + /ms/2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true + /ms/2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + /nanoid/3.3.4: resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true dev: true + /negotiator/0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + dev: true + /node-domexception/1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -926,10 +1191,42 @@ packages: engines: {node: '>=0.10.0'} dev: true + /object-inspect/1.12.3: + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + dev: true + + /on-finished/2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: true + + /on-finished/2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: true + + /on-headers/1.0.2: + resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} + engines: {node: '>= 0.8'} + dev: true + + /parseurl/1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + dev: true + /path-parse/1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true + /path-to-regexp/0.1.7: + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + dev: true + /picocolors/1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: true @@ -951,10 +1248,40 @@ packages: /preact/10.11.3: resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==} + /proxy-addr/2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + dev: true + /proxy-from-env/1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} dev: true + /qs/6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + dev: true + + /range-parser/1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + dev: true + + /raw-body/2.5.1: + resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + dev: true + /readdirp/3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -979,6 +1306,18 @@ packages: fsevents: 2.3.2 dev: true + /safe-buffer/5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + dev: true + + /safe-buffer/5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true + + /safer-buffer/2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: true + /sass/1.57.1: resolution: {integrity: sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==} engines: {node: '>=12.0.0'} @@ -994,11 +1333,61 @@ packages: hasBin: true dev: true + /send/0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + dev: true + + /serve-static/1.15.0: + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.18.0 + transitivePeerDependencies: + - supports-color + dev: true + + /setprototypeof/1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + dev: true + + /side-channel/1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + object-inspect: 1.12.3 + dev: true + /source-map-js/1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} dev: true + /statuses/2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + dev: true + /supports-color/5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -1023,6 +1412,24 @@ packages: is-number: 7.0.0 dev: true + /toidentifier/1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + dev: true + + /type-is/1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + dev: true + + /unpipe/1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + dev: true + /update-browserslist-db/1.0.10_browserslist@4.21.5: resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} hasBin: true @@ -1034,6 +1441,16 @@ packages: picocolors: 1.0.0 dev: true + /utils-merge/1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + dev: true + + /vary/1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: true + /vite/4.0.4_sass@1.57.1: resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==} engines: {node: ^14.18.0 || >=16.0.0} diff --git a/server.js b/server.js new file mode 100644 index 0000000..7cb2b93 --- /dev/null +++ b/server.js @@ -0,0 +1,76 @@ +import express from 'express' +import { createServer as createViteServer } from 'vite' +import { getDevRoutesMetadata } from './meta/routes.js' + +import fetch from 'node-fetch' + +import { readFile } from 'fs/promises' +import { dirname, resolve } from 'path' + +import morgan from 'morgan' + +import { fileURLToPath } from 'url' +const __dirname = fileURLToPath(new URL('.', import.meta.url)) + +async function main() { + const routes = await getDevRoutesMetadata('http://127.0.0.1:4000/api/development/routes') + console.dir(routes) + + const app = express() + + app.use(morgan(':method :url :status :response-time ms - :res[content-length]')) + + const vite = await createViteServer({ + server: { middlewareMode: true }, + appType: 'custom', + }) + + app.use(vite.middlewares) + + Object.entries(routes.static).forEach(([route, file]) => { + app.get(route, async (_req, res) => { + let htmlPage = await readFile(resolve(__dirname, './frontend/', file), 'utf8') + htmlPage = htmlPage.replace(/\.\//g, '/' + dirname(file) + '/') + + const html = await vite.transformIndexHtml(file, htmlPage) + console.dir(file) + + res.writeHead(200, { 'Content-Type': 'text/html' }).end(html) + }) + }) + + Object.entries(routes.dynamic).forEach(([route, file]) => { + app.get(route, async (req, res) => { + let htmlPage = await readFile(resolve(__dirname, './frontend/', file), 'utf8') + htmlPage = htmlPage.replace(/\.\//g, '/' + dirname(file) + '/') + + const html = await vite.transformIndexHtml(file, htmlPage) + + console.log('req.url = ', req.url) + console.log('req.originalUrl = ', req.originalUrl) + + const templateHtmlReq = await fetch('http://127.0.0.1:4000/api/development/render', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + route, + page: html, + request: { + params: req.params, + query: req.query, + }, + }), + }) + + const renderedHtml = await templateHtmlReq.json() + + res.writeHead(200, { 'Content-Type': 'text/html' }).end(renderedHtml) + }) + }) + + app.listen(3000, () => { + console.log(`Listening on port 3000...`) + }) +} + +main() diff --git a/services/server/articles/articles.go b/services/server/articles/articles.go new file mode 100644 index 0000000..5705453 --- /dev/null +++ b/services/server/articles/articles.go @@ -0,0 +1,57 @@ +package articles + +import ( + "html/template" + "phc/website/services/server/dev" + "phc/website/services/server/router" + "phc/website/sl" +) + +func Configure(l *sl.ServiceLocator) error { + router.UseRouteTemplatedPage(l, "/articles", + "pages/articles/index.html", + func(w dev.ResponseWriter, r dev.Request) error { + tmpl := template.New("") + + tmpl, err := tmpl.Parse(string(r.Page())) + if err != nil { + return err + } + + ctx := map[string]any{ + "Example": "Bla bla", + } + + if err := tmpl.Execute(w, ctx); err != nil { + return err + } + + return nil + }, + ) + + router.UseRouteTemplatedPage(l, "/articles/:slug", + "pages/articles/article.html", + func(w dev.ResponseWriter, r dev.Request) error { + tmpl := template.New("") + + tmpl, err := tmpl.Parse(string(r.Page())) + if err != nil { + return err + } + + ctx := map[string]any{ + "Title": r.Param("slug"), + "Example": "Bla bla " + r.Param("slug"), + } + + if err := tmpl.Execute(w, ctx); err != nil { + return err + } + + return nil + }, + ) + + return nil +} diff --git a/services/server/dev/dev.go b/services/server/dev/dev.go index fe7d3b7..9a67362 100644 --- a/services/server/dev/dev.go +++ b/services/server/dev/dev.go @@ -1,49 +1,157 @@ package dev import ( + "bytes" + "fmt" + "io" "log" "path" "phc/website/services/server/routes" "phc/website/sl" + "github.com/alecthomas/repr" "github.com/gofiber/fiber/v2" ) -var Slot = sl.NewSlot[*Dev]() +// slot represents a private "write only" service +var slot = sl.NewSlot[*devService]() -type Dev struct { - HtmlRouteBindings map[string]string +// InjectInto a [*sl.ServiceLocator] an instance of the dev service +func InjectInto(l *sl.ServiceLocator) { + sl.InjectLazy(l, slot, Configure) } -func New(l *sl.ServiceLocator) (*Dev, error) { - dev := &Dev{ +func UseRoutesMetadata(l *sl.ServiceLocator) map[string]any { + dev, err := sl.Use(l, slot) + if err != nil { + log.Fatal(err) + } + + return map[string]any{ + "static": dev.staticRoutes, + "dynamic": dev.dynamicRoutes, + } +} + +type Request interface { + Page() []byte + Param(key string) string + Query(key string) string +} + +type ResponseWriter interface { + io.Writer +} + +// devServerRequest is used when handling request from the dev server where params and queries are parsed by express +type devServerRequest struct { + page []byte + params map[string]string + query map[string]string +} + +func (r devServerRequest) Page() []byte { + return r.page +} + +func (r devServerRequest) Param(key string) string { + return r.params[key] +} + +func (r devServerRequest) Query(key string) string { + return r.query[key] +} + +// Handler is a custom routes handler +type Handler func(ResponseWriter, Request) error + +type devService struct { + staticRoutes map[string]string + dynamicRoutes map[string]string + + dynamicRoutesHandlers map[string]Handler +} + +func Configure(l *sl.ServiceLocator) (*devService, error) { + d := &devService{ + map[string]string{}, map[string]string{}, + map[string]Handler{}, } - router, err := sl.Use(l, routes.Api) + api, err := sl.Use(l, routes.Api) if err != nil { return nil, err } - router.Get("/dev/routes", func(c *fiber.Ctx) error { - return c.JSON(dev.HtmlRouteBindings) + api.Get("/development/routes", func(c *fiber.Ctx) error { + return c.JSON(map[string]any{ + "static": d.staticRoutes, + "dynamic": d.dynamicRoutes, + }) }) - return dev, nil + api.Post("/development/render", func(c *fiber.Ctx) error { + var data struct { + Route string `json:"route"` + Page string `json:"page"` + Request struct { + ParamsMap map[string]string `json:"params"` + QueryMap map[string]string `json:"query"` + } `json:"request"` + } + + if err := c.BodyParser(&data); err != nil { + return err + } + + repr.Print(data) + + handler, ok := d.dynamicRoutesHandlers[data.Route] + if !ok { + return fmt.Errorf(`no handler for "%s"`, data.Route) + } + + var buf bytes.Buffer + if err := handler(&buf, devServerRequest{ + []byte(data.Page), + data.Request.ParamsMap, + data.Request.QueryMap, + }); err != nil { + return err + } + + return c.JSON(buf.String()) + }) + + return d, nil } -// UseVitePage this hook will link the provided "mountPoint" to the "frontendHtml" page -func UseVitePage(l *sl.ServiceLocator, mountPoint, frontendHtml string) func(c *fiber.Ctx) error { - log.Printf(`registering vite route %q for %q`, frontendHtml, mountPoint) +// RegisterRoute will register the provided "mountPoint" to the "frontendHtml" page +func RegisterRoute(l *sl.ServiceLocator, mountPoint, frontendFile string) { + log.Printf(`registering vite route %q for %q`, frontendFile, mountPoint) - dev, err := sl.Use(l, Slot) + dev, err := sl.Use(l, slot) if err != nil { log.Fatal(err) } - dev.HtmlRouteBindings[mountPoint] = frontendHtml + dev.staticRoutes[mountPoint] = frontendFile + log.Print(dev) +} - return func(c *fiber.Ctx) error { - return c.SendFile(path.Join("./out/frontend/", frontendHtml)) +func RegisterDynamicRoute(l *sl.ServiceLocator, mountPoint, frontendFile string, handler Handler) { + log.Printf(`registering vite route %q for %q`, frontendFile, mountPoint) + + dev, err := sl.Use(l, slot) + if err != nil { + log.Fatal(err) } + + dev.dynamicRoutes[mountPoint] = frontendFile + dev.dynamicRoutesHandlers[mountPoint] = handler +} + +func GetArtifactPath(frontendFile string) string { + return path.Join("./out/frontend/", frontendFile) } diff --git a/services/server/lista-utenti/lista-utenti.go b/services/server/listaUtenti/lista-utenti.go similarity index 62% rename from services/server/lista-utenti/lista-utenti.go rename to services/server/listaUtenti/lista-utenti.go index aa0caa4..28d1b82 100644 --- a/services/server/lista-utenti/lista-utenti.go +++ b/services/server/listaUtenti/lista-utenti.go @@ -1,8 +1,8 @@ -package lista_utenti +package listaUtenti import ( "phc/website/services/database" - "phc/website/services/server/dev" + "phc/website/services/server/router" "phc/website/services/server/routes" "phc/website/sl" @@ -10,21 +10,19 @@ import ( ) func Configure(l *sl.ServiceLocator) error { + router.UseRoutePage(l, "/utenti", "pages/lista-utenti/index.html") + db, err := sl.Use(l, database.Slot) if err != nil { return err } - r, err := sl.Use(l, routes.Root) + api, err := sl.Use(l, routes.Api) if err != nil { return err } - r.Get("/utenti", - dev.UseVitePage(l, "/utenti", "pages/lista-utenti/index.html"), - ) - - r.Get("/api/lista-utenti", func(c *fiber.Ctx) error { + api.Get("/lista-utenti", func(c *fiber.Ctx) error { users, err := db.ReadUsers() if err != nil { return err diff --git a/services/server/lista-utenti/lista-utenti_test.go b/services/server/listaUtenti/lista-utenti_test.go similarity index 98% rename from services/server/lista-utenti/lista-utenti_test.go rename to services/server/listaUtenti/lista-utenti_test.go index 022e8d1..de48e01 100644 --- a/services/server/lista-utenti/lista-utenti_test.go +++ b/services/server/listaUtenti/lista-utenti_test.go @@ -1,4 +1,4 @@ -package lista_utenti_test +package listaUtenti_test import ( "context" diff --git a/services/server/router/router.go b/services/server/router/router.go new file mode 100644 index 0000000..bcde8d3 --- /dev/null +++ b/services/server/router/router.go @@ -0,0 +1,72 @@ +package router + +import ( + "bytes" + "log" + "os" + "phc/website/services/server/dev" + "phc/website/services/server/routes" + "phc/website/sl" + + "github.com/gofiber/fiber/v2" +) + +// assert type of [ServerRequest] is [dev.Request] +var _ dev.Request = ServerRequest{} + +// ServerRequest is used when the request is directly for the Go server +type ServerRequest struct { + page []byte + fiberContext *fiber.Ctx +} + +func (r ServerRequest) Page() []byte { + return r.page +} + +func (ctx ServerRequest) Param(key string) string { + return ctx.fiberContext.Params(key) +} + +func (ctx ServerRequest) Query(key string) string { + return ctx.fiberContext.Query(key) +} + +func UseRoutePage(l *sl.ServiceLocator, route, frontendFile string) { + root, err := sl.Use(l, routes.Root) + if err != nil { + log.Fatal(err) + } + + dev.RegisterRoute(l, route, frontendFile) + + root.Get(route, func(c *fiber.Ctx) error { + return c.SendFile(dev.GetArtifactPath(frontendFile)) + }) +} + +func UseRouteTemplatedPage(l *sl.ServiceLocator, route, frontendFile string, handler dev.Handler) { + r, err := sl.Use(l, routes.Root) + if err != nil { + log.Fatal(err) + } + + dev.RegisterDynamicRoute(l, route, frontendFile, handler) + + r.Get(route, func(c *fiber.Ctx) error { + rawPage, err := os.ReadFile(dev.GetArtifactPath(frontendFile)) + if err != nil { + return err + } + + var buf bytes.Buffer + if err := handler(&buf, ServerRequest{ + rawPage, + c, + }); err != nil { + return err + } + + return c.Type(".html").Send(buf.Bytes()) + }) +} diff --git a/services/server/routes/routes.go b/services/server/routes/routes.go index 1eb795c..742db29 100644 --- a/services/server/routes/routes.go +++ b/services/server/routes/routes.go @@ -7,5 +7,4 @@ import ( ) var Root = sl.NewSlot[fiber.Router]() - var Api = sl.NewSlot[fiber.Router]() diff --git a/services/server/server.go b/services/server/server.go index 8557e78..c130ace 100644 --- a/services/server/server.go +++ b/services/server/server.go @@ -1,8 +1,9 @@ package server import ( + "phc/website/services/server/articles" "phc/website/services/server/dev" - lista_utenti "phc/website/services/server/lista-utenti" + "phc/website/services/server/listaUtenti" "phc/website/services/server/routes" "phc/website/sl" @@ -13,17 +14,17 @@ type Server struct{ Router *fiber.App } func Configure(l *sl.ServiceLocator) (*Server, error) { r := fiber.New(fiber.Config{}) + r.Static("/assets", "./out/frontend/assets") - sl.InjectValue(l, routes.Root, r.Group("/")) + dev.InjectInto(l) + + sl.InjectValue(l, routes.Root, fiber.Router(r)) sl.InjectValue(l, routes.Api, r.Group("/api")) - devServerInterop, err := dev.New(l) - if err != nil { + if err := listaUtenti.Configure(l); err != nil { return nil, err } - sl.InjectValue(l, dev.Slot, devServerInterop) - - if err := lista_utenti.Configure(l); err != nil { + if err := articles.Configure(l); err != nil { return nil, err } diff --git a/sl/sl.go b/sl/sl.go index 13f21c0..ec7ef98 100644 --- a/sl/sl.go +++ b/sl/sl.go @@ -28,6 +28,7 @@ func (s *slot) checkInitialized(l *ServiceLocator) error { log.Printf(`initialized lazy value of type %T for slot of type %s`, v, s.typ) + s.created = true s.value = v } @@ -47,7 +48,12 @@ func New() *ServiceLocator { func InjectValue[T any](l *ServiceLocator, slotKey SlotKey[T], value T) T { log.Printf(`injected value of type %T for slot of type %s`, value, getTypeName[T]()) - l.providers[slotKey] = &slot{nil, true, value, getTypeName[T]()} + l.providers[slotKey] = &slot{ + nil, + true, + value, + getTypeName[T](), + } return value } diff --git a/vite.config.js b/vite.config.js index c31fffb..58e66d8 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,68 +1,58 @@ import { defineConfig } from 'vite' -import fetch from 'node-fetch' - -import { readFile } from 'fs/promises' -import { dirname, resolve } from 'path' +import { dirname, join, resolve } from 'path' import preactPlugin from '@preact/preset-vite' +import { getBuildRoutesMetadata } from './meta/routes.js' -const retriveGoRoutes = { - async build() { - console.log('Loading routes from disk...') - - const routesRaw = await readFile('out/routes.json', 'utf8') - return JSON.parse(routesRaw) - }, - async serve() { - console.log('Loading routes from go server...') +import crypto from 'crypto' - const routesReq = await fetch('http://127.0.0.1:4000/api/dev/routes') - const routes = await routesReq.json() +/** @type {import('vite').UserConfig} */ +const sharedConfig = { + root: './frontend', + plugins: [preactPlugin()], +} - return routes - }, +function routesToRollupInput([route, file]) { + const chunkName = + file + .replaceAll('.html', '') + .replaceAll('index', '') + .replace(/^\/|\/$/, '') + .replaceAll('/', '-') + + '-' + + crypto.createHash('md5').update(route).update(file).digest('hex').slice(0, 8) + + return [chunkName, join(__dirname, 'frontend', file)] } export default defineConfig(async config => { - let routes = await retriveGoRoutes[config.command]() - console.dir(routes) - - return { - root: 'frontend', - build: { - outDir: '../out/frontend', - rollupOptions: { - input: { - 'main': resolve(__dirname, 'frontend/pages/index.html'), - 'lista-utenti': resolve(__dirname, 'frontend/pages/lista-utenti/index.html'), + if (config.command === 'build') { + const routes = await getBuildRoutesMetadata('out/routes.json') + const input = Object.fromEntries( + [...Object.entries(routes.static), ...Object.entries(routes.dynamic).map(([route, { htmlFile }]) => [route, htmlFile])].map( + routesToRollupInput + ) + ) + + console.dir(input) + + return { + ...sharedConfig, + build: { + outDir: '../out/frontend', + rollupOptions: { + input, }, }, - }, - server: { - port: 3000, - proxy: { - '/api': 'http://localhost:4000/', - }, - }, - plugins: [ - preactPlugin(), - { - name: 'custom-router', - configureServer(server) { - Object.entries(routes).forEach(([route, file]) => { - server.middlewares.use(route, async (req, res, next) => { - let htmlPage = await readFile(resolve(__dirname, './frontend/', file), 'utf8') - htmlPage = htmlPage.replace(/\.\//g, '/' + dirname(file) + '/') - - const url = file - const html = await server.transformIndexHtml(url, htmlPage) - - console.log(url) - - res.writeHead(200, { 'Content-Type': 'text/html' }).end(html) - }) - }) + } + } else { + return { + ...sharedConfig, + server: { + port: 3000, + proxy: { + '/api': 'http://localhost:4000/', }, }, - ], + } } })