diff --git a/CMD_HISTORY.md b/CMD_HISTORY.md index ae6c175..547777e 100644 --- a/CMD_HISTORY.md +++ b/CMD_HISTORY.md @@ -55,3 +55,10 @@ Commands were run in the order listed below on a debian based system. cd frontend pnpm install ``` + +- Add Vue-router & Villus + ```bash + cd frontend + pnpm install villus graphql + pnpm install graphql-tag + ``` diff --git a/frontend/index.html b/frontend/index.html index 8388c4b..6882883 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,9 +2,9 @@ - + - Vite + Vue + YetAnotherTodoList
diff --git a/frontend/package.json b/frontend/package.json index 7f1fcd7..2943b8c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,7 +9,11 @@ "preview": "vite preview" }, "dependencies": { - "vue": "^3.3.4" + "graphql": "^16.8.1", + "graphql-tag": "^2.12.6", + "villus": "^3.2.0", + "vue": "^3.3.4", + "vue-router": "^4.2.5" }, "devDependencies": { "@vitejs/plugin-vue": "^4.2.3", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 526ca93..b76e2d2 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -5,9 +5,21 @@ settings: excludeLinksFromLockfile: false dependencies: + graphql: + specifier: ^16.8.1 + version: 16.8.1 + graphql-tag: + specifier: ^2.12.6 + version: 2.12.6(graphql@16.8.1) + villus: + specifier: ^3.2.0 + version: 3.2.0(graphql@16.8.1)(vue@3.3.6) vue: specifier: ^3.3.4 version: 3.3.6 + vue-router: + specifier: ^4.2.5 + version: 4.2.5(vue@3.3.6) devDependencies: '@vitejs/plugin-vue': @@ -288,6 +300,10 @@ packages: '@vue/compiler-dom': 3.3.6 '@vue/shared': 3.3.6 + /@vue/devtools-api@6.5.1: + resolution: {integrity: sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==} + dev: false + /@vue/reactivity-transform@3.3.6: resolution: {integrity: sha512-RlJl4dHfeO7EuzU1iJOsrlqWyJfHTkJbvYz/IOJWqu8dlCNWtxWX377WI0VsbAgBizjwD+3ZjdnvSyyFW1YVng==} dependencies: @@ -371,6 +387,21 @@ packages: dev: true optional: true + /graphql-tag@2.12.6(graphql@16.8.1): + resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} + engines: {node: '>=10'} + peerDependencies: + graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + graphql: 16.8.1 + tslib: 2.6.2 + dev: false + + /graphql@16.8.1: + resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + dev: false + /magic-string@0.30.5: resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} engines: {node: '>=12'} @@ -409,6 +440,20 @@ packages: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: false + + /villus@3.2.0(graphql@16.8.1)(vue@3.3.6): + resolution: {integrity: sha512-kb4Dv6Uvu7FSqfVnXTFb7I+VDv9fzFG2Emqu1eu9acY/Y3xM8xqy/I8w/HexNYDkofx5fGIgWk9nvfEWROtlGw==} + peerDependencies: + graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + vue: ^3.0.0 || ^2.7.0 + dependencies: + graphql: 16.8.1 + vue: 3.3.6 + dev: false + /vite@4.5.0: resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==} engines: {node: ^14.18.0 || >=16.0.0} @@ -444,6 +489,15 @@ packages: fsevents: 2.3.3 dev: true + /vue-router@4.2.5(vue@3.3.6): + resolution: {integrity: sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==} + peerDependencies: + vue: ^3.2.0 + dependencies: + '@vue/devtools-api': 6.5.1 + vue: 3.3.6 + dev: false + /vue@3.3.6: resolution: {integrity: sha512-jJIDETeWJnoY+gfn4ZtMPMS5KtbP4ax+CT4dcQFhTnWEk8xMupFyQ0JxL28nvT/M4+p4a0ptxaV2WY0LiIxvRg==} peerDependencies: diff --git a/frontend/public/vite.svg b/frontend/public/favicon.svg similarity index 100% rename from frontend/public/vite.svg rename to frontend/public/favicon.svg diff --git a/frontend/src/.d.ts b/frontend/src/.d.ts new file mode 100644 index 0000000..34dfa67 --- /dev/null +++ b/frontend/src/.d.ts @@ -0,0 +1 @@ +declare module 'vue/dist/vue.esm-bundler.js'; diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 341dbf0..436641e 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,30 +1,21 @@ - - + diff --git a/frontend/src/components/HelloWorld.vue b/frontend/src/components/HelloWorld.vue index f5e4f53..97cd48c 100644 --- a/frontend/src/components/HelloWorld.vue +++ b/frontend/src/components/HelloWorld.vue @@ -1,40 +1,43 @@ diff --git a/frontend/src/components/Users.vue b/frontend/src/components/Users.vue new file mode 100644 index 0000000..749ce83 --- /dev/null +++ b/frontend/src/components/Users.vue @@ -0,0 +1,38 @@ + + + + diff --git a/frontend/src/main.js b/frontend/src/main.js index 2425c0f..e130536 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -1,5 +1,26 @@ -import { createApp } from 'vue' +import { createApp } from 'vue/dist/vue.esm-bundler.js' +import { createClient } from 'villus' +import { createRouter, createWebHistory } from 'vue-router' import './style.css' import App from './App.vue' +import Home from './views/Home.vue' +import About from './views/About.vue' -createApp(App).mount('#app') +const NotFound = { template: '
Sorry, page not found (404)
' } + +const routes = [ + { path: '/', name: 'Home', component: Home }, + { path: '/about', name: 'About', component: About }, + { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}) + +const villusClient = createClient({ + url: '/api' +}) + +createApp(App).use(router).use(villusClient).mount('#app') diff --git a/frontend/src/style.css b/frontend/src/style.css index 84a0050..d737880 100644 --- a/frontend/src/style.css +++ b/frontend/src/style.css @@ -1,89 +1,95 @@ :root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; } a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; + font-weight: 500; + color: #646cff; + text-decoration: inherit; } + a:hover { - color: #535bf2; + color: #535bf2; } a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; + font-weight: 500; + color: #646cff; + text-decoration: inherit; } + a:hover { - color: #535bf2; + color: #535bf2; } body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; } h1 { - font-size: 3.2em; - line-height: 1.1; + font-size: 3.2em; + line-height: 1.1; } button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; } + button:hover { - border-color: #646cff; + border-color: #646cff; } + button:focus, button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; + outline: 4px auto -webkit-focus-ring-color; } .card { - padding: 2em; + padding: 2em; } #app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; } @media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } } diff --git a/frontend/src/views/About.vue b/frontend/src/views/About.vue new file mode 100644 index 0000000..f0ecbe0 --- /dev/null +++ b/frontend/src/views/About.vue @@ -0,0 +1,7 @@ + + + diff --git a/frontend/src/views/Home.vue b/frontend/src/views/Home.vue new file mode 100644 index 0000000..d223d67 --- /dev/null +++ b/frontend/src/views/Home.vue @@ -0,0 +1,8 @@ + + + diff --git a/server/handleDevTools.go b/server/handleDevTools.go index ee00bf1..27eb79b 100644 --- a/server/handleDevTools.go +++ b/server/handleDevTools.go @@ -20,11 +20,11 @@ along with this program. If not, see . package server import ( - "net/http" - "github.com/99designs/gqlgen/graphql/playground" + "somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/globals" ) -func handleDevTools() { - http.Handle("/playground", playground.Handler("GraphQL playground", "/api")) +func handleDevTools(port int) { + mux.Handle("/playground", playground.Handler("GraphQL playground", "/api")) + globals.Logger.Printf("connect to http://localhost:%v/ for GraphQL playground", port) } diff --git a/server/handleDevTools.mock.go b/server/handleDevTools.mock.go index c31422d..7548ddf 100644 --- a/server/handleDevTools.mock.go +++ b/server/handleDevTools.mock.go @@ -19,5 +19,5 @@ along with this program. If not, see . */ package server -func handleDevTools() { +func handleDevTools(_ int) { } diff --git a/server/handleFrontend.go b/server/handleFrontend.go index 8f1efc6..756d04a 100644 --- a/server/handleFrontend.go +++ b/server/handleFrontend.go @@ -34,7 +34,21 @@ func handleFrontend() { if err != nil { log.Fatalln(err) } - frontendFS := http.FileServer(http.FS(stripped)) - http.Handle("/", frontendFS) + mux.Handle("/assets/", frontendFS) + mux.HandleFunc("/", indexHandler) + // TODO: redirect from vue to 404 page (on go/proxy server) +} + +// Following Nathan Tsao's implementation on https://betterprogramming.pub/how-to-serve-a-single-page-application-using-go-4b9a38d92987 +func indexHandler(w http.ResponseWriter, r *http.Request) { + // TODO: add map with path:type + if r.URL.Path == "/vite.svg" { + rawFile, _ := frontend.ReadFile("dist/vite.svg") + w.Header().Add("Content-Type", "image/svg+xml") + w.Write(rawFile) + return + } + rawFile, _ := frontend.ReadFile("dist/index.html") + w.Write(rawFile) } diff --git a/server/main.go b/server/main.go index d7c096a..9709244 100644 --- a/server/main.go +++ b/server/main.go @@ -26,16 +26,20 @@ import ( "somepi.ddns.net/gitea/gilex-dev/YetAnotherToDoList/graph" ) -func StartServer(port int) { - handleDevTools() // controlled by 'dev' tag - handleFrontend() // controlled by 'headless' tag +var mux *http.ServeMux - http.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) { +func StartServer(port int) { + mux = http.NewServeMux() + + handleDevTools(port) // controlled by 'dev' tag + handleFrontend() // controlled by 'headless' tag + + mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "%s %s", globals.Version, globals.CommitHash) }) srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}})) - http.Handle("/api", srv) - globals.Logger.Printf("connect to http://localhost:%v/ for GraphQL playground", port) - globals.Logger.Fatal(http.ListenAndServe(":"+strconv.FormatInt(int64(port), 10), nil)) + mux.Handle("/api", srv) + + globals.Logger.Fatal(http.ListenAndServe(":"+strconv.FormatInt(int64(port), 10), mux)) }