From 090b96fb7dc8a16b89220dea463dc22a06d6a85c Mon Sep 17 00:00:00 2001 From: ochom Date: Thu, 13 Jun 2024 18:29:51 +0300 Subject: [PATCH] draft deploy with ui --- .air.toml | 2 +- .gitignore | 3 +- Dockerfile | 18 +++++++- Makefile | 6 +++ cmd/main.go | 25 +++++++++-- go.mod | 1 + go.sum | 2 + src/api/app.go | 21 +++++++++ src/api/auth.go | 61 +++++++++++++++++++++++++++ web/.eslintrc.cjs | 14 ++++++ web/.gitignore | 24 +++++++++++ web/.prettierrc.json | 7 +++ web/README.md | 8 ++++ web/bun.lockb | Bin 0 -> 127759 bytes web/index.html | 22 ++++++++++ web/package.json | 26 ++++++++++++ web/public/vite.svg | 1 + web/src/App.css | 5 +++ web/src/App.jsx | 77 +++++++++++++++++++++++++++++++++ web/src/Login.css | 82 ++++++++++++++++++++++++++++++++++++ web/src/Session.jsx | 44 +++++++++++++++++++ web/src/assets/react.svg | 1 + web/src/constants/api.js | 1 + web/src/hooks/useSession.js | 13 ++++++ web/src/main.jsx | 12 ++++++ web/vite.config.js | 24 +++++++++++ 26 files changed, 493 insertions(+), 7 deletions(-) create mode 100644 src/api/app.go create mode 100644 src/api/auth.go create mode 100644 web/.eslintrc.cjs create mode 100644 web/.gitignore create mode 100644 web/.prettierrc.json create mode 100644 web/README.md create mode 100755 web/bun.lockb create mode 100644 web/index.html create mode 100644 web/package.json create mode 100644 web/public/vite.svg create mode 100644 web/src/App.css create mode 100644 web/src/App.jsx create mode 100644 web/src/Login.css create mode 100644 web/src/Session.jsx create mode 100644 web/src/assets/react.svg create mode 100644 web/src/constants/api.js create mode 100644 web/src/hooks/useSession.js create mode 100644 web/src/main.jsx create mode 100644 web/vite.config.js diff --git a/.air.toml b/.air.toml index 1b8c09a..619e647 100644 --- a/.air.toml +++ b/.air.toml @@ -7,7 +7,7 @@ tmp_dir = "tmp" bin = "./tmp/main" cmd = "go build -o ./tmp/main ./cmd/main.go" delay = 1000 - exclude_dir = ["assets", "tmp", "vendor", "testdata"] + exclude_dir = ["assets", "tmp", "vendor", "testdata", "web", "build"] exclude_file = [] exclude_regex = ["_test.go"] exclude_unchanged = false diff --git a/.gitignore b/.gitignore index d7fbdbd..6e2a9a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ tmp -secret.sh \ No newline at end of file +secret.sh +build diff --git a/Dockerfile b/Dockerfile index b2451ac..68e7d0e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,15 +16,31 @@ COPY . ./ RUN go build -o /server ./cmd/main.go +## +## Build UI +## + +FROM oven/bun:1.0 AS ui + +WORKDIR /app + +COPY web ./ + +RUN bun install + +RUN bun run build ## ## Deploy ## -FROM gcr.io/distroless/base-debian12:nonroot +FROM busybox:1.35.0-uclibc AS deploy WORKDIR / +RUN mkdir -p /web + COPY --from=build /server . +COPY --from=ui /app/build /web/build EXPOSE 16321 EXPOSE 6321 diff --git a/Makefile b/Makefile index 0be8b57..78518e4 100644 --- a/Makefile +++ b/Makefile @@ -7,3 +7,9 @@ tidy: lint: @echo "Running linter..." @golangci-lint run + +ui: + @cd web && bun run build + +docker: + @docker build -t quickmq:latest . \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go index f2435d5..ae55bdc 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -7,18 +7,29 @@ import ( "time" "github.com/ochom/gutils/logs" + "github.com/ochom/quickmq/src/api" "github.com/ochom/quickmq/src/app" ) func main() { - svr := app.New() - port := ":16321" + coreServer := app.New() + webServer := api.New() + + // run core go func() { - if err := svr.Listen(port); err != nil { + if err := coreServer.Listen(":6321"); err != nil { panic(err) } }() + // run api and web + go func() { + if err := webServer.Listen(":16321"); err != nil { + panic(err) + } + }() + + // go run consumer daemon go func() { stopSignal := make(chan bool, 1) logs.Info("starting consumers daemon") @@ -34,7 +45,13 @@ func main() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - if err := svr.ShutdownWithContext(ctx); err != nil { + // shutdown core server + if err := coreServer.ShutdownWithContext(ctx); err != nil { + panic(err) + } + + // shutdown api server + if err := webServer.ShutdownWithContext(ctx); err != nil { panic(err) } diff --git a/go.mod b/go.mod index a0b67ae..7a02b8a 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( require ( github.com/andybalholm/brotli v1.1.0 // indirect github.com/gofiber/utils/v2 v2.0.0-beta.4 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/google/uuid v1.6.0 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/klauspost/compress v1.17.9 // indirect diff --git a/go.sum b/go.sum index 7af6623..66b2c15 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/gofiber/fiber/v3 v3.0.0-beta.2 h1:mVVgt8PTaHGup3NGl/+7U7nEoZaXJ5OComV github.com/gofiber/fiber/v3 v3.0.0-beta.2/go.mod h1:w7sdfTY0okjZ1oVH6rSOGvuACUIt0By1iK0HKUb3uqM= github.com/gofiber/utils/v2 v2.0.0-beta.4 h1:1gjbVFFwVwUb9arPcqiB6iEjHBwo7cHsyS41NeIW3co= github.com/gofiber/utils/v2 v2.0.0-beta.4/go.mod h1:sdRsPU1FXX6YiDGGxd+q2aPJRMzpsxdzCXo9dz+xtOY= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= diff --git a/src/api/app.go b/src/api/app.go new file mode 100644 index 0000000..83b59af --- /dev/null +++ b/src/api/app.go @@ -0,0 +1,21 @@ +package api + +import ( + "github.com/gofiber/fiber/v3" +) + +func New() *fiber.App { + app := fiber.New() + + // rest apis + app.Post("/login", login) + app.Get("/user", loadUSer) + + // serve other static files + app.Static("/", "web/build") + app.Get("*", func(c fiber.Ctx) error { + return c.SendFile("web/build/index.html") + }) + + return app +} diff --git a/src/api/auth.go b/src/api/auth.go new file mode 100644 index 0000000..5da3dbb --- /dev/null +++ b/src/api/auth.go @@ -0,0 +1,61 @@ +package api + +import ( + "time" + + "github.com/gofiber/fiber/v3" + "github.com/ochom/gutils/auth" + "github.com/ochom/gutils/env" + "github.com/ochom/gutils/uuid" +) + +func login(c fiber.Ctx) error { + var data struct { + Username string `json:"username"` + Password string `json:"password"` + } + if err := c.Bind().Body(&data); err != nil { + return err + } + + username := env.Get("QUICK_MQ_USERNAME", "admin") + password := env.Get("QUICK_MQ_PASSWORD", "admin") + + if data.Username != username { + return c.Status(400).JSON(fiber.Map{"status": "error", "message": "Invalid username"}) + } + + if data.Password != password { + return c.Status(400).JSON(fiber.Map{"status": "error", "message": "Invalid password"}) + } + + token, err := auth.GenerateAuthTokens(map[string]string{"user": "admin", "session_id": uuid.New()}) + if err != nil { + return err + } + + // Set a cookie + cookie := fiber.Cookie{ + Name: "jwt", + Value: token["token"], + Expires: time.Now().Add(time.Hour * 24), + HTTPOnly: true, + } + c.Cookie(&cookie) + + return c.JSON(fiber.Map{"status": "success", "message": "Logged in", "username": "admin"}) +} + +func loadUSer(c fiber.Ctx) error { + token := c.Cookies("jwt") + claims, err := auth.GetAuthClaims(token) + if err != nil { + return c.Status(401).JSON(fiber.Map{"status": "error", "message": "Unauthorized", "data": err.Error()}) + } + + if claims["user"] != "admin" { + return c.Status(401).JSON(fiber.Map{"status": "error", "message": "Unauthorized"}) + } + + return c.JSON(fiber.Map{"status": "success", "message": "Logged in", "username": "admin"}) +} diff --git a/web/.eslintrc.cjs b/web/.eslintrc.cjs new file mode 100644 index 0000000..73cc6ce --- /dev/null +++ b/web/.eslintrc.cjs @@ -0,0 +1,14 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: ['eslint:recommended', 'plugin:react/recommended', 'plugin:react/jsx-runtime', 'plugin:react-hooks/recommended'], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, + settings: { react: { version: '18.2' } }, + plugins: ['react-refresh'], + rules: { + 'react/jsx-no-target-blank': 'off', + 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], + 'react/prop-types': 'off' + } +}; diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/web/.prettierrc.json b/web/.prettierrc.json new file mode 100644 index 0000000..e7fe6d5 --- /dev/null +++ b/web/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "singleQuote": true, + "jsxSingleQuote": true, + "arrowParens": "avoid", + "trailingComma": "none", + "printWidth": 200 +} \ No newline at end of file diff --git a/web/README.md b/web/README.md new file mode 100644 index 0000000..f768e33 --- /dev/null +++ b/web/README.md @@ -0,0 +1,8 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh diff --git a/web/bun.lockb b/web/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..3de944031e5def776010f8196aef24d63a5e453c GIT binary patch literal 127759 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 + + + + + + + Vite + React + + + + + + + +
+ + + + \ No newline at end of file diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..0de7115 --- /dev/null +++ b/web/package.json @@ -0,0 +1,26 @@ +{ + "name": "web", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.57.0", + "eslint-plugin-react": "^7.34.1", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "vite": "^5.2.0" + } +} diff --git a/web/public/vite.svg b/web/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/web/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/App.css b/web/src/App.css new file mode 100644 index 0000000..df019d6 --- /dev/null +++ b/web/src/App.css @@ -0,0 +1,5 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; +} \ No newline at end of file diff --git a/web/src/App.jsx b/web/src/App.jsx new file mode 100644 index 0000000..d3bcc80 --- /dev/null +++ b/web/src/App.jsx @@ -0,0 +1,77 @@ +import { useRef, useState } from 'react'; +import './App.css'; +import './Login.css'; +import { useSession } from './hooks/useSession'; +import { baseUrl } from './constants/api'; + +function Login() { + const { setUser } = useSession(); + const usernameRef = useRef(null); + const passwordRef = useRef(null); + const [loading, setLoading] = useState(false); + + const submit = async e => { + e.preventDefault(); + setLoading(true); + try { + const res = await fetch(`${baseUrl}/login`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + username: usernameRef.current.value, + password: passwordRef.current.value + }) + }); + + if (res.status !== 200) { + throw new Error('Invalid username or password'); + } + + const data = await res.json(); + setUser(data); + } catch (error) { + alert(error?.message); + } finally { + setLoading(false); + } + }; + + return ( +
+
+ QuickMQ +
+
+
+ + +
+
+ + +
+
+ +
+
+
+ ); +} + +function App() { + const { user } = useSession(); + + if (!user) return ; + + return ( +
+

Dashboard

+
+ ); +} + +export default App; diff --git a/web/src/Login.css b/web/src/Login.css new file mode 100644 index 0000000..0ab888f --- /dev/null +++ b/web/src/Login.css @@ -0,0 +1,82 @@ +:root { + --primary-color: #3f51b5; + --secondary-color: #fd00db; + --error: #ff1100; + --success: #4caf50; + --warning: #ff9800; + --info: #2196f3; + --white: #fff; + --black: #000; + --grey: #b6b6b6; +} + +* { + box-sizing: border-box; + font-family: 'Poppins', sans-serif; +} + +body { + margin: 0; + padding: 0; + display: flex; + background: var(--white); +} + +.login-box { + display: flex; + flex-direction: column; + margin: 100px auto 0; + width: 400px; + border: 1px solid var(--grey); + border-radius: 15px; + padding: 30px; +} + +.lobo-box { + width: 100%; + text-align: center; + font-size: 30px; + font-weight: 600; +} + +.form-area { + display: flex; + flex-direction: column; +} + +.input-area { + display: flex; + flex-direction: column; + margin-bottom: 15px; +} + +.input-area label { + margin-bottom: 5px; +} + +.input-area input { + padding: 10px; + border: 1px solid var(--grey); + border-radius: 5px; +} + +.input-validation { + color: var(--error); +} + +.button-area { + display: flex; + justify-content: center; +} + + +.button-area button { + padding: 10px; + border: 1px solid var(--primary-color); + border-radius: 5px; + background: var(--primary-color); + cursor: pointer; + color: var(--white); + width: 100%; + font-size: large; +} \ No newline at end of file diff --git a/web/src/Session.jsx b/web/src/Session.jsx new file mode 100644 index 0000000..bb8f816 --- /dev/null +++ b/web/src/Session.jsx @@ -0,0 +1,44 @@ +import { useEffect, useState } from 'react'; +import { SessionContext } from './hooks/useSession'; +import { baseUrl } from './constants/api'; + +export default function Session({ children }) { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const getUser = async () => { + try { + const res = await fetch(`${baseUrl}/user`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }); + + if (res.status !== 200) { + throw new Error('Failed to get user'); + } + + const data = await res.json(); + setUser(data); + } catch (error) { + console.log(error); + } finally { + setLoading(false); + } + }; + + getUser(); + }, []); + + const handleSetUser = newUser => { + setUser(newUser); + }; + + if (loading) { + return
Loading...
; + } + + return {children}; +} diff --git a/web/src/assets/react.svg b/web/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/web/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/constants/api.js b/web/src/constants/api.js new file mode 100644 index 0000000..715e3de --- /dev/null +++ b/web/src/constants/api.js @@ -0,0 +1 @@ +export const baseUrl = import.meta.env.MODE === 'development' ? '/api' : ''; diff --git a/web/src/hooks/useSession.js b/web/src/hooks/useSession.js new file mode 100644 index 0000000..843bda0 --- /dev/null +++ b/web/src/hooks/useSession.js @@ -0,0 +1,13 @@ +import { createContext, useContext } from 'react'; + +export const SessionContext = createContext({ user: null, setUser: () => {} }); + +export const useSession = () => { + const context = useContext(SessionContext); + if (!context) { + throw new Error('useSession must be used within a Session'); + } + + const { user, setUser } = context; + return { user, setUser }; +}; diff --git a/web/src/main.jsx b/web/src/main.jsx new file mode 100644 index 0000000..05116c9 --- /dev/null +++ b/web/src/main.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App.jsx'; +import Session from './Session.jsx'; + +ReactDOM.createRoot(document.getElementById('root')).render( + + + + + +); diff --git a/web/vite.config.js b/web/vite.config.js new file mode 100644 index 0000000..1e15865 --- /dev/null +++ b/web/vite.config.js @@ -0,0 +1,24 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + build: { + outDir: 'build' + }, + resolve: { + alias: { + src: '/src' + } + }, + server: { + proxy: { + '/api': { + target: 'http://localhost:16321', + changeOrigin: true, + rewrite: path => path.replace(/^\/api/, '') + } + } + } +});