mirror of
https://github.com/0ceanSlim/grain.git
synced 2024-11-22 08:37:13 +00:00
frontend added from GoStart scaffolf
This commit is contained in:
parent
12e9856169
commit
44636c95d9
@ -1,5 +1,7 @@
|
|||||||
mongodb:
|
mongodb:
|
||||||
uri: "mongodb://localhost:27017/"
|
uri: "mongodb://localhost:27017/"
|
||||||
database: "grain"
|
database: "grain"
|
||||||
server:
|
relay:
|
||||||
address: ":8080"
|
port: ":8080"
|
||||||
|
web:
|
||||||
|
port: ":8181"
|
||||||
|
23
main.go
23
main.go
@ -8,6 +8,7 @@ import (
|
|||||||
"grain/relay"
|
"grain/relay"
|
||||||
"grain/relay/db"
|
"grain/relay/db"
|
||||||
"grain/relay/utils"
|
"grain/relay/utils"
|
||||||
|
"grain/web"
|
||||||
|
|
||||||
"golang.org/x/net/websocket"
|
"golang.org/x/net/websocket"
|
||||||
)
|
)
|
||||||
@ -26,11 +27,23 @@ func main() {
|
|||||||
}
|
}
|
||||||
defer db.DisconnectDB()
|
defer db.DisconnectDB()
|
||||||
|
|
||||||
// Start WebSocket relay
|
// Run the WebSocket server in a goroutine
|
||||||
http.Handle("/", websocket.Handler(relay.Listener))
|
go func() {
|
||||||
fmt.Println("WebSocket server started on", config.Server.Address)
|
fmt.Printf("WebSocket server is running on ws://localhost%s\n", config.Relay.Port)
|
||||||
err = http.ListenAndServe(config.Server.Address, nil)
|
err := http.ListenAndServe(config.Relay.Port, websocket.Handler(relay.Listener))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error starting server:", err)
|
fmt.Println("Error starting WebSocket server:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Run the HTTP server for serving static files and home page
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/", web.RootHandler)
|
||||||
|
mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("web/static"))))
|
||||||
|
|
||||||
|
fmt.Printf("Http server is running on http://localhost%s\n", config.Web.Port)
|
||||||
|
err = http.ListenAndServe(config.Web.Port, mux)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error starting web server:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,12 @@ type Config struct {
|
|||||||
URI string `yaml:"uri"`
|
URI string `yaml:"uri"`
|
||||||
Database string `yaml:"database"`
|
Database string `yaml:"database"`
|
||||||
} `yaml:"mongodb"`
|
} `yaml:"mongodb"`
|
||||||
Server struct {
|
Relay struct {
|
||||||
Address string `yaml:"address"`
|
Port string `yaml:"port"`
|
||||||
} `yaml:"server"`
|
} `yaml:"relay"`
|
||||||
|
Web struct {
|
||||||
|
Port string `yaml:"port"`
|
||||||
|
} `yaml:"web"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig(filename string) (*Config, error) {
|
func LoadConfig(filename string) (*Config, error) {
|
||||||
|
61
web/http.go
Normal file
61
web/http.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RootHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
data := PageData{
|
||||||
|
Title: "GRAIN Relay",
|
||||||
|
}
|
||||||
|
RenderTemplate(w, data, "index.html")
|
||||||
|
}
|
||||||
|
type PageData struct {
|
||||||
|
Title string
|
||||||
|
Theme string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define the base directories for views and templates
|
||||||
|
const (
|
||||||
|
viewsDir = "web/views/"
|
||||||
|
templatesDir = "web/views/templates/"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Define the common layout templates filenames
|
||||||
|
var templateFiles = []string{
|
||||||
|
"#layout.html",
|
||||||
|
"header.html",
|
||||||
|
"footer.html",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the common templates with full paths
|
||||||
|
var layout = PrependDir(templatesDir, templateFiles)
|
||||||
|
|
||||||
|
func RenderTemplate(w http.ResponseWriter, data PageData, view string) {
|
||||||
|
|
||||||
|
// Append the specific template for the route
|
||||||
|
templates := append(layout, viewsDir+view)
|
||||||
|
|
||||||
|
// Parse all templates
|
||||||
|
tmpl, err := template.ParseFiles(templates...)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the "layout" template
|
||||||
|
err = tmpl.ExecuteTemplate(w, "layout", data)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to prepend a directory path to a list of filenames
|
||||||
|
func PrependDir(dir string, files []string) []string {
|
||||||
|
var fullPaths []string
|
||||||
|
for _, file := range files {
|
||||||
|
fullPaths = append(fullPaths, dir+file)
|
||||||
|
}
|
||||||
|
return fullPaths
|
||||||
|
}
|
1
web/static/custom.min.css
vendored
Normal file
1
web/static/custom.min.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/*! tailwindcss v3.4.3 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{-webkit-text-size-adjust:100%;font-feature-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-variation-settings:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-feature-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{font-feature-settings:inherit;color:inherit;font-family:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}:root{--color-bgPrimary:#101010;--color-bgSecondary:#282828;--color-bgInverted:#e1e1e1;--color-textPrimary:#fff;--color-textSecondary:#ebebeb;--color-textMuted:#c8c8c8;--color-textInverted:#101010}:root[data-theme=light]{--color-bgPrimary:#c8c8c8;--color-bgSecondary:#e6e6e6;--color-bgInverted:#505050;--color-textPrimary:#000;--color-textSecondary:#141414;--color-textMuted:#646464;--color-textInverted:#fff}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.mb-8{margin-bottom:2rem}.mt-8{margin-top:2rem}.rounded-md{border-radius:.375rem}.bg-amber-400{--tw-bg-opacity:1;background-color:rgb(251 191 36/var(--tw-bg-opacity))}.bg-bgPrimary{background-color:var(--color-bgPrimary)}.bg-blue-400{--tw-bg-opacity:1;background-color:rgb(96 165 250/var(--tw-bg-opacity))}.p-2{padding:.5rem}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-3xl{font-size:1.875rem;line-height:2.25rem}.font-bold{font-weight:700}.text-textMuted{color:var(--color-textMuted)}.text-textPrimary{color:var(--color-textPrimary)}.text-textSecondary{color:var(--color-textSecondary)}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}
|
BIN
web/static/img/favicon.ico
Normal file
BIN
web/static/img/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
26
web/style/input.css
Normal file
26
web/style/input.css
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
@config "tailwind.config.js";
|
||||||
|
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
:root {
|
||||||
|
--color-bgPrimary: rgb(16, 16, 16);
|
||||||
|
--color-bgSecondary: rgb(40, 40, 40);
|
||||||
|
--color-bgInverted: rgb(225, 225, 225);
|
||||||
|
--color-textPrimary: rgb(255, 255, 255);
|
||||||
|
--color-textSecondary: rgb(235, 235, 235);
|
||||||
|
--color-textMuted: rgb(200, 200, 200);
|
||||||
|
--color-textInverted: rgb(16, 16, 16);
|
||||||
|
}
|
||||||
|
:root[data-theme="light"] {
|
||||||
|
--color-bgPrimary: rgb(200, 200, 200);
|
||||||
|
--color-bgSecondary: rgb(230, 230, 230);
|
||||||
|
--color-bgInverted: rgb(80, 80, 80);
|
||||||
|
--color-textPrimary: rgb(0, 0, 0);
|
||||||
|
--color-textSecondary: rgb(20, 20, 20);
|
||||||
|
--color-textMuted: rgb(100, 100, 100);
|
||||||
|
--color-textInverted: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
}
|
25
web/style/readme.md
Normal file
25
web/style/readme.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Information
|
||||||
|
|
||||||
|
This repository INCLUDES a minified version of the custom css used to style the web views with themes defined in the input.css. If you want to change anything about the configuration or the input, you will need to rebuild the custom minified css by using the [Tailwind standalone CLI Tool](https://github.com/tailwindlabs/tailwindcss/releases).
|
||||||
|
|
||||||
|
For Tailwind to Rebuild the CSS, Tailwind must be run to compile the new styling.
|
||||||
|
|
||||||
|
To do this run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tailwindcss -i web/style/input.css -o web/static/custom.min.css --minify
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
You can run a watcher while in development to automatically rebuild the `tailwind.min.css` whenever a file in the project directory is modified.
|
||||||
|
|
||||||
|
To do this run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tailwindcss -i web/style/input.css -o web/static/custom.min.css --watch --minify
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dark Mode
|
||||||
|
|
||||||
|
Yes... This framework is designed with "Dark Mode" as the default theme. As all things should be.
|
18
web/style/tailwind.config.js
Normal file
18
web/style/tailwind.config.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: ["./**/*.{html,js}"],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
bgPrimary: "var(--color-bgPrimary)",
|
||||||
|
bgSecondary: "var(--color-bgSecondary)",
|
||||||
|
bgInverted: "var(--color-bgInverted)",
|
||||||
|
textPrimary: "var(--color-textPrimary)",
|
||||||
|
textSecondary: "var(--color-textSecondary)",
|
||||||
|
textMuted: "var(--color-textMuted)",
|
||||||
|
textInverted: "var(--color-textInverted)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
5
web/views/index.html
Normal file
5
web/views/index.html
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{{define "view"}}
|
||||||
|
<main class="flex flex-col items-center justify-center p-8">
|
||||||
|
<div class="mb-4">You are now viewing the {{.Title}}</div>
|
||||||
|
</main>
|
||||||
|
{{end}}
|
34
web/views/templates/#layout.html
Normal file
34
web/views/templates/#layout.html
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{{define "layout"}}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="{{.Theme}}">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<!--
|
||||||
|
CDNs are used for ease of development. In a Production enviornment,
|
||||||
|
build a proper minified custom CSS using the Tailwind CLI tool and serve
|
||||||
|
the latest available minified htmx and custom css directly from your server.
|
||||||
|
To download a copy of htmx: https://htmx.org/docs/#download-a-copy
|
||||||
|
|
||||||
|
<link href="/static/tailwind.min.css" rel="stylesheet" />
|
||||||
|
<link href="/static/htmx.min.js">
|
||||||
|
-->
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script
|
||||||
|
src="https://unpkg.com/htmx.org@1.9.12"
|
||||||
|
integrity="sha384-ujb1lZYygJmzgSwoxRggbCHcjc0rB2XoQrxeTUQyRjrOnlCoYta87iKBWq3EsdM2"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
></script>
|
||||||
|
<!--
|
||||||
|
link the custom minified styling included in this repo, built from the configuration
|
||||||
|
in the /web/style directory
|
||||||
|
-->
|
||||||
|
<link href="/static/custom.min.css" rel="stylesheet" />
|
||||||
|
<link rel="icon" href="/static/img/favicon.ico" type="image/x-icon" />
|
||||||
|
<title>{{.Title}}</title>
|
||||||
|
</head>
|
||||||
|
<body class="font-mono text-center text-textPrimary bg-bgPrimary">
|
||||||
|
{{template "header" .}} {{template "view" .}} {{template "footer" .}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{end}}
|
5
web/views/templates/footer.html
Normal file
5
web/views/templates/footer.html
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{{define "footer"}}
|
||||||
|
<footer class="text-textMuted">
|
||||||
|
<p>© 2024 GRAIN 🌾, made with 💜 by OceanSlim</p>
|
||||||
|
</footer>
|
||||||
|
{{end}}
|
7
web/views/templates/header.html
Normal file
7
web/views/templates/header.html
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{{define "header"}}
|
||||||
|
<header>
|
||||||
|
<h1 class="mt-8 mb-8 text-3xl font-bold text-textSecondary">
|
||||||
|
Welcome to the GRAIN Frontend 🌾
|
||||||
|
</h1>
|
||||||
|
</header>
|
||||||
|
{{end}}
|
Loading…
Reference in New Issue
Block a user