diff --git a/.gitignore b/.gitignore
index ecdef5b..4740311 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-static
\ No newline at end of file
+static
+server/toe
diff --git a/generate.py b/generate.py
index adf273a..b6b701c 100644
--- a/generate.py
+++ b/generate.py
@@ -13,8 +13,8 @@ if help_mode:
print(f'Args:')
print(f'\thelp - Show this message')
print(f'\tfile - File mode with .html extension')
- print(f'\tserver - Server mode without .html extension for use folder as static files')
- print(f'\t[path]/ - Directory for save files')
+ print(f'\tserver - Server mode without .html extension for use folder as static pages')
+ print(f'\t[path]/ - Directory for save pages')
exit(0)
else:
if file_mode and server_mode:
@@ -39,12 +39,39 @@ def check_win(board) -> str:
return next((board[line[0]] for line in lines if board[line[0]] == board[line[1]] == board[line[2]] != '-'), '-')
minify = lambda s: s.replace('\n', '').replace('\t', '').replace(' ', '')
-files = {
- 'index':
- minify(f"""{' ' if server_mode else ''}
oxTicTacToe
- oxTicTacToe.html Select team for start [ O | X ]
- source code """)
+style = minify("""
+body{margin:0;padding:20px;font-family:Arial,sans-serif;text-align:center;background:#fff}h1{font-size:24px;margin-bottom:20px}
+a{text-decoration:none}img{width:10vw}a,h1 a{color:#007bff}table{margin:0 auto;border-collapse:collapse;max-width:100%;overflow-x:auto}
+td,td a{line-height:50px}td{border:1px solid #ccc;padding:10px;width:50px;height:50px;text-align:center;font-size:18px;vertical-align:middle}
+td a{display:block;width:100%;height:100%;color:#007bff}td:hover{background:#eee}@media (max-width:600px){body{padding:10px}img{width:20vw}
+h1{font-size:20px;margin-bottom:10px}td{padding:5px;width:40px;height:40px;font-size:16px}td,td a{line-height:40px}}""")
+apply_css = minify(f"const style=document.createElement('style');style.textContent='{style}';document.head.appendChild(style);")
+preload = minify("""
+document.addEventListener('mouseover', e => {
+ const a = e.target.closest('a[href]');
+ if (!a) return;
+ if (!a.dataset.prefetched) {
+ a.dataset.prefetched = true;
+ fetch(a.href, {mode: 'no-cors'});
}
+});
+""")
+pages = {
+ 'index':
+ minify(f"""
+
+ oxTicTacToe oxTicTacToe.html
+ Select team for start [ O | X ]
+ {'multiplayer mode ' if server_mode else ''}
+ source code
+ """)
+}
+
+scripts = {
+ 's': apply_css,
+ 'p': preload,
+ 'a': (apply_css+preload)
+}
try: iterable = tqdm(itertools.product('-xo', repeat=10))
except: iterable = itertools.product('-xo', repeat=10)
@@ -61,8 +88,8 @@ for field_tuple in iterable:
line = lambda n: "index.html"
title = f'Draft'
else: title = field[0]
- content = minify(f"""{' ' if server_mode else ''}
- oxTTT:{title}
+ content = minify(f"""
+ oxTTT:{title}
""")
- files[field] = f"{'' if win != '-' else ''}{content}{' ' if win != '-' else ''}"
+ """)
+ pages[field] = f"{'' if win != '-' else ''}{content}{' ' if win != '-' else ''}"
os.makedirs(path, exist_ok=True)
-for filename, content in files.items():
+for filename, content in pages.items():
with open(f'{path}{filename}{".html" if file_mode else ""}', 'w+', encoding='utf-8') as f:
f.write((' ' if server_mode else '')+(content.replace('.html', '') if server_mode else content))
-with open(f'{path}s.js', 'w+', encoding='utf-8') as f:
- f.write(minify("""
- const style=document.createElement('style');
- style.textContent='
- body{margin:0;padding:20px;font-family:Arial,sans-serif;text-align:center}
- h1{font-size:24px;margin-bottom:20px}a{text-decoration:none;color:#007bff}
- h1 a{color:#007bff}table{margin:0 auto;border-collapse:collapse}
- td{border:1px solid #ccc;padding:10px;width:50px;height:50px;
- text-align:center;font-size:18px;line-height:50px;vertical-align:middle}
- td a{display:block;width:100%;height:100%;line-height:50px;color:#007bff}
- td:hover{background:#f0f0f0}';document.head.appendChild(style);"""))
\ No newline at end of file
+
+for filename, content in scripts.items():
+ with open(f'{path}{filename}.js', 'w+', encoding='utf-8') as f:
+ f.write(content)
+
+with open(f'{path}s.css', 'w+', encoding='utf-8') as f:
+ f.write(style)
diff --git a/go.mod b/go.mod
index 832ec82..1eaa642 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.25.3
require (
github.com/gin-contrib/gzip v1.2.5
github.com/gin-gonic/gin v1.11.0
+ github.com/google/uuid v1.6.0
)
require (
diff --git a/go.sum b/go.sum
index 5226eb0..faa0e78 100644
--- a/go.sum
+++ b/go.sum
@@ -32,6 +32,8 @@ github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7Lk
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
diff --git a/server/main.go b/server/main.go
index a6cff45..cef1774 100644
--- a/server/main.go
+++ b/server/main.go
@@ -4,35 +4,64 @@ import (
// "flag"
// "fmt"
"embed"
+ "fmt"
"net/http"
"strings"
"github.com/gin-contrib/gzip"
"github.com/gin-gonic/gin"
+ "github.com/google/uuid"
)
-//go:embed static
+//go:embed toe
var staticFiles embed.FS
func SetCustomContentType() gin.HandlerFunc {
- return func(c *gin.Context) {
+ return func(c *gin.Context) {
requestPath := c.Request.URL.Path
- if strings.HasPrefix(requestPath, "/l/static") {
- c.Header("Content-Type", "text/html; charset=UTF-8")
+ if strings.Contains(requestPath, ".") {
+ c.Header("Cache-Control", "public, max-age=31536000, immutable")
}
- c.Next()
- }
+ if strings.HasPrefix(requestPath, "/tic/tac/toe") && !strings.Contains(requestPath, ".") {
+ c.Header("Content-Type", "text/html; charset=UTF-8")
+ } else if strings.HasSuffix(requestPath, ".ico") {
+ c.Header("Content-Type", "image/svg+xml")
+ } else if strings.HasSuffix(requestPath, ".js") {
+ c.Header("Content-Type", "application/javascript")
+ } else if strings.HasSuffix(requestPath, ".css") {
+ c.Header("Content-Type", "text/css")
+ }
+ c.Next()
+ }
}
+var favicon = " "
+
func main() {
embeddedFilesSystem := http.FS(staticFiles)
router := gin.Default()
router.Use(SetCustomContentType())
router.Use(gzip.Gzip(gzip.DefaultCompression))
- router.GET("/ping", func(c *gin.Context) {
- c.JSON(200, gin.H{"message": "pong"})
- })
- router.StaticFS("/l", embeddedFilesSystem)
- router.Run(":8080")
-}
\ No newline at end of file
+ router.GET("/", func(c *gin.Context) {
+ c.Redirect(http.StatusMovedPermanently, "/tic/tac/toe/index")
+ })
+ router.GET("/favicon.ico", func(c *gin.Context) {
+ c.String(http.StatusOK, favicon)
+ })
+ router.GET("/multiplayer", multiplayer)
+ router.StaticFS("/tic/tac", embeddedFilesSystem)
+ router.Run(":8080")
+}
+
+func multiplayer(c *gin.Context) {
+ if c.GetHeader("Accept") == "*/*" {
+ c.String(http.StatusTeapot, "Preload not allowed")
+ }
+ u, err := uuid.NewV7()
+ if err != nil {
+ fmt.Println("Ошибка:", err)
+ return
+ }
+ c.String(http.StatusOK, fmt.Sprintf("ToDo\nUUID7: %s", u.String()))
+}