Attempt 2

This commit is contained in:
2025-09-12 02:13:57 +02:00
parent 3ea7308af0
commit e749ec772e
30 changed files with 143 additions and 86 deletions

View File

@@ -26,10 +26,8 @@ RUN apk add --no-cache ca-certificates
## Final build
# Get
COPY --from=builder /web-server/build ./build/
COPY ./build/stylesheet.css ./layouts/
COPY ./src/layouts ./layouts/
COPY ./config ./config/
COPY ./shared.env ./config/shared.env
COPY ./src/layouts ./layouts
COPY ./.env ./config/.env
# Run
CMD ["./build/web_server"]

View File

@@ -1,12 +1,12 @@
# Variables
CSS_INPUT:=./src/layouts/styles/global.css
CSS_INPUT:=./src/layouts/static/styles/global.css
CSS_OUTPUT:=./build/stylesheet.css
NAME:=web-app
all: css
# Build Tailwind
css: ./src/layouts/**/*.css ./src/layouts/**/*.tmpl
css: $(CSS_INPUT) ./src/layouts/**/*.tmpl
npx tailwindcss -i $(CSS_INPUT) -o $(CSS_OUTPUT) --minify
# Build App

File diff suppressed because one or more lines are too long

View File

@@ -7,6 +7,8 @@ services:
networks:
pagerino_net:
restart: on-failure:3
ports:
- "8222:8222"
networks:
pagerino_net:

View File

@@ -1,8 +1,8 @@
{{ define "meta" -}}
<title>{{ .meta.Title }}</title>
<link rel="icon" type="image/x-icon" href="/static/{{ if .meta.icon }}{{ .meta.icon }}{{ else }}favicon.ico{{ end }}">
<link rel="icon" type="image/x-icon" href="/static/{{ if .meta.Icon }}{{ .meta.Icon }}{{ else }}favicon.ico{{ end }}">
<meta name="author" content="Olek" />
<meta name="description"
content={{ .meta.description }} />
content={{ .meta.Description }} />
{{- end }}

View File

@@ -1,17 +1,32 @@
{{ define "navbar" }}
<nav class="fixed top-0 w-full bg-[var(--primary-bg)] shadow-md z-50 transition-all duration-300">
<nav class="sticky top-0 w-full bg-[var(--primary-bg)] shadow-md z-50 transition-all duration-300">
<div id="navbar-inner" class="max-w-7xl mx-auto flex items-center justify-between px-4 py-3 md:py-4 transition-all duration-300">
<!-- Logo -->
<a href="/" class="flex items-center transition-all duration-300" id="logo-container">
<img src="/static/logo.png" alt="Logo" class="h-12 md:h-16 transition-all duration-300" id="logo-img"/>
<span class="ml-2 text-[var(--accent)] font-bold text-xl md:text-2xl">MyApp</span>
<a href="/" class="flex items-start transition-all duration-300" id="logo-container">
<img src="/static/GORAKLOGO.png" alt="Logo" class="h-12 md:h-16 transition-all duration-300" id="logo-img"/>
<span class="ml-2 text-[var(--accent)] font-bold text-xl md:text-2xl">Pagerino</span>
</a>
<!-- Nav Links -->
<ul class="hidden md:flex space-x-6 text-[var(--text-primary)] font-medium">
<li><a href="/network" class="hover:text-[var(--accent)] transition-colors">Network</a></li>
<li><a href="/map" class="hover:text-[var(--accent)] transition-colors">Map</a></li>
<li><a href="/analytics" class="hover:text-[var(--accent)] transition-colors">Analytics</a></li>
<ul class="hidden md:flex space-x-10 text-[var(--text-primary)] font-semibold">
<li>
<a href="/network"
class="px-4 py-2 rounded-lg hover:bg-[var(--accent)] hover:text-white transition-colors duration-200">
Network
</a>
</li>
<li>
<a href="/map"
class="px-4 py-2 rounded-lg hover:bg-[var(--accent)] hover:text-white transition-colors duration-200">
Map
</a>
</li>
<li>
<a href="/analytics"
class="px-4 py-2 rounded-lg hover:bg-[var(--accent)] hover:text-white transition-colors duration-200">
Analytics
</a>
</li>
</ul>
<!-- User / Login -->

View File

@@ -0,0 +1,29 @@
{{ define "websocket" -}}
<script>
let socket; // Global socket variable
function initWebSocket() { //TODO Make connection persistent across all pages
if (socket) return; // Single connection
socket = new WebSocket("ws://" + window.location.host + "/ws");
socket.onopen = function() {
console.log("Connected to server");
};
socket.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log("Message from server:", data);
};
let retries = 0
socket.onclose = function() {
console.log("Disconnected");
socket = null; // For reconnection
setTimeout(initWebSocket, 1000)
};
}
window.addEventListener("load", initWebSocket);
</script>
{{- end }}

View File

