dkforest

A forum and chat platform (onion)
git clone https://git.dasho.dev/n0tr1v/dkforest.git
Log | Files | Refs | LICENSE

commit 6b6b7201c3b6725cffc0dbc13a85f6899bc56397
parent 79629fa300022a89071c99e7bebf2e926827dc76
Author: n0tr1v <n0tr1v@protonmail.com>
Date:   Sun, 11 Jun 2023 13:49:35 -0700

insanity v2

Diffstat:
Mpkg/web/handlers/handlers.go | 231+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mpkg/web/handlers/interceptors/chess.go | 48+++++++++++++++++++++++++++++++-----------------
Mpkg/web/middlewares/middlewares.go | 3++-
Mpkg/web/web.go | 2++
4 files changed, 257 insertions(+), 27 deletions(-)

diff --git a/pkg/web/handlers/handlers.go b/pkg/web/handlers/handlers.go @@ -4907,6 +4907,181 @@ func chessGamePromoB(g *interceptors.ChessGame) { g.MoveStr("axb7") } +func chessGameKingSideCastle(g *interceptors.ChessGame) { + g.MoveStr("e3") + g.MoveStr("e6") + g.MoveStr("Be2") + g.MoveStr("Be7") + g.MoveStr("Nf3") + g.MoveStr("Nf6") +} + +func chessGameQueenSideCastle(g *interceptors.ChessGame) { + g.MoveStr("d4") + g.MoveStr("d5") + g.MoveStr("Qd3") + g.MoveStr("Qd6") + g.MoveStr("Bd2") + g.MoveStr("Bd7") + g.MoveStr("Nc3") + g.MoveStr("Nc6") +} + +func ChessGameFormHandler(c echo.Context) error { + key := c.Param("key") + out := `<style> +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +/* --- end --- */ +.newBoard { + position: relative; + aspect-ratio: 1 / 1; + width: 100%; + min-height: 360px; +} +.newBoard .img { + position: absolute; + width: 12.5%; + height: 12.5%; + background-size: 100%; +} +.idk { + width: 100%; + height: 100%; +} +label { + position: absolute; + width: 12.5%; + height: 12.5%; +} +input[type=checkbox] { + display:none; +} +input[type=checkbox] + label { + display: inline-block; + padding: 0 0 0 0; + margin: 0 0 0 0; + background-size: 100%; + border: 3px solid transparent; + box-sizing: border-box; +} +input[type=checkbox]:checked + label { + display: inline-block; + background-size: 100%; + border: 3px solid red; +} +</style>` + + authUser := c.Get("authUser").(*database.User) + g := interceptors.ChessInstance.GetGame(key) + isFlipped := authUser.ID == g.Player2.ID + + out += `<form method="post" action="/chess/` + key + `"> +<table class="newBoard">` + for i := 0; i < 64; i++ { + id := i + mm := 0 + switch i % 8 { + case 0: + mm = -7 + case 1: + mm = -5 + case 2: + mm = -3 + case 3: + mm = -1 + case 4: + mm = 1 + case 5: + mm = 3 + case 6: + mm = 5 + case 7: + mm = 7 + } + if !isFlipped { + id = (63 - i) + mm + } else { + id = i - mm + } + if i%8 == 0 { + if i != 0 { + out += "</tr>" + } + out += "<tr>" + } + idStr := strconv.Itoa(id) + out += `<td><div class="idk">` + out += `<input name="sq_` + idStr + `" id="sq_` + idStr + `" type="checkbox" value="1" /><label for="sq_` + idStr + `"></label>` + out += "</div></td>" + } + csrf, _ := c.Get("csrf").(string) + out += `</tr></table> + <input type="hidden" name="csrf" value="` + csrf + `" /> + <input type="hidden" name="message" value="/pm {{ .Username }} /c move" /> + <div style="width: 100%; display: flex; margin: 5px 0;"> + <div> + <button type="submit" style="background-color: #aaa;">Move</button> + </div> + <div style="margin-left: auto;"> + <span style="color: #aaa; margin-left: 20px;">Promo:</span> + <select name="promotion" style="background-color: #aaa;"> + <option value="queen">Queen</option> + <option value="rook">Rook</option> + <option value="knight">Knight</option> + <option value="bishop">Bishop</option> + </select> + </div> + </div> +</form>` + return c.HTML(http.StatusOK, out) +} + func ChessGameHandler(c echo.Context) error { authUser := c.Get("authUser").(*database.User) db := c.Get("database").(*database.DkfDB) @@ -4919,7 +5094,7 @@ func ChessGameHandler(c echo.Context) error { user2, _ := db.GetUserByID(30814) interceptors.ChessInstance.NewGame(key, user1, user2) g = interceptors.ChessInstance.GetGame(key) - chessGamePromoB(g) + chessGameQueenSideCastle(g) //return c.Redirect(http.StatusFound, "/") } @@ -4944,18 +5119,18 @@ func ChessGameHandler(c echo.Context) error { isFlipped = true } - isYourTurnFn := func() bool { - return authUser.ID == g.Player1.ID && g.Game.Position().Turn() == chess.White || - authUser.ID == g.Player2.ID && g.Game.Position().Turn() == chess.Black - } - isYourTurn := isYourTurnFn() + //isYourTurnFn := func() bool { + // return authUser.ID == g.Player1.ID && g.Game.Position().Turn() == chess.White || + // authUser.ID == g.Player2.ID && g.Game.Position().Turn() == chess.Black + //} + //isYourTurn := isYourTurnFn() // If you are not a spectator, and it's your turn to play, we just render the form directly. - if isYourTurn || g.Game.Outcome() != chess.NoOutcome { + if g.Game.Outcome() != chess.NoOutcome { c.Response().Header().Set(echo.HeaderContentType, echo.MIMETextHTMLCharsetUTF8) c.Response().WriteHeader(http.StatusOK) _, _ = c.Response().Write([]byte(cssReset)) - card1 := g.DrawPlayerCard(isFlipped, isYourTurn) + card1 := g.DrawPlayerCard(key, isFlipped, false) _, _ = c.Response().Write([]byte(fmt.Sprintf(`<div id="div_0">%s</div>`, card1))) return nil } @@ -4984,7 +5159,7 @@ func ChessGameHandler(c echo.Context) error { if isSpectator { card1 = g.DrawSpectatorCard(isFlipped) } else { - card1 = g.DrawPlayerCard(isFlipped, isYourTurn) + card1 = g.DrawPlayerCard(key, isFlipped, false) } _, _ = c.Response().Write([]byte(fmt.Sprintf(`<div id="div_0">%s</div>`, card1))) @@ -5055,6 +5230,44 @@ Loop: }(payload, c) } + if payload.Turn == chess.White && payload.Move.HasTag(chess.KingSideCastle) { + x := int(chess.F1.File()) + y := int(chess.F1.Rank()) + if isFlipped { + x = 7 - x + } else { + y = 7 - y + } + _, _ = c.Response().Write([]byte(fmt.Sprintf(`<style>#img_7 { left: calc(%d*12.5%%) !important; top: calc(%d*12.5%%) !important; }</style>`, x, y))) + } else if payload.Turn == chess.Black && payload.Move.HasTag(chess.KingSideCastle) { + x := int(chess.F8.File()) + y := int(chess.F8.Rank()) + if isFlipped { + x = 7 - x + } else { + y = 7 - y + } + _, _ = c.Response().Write([]byte(fmt.Sprintf(`<style>#img_63 { left: calc(%d*12.5%%) !important; top: calc(%d*12.5%%) !important; }</style>`, x, y))) + } else if payload.Turn == chess.White && payload.Move.HasTag(chess.QueenSideCastle) { + x := int(chess.D1.File()) + y := int(chess.D1.Rank()) + if isFlipped { + x = 7 - x + } else { + y = 7 - y + } + _, _ = c.Response().Write([]byte(fmt.Sprintf(`<style>#img_0 { left: calc(%d*12.5%%) !important; top: calc(%d*12.5%%) !important; }</style>`, x, y))) + } else if payload.Turn == chess.Black && payload.Move.HasTag(chess.QueenSideCastle) { + x := int(chess.D8.File()) + y := int(chess.D8.Rank()) + if isFlipped { + x = 7 - x + } else { + y = 7 - y + } + _, _ = c.Response().Write([]byte(fmt.Sprintf(`<style>#img_56 { left: calc(%d*12.5%%) !important; top: calc(%d*12.5%%) !important; }</style>`, x, y))) + } + _, _ = c.Response().Write([]byte(`<style>.square { background-color: transparent !important; }</style>`)) _, _ = c.Response().Write([]byte(`<style>.square_` + strconv.Itoa(int(payload.Move.S1())) + `, .square_` + strconv.Itoa(int(payload.Move.S2())) + ` { background-color: rgba(0, 255, 0, 0.2) !important; }</style>`)) diff --git a/pkg/web/handlers/interceptors/chess.go b/pkg/web/handlers/interceptors/chess.go @@ -383,11 +383,11 @@ func (g *ChessGame) renderBoardB64(isFlipped bool) string { return imgB64 } -func (g *ChessGame) DrawPlayerCard(isBlack, isYourTurn bool) string { - return g.drawPlayerCard("", isBlack, isYourTurn) +func (g *ChessGame) DrawPlayerCard(key string, isBlack, isYourTurn bool) string { + return g.drawPlayerCard(key, "", isBlack, isYourTurn) } -func (g *ChessGame) drawPlayerCard(roomName string, isBlack, isYourTurn bool) string { +func (g *ChessGame) drawPlayerCard(key, roomName string, isBlack, isYourTurn bool) string { enemy := utils.Ternary(isBlack, g.Player1, g.Player2) imgB64 := g.renderBoardB64(isBlack) @@ -419,7 +419,8 @@ func (g *ChessGame) drawPlayerCard(roomName string, isBlack, isYourTurn bool) st <input type="hidden" name="message" value="resign" /> <button type="submit" style="background-color: #aaa; margin: 5px 0;">Resign</button> </form> - <div> + <div style="position: relative;"> + <iframe src="/chess/{{ .Key }}/form" style="position: absolute; top: 0; left: 0; border: 0px solid red; z-index: 999; width: 100%; height: 100%;"></iframe> <form method="post"{{ if .InChat }} action="/api/v1/chess"{{ end }} style="aspect-ratio: 1/1; height: 70%;"> {{ .Table }} {{ if .InChat }} @@ -466,17 +467,17 @@ func (g *ChessGame) drawPlayerCard(roomName string, isBlack, isYourTurn bool) st ` data := map[string]any{ - "RoomName": roomName, - "IsYourTurn": isYourTurn, - "InChat": roomName != "", - "White": g.Player1, - "Black": g.Player2, - "Username": enemy.Username, - "Table": template.HTML(g.renderBoardHTML(isBlack, imgB64, false)), - "ImgB64": imgB64, - "Outcome": g.Game.Outcome().String(), - "GameOver": g.Game.Outcome() != chess.NoOutcome, - "PGN": g.Game.String(), + "Key": key, + "RoomName": roomName, + "InChat": roomName != "", + "White": g.Player1, + "Black": g.Player2, + "Username": enemy.Username, + "Table": template.HTML(g.renderBoardHTML(isBlack, imgB64, false)), + "ImgB64": imgB64, + "Outcome": g.Game.Outcome().String(), + "GameOver": g.Game.Outcome() != chess.NoOutcome, + "PGN": g.Game.String(), } fns := template.FuncMap{ @@ -672,6 +673,19 @@ func (b *Chess) SendMove(gameKey string, userID database.UserID, g *ChessGame, c delete(g.PiecesCache, mov.S1()) delete(g.PiecesCache, mov.S2()) g.PiecesCache[mov.S2()] = idStr1 + if turn == chess.White && mov.HasTag(chess.KingSideCastle) { + delete(g.PiecesCache, chess.H1) + g.PiecesCache[chess.F1] = "img_7" + } else if turn == chess.Black && mov.HasTag(chess.KingSideCastle) { + delete(g.PiecesCache, chess.H8) + g.PiecesCache[chess.F8] = "img_63" + } else if turn == chess.White && mov.HasTag(chess.QueenSideCastle) { + delete(g.PiecesCache, chess.A1) + g.PiecesCache[chess.D1] = "img_0" + } else if turn == chess.Black && mov.HasTag(chess.QueenSideCastle) { + delete(g.PiecesCache, chess.A8) + g.PiecesCache[chess.D8] = "img_56" + } ChessPubSub.Pub(gameKey, chessMov) // Notify (pm) the opponent that you made a move @@ -766,10 +780,10 @@ func (b *Chess) PlayMove(enemyUsername database.Username, pos string, authUser d logrus.Error(err) } - card1 := g.drawPlayerCard(roomName, false, true) + card1 := g.drawPlayerCard(gameKey, roomName, false, true) _, _ = b.db.CreateMsg(card1, card1, roomKey, roomID, b.zeroID, &g.Player1.ID) - card1 = g.drawPlayerCard(roomName, true, true) + card1 = g.drawPlayerCard(gameKey, roomName, true, true) _, _ = b.db.CreateMsg(card1, card1, roomKey, roomID, b.zeroID, &g.Player2.ID) return nil diff --git a/pkg/web/middlewares/middlewares.go b/pkg/web/middlewares/middlewares.go @@ -273,7 +273,8 @@ func IsAuthMiddleware(next echo.HandlerFunc) echo.HandlerFunc { db.DB().Model(user).Update("last_seen_at", time.Now()) // Prevent clickjacking by setting the header on every logged in page - if !strings.Contains(c.Path(), "/api/v1/chat/messages") && + if !strings.Contains(c.Path(), "/chess/:key/form") && + !strings.Contains(c.Path(), "/api/v1/chat/messages") && !strings.Contains(c.Path(), "/api/v1/chat/messages/:roomName/stream") && !strings.Contains(c.Path(), "/api/v1/chat/top-bar") && !strings.Contains(c.Path(), "/api/v1/chat/controls") { diff --git a/pkg/web/web.go b/pkg/web/web.go @@ -102,6 +102,8 @@ func getMainServer(db *database.DkfDB, i18nBundle *i18n.Bundle, renderer *tmp.Te authGroup.POST("/chess", handlers.ChessHandler) authGroup.GET("/chess/:key", handlers.ChessGameHandler) authGroup.POST("/chess/:key", handlers.ChessGameHandler) + authGroup.GET("/chess/:key/form", handlers.ChessGameFormHandler) + authGroup.POST("/chess/:key/form", handlers.ChessGameFormHandler) authGroup.GET("/settings/chat", handlers.SettingsChatHandler) authGroup.POST("/settings/chat", handlers.SettingsChatHandler, middlewares.AuthRateLimitMiddleware(2*time.Second, 1)) authGroup.GET("/settings/chat/pm", handlers.SettingsChatPMHandler)