commit ab174b8de5c9efe3104dbd744bab6631007a6a7b Author: Max Doe Date: Tue Jan 28 18:43:37 2025 +0300 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cb7b2f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +*.db \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b7dbf82 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 dixxe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4d2e05b --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# Personal web +This website is a showcase of my skills and interests. + +## Can I use this website? +Yes you can! But you must follow MIT license if you doing so. + +To start website you need to: +- Create .env file and populate it with LOGIN, PASSWORD for admin panel. +- Create blogs.db database and populate it with `initdb()` method. +- run compiled binary with `./personal-website ""` to start HTTP website or `./personal-website domain.com subdomain.domain.com` to start HTTPS website + +## How to contribute? + +1. Fork the repository +2. Create a new branch (`git checkout -b feature/AmazingFeature`) +3. Make your changes +4. Commit your changes (`git commit -m 'Add some AmazingFeature'`) +5. Push to the branch (`git push origin feature/AmazingFeature`) +6. Open a Pull Request + +## ToDo +- [x] Comments and code refactoring +- [ ] Comments and likes for blog \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..0782e05 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1736012469, + "narHash": "sha256-/qlNWm/IEVVH7GfgAIyP6EsVZI6zjAx1cV5zNyrs+rI=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..4ea039e --- /dev/null +++ b/flake.nix @@ -0,0 +1,29 @@ +{ + description = "A portable go dev flake!"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + }; + + outputs = { nixpkgs, ... }@inputs: + let + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${system}; + in + { + devShells.${system} = { + default = pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + delve + go + gopls + templ + ]; + + hardeningDisable = [ "fortify" ]; + + shellHook = "echo Welcome to go-backend environment"; + }; + }; + }; +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a347bd8 --- /dev/null +++ b/go.mod @@ -0,0 +1,32 @@ +module github.com/dixxe/personal-website + +go 1.23.2 + +require github.com/a-h/templ v0.3.819 + +require ( + github.com/go-chi/chi/v5 v5.2.0 + github.com/joho/godotenv v1.5.1 + golang.org/x/crypto v0.32.0 + modernc.org/sqlite v1.34.4 +) + +require ( + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/kaptinlin/jsonrepair v0.1.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.21.0 // indirect + modernc.org/gc/v3 v3.0.0-20241223112719-96e2e1e4408d // indirect + modernc.org/libc v1.61.6 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.8.0 // indirect + modernc.org/strutil v1.2.1 // indirect + modernc.org/token v1.1.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..caab1f4 --- /dev/null +++ b/go.sum @@ -0,0 +1,69 @@ +github.com/a-h/templ v0.3.819 h1:KDJ5jTFN15FyJnmSmo2gNirIqt7hfvBD2VXVDTySckM= +github.com/a-h/templ v0.3.819/go.mod h1:iDJKJktpttVKdWoTkRNNLcllRI+BlpopJc+8au3gOUo= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0= +github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +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/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/kaptinlin/jsonrepair v0.1.0 h1:71Pwmyqan+fZPStHDSBTJngPhxtTkjEeWciZF3405cs= +github.com/kaptinlin/jsonrepair v0.1.0/go.mod h1:hW2xIfVQwmskoVSv9g8g3ITOkEiOKKRj934dDCVW6aM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= +golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +modernc.org/cc/v4 v4.24.2 h1:uektamHbSXU7egelXcyVpMaaAsrRH4/+uMKUQAQUdOw= +modernc.org/cc/v4 v4.24.2/go.mod h1:T1lKJZhXIi2VSqGBiB4LIbKs9NsKTbUXj4IDrmGqtTI= +modernc.org/ccgo/v4 v4.23.5 h1:6uAwu8u3pnla3l/+UVUrDDO1HIGxHTYmFH6w+X9nsyw= +modernc.org/ccgo/v4 v4.23.5/go.mod h1:FogrWfBdzqLWm1ku6cfr4IzEFouq2fSAPf6aSAHdAJQ= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.6.0 h1:Tiw3pezQj7PfV8k4Dzyu/vhRHR2e92kOXtTFU8pbCl4= +modernc.org/gc/v2 v2.6.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= +modernc.org/gc/v3 v3.0.0-20241223112719-96e2e1e4408d h1:d0JExN5U5FjUVHCP6L9DIlLJBZveR6KUM4AvfDUL3+k= +modernc.org/gc/v3 v3.0.0-20241223112719-96e2e1e4408d/go.mod h1:qBSLm/exCqouT2hrfyTKikWKG9IPq8EoX5fS00l3jqk= +modernc.org/libc v1.61.6 h1:L2jW0wxHPCyHK0YSHaGaVlY0WxjpG/TTVdg6gRJOPqw= +modernc.org/libc v1.61.6/go.mod h1:G+DzuaCcReUYYg4nNSfigIfTDCENdj9EByglvaRx53A= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= +modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.34.4 h1:sjdARozcL5KJBvYQvLlZEmctRgW9xqIZc2ncN7PU0P8= +modernc.org/sqlite v1.34.4/go.mod h1:3QQFCG2SEMtc2nv+Wq4cQCH7Hjcg+p/RMlS1XK+zwbk= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/iternal/controllers/AdminController.go b/iternal/controllers/AdminController.go new file mode 100644 index 0000000..808ccdf --- /dev/null +++ b/iternal/controllers/AdminController.go @@ -0,0 +1,71 @@ +/* +This controller handles admin stuff and allows to redact databases. + +It has some noodles in it but this is how it works. +1. Users enters on "/admin" and this controller handles GetAdminLogin() +2. Than frontend passes a form with login and password to PostAdminLogin() +3. In PostAdminLogin() backend checks that login and password are equal to + ones that defined in .env file (without it code will panic! I will rewrite this) +4. If everything correct controller goes to GetAdminPanel() and shows admin-panel + template + +Currently page is vulnerable to bruteforce. +I will try to implement JWT in future. -d1xxe +Best practice is to move login to middleware- -TODO +*/ + +package controllers + +import ( + "context" + "log" + "net/http" + "os" + + "github.com/dixxe/personal-website/iternal/pkg/repositories" + "github.com/dixxe/personal-website/web/templates" + "github.com/joho/godotenv" +) + +// This controller has been written to control databases and blog. SECUIRITY-WARNING! +func GetAdminPanel(w http.ResponseWriter, r *http.Request) { + posts, err := repositories.Blog.GetAllValues() + + if err != nil { + log.Println(err) + component := templates.AdminPanelPage([]repositories.Post{}) + component.Render(context.Background(), w) + return + } + + component := templates.AdminPanelPage(posts) + component.Render(context.Background(), w) +} + +// This controller has been written to add protection for admin-panel. +func GetAdminLogin(w http.ResponseWriter, r *http.Request) { + // Respond with page that contains form for login and password. + component := templates.LoginPage() + component.Render(context.Background(), w) +} + +// This controller handles passed form and checks for its validity. +// If everything right it passes to GetAdminPanel. +func PostAdminLogin(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + login := r.FormValue("login") + password := r.FormValue("password") + + if err := godotenv.Load(); err != nil { + // I better disable admin panel at all if .env not located. + component := templates.ErrorPage(404, "Admin-panel not configured.") + component.Render(context.Background(), w) + return + } + + admin_login, _ := os.LookupEnv("LOGIN") + admin_password, _ := os.LookupEnv("PASSWORD") + if login == admin_login && password == admin_password { + GetAdminPanel(w, r) + } +} diff --git a/iternal/controllers/BlogController.go b/iternal/controllers/BlogController.go new file mode 100644 index 0000000..9a327c2 --- /dev/null +++ b/iternal/controllers/BlogController.go @@ -0,0 +1,91 @@ +/* +Controller that handles everything d1xxe blog related features. +Under the hood it operates with repositories.Blog to get all information from +local database. + +In a nutshell it's pretty simple. +*/ +package controllers + +import ( + "context" + "fmt" + "log" + "net/http" + "strconv" + + "github.com/dixxe/personal-website/iternal/pkg/repositories" + "github.com/dixxe/personal-website/web/templates" + "github.com/go-chi/chi/v5" +) + +// This controller passes all posts in database in template and responds with page with all size-reduced posts. +func GetShowBlog(w http.ResponseWriter, r *http.Request) { + posts, err := repositories.Blog.GetAllValues() + if err != nil { + component := templates.ErrorPage(500, "Ошибка при обработке базы данных.") + // For some reason I can't set status code for request x-x + log.Println(err) + component.Render(context.Background(), w) + return + } + + component := templates.ShowBlogPage(posts) + component.Render(context.Background(), w) +} + +// This controller looks for specific post in database and handles all possible errors. Responds with page with post. +func GetPost(w http.ResponseWriter, r *http.Request) { + id, err := strconv.Atoi(chi.URLParam(r, "id")) + + if err != nil { + component := templates.ErrorPage(500, "Не удалось преобразовать id в целое число.") + log.Println(err) + component.Render(context.Background(), w) + return + } + + post, err := repositories.Blog.GetValueByID(id) + + if err != nil { + component := templates.ErrorPage(404, "Не удалось найти нужный пост.") + log.Println(err) + component.Render(context.Background(), w) + return + + } + component := templates.ShowPost(post) + component.Render(context.Background(), w) + +} + +// Controller that handles post creation. Responds with 200 if everything OK +func PostCreatePost(w http.ResponseWriter, r *http.Request) { + r.ParseForm() // Populating form. + header := r.FormValue("header") // To get value you need to specify name="header" in the form. + content := r.FormValue("content") + + // Creating a new post with 0 Id, don't worry database handles id assignment + // itself. And in the InsertValue() method I don't use Post.Id value + newPost := repositories.Post{Id: 0, Header: header, Content: content} + + // Process database is async way. WARNING errors are NOT HANDLED + go repositories.Blog.InsertValue(newPost) + + fmt.Println("Created post") +} + +// Controller that handles post deletion. Responds with 200 if everything is OK +func PostDeletePost(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + id, err := strconv.Atoi(r.FormValue("id")) + + if err != nil { + component := templates.ErrorPage(500, "Ошибка при переводе id (int) в string.") + r.Response.StatusCode = 500 + log.Println(err) + component.Render(context.Background(), w) + } + // Process database is async way. WARNING errors are NOT HANDLED + go repositories.Blog.DeleteValueByID(id) +} diff --git a/iternal/controllers/CCTweakedController.go b/iternal/controllers/CCTweakedController.go new file mode 100644 index 0000000..6f4552d --- /dev/null +++ b/iternal/controllers/CCTweakedController.go @@ -0,0 +1,74 @@ +package controllers + +import ( + "encoding/json" + "fmt" + "io" + "log" + "net/http" + + "github.com/kaptinlin/jsonrepair" +) + +type Message struct { + Content string `json:"content"` + Msg_type int `json:"msg_type"` +} + +var message_queue = []Message{} + +func preprocess_message(r *http.Request) []byte { + message_bytes, _ := io.ReadAll(r.Body) + message_body := string(message_bytes) + + repaired, err := jsonrepair.JSONRepair(message_body) + if err != nil { + log.Printf("Failed to fix JSON: %v", err) + } + repaired_bytes := []byte(repaired) + + return repaired_bytes +} + +func decodeMessage(r *http.Request) (Message, error) { + var messageRC Message + + processed_message := preprocess_message(r) + + err := json.Unmarshal(processed_message, &messageRC) + if err != nil { + log.Println(err) + return Message{}, err + } + return messageRC, nil +} + +func clearQueue() { + if len(message_queue) <= 1 { + return + } + message_queue = message_queue[1:] +} + +func PostSendMessage(w http.ResponseWriter, r *http.Request) { + + decoded_message, err := decodeMessage(r) + if err != nil { + w.WriteHeader(400) + fmt.Fprintf(w, "Failed to decode message %v", err) + log.Printf("Failed to decode message %v", err) + return + } + message_queue = append(message_queue, decoded_message) + + clearQueue() + +} + +func GetMessage(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + if len(message_queue) == 0 { + message_queue = append(message_queue, Message{"no message accepted yet", 404}) + } + fmt.Fprintln(w, message_queue[0]) +} diff --git a/iternal/controllers/IndexController.go b/iternal/controllers/IndexController.go new file mode 100644 index 0000000..125fd69 --- /dev/null +++ b/iternal/controllers/IndexController.go @@ -0,0 +1,20 @@ +/* +Controller that only shows index template! Simple as it is! +*/ + +package controllers + +import ( + "context" + "net/http" + + "github.com/dixxe/personal-website/web/templates" +) + +// Controller that shows "/" root page. +func GetIndexHandler(w http.ResponseWriter, r *http.Request) { + // Templates are components and this is basic way to render them. + // I use this way across all code + component := templates.IndexPage() + component.Render(context.Background(), w) +} diff --git a/iternal/pkg/RepositoryInterface.go b/iternal/pkg/RepositoryInterface.go new file mode 100644 index 0000000..5d1bd9d --- /dev/null +++ b/iternal/pkg/RepositoryInterface.go @@ -0,0 +1,26 @@ +/* +Special interface that I made to make system more reusable. +So if you need in future another db it should satisfy this interface. +Generic T represents field of database. +In this approach DB must be open everytime. + +Why interface? For example you create a function that executes something on database +and you need to execute this command automaticly for any DB that contains service.Post passed. +So you will define it like this +func exec_fix_for_db(repo service.Repository[Post]) { + repo.ExecSpecific("Very important fix!!") +} +*/ + +package pkg + +import "database/sql" + +type Repository[T any] interface { + GetAllValues() ([]T, error) + GetValueByID(id int) (T, error) + InsertValue(T) (id int, err error) + DeleteValueByID(id int) error + ExecSpecific(SQL_command string) (sql.Result, error) + QuerySpecific(SQL_command string) (*sql.Rows, error) +} diff --git a/iternal/pkg/repositories/BlogRepository.go b/iternal/pkg/repositories/BlogRepository.go new file mode 100644 index 0000000..6881378 --- /dev/null +++ b/iternal/pkg/repositories/BlogRepository.go @@ -0,0 +1,116 @@ +/* +Managing blogRepo database with a little bit awful execution. +This can be considered as any-repository database example. +In this realisation database is *always* stays open after xxxRepository intitialization. +Because if something close it the struct will point to nothing and obviously this is bad. +*/ + +package repositories + +import ( + "database/sql" + "fmt" + + _ "modernc.org/sqlite" +) + +var database_name = "blogs.db" // You can change it if you want. + +// Defining it to use it later via repository.Blog +// Why here? Because it's best place for anything blog related! +var Blog blogRepository = blogRepository{Database: OpenDb(database_name)} + +// Post structure for database field. +type Post struct { + Id int + Header string + Content string +} + +// This struct implemets Repository[Post] +type blogRepository struct { + Database *sql.DB +} + +func (blogRepo blogRepository) GetAllValues() ([]Post, error) { + db := blogRepo.Database + + rows, err := db.Query("SELECT * from blogs") + if err != nil { + return nil, err + } + defer rows.Close() + + posts := []Post{} + + for rows.Next() { + p := Post{} + err := rows.Scan(&p.Id, &p.Header, &p.Content) + if err != nil { + fmt.Println(err) + continue + } + posts = append(posts, p) + } + fmt.Println("Readed all posts") + + return posts, err +} + +func (blogRepo blogRepository) GetValueByID(id int) (Post, error) { + db := blogRepo.Database + + row := db.QueryRow("SELECT * from blogs where id = ?", id) + p := Post{} + err := row.Scan(&p.Id, &p.Header, &p.Content) + if err != nil { + return Post{}, err + } + fmt.Println("Get one post") + + return p, err +} + +func (blogRepo blogRepository) DeleteValueByID(id int) error { + db := blogRepo.Database + //defer db.Close() + + _, err := db.Exec("DELETE from blogs where id = ?", id) + if err != nil { + return err + } + fmt.Println("Delete one post") + return err +} + +// Returning last inserted id +func (blogRepo blogRepository) InsertValue(postToInsert Post) (int, error) { + db := blogRepo.Database + + result, err := db.Exec("INSERT into blogs (header, content) values (?,?)", + postToInsert.Header, postToInsert.Content) + + if err != nil { + return 0, err + } + + id, _ := result.LastInsertId() + + return int(id), err +} + +func (blogRepo blogRepository) ExecSpecific(SQL_command string) (sql.Result, error) { + result, err := blogRepo.Database.Exec(SQL_command) + if err != nil { + return nil, err + } + return result, err +} + +func (blogRepo blogRepository) QuerySpecific(SQL_command string) (*sql.Rows, error) { + result, err := blogRepo.Database.Query(SQL_command) + if err != nil { + return nil, err + } + return result, err +} diff --git a/iternal/pkg/repositories/SQL-basic-functions.go b/iternal/pkg/repositories/SQL-basic-functions.go new file mode 100644 index 0000000..394df14 --- /dev/null +++ b/iternal/pkg/repositories/SQL-basic-functions.go @@ -0,0 +1,40 @@ +/* +Some SQL tools to reduce boilerplate. +Probably will be rewritten in future. +*/ +package repositories + +import ( + "database/sql" + "fmt" + "os" + + "github.com/dixxe/personal-website/iternal/pkg" +) + +func OpenDb(database_name string) *sql.DB { + if _, err := os.Stat("./" + database_name); err != nil { + panic(database_name + " not found!") + } + db, err := sql.Open("sqlite", database_name) + if err != nil { + panic(err) + } + fmt.Println("Database was openned") + + return db +} + +// I don't know a way how to automate this process. +func InitDb(repo pkg.Repository[Post]) { + //defer db.Close() + + repo.ExecSpecific(` + CREATE TABLE blogs( + id INTEGER PRIMARY KEY AUTOINCREMENT, + header TEXT, + content TEXT + ); + `) + fmt.Println("Database was initiated.") +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..9fa9861 --- /dev/null +++ b/main.go @@ -0,0 +1,75 @@ +package main + +/* + Main file with routes and nothing else. +*/ + +import ( + "log" + "net/http" + "os" + + "github.com/dixxe/personal-website/iternal/controllers" + "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" + "golang.org/x/crypto/acme/autocert" +) + +var domains = os.Args[1:] + +func main() { + + log.Println(` + ____ _ ___ ____ _ _____ ____ ____ _____ _____ +/ _ \/ \\ \//\ \/// __/ / __\/ _ \/ __// __/ +| | \|| | \ / \ / | \ _____ | \/|| / \|| | _| \ +| |_/|| | / \ / \ | /_\____\| __/| |-||| |_//| /_ +\____/\_//__/\\/__/\\\____\ \_/ \_/ \|\____\\____\ + `) + + r := chi.NewRouter() + r.Use(middleware.Logger) + r.Use(middleware.StripSlashes) + + r.Get("/", controllers.GetIndexHandler) + + r.Get("/blog", controllers.GetShowBlog) + r.Get("/post/{id}", controllers.GetPost) + r.Post("/post", controllers.PostCreatePost) + r.Post("/post/delete", controllers.PostDeletePost) + + r.Post("/admin/login", controllers.PostAdminLogin) + r.Get("/admin", controllers.GetAdminLogin) + + r.Post("/cctweaked", controllers.PostSendMessage) + r.Get("/cctweaked", controllers.GetMessage) + + fs := http.FileServer(http.Dir("web/static")) + r.Handle("/static/*", http.StripPrefix("/static/", fs)) + + log.Println(domains) + + if domains[0] == "" { + + log.Println("HTTP website started") + log.Fatal(http.ListenAndServe("localhost:8080", r)) + + } else { + + log.Println("HTTPS website started with HTTP redirect.") + go redirectHTTPServer() + for _, domain := range domains { + log.Fatal(http.Serve(autocert.NewListener(domain), r)) + } + } +} + +func redirectHTTPServer() { + if err := http.ListenAndServe(":8080", http.HandlerFunc(redirectTLS)); err != nil { + log.Fatalf("ListenAndServe error: %v", err) + } +} + +func redirectTLS(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, "https://"+domains[0]+":443"+r.RequestURI, http.StatusMovedPermanently) +} diff --git a/test/DecodeMessage_test.go b/test/DecodeMessage_test.go new file mode 100644 index 0000000..bf1e5e7 --- /dev/null +++ b/test/DecodeMessage_test.go @@ -0,0 +1,78 @@ +package test + +import ( + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + "unicode/utf8" + + "github.com/dixxe/personal-website/iternal/controllers" +) + +func TestPostMessage(t *testing.T) { + + test_value := `{"content":"banan","msg_type":10}` + + body := strings.NewReader(test_value) + req, err := http.NewRequest("POST", "/cctweaked", body) + if err != nil { + t.Fatal(err) + } + rr := httptest.NewRecorder() + + http.HandlerFunc(controllers.PostSendMessage).ServeHTTP(rr, req) + + req, err = http.NewRequest("GET", "/cctweaked", nil) + if err != nil { + t.Fatal(err) + } + http.HandlerFunc(controllers.GetMessage).ServeHTTP(rr, req) + + expected := `{banan 10}` + + if strings.Trim(rr.Body.String(), "\n ") != expected { + t.Errorf("handler returned unexpected body: got %v want %v", + rr.Body.String(), expected) + } + +} + +func FuzzPostMessage(f *testing.F) { + f.Add(0, "banan") + f.Fuzz(func(t *testing.T, msg_type int, content string) { + + if !utf8.Valid([]byte(content)) { + return + } + + body := strings.NewReader(fmt.Sprintf(`{"content":"%v","msg_type":%v}`, + content, msg_type)) + + req, err := http.NewRequest("POST", "/cctweaked", body) + if err != nil { + t.Fatal(err) + } + rr := httptest.NewRecorder() + + http.HandlerFunc(controllers.PostSendMessage).ServeHTTP(rr, req) + + req, err = http.NewRequest("GET", "/cctweaked", nil) + if err != nil { + t.Fatal(err) + } + http.HandlerFunc(controllers.GetMessage).ServeHTTP(rr, req) + + expected := fmt.Sprintf(`{%v %v}`, content, msg_type) + + if rr.Result().StatusCode == 400 { + return + } + + if strings.Trim(rr.Body.String(), "\n ") != expected { + t.Errorf("handler returned unexpected body: got %v want %v", + rr.Body.String(), expected) + } + }) +} diff --git a/test/readme_future_me.md b/test/readme_future_me.md new file mode 100644 index 0000000..04d122d --- /dev/null +++ b/test/readme_future_me.md @@ -0,0 +1,18 @@ +# Dear future me. +You can be proud of yourself that you have a very own backend and website. But please I BEG YOU + +## WRITE THE DAMN TESTS + +Not only you work with multi-directory project structure, but you test everything by **running website and look at endpoints!!** + +### THIS CAN NOT CONTINUE + +Please, dear future me. Have mercy to write better and modular code that *can be* tested by **UNIT-test** and **FUZZING-test** + +At least do this for + +- SQL-related stuff +- Any IO operations (in future) +- Any calculations/conversations/user-services + +### THANK YOU!! diff --git a/test/testdata/fuzz/FuzzPostMessage/46b16ae58246e7dc b/test/testdata/fuzz/FuzzPostMessage/46b16ae58246e7dc new file mode 100644 index 0000000..b143fee --- /dev/null +++ b/test/testdata/fuzz/FuzzPostMessage/46b16ae58246e7dc @@ -0,0 +1,3 @@ +go test fuzz v1 +int(34) +string("\"+") diff --git a/web/static/fonts/Disket-Mono-Bold.ttf b/web/static/fonts/Disket-Mono-Bold.ttf new file mode 100644 index 0000000..a33bfcc Binary files /dev/null and b/web/static/fonts/Disket-Mono-Bold.ttf differ diff --git a/web/static/fonts/Disket-Mono-Regular.ttf b/web/static/fonts/Disket-Mono-Regular.ttf new file mode 100644 index 0000000..b564c60 Binary files /dev/null and b/web/static/fonts/Disket-Mono-Regular.ttf differ diff --git a/web/static/fonts/codec-pro.regular.ttf b/web/static/fonts/codec-pro.regular.ttf new file mode 100644 index 0000000..a6a1a59 Binary files /dev/null and b/web/static/fonts/codec-pro.regular.ttf differ diff --git a/web/static/styling/basic-styling.templ b/web/static/styling/basic-styling.templ new file mode 100644 index 0000000..f3d0e30 --- /dev/null +++ b/web/static/styling/basic-styling.templ @@ -0,0 +1,103 @@ +package styling + +/* +In this file I write main CSS for all webpages. +Also I store there some variables that I use a lot. +Currently all elements are not sorted. +Be aware that class names in final html page will be random. +*/ + + +var mainColor = "#ffb37f" +var bgColor = "#272727" +var bgVariation = "#1f1f1f" // Color simmilar to bg +var contrastColor = "#92ffd1" // Contrast to mainColor +var textColor = "#FFFFFF"; + +css BlogFormInput() { + width: 70%; + height: 150px; + padding: 12px 20px; + box-sizing: border-box; + border: 2px solid { bgVariation }; + border-radius: 4px; + background-color: #f8f8f8; + font-size: 13px; + resize: none; +} + +css FileHeader() { + color: { mainColor }; + text-align: center; +} + +css Header() { + color: { mainColor }; + text-align: left; +} + +css HighlightText() { + color: { contrastColor }; + font-weight: bold; +} + +css Textcontainer() { + block-size: fit-content; + padding: 5px; + text-wrap: wrap; + margin: 10px; + color: { textColor }; +} + +css BlogContainer() { + border: 2px dashed gray; + margin: 20px; +} + +css HelloContainer() { + height: fit-content; + padding: 50px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + color: { textColor }; + background: { bgVariation }; +} + +css BlockContainer() { + height: fit-content; + display: flex; + flex-direction: column; + justify-content: center; + background: { bgVariation }; +} + +css PostScriptum() { + text-align: center; + font-size: 12px; + color: { contrastColor }; +} + +css DefaultTable() { + border-collapse: collapse; + border: 1px solid; + color: { textColor }; +} + +css CenterPageContainer() { + padding: 15px; + position: absolute; + top: 50%; + left: 50%; + -ms-transform: translateX(-50%) translateY(-50%); + -webkit-transform: translate(-50%,-50%); + transform: translate(-50%,-50%); +} + +css CenterContainer() { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} diff --git a/web/static/styling/basic-styling_templ.go b/web/static/styling/basic-styling_templ.go new file mode 100644 index 0000000..46c8fa4 --- /dev/null +++ b/web/static/styling/basic-styling_templ.go @@ -0,0 +1,184 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.3.819 +package styling + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +/* +In this file I write main CSS for all webpages. +Also I store there some variables that I use a lot. +Currently all elements are not sorted. +Be aware that class names in final html page will be random. +*/ + +var mainColor = "#ffb37f" +var bgColor = "#272727" +var bgVariation = "#1f1f1f" // Color simmilar to bg +var contrastColor = "#92ffd1" // Contrast to mainColor +var textColor = "#FFFFFF" + +func BlogFormInput() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(`width:70%;`) + templ_7745c5c3_CSSBuilder.WriteString(`height:150px;`) + templ_7745c5c3_CSSBuilder.WriteString(`padding:12px 20px;`) + templ_7745c5c3_CSSBuilder.WriteString(`box-sizing:border-box;`) + templ_7745c5c3_CSSBuilder.WriteString(`border:2px solid { bgVariation };`) + templ_7745c5c3_CSSBuilder.WriteString(`border-radius:4px;`) + templ_7745c5c3_CSSBuilder.WriteString(`background-color:#f8f8f8;`) + templ_7745c5c3_CSSBuilder.WriteString(`font-size:13px;`) + templ_7745c5c3_CSSBuilder.WriteString(`resize:none;`) + templ_7745c5c3_CSSID := templ.CSSID(`BlogFormInput`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func FileHeader() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(string(templ.SanitizeCSS(`color`, mainColor))) + templ_7745c5c3_CSSBuilder.WriteString(`text-align:center;`) + templ_7745c5c3_CSSID := templ.CSSID(`FileHeader`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func Header() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(string(templ.SanitizeCSS(`color`, mainColor))) + templ_7745c5c3_CSSBuilder.WriteString(`text-align:left;`) + templ_7745c5c3_CSSID := templ.CSSID(`Header`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func HighlightText() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(string(templ.SanitizeCSS(`color`, contrastColor))) + templ_7745c5c3_CSSBuilder.WriteString(`font-weight:bold;`) + templ_7745c5c3_CSSID := templ.CSSID(`HighlightText`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func Textcontainer() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(`block-size:fit-content;`) + templ_7745c5c3_CSSBuilder.WriteString(`padding:5px;`) + templ_7745c5c3_CSSBuilder.WriteString(`text-wrap:wrap;`) + templ_7745c5c3_CSSBuilder.WriteString(`margin:10px;`) + templ_7745c5c3_CSSBuilder.WriteString(string(templ.SanitizeCSS(`color`, textColor))) + templ_7745c5c3_CSSID := templ.CSSID(`Textcontainer`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func BlogContainer() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(`border:2px dashed gray;`) + templ_7745c5c3_CSSBuilder.WriteString(`margin:20px;`) + templ_7745c5c3_CSSID := templ.CSSID(`BlogContainer`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func HelloContainer() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(`height:fit-content;`) + templ_7745c5c3_CSSBuilder.WriteString(`padding:50px;`) + templ_7745c5c3_CSSBuilder.WriteString(`display:flex;`) + templ_7745c5c3_CSSBuilder.WriteString(`flex-direction:column;`) + templ_7745c5c3_CSSBuilder.WriteString(`justify-content:center;`) + templ_7745c5c3_CSSBuilder.WriteString(`align-items:center;`) + templ_7745c5c3_CSSBuilder.WriteString(string(templ.SanitizeCSS(`color`, textColor))) + templ_7745c5c3_CSSBuilder.WriteString(string(templ.SanitizeCSS(`background`, bgVariation))) + templ_7745c5c3_CSSID := templ.CSSID(`HelloContainer`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func BlockContainer() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(`height:fit-content;`) + templ_7745c5c3_CSSBuilder.WriteString(`display:flex;`) + templ_7745c5c3_CSSBuilder.WriteString(`flex-direction:column;`) + templ_7745c5c3_CSSBuilder.WriteString(`justify-content:center;`) + templ_7745c5c3_CSSBuilder.WriteString(string(templ.SanitizeCSS(`background`, bgVariation))) + templ_7745c5c3_CSSID := templ.CSSID(`BlockContainer`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func PostScriptum() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(`text-align:center;`) + templ_7745c5c3_CSSBuilder.WriteString(`font-size:12px;`) + templ_7745c5c3_CSSBuilder.WriteString(string(templ.SanitizeCSS(`color`, contrastColor))) + templ_7745c5c3_CSSID := templ.CSSID(`PostScriptum`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func DefaultTable() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(`border-collapse:collapse;`) + templ_7745c5c3_CSSBuilder.WriteString(`border:1px solid;`) + templ_7745c5c3_CSSBuilder.WriteString(string(templ.SanitizeCSS(`color`, textColor))) + templ_7745c5c3_CSSID := templ.CSSID(`DefaultTable`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func CenterPageContainer() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(`padding:15px;`) + templ_7745c5c3_CSSBuilder.WriteString(`position:absolute;`) + templ_7745c5c3_CSSBuilder.WriteString(`top:50%;`) + templ_7745c5c3_CSSBuilder.WriteString(`left:50%;`) + templ_7745c5c3_CSSBuilder.WriteString(`-ms-transform:translateX(-50%) translateY(-50%);`) + templ_7745c5c3_CSSBuilder.WriteString(`-webkit-transform:translate(-50%,-50%);`) + templ_7745c5c3_CSSBuilder.WriteString(`transform:translate(-50%,-50%);`) + templ_7745c5c3_CSSID := templ.CSSID(`CenterPageContainer`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +func CenterContainer() templ.CSSClass { + templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() + templ_7745c5c3_CSSBuilder.WriteString(`display:flex;`) + templ_7745c5c3_CSSBuilder.WriteString(`flex-direction:column;`) + templ_7745c5c3_CSSBuilder.WriteString(`justify-content:center;`) + templ_7745c5c3_CSSBuilder.WriteString(`align-items:center;`) + templ_7745c5c3_CSSID := templ.CSSID(`CenterContainer`, templ_7745c5c3_CSSBuilder.String()) + return templ.ComponentCSSClass{ + ID: templ_7745c5c3_CSSID, + Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), + } +} + +var _ = templruntime.GeneratedTemplate diff --git a/web/templates/admin.templ b/web/templates/admin.templ new file mode 100644 index 0000000..5f3d400 --- /dev/null +++ b/web/templates/admin.templ @@ -0,0 +1,76 @@ +/* +Collection of pages which handles everything admin-panel related. +Login page collects information and passes it via post method to backend +which processes passed form and shows templ AdminPanelPage(). +If form incorrected than it shows nothing. +*/ +package templates + +import "github.com/dixxe/personal-website/web/static/styling" +import "strconv" +import "github.com/dixxe/personal-website/iternal/pkg/repositories" + +// Template to reduce boilerplate and untie everything +templ postsTable(blogPosts []repositories.Post) { + if len(blogPosts) == 0 { +