@@ -1,51 +0,0 @@
{{ define "layout" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="../../build/stylesheet.css" rel="stylesheet">
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
{{ template "meta" . }}
</head>
<body class="bg-gray-100">
{{ template "navbar" . }}
<main>
{{ template "content" . }}
</main>
<footer>
{{ template "footer" . }}
</footer>
<script>
let socket; // Global socket variable
function initWebSocket() { //TODO Make connection persistent across all pages
if (socket) return; // Single connection
socket = new WebSocket("ws://" + window.location.host + "/ws");
socket.onopen = function() {
console.log("Connected to server");
};
socket.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log("Message from server:", data);
};
let retries = 0
socket.onclose = function() {
console.log("Disconnected");
socket = null; // For reconnection
setTimeout(initWebSocket, 1000)
};
}
window.addEventListener("load", initWebSocket);
</script>
</body>
</html>
{{ end }}

View File

@@ -1,4 +1,4 @@
{{ define "content" -}}
{{ define "home" -}}
<h1>
Hello, Pagerino User!
</h1>

View File

@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/static/styles/stylesheet.css" rel="stylesheet">
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
<link rel="manifest" href="/static/site.webmanifest" />
{{ template "meta" . }}
</head>
<body class="bg-gray-100">
{{ template "navbar" . }}
<main>
{{ if eq .Page "login" }}
{{ template "login" . }}
{{ else if eq .Page "home" }}
{{ template "home" . }}
{{ end }}
</main>
<footer>
{{ template "footer" . }}
</footer>
</body>
</html>

View File

@@ -1,11 +1,35 @@
{{ define "content" -}}
<form method="POST" action="/login">
<label>Username</label>
<input type="text" name="username" required />
{{ define "login" -}}
<div class="flex items-center justify-center min-h-screen bg-gray-100">
<form method="POST" action="/login"
class="bg-white shadow-lg rounded-2xl p-8 w-full max-w-md space-y-6">
<h2 class="text-2xl font-bold text-center text-gray-800">Login</h2>
<label>Password</label>
<input type="password" name="password" required />
<!-- Username -->
<div>
<label for="username" class="block text-sm font-medium text-gray-600">Username</label>
<input type="text" id="username" name="username" required
class="mt-1 w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:outline-none" />
</div>
<button type="submit">Login</button>
</form>
<!-- Password -->
<div>
<label for="password" class="block text-sm font-medium text-gray-600">Password</label>
<input type="password" id="password" name="password" required
class="mt-1 w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:outline-none" />
</div>
<!-- Submit -->
<button type="submit"
class="w-full bg-blue-600 text-white py-2 rounded-lg font-medium hover:bg-blue-700 transition-colors">
Login
</button>
<!-- Optional link -->
<p class="text-sm text-center text-gray-500">
Dont have an account?
<a href="/register" class="text-blue-600 hover:underline">Sign up</a>
</p>
</form>
</div>
{{- end }}

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 845 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

File diff suppressed because one or more lines are too long

View File

@@ -58,7 +58,7 @@ type UserData struct {
type PageMeta struct {
Title string
Description string
IconFile string
Icon string
}
// === Utilities ===
@@ -176,7 +176,7 @@ func main() {
signal.Notify(Data.ExitSig, syscall.SIGINT, syscall.SIGTERM)
// = Load configs
godotenv.Load("./config/app_config.env", "./config/shared.env", "./config/socket_config.env")
godotenv.Load("./config/.env")
// = Setup logger
log.SetFlags(log.Lmsgprefix | log.LUTC | log.Lshortfile)
@@ -186,7 +186,7 @@ func main() {
router := gin.Default()
// Load assets and HTML templates
router.Static("./layouts/static", "./layouts/static")
router.Static("/static", "./layouts/static")
router.LoadHTMLGlob("./layouts/**/*.tmpl")
HOME_NAME := os.Getenv("HOME_NAME")
@@ -215,7 +215,7 @@ func main() {
dial_opts...,
)
if err != nil {
log.Println("Failed to connect to gRPC API")
log.Println("Failed to connect to gRPC API: " + err.Error())
return
}
Data.ApiUserClient = api_user.NewUserServiceClient(grpc_client)
@@ -232,21 +232,25 @@ func main() {
title = strings.ToTitle(HOME_NAME)
}
ctx.HTML(http.StatusOK, "home.tmpl", gin.H{
ctx.HTML(http.StatusOK, "index.tmpl", gin.H{
"meta": PageMeta{
Title: title,
Description: "Pagerino system home page",
//Icon: "",
},
"Page": "home",
})
})
// Login page (escape auth handler)
router.GET("/login", func(ctx *gin.Context) {
ctx.HTML(http.StatusOK, "login.tmpl", gin.H{
ctx.HTML(http.StatusOK, "index.tmpl", gin.H{
"meta": PageMeta{
Title: "Login",
Description: "Pagerino system login page",
//Icon: "",
},
"Page": "login",
})
})
@@ -257,7 +261,7 @@ func main() {
// Run server in another thread for graceful shutdown
go func() {
if err := router.Run("localhost:" + Data.FindEnv("WEB_PORT")); err != nil && err != http.ErrServerClosed {
if err := router.Run(":" + Data.FindEnv("WEB_PORT")); err != nil && err != http.ErrServerClosed {
log.Println("HTTP Server error: " + err.Error())
}
log.Println("HTTP server shutdown.")

Binary file not shown.