База данных блога не загрузилась.

+ } + + + + + + + + + + for _, post := range blogPosts { + + + + + + } + +
ID Header Content
{strconv.Itoa(post.Id)} {post.Header} {post.Content}
+} + +templ AdminPanelPage(blogPosts []repositories.Post) { + @BasicPageBlock() + +

Очень защищенная админ-панель

+
+ +
+

Добавление новых постов

+
+

Header:

+

Content:


") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var18 = []any{styling.Header()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var18...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "

Удаление постов

Please specify post ID:


") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = postsTable(blogPosts).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +func LoginPage() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var20 := templ.GetChildren(ctx) + if templ_7745c5c3_Var20 == nil { + templ_7745c5c3_Var20 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = BasicPageBlock().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var21 = []any{styling.CenterPageContainer(), styling.Textcontainer()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var21...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "

Login:

Password:

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/web/templates/blog.templ b/web/templates/blog.templ new file mode 100644 index 0000000..3d339e8 --- /dev/null +++ b/web/templates/blog.templ @@ -0,0 +1,89 @@ +/* +Template that grew in complexity recently. It's was first template that supports JS. + +1. Template contains local templ blocks their names starting with lowercase to make it DRY +2. JS script is seperated and should be called *after* page construction. +3. Templ ShowBlogPage contains some math to dynamicly reduce post size + +TODO - make it more DRY and modular in general +*/ +package templates + +import "github.com/dixxe/personal-website/web/static/styling" +import "github.com/dixxe/personal-website/iternal/pkg/repositories" +import "fmt" +import "strings" +import "math" + +var markedHandle = templ.NewOnceHandle() + +templ runMarked(markdown string) { + + @templ.JSONScript("md", markdown) + + @markedHandle.Once() { + + + + } + +} + +templ blogHeader() { +

Блог дихуса

+

Вернуться Домой

+
+} + +templ ShowPost(post repositories.Post){ + @BasicPageBlock() + @blogHeader() + +
+

+ {post.Header} +

+
+
+ + @runMarked(post.Content) +} + + +templ ShowBlogPage(posts []repositories.Post) { + @BasicPageBlock() + @blogHeader() + + for i := len(posts) - 1; i>=0; i-- { +
+

+ + {posts[i].Header} + +

+ + {{ + // It's probably awfull practice, but here I calculate + // post size and make it smaller.. In frontend! Magic! + postWords := strings.Fields(posts[i].Content) + postCap := int(math.Sqrt(float64(len(postWords))*5)) + shortStr := postWords[:postCap] + + }} + +

{strings.Join(shortStr, " ")}...

+ + Читать полностью... + +
+ + } + + @UsefulLinks() +} diff --git a/web/templates/blog_templ.go b/web/templates/blog_templ.go new file mode 100644 index 0000000..fb8c1a7 --- /dev/null +++ b/web/templates/blog_templ.go @@ -0,0 +1,418 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.3.819 +/* + + +Template that grew in complexity recently. It's was first template that supports JS. + + + + + +1. Template contains local templ blocks their names starting with lowercase to make it DRY + + +2. JS script is seperated and should be called *after* page construction. + + +3. Templ ShowBlogPage contains some math to dynamicly reduce post size + + + + + +TODO - make it more DRY and modular in general + + +*/ + +package templates + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import "github.com/dixxe/personal-website/web/static/styling" +import "github.com/dixxe/personal-website/iternal/pkg/repositories" +import "fmt" +import "strings" +import "math" + +var markedHandle = templ.NewOnceHandle() + +func runMarked(markdown string) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templ.JSONScript("md", markdown).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) + templ_7745c5c3_Err = markedHandle.Once().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +func blogHeader() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var3 := templ.GetChildren(ctx) + if templ_7745c5c3_Var3 == nil { + templ_7745c5c3_Var3 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + var templ_7745c5c3_Var4 = []any{styling.FileHeader()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var4...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "

Блог дихуса

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var6 = []any{styling.PostScriptum()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var6...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "

Вернуться Домой


") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +func ShowPost(post repositories.Post) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var8 := templ.GetChildren(ctx) + if templ_7745c5c3_Var8 == nil { + templ_7745c5c3_Var8 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = BasicPageBlock().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = blogHeader().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var9 = []any{styling.BlogContainer(), styling.CenterContainer(), styling.Textcontainer()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var9...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var11 = []any{styling.Header(), styling.HighlightText()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var11...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var13 string + templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(post.Header) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/blog.templ`, Line: 50, Col: 18} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = runMarked(post.Content).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +func ShowBlogPage(posts []repositories.Post) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var14 := templ.GetChildren(ctx) + if templ_7745c5c3_Var14 == nil { + templ_7745c5c3_Var14 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = BasicPageBlock().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = blogHeader().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + for i := len(posts) - 1; i >= 0; i-- { + var templ_7745c5c3_Var15 = []any{styling.Textcontainer(), styling.BlogContainer()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var15...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var17 = []any{styling.Header()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var17...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var20 string + templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(posts[i].Header) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/blog.templ`, Line: 67, Col: 36} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + + // It's probably awfull practice, but here I calculate + // post size and make it smaller.. In frontend! Magic! + postWords := strings.Fields(posts[i].Content) + postCap := int(math.Sqrt(float64(len(postWords)) * 5)) + shortStr := postWords[:postCap] + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var21 string + templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(strings.Join(shortStr, " ")) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/blog.templ`, Line: 80, Col: 62} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "...

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var22 = []any{styling.HighlightText()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var22...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "Читать полностью...
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = UsefulLinks().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/web/templates/constructionBlocks.templ b/web/templates/constructionBlocks.templ new file mode 100644 index 0000000..94da4dc --- /dev/null +++ b/web/templates/constructionBlocks.templ @@ -0,0 +1,60 @@ +/* +Small block that I write to reuse in other templates to remove boilerplate. +Syntax to reuse templates: @BasicPageBlock() +*/ +package templates + +import "github.com/dixxe/personal-website/web/static/styling" + +// Use this block in every page. It contains dependecies and reduces boilerplate +templ BasicPageBlock() { + + + + + + + + + + + dixxe's website + + + + + +} + +// Small styled line of links to my friends and contact information. +templ UsefulLinks() { +
+ +

+ You can find source code of this website + here. +

+ +

Telegram - "@d1xxe"

+

Matrix - "@d1xxe:matrix.org"

+ +

My friends: + Lunf + Madam_ovi +

+ +
+ +} diff --git a/web/templates/constructionBlocks_templ.go b/web/templates/constructionBlocks_templ.go new file mode 100644 index 0000000..8bec65b --- /dev/null +++ b/web/templates/constructionBlocks_templ.go @@ -0,0 +1,102 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.3.819 +/* + + +Small block that I write to reuse in other templates to remove boilerplate. + + +Syntax to reuse templates: @BasicPageBlock() + + +*/ + +package templates + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import "github.com/dixxe/personal-website/web/static/styling" + +// Use this block in every page. It contains dependecies and reduces boilerplate +func BasicPageBlock() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "dixxe's website") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +// Small styled line of links to my friends and contact information. +func UsefulLinks() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var2 := templ.GetChildren(ctx) + if templ_7745c5c3_Var2 == nil { + templ_7745c5c3_Var2 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + var templ_7745c5c3_Var3 = []any{styling.BlockContainer(), styling.PostScriptum()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var3...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "

You can find source code of this website here.

Telegram - \"@d1xxe\"

Matrix - \"@d1xxe:matrix.org\"

My friends: Lunf Madam_ovi

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/web/templates/errorPage.templ b/web/templates/errorPage.templ new file mode 100644 index 0000000..7c0ae7f --- /dev/null +++ b/web/templates/errorPage.templ @@ -0,0 +1,14 @@ +package templates + +import "github.com/dixxe/personal-website/web/static/styling" +import "strconv" + +templ ErrorPage(code int, additional_info string) { + @BasicPageBlock() + +
+

Error { strconv.Itoa(code) }

+

Something went wrong. {additional_info}

+

Go home

+
+} \ No newline at end of file diff --git a/web/templates/errorPage_templ.go b/web/templates/errorPage_templ.go new file mode 100644 index 0000000..c556196 --- /dev/null +++ b/web/templates/errorPage_templ.go @@ -0,0 +1,135 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.3.819 +package templates + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import "github.com/dixxe/personal-website/web/static/styling" +import "strconv" + +func ErrorPage(code int, additional_info string) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = BasicPageBlock().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var2 = []any{styling.Textcontainer(), styling.CenterPageContainer()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var4 = []any{styling.Header()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var4...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "

Error ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var6 string + templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(code)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/errorPage.templ`, Line: 10, Col: 65} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "

Something went wrong. ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var7 string + templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(additional_info) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/templates/errorPage.templ`, Line: 11, Col: 50} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var8 = []any{styling.PostScriptum()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var8...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "

Go home

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/web/templates/index.templ b/web/templates/index.templ new file mode 100644 index 0000000..1c4ea17 --- /dev/null +++ b/web/templates/index.templ @@ -0,0 +1,71 @@ +/* +Main page of website that shows on root. +Contains useful information and links to other pages +*/ +package templates + +import "github.com/dixxe/personal-website/web/static/styling" + +templ IndexPage() { + @BasicPageBlock() + +
+

+ Greetings! I'm +
+ d1xxe +
+ and this is my +
+ personal + website +

+
+ +
+ +

+ About me +

+ +

+ I'm + 16 + years old junior software engineer. +
+ Born & live in Russia 🇷🇺 +
+ I speak native Russian and English(B2) +

+ +

+ I have skills in + Go, Java, Rust, Nix + and + Linux +

+ +

+ I have a passion to learn new, complex things. +
+ Currently I'm learning SQL, DBMS +

+
+ +
+ +
+

I don't have any finished project yet :P

+
+ +
+ +
+

More stuff from me

+ + Personal blog + +
+ + @UsefulLinks() +} \ No newline at end of file diff --git a/web/templates/index_templ.go b/web/templates/index_templ.go new file mode 100644 index 0000000..e861a73 --- /dev/null +++ b/web/templates/index_templ.go @@ -0,0 +1,255 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.3.819 +/* + + +Main page of website that shows on root. + + +Contains useful information and links to other pages + + +*/ + +package templates + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import "github.com/dixxe/personal-website/web/static/styling" + +func IndexPage() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = BasicPageBlock().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var2 = []any{styling.HelloContainer()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "

Greetings! I'm
d1xxe
and this is my
personal website

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var4 = []any{styling.Textcontainer(), styling.CenterContainer()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var4...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var6 = []any{styling.Header()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var6...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "

About me

I'm ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var8 = []any{styling.HighlightText()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var8...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "16 years old junior software engineer.
Born & live in Russia 🇷🇺
I speak native Russian and English(B2)

I have skills in ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var10 = []any{styling.HighlightText()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var10...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "Go, Java, Rust, Nix and ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var12 = []any{styling.HighlightText()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var12...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "Linux

I have a passion to learn new, complex things.
Currently I'm learning SQL, DBMS


") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var14 = []any{styling.CenterContainer(), styling.Textcontainer()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var14...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "

I don't have any finished project yet :P


") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var16 = []any{styling.CenterContainer(), styling.Textcontainer()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var16...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var18 = []any{styling.Header()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var18...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "

More stuff from me

Personal blog
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = UsefulLinks().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/web/templates/wip.templ b/web/templates/wip.templ new file mode 100644 index 0000000..9212478 --- /dev/null +++ b/web/templates/wip.templ @@ -0,0 +1,16 @@ +/* +Placeholder webpage for not-implemented functional. +Links back to root (index). +*/ +package templates + +import "github.com/dixxe/personal-website/web/static/styling" + +templ WIPPage() { + @BasicPageBlock() + +
+

Page is under construction

+

Вернуться Домой

+
+} \ No newline at end of file diff --git a/web/templates/wip_templ.go b/web/templates/wip_templ.go new file mode 100644 index 0000000..de53239 --- /dev/null +++ b/web/templates/wip_templ.go @@ -0,0 +1,119 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.3.819 +/* + + +Placeholder webpage for not-implemented functional. + + +Links back to root (index). + + +*/ + +package templates + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import "github.com/dixxe/personal-website/web/static/styling" + +func WIPPage() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = BasicPageBlock().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var2 = []any{styling.Textcontainer(), styling.CenterPageContainer()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var4 = []any{styling.Header()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var4...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "

Page is under construction

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var6 = []any{styling.PostScriptum()} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var6...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "

Вернуться Домой

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate