Browse Source

Initial commit

master
commit
4cde747404
  1. 1
      .gitignore
  2. 14
      LICENSE
  3. 13
      Makefile
  4. 23
      README.md
  5. 40
      pages/base.html
  6. 11
      pages/index.html
  7. 11
      pages/otherpage.html
  8. 1
      scripts/someScript.js
  9. 87
      src/conf/conf.go
  10. 79
      src/db/db.go
  11. 72
      src/db/testEntity.go
  12. 43
      src/encryption/encryption.go
  13. 25
      src/go.mod
  14. 57
      src/go.sum
  15. 54
      src/logger/logger.go
  16. 79
      src/main.go
  17. 96
      src/server/api.go
  18. 39
      src/server/page.go
  19. 179
      src/server/server.go
  20. 4085
      static/bootstrap/css/bootstrap-grid.css
  21. 1
      static/bootstrap/css/bootstrap-grid.css.map
  22. 6
      static/bootstrap/css/bootstrap-grid.min.css
  23. 1
      static/bootstrap/css/bootstrap-grid.min.css.map
  24. 4084
      static/bootstrap/css/bootstrap-grid.rtl.css
  25. 1
      static/bootstrap/css/bootstrap-grid.rtl.css.map
  26. 6
      static/bootstrap/css/bootstrap-grid.rtl.min.css
  27. 1
      static/bootstrap/css/bootstrap-grid.rtl.min.css.map
  28. 593
      static/bootstrap/css/bootstrap-reboot.css
  29. 1
      static/bootstrap/css/bootstrap-reboot.css.map
  30. 6
      static/bootstrap/css/bootstrap-reboot.min.css
  31. 1
      static/bootstrap/css/bootstrap-reboot.min.css.map
  32. 590
      static/bootstrap/css/bootstrap-reboot.rtl.css
  33. 1
      static/bootstrap/css/bootstrap-reboot.rtl.css.map
  34. 6
      static/bootstrap/css/bootstrap-reboot.rtl.min.css
  35. 1
      static/bootstrap/css/bootstrap-reboot.rtl.min.css.map
  36. 5399
      static/bootstrap/css/bootstrap-utilities.css
  37. 1
      static/bootstrap/css/bootstrap-utilities.css.map
  38. 6
      static/bootstrap/css/bootstrap-utilities.min.css
  39. 1
      static/bootstrap/css/bootstrap-utilities.min.css.map
  40. 5390
      static/bootstrap/css/bootstrap-utilities.rtl.css
  41. 1
      static/bootstrap/css/bootstrap-utilities.rtl.css.map
  42. 6
      static/bootstrap/css/bootstrap-utilities.rtl.min.css
  43. 1
      static/bootstrap/css/bootstrap-utilities.rtl.min.css.map
  44. 12071
      static/bootstrap/css/bootstrap.css
  45. 1
      static/bootstrap/css/bootstrap.css.map
  46. 6
      static/bootstrap/css/bootstrap.min.css
  47. 1
      static/bootstrap/css/bootstrap.min.css.map
  48. 12035
      static/bootstrap/css/bootstrap.rtl.css
  49. 1
      static/bootstrap/css/bootstrap.rtl.css.map
  50. 6
      static/bootstrap/css/bootstrap.rtl.min.css
  51. 1
      static/bootstrap/css/bootstrap.rtl.min.css.map
  52. 6306
      static/bootstrap/js/bootstrap.bundle.js
  53. 1
      static/bootstrap/js/bootstrap.bundle.js.map
  54. 7
      static/bootstrap/js/bootstrap.bundle.min.js
  55. 1
      static/bootstrap/js/bootstrap.bundle.min.js.map
  56. 4439
      static/bootstrap/js/bootstrap.esm.js
  57. 1
      static/bootstrap/js/bootstrap.esm.js.map
  58. 7
      static/bootstrap/js/bootstrap.esm.min.js
  59. 1
      static/bootstrap/js/bootstrap.esm.min.js.map
  60. 4486
      static/bootstrap/js/bootstrap.js
  61. 1
      static/bootstrap/js/bootstrap.js.map
  62. 7
      static/bootstrap/js/bootstrap.min.js
  63. 1
      static/bootstrap/js/bootstrap.min.js.map
  64. BIN
      static/images/favicon.ico

1
.gitignore vendored

@ -0,0 +1 @@
bin/

14
LICENSE

@ -0,0 +1,14 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

13
Makefile

@ -0,0 +1,13 @@
server_binary:=gohst
all:
mkdir -p bin && \
cd src && CGO_ENABLED=0 go build && mv $(server_binary) ../bin && \
cd .. && \
cp -r pages bin && \
cp -r scripts bin && \
cp -r static bin
clean:
rm -rf bin

23
README.md

@ -0,0 +1,23 @@
# GoHST
## Go HTTP Server Template
This is the usual template I follow when all I need is to have a quick "just works" web server with the ability to extend it further when needed.
Your code goes to the `src`; your HTML pages go the `pages`; your JS files go to the `scripts`; your static content goes to `static`.
Code directories to consider:
- `conf` is a configuration file for the server to use. You can specify a port to work on, base directory where to look pages in and paths to the SSL keys. In larger projects it is bound to contain much more.
- `db` is where the database logic resides, base structures and helper functions to extract/add them from/to the database.
- `encryption` is just a bunch of helper functions to deal with BASE64 or SHA
- `logger` is for as-basic-as-it-gets logger usage
- `server/api` is where I usually write API|Page-specific handlers
- `server/page` contains a helper function to merge `pages/base.html` and any other page together
- `server/server` is a glue between everything there is. The actual server stuff is happening there
With some work it's possible to strip everything unneeded and to just have a static web server.
There is a `modernc.org/sqlite` dependency which is there only for "compile, run and see" ability. Otherwise, even if you don't use a database it won't launch. It is up to you to replace it with another driver or if you don't need it, strip `src/db` completely.
# License
Do What The Fuck You Want To Public License for all files except for, obviously, bootstrap, as well as for src/logger which is under MIT

40
pages/base.html

@ -0,0 +1,40 @@
{{ define "base" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>dela</title>
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
<link rel="shortcut icon" href="/static/images/favicon.ico" type="image/x-icon">
</head>
<body class="d-flex flex-column h-100">
<header class="d-flex flex-wrap align-items-center justify-content-center justify-content-md-between py-3 mb-4 border-bottom">
<div class="col-md-3 mb-2 mb-md-0">
<a href="/" class="d-inline-flex link-body-emphasis text-decoration-none">
<svg class="bi" width="40" height="32" role="img" aria-label="Bootstrap"><use xlink:href="#bootstrap"></use></svg>
</a>
</div>
<div class="col-md-3 text-end" id="bar-auth">
<a href="/otherpage" class="btn btn-outline-primary me-2">Other page</a>
</div>
</header>
<div style="margin: auto;
margin-top: 5ch;
margin-bottom: 10ch;
max-width: 120ch;">
{{ template "content" . }}
</div>
</body>
</html>
<script src="/scripts/someScript.js"></script>
{{ end }}

11
pages/index.html

@ -0,0 +1,11 @@
{{ template "base" . }}
{{ define "content" }}
<h3>Index page</h3>
<p>
This is an index page
</p>
{{ end }}

11
pages/otherpage.html

@ -0,0 +1,11 @@
{{ template "base" . }}
{{ define "content" }}
<h3>Other page</h3>
<p>
This is a some other page
</p>
{{ end }}

1
scripts/someScript.js

@ -0,0 +1 @@
alert("This is coming from \"someScript.js\" in scripts folder and is loaded via base.html template");

87
src/conf/conf.go

@ -0,0 +1,87 @@
/*
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
*/
// Kasyanov N.A. (Unbewohnte), 2023
package conf
import (
"encoding/json"
"io"
"os"
)
type Conf struct {
Port uint16 `json:"port"`
CertFilePath string `json:"cert_file_path"`
KeyFilePath string `json:"key_file_path"`
BaseContentDir string `json:"base_content_dir"`
ProdDBName string `json:"production_db"`
}
// Creates a default server configuration
func Default() Conf {
return Conf{
Port: 8080,
CertFilePath: "",
KeyFilePath: "",
BaseContentDir: ".",
ProdDBName: "database.db",
}
}
// Tries to retrieve configuration from given json file
func FromFile(path string) (Conf, error) {
configFile, err := os.Open(path)
if err != nil {
return Default(), err
}
defer configFile.Close()
confBytes, err := io.ReadAll(configFile)
if err != nil {
return Default(), err
}
var config Conf
err = json.Unmarshal(confBytes, &config)
if err != nil {
return Default(), err
}
return config, nil
}
// Create empty configuration file
func Create(path string, conf Conf) (Conf, error) {
configFile, err := os.Create(path)
if err != nil {
return Default(), err
}
defer configFile.Close()
configJsonBytes, err := json.MarshalIndent(conf, "", " ")
if err != nil {
return conf, err
}
_, err = configFile.Write(configJsonBytes)
if err != nil {
return conf, nil
}
return conf, nil
}

79
src/db/db.go

@ -0,0 +1,79 @@
/*
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
*/
// Kasyanov N.A. (Unbewohnte), 2023
package db
import (
"database/sql"
"os"
_ "modernc.org/sqlite" // For example this one
)
// Database wrapper
type DB struct {
*sql.DB
}
func setUpTables(db *DB) error {
// Table for test entities to be stored in
_, err := db.Exec(`CREATE TABLE IF NOT EXISTS test(id INTEGER PRIMARY KEY, data TEXT NOT NULL)`)
if err != nil {
return err
}
return nil
}
// Open database
func FromFile(path string) (*DB, error) {
driver, err := sql.Open("sqlite", path)
if err != nil {
return nil, err
}
dbase := &DB{driver}
err = setUpTables(dbase)
if err != nil {
return nil, err
}
return dbase, nil
}
// Create database file
func Create(path string) (*DB, error) {
dbFile, err := os.Create(path)
if err != nil {
return nil, err
}
dbFile.Close()
driver, err := sql.Open("sqlite", path)
if err != nil {
return nil, err
}
dbase := &DB{driver}
err = setUpTables(dbase)
if err != nil {
return nil, err
}
return dbase, nil
}

72
src/db/testEntity.go

@ -0,0 +1,72 @@
/*
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
*/
// Kasyanov N.A. (Unbewohnte), 2023
package db
import "database/sql"
type TestEntity struct {
Text string `json:"text"`
}
func scanTestEntity(rows *sql.Rows) (*TestEntity, error) {
rows.Next()
var entity TestEntity
err := rows.Scan(&entity.Text)
if err != nil {
return nil, err
}
return &entity, nil
}
// Searches for TestEntity with text and returns it
func (db *DB) GetTestEntity(text string) (*TestEntity, error) {
rows, err := db.Query("SELECT * FROM test WHERE text=?", text)
if err != nil {
return nil, err
}
defer rows.Close()
TestEntity, err := scanTestEntity(rows)
if err != nil {
return nil, err
}
return TestEntity, nil
}
// Creates a new TestEntity in the database
func (db *DB) CreateTestEntity(newTestEntity TestEntity) error {
_, err := db.Exec(
"INSERT INTO test(text) VALUES(?)",
newTestEntity.Text,
)
return err
}
// Deletes TestEntity with given text
func (db *DB) DeleteTestEntity(text string) error {
_, err := db.Exec(
"DELETE FROM test WHERE text=?",
text,
)
return err
}

43
src/encryption/encryption.go

@ -0,0 +1,43 @@
/*
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
*/
// Kasyanov N.A. (Unbewohnte), 2023
package encryption
import (
"crypto/sha256"
"encoding/base64"
"fmt"
)
// Encodes given string via Base64
func EncodeStringBase64(str string) string {
return base64.StdEncoding.EncodeToString([]byte(str))
}
// Decodes given string via Base64
func DecodeStringBase64(encodedStr string) string {
decodedBytes, _ := base64.StdEncoding.DecodeString(encodedStr)
return string(decodedBytes)
}
// Returns HEX string of SHA256'd data
func SHA256Hex(data []byte) string {
hash := sha256.New()
hash.Write(data)
return fmt.Sprintf("%x", hash.Sum(nil))
}

25
src/go.mod

@ -0,0 +1,25 @@
module Unbewohnte/gohst
go 1.18
require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/mod v0.3.0 // indirect
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect
modernc.org/ccgo/v3 v3.16.13 // indirect
modernc.org/libc v1.24.1 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.6.0 // indirect
modernc.org/opt v0.1.3 // indirect
modernc.org/sqlite v1.25.0 // indirect
modernc.org/strutil v1.1.3 // indirect
modernc.org/token v1.0.1 // indirect
)

57
src/go.sum

@ -0,0 +1,57 @@
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/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 h1:M8tBwCtWD/cZV9DZpFYRUgaymAYAr+aIUTWzDaM3uPs=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM=
modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o=
modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA=
modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU=
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=

54
src/logger/logger.go

@ -0,0 +1,54 @@
/*
The MIT License (MIT)
Copyright © 2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
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.
*/
package logger
import (
"io"
"log"
"os"
)
// 3 basic loggers in global space
var (
// neutral information logger
infoLog *log.Logger
// warning-level information logger
warningLog *log.Logger
// errors information logger
errorLog *log.Logger
)
func init() {
infoLog = log.New(os.Stdout, "[INFO] ", log.Ldate|log.Ltime)
warningLog = log.New(os.Stdout, "[WARNING] ", log.Ldate|log.Ltime)
errorLog = log.New(os.Stdout, "[ERROR] ", log.Ldate|log.Ltime)
}
// Set up loggers to write to the given writer
func SetOutput(writer io.Writer) {
if writer == nil {
writer = io.Discard
}
infoLog.SetOutput(writer)
warningLog.SetOutput(writer)
errorLog.SetOutput(writer)
}
func Info(format string, a ...interface{}) {
infoLog.Printf(format, a...)
}
func Warning(format string, a ...interface{}) {
warningLog.Printf(format, a...)
}
func Error(format string, a ...interface{}) {
errorLog.Printf(format, a...)
}

79
src/main.go

@ -0,0 +1,79 @@
/*
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
*/
package main
import (
"Unbewohnte/gohst/conf"
"Unbewohnte/gohst/logger"
"Unbewohnte/gohst/server"
"os"
"path/filepath"
)
const ConfName string = "conf.json"
var (
WDir string
Conf conf.Conf
)
func init() {
// Initialize logging
logger.SetOutput(os.Stdout)
// Work out the working directory
exePath, err := os.Executable()
if err != nil {
logger.Error("[Init] Failed to retrieve executable's path: %s", err)
os.Exit(1)
}
WDir = filepath.Dir(exePath)
logger.Info("[Init] Working in \"%s\"", WDir)
// Open configuration, create if does not exist
Conf, err = conf.FromFile(filepath.Join(WDir, ConfName))
if err != nil {
_, err = conf.Create(filepath.Join(WDir, ConfName), conf.Default())
if err != nil {
logger.Error("[Init] Failed to create a new configuration file: %s", err)
os.Exit(1)
}
logger.Info("[Init] Created a new configuration file")
os.Exit(0)
}
logger.Info("[Init] Opened existing configuration file")
if Conf.BaseContentDir == "." {
Conf.BaseContentDir = WDir
}
logger.Info("[Init] Successful initializaion!")
}
func main() {
server, err := server.New(Conf)
if err != nil {
logger.Error("[Main] Failed to initialize a new server with conf (%+v): %s", Conf, err)
return
}
logger.Info("[Main] Successfully initialized a new server instance with conf (%+v)", Conf)
err = server.Start()
if err != nil {
logger.Error("[Main] Fatal server failure: %s. Exiting...", err)
return
}
}

96
src/server/api.go

@ -0,0 +1,96 @@
/*
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
*/
// Kasyanov N.A. (Unbewohnte), 2023
package server
import (
"Unbewohnte/gohst/db"
"Unbewohnte/gohst/logger"
"encoding/json"
"io"
"net/http"
)
func (s *Server) entityEndpoint(w http.ResponseWriter, req *http.Request) {
switch req.Method {
case http.MethodDelete:
// Delete an already existing entity
defer req.Body.Close()
// Read body
body, err := io.ReadAll(req.Body)
if err != nil {
logger.Warning("[Server] Failed to read request body to delete an entity: %s", err)
http.Error(w, "Failed to read body", http.StatusInternalServerError)
return
}
// Unmarshal JSON
var entity db.TestEntity
err = json.Unmarshal(body, &entity)
if err != nil {
logger.Warning("[Server] Received invalid entity JSON for deletion: %s", err)
http.Error(w, "Invalid entity JSON", http.StatusBadRequest)
return
}
err = s.db.DeleteTestEntity(entity.Text)
if err != nil {
logger.Error("[Server] Failed to delete %s: %s", entity.Text, err)
http.Error(w, "Failed to delete entity or TODO contents", http.StatusInternalServerError)
return
}
// Success!
w.WriteHeader(http.StatusOK)
case http.MethodPost:
// Create a new entity
defer req.Body.Close()
// Read body
body, err := io.ReadAll(req.Body)
if err != nil {
logger.Warning("[Server] Failed to read request body to create a new entity: %s", err)
http.Error(w, "Failed to read body", http.StatusInternalServerError)
return
}
// Unmarshal JSON
var newEntity db.TestEntity
err = json.Unmarshal(body, &newEntity)
if err != nil {
logger.Warning("[Server] Received invalid entity JSON for creation: %s", err)
http.Error(w, "Invalid entity JSON", http.StatusBadRequest)
return
}
// Add entity to the database
err = s.db.CreateTestEntity(newEntity)
if err != nil {
http.Error(w, "Failed to create a new entity", http.StatusInternalServerError)
return
}
// Success!
w.WriteHeader(http.StatusOK)
logger.Info("[Server] Created a new entity \"%s\"", newEntity.Text)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}

39
src/server/page.go

@ -0,0 +1,39 @@
/*
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
*/
// Kasyanov N.A. (Unbewohnte), 2023
package server
import (
"Unbewohnte/gohst/logger"
"html/template"
"path/filepath"
)
// Constructs a pageName template via inserting basePageName in pagesDir
func getPage(pagesDir string, basePageName string, pageName string) (*template.Template, error) {
page, err := template.ParseFiles(
filepath.Join(pagesDir, basePageName),
filepath.Join(pagesDir, pageName),
)
if err != nil {
logger.Error("Failed to parse page files (pagename is \"%s\"): %s", pageName, err)
return nil, err
}
return page, nil
}

179
src/server/server.go

@ -0,0 +1,179 @@
/*
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
*/
// Kasyanov N.A. (Unbewohnte), 2023
package server
import (
"Unbewohnte/gohst/conf"
"Unbewohnte/gohst/db"
"Unbewohnte/gohst/logger"
"context"
"fmt"
"net/http"
"os"
"path/filepath"
"time"
)
const (
PagesDirName string = "pages"
StaticDirName string = "static"
ScriptsDirName string = "scripts"
)
type Server struct {
config conf.Conf
db *db.DB
http http.Server
}
// Creates a new server instance with provided config
func New(config conf.Conf) (*Server, error) {
var server Server = Server{}
server.config = config
// Check if required directories are present
_, err := os.Stat(filepath.Join(config.BaseContentDir, PagesDirName))
if err != nil {
logger.Error("[Server] A directory with HTML pages is not available: %s", err)
return nil, err
}
_, err = os.Stat(filepath.Join(config.BaseContentDir, ScriptsDirName))
if err != nil {
logger.Error("[Server] A directory with scripts is not available: %s", err)
return nil, err
}
_, err = os.Stat(filepath.Join(config.BaseContentDir, StaticDirName))
if err != nil {
logger.Error("[Server] A directory with static content is not available: %s", err)
return nil, err
}
// Get database working
serverDB, err := db.FromFile(filepath.Join(config.BaseContentDir, config.ProdDBName))
if err != nil {
// Create one then
serverDB, err = db.Create(filepath.Join(config.BaseContentDir, config.ProdDBName))
if err != nil {
logger.Error("Failed to create a new database: %s", err)
return nil, err
}
}
server.db = serverDB
logger.Info("Opened a database successfully")
// Start constructing an http server configuration
server.http = http.Server{
Addr: fmt.Sprintf(":%d", server.config.Port),
}
// Configure paths' callbacks
mux := http.NewServeMux()
mux.Handle(
"/static/",
http.StripPrefix("/static/", http.FileServer(
http.Dir(filepath.Join(server.config.BaseContentDir, StaticDirName))),
),
)
mux.Handle(
"/scripts/",
http.StripPrefix("/scripts/", http.FileServer(
http.Dir(filepath.Join(server.config.BaseContentDir, ScriptsDirName))),
),
)
// Handle page requests
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
switch req.URL.Path {
case "/":
requestedPage, err := getPage(
filepath.Join(server.config.BaseContentDir, PagesDirName), "base.html", "index.html",
)
if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)
}
requestedPage.ExecuteTemplate(w, "index.html", nil)
default:
requestedPage, err := getPage(
filepath.Join(server.config.BaseContentDir, PagesDirName),
"base.html",
req.URL.Path[1:]+".html",
)
if err == nil {
requestedPage.ExecuteTemplate(w, req.URL.Path[1:]+".html", nil)
} else {
// Redirect to the index
index, err := getPage(
filepath.Join(server.config.BaseContentDir, PagesDirName),
"base.html",
req.URL.Path[1:]+".html",
)
if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)
}
index.ExecuteTemplate(w, "index.html", nil)
}
}
})
// Paths which need to be handled differently come here
mux.HandleFunc("/api/test_entity", server.entityEndpoint)
server.http.Handler = mux
logger.Info("[Server] Created an HTTP server instance")
return &server, nil
}
// Launches server instance
func (s *Server) Start() error {
if s.config.CertFilePath != "" && s.config.KeyFilePath != "" {
logger.Info("[Server] Using TLS")
logger.Info("[Server] HTTP server is going live on port %d!", s.config.Port)
err := s.http.ListenAndServeTLS(s.config.CertFilePath, s.config.KeyFilePath)
if err != nil && err != http.ErrServerClosed {
logger.Error("[Server] Fatal server error: %s", err)
return err
}
} else {
logger.Info("[Server] Not using TLS")
logger.Info("[Server] HTTP server is going live on port %d!", s.config.Port)
err := s.http.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
logger.Error("[Server] Fatal server error: %s", err)
return err
}
}
return nil
}
// Stops the server immediately
func (s *Server) Stop() {
ctx, cfunc := context.WithDeadline(context.Background(), time.Now().Add(time.Second*10))
s.http.Shutdown(ctx)
cfunc()
}

4085
static/bootstrap/css/bootstrap-grid.css vendored

File diff suppressed because it is too large Load Diff

1
static/bootstrap/css/bootstrap-grid.css.map

File diff suppressed because one or more lines are too long

6
static/bootstrap/css/bootstrap-grid.min.css vendored

File diff suppressed because one or more lines are too long

1
static/bootstrap/css/bootstrap-grid.min.css.map

File diff suppressed because one or more lines are too long

4084
static/bootstrap/css/bootstrap-grid.rtl.css

File diff suppressed because it is too large Load Diff

1
static/bootstrap/css/bootstrap-grid.rtl.css.map

File diff suppressed because one or more lines are too long

6
static/bootstrap/css/bootstrap-grid.rtl.min.css vendored

File diff suppressed because one or more lines are too long

1
static/bootstrap/css/bootstrap-grid.rtl.min.css.map

File diff suppressed because one or more lines are too long

593
static/bootstrap/css/bootstrap-reboot.css vendored

@ -0,0 +1,593 @@
/*!
* Bootstrap Reboot v5.3.0 (https://getbootstrap.com/)
* Copyright 2011-2023 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
:root,
[data-bs-theme=light] {
--bs-blue: #0d6efd;
--bs-indigo: #6610f2;
--bs-purple: #6f42c1;
--bs-pink: #d63384;
--bs-red: #dc3545;
--bs-orange: #fd7e14;
--bs-yellow: #ffc107;
--bs-green: #198754;
--bs-teal: #20c997;
--bs-cyan: #0dcaf0;
--bs-black: #000;
--bs-white: #fff;
--bs-gray: #6c757d;
--bs-gray-dark: #343a40;
--bs-gray-100: #f8f9fa;
--bs-gray-200: #e9ecef;
--bs-gray-300: #dee2e6;
--bs-gray-400: #ced4da;
--bs-gray-500: #adb5bd;
--bs-gray-600: #6c757d;
--bs-gray-700: #495057;
--bs-gray-800: #343a40;
--bs-gray-900: #212529;
--bs-primary: #0d6efd;
--bs-secondary: #6c757d;
--bs-success: #198754;
--bs-info: #0dcaf0;
--bs-warning: #ffc107;
--bs-danger: #dc3545;
--bs-light: #f8f9fa;
--bs-dark: #212529;
--bs-primary-rgb: 13, 110, 253;
--bs-secondary-rgb: 108, 117, 125;
--bs-success-rgb: 25, 135, 84;
--bs-info-rgb: 13, 202, 240;
--bs-warning-rgb: 255, 193, 7;
--bs-danger-rgb: 220, 53, 69;
--bs-light-rgb: 248, 249, 250;
--bs-dark-rgb: 33, 37, 41;
--bs-primary-text-emphasis: #052c65;
--bs-secondary-text-emphasis: #2b2f32;
--bs-success-text-emphasis: #0a3622;
--bs-info-text-emphasis: #055160;
--bs-warning-text-emphasis: #664d03;
--bs-danger-text-emphasis: #58151c;
--bs-light-text-emphasis: #495057;
--bs-dark-text-emphasis: #495057;
--bs-primary-bg-subtle: #cfe2ff;
--bs-secondary-bg-subtle: #e2e3e5;
--bs-success-bg-subtle: #d1e7dd;
--bs-info-bg-subtle: #cff4fc;
--bs-warning-bg-subtle: #fff3cd;
--bs-danger-bg-subtle: #f8d7da;
--bs-light-bg-subtle: #fcfcfd;
--bs-dark-bg-subtle: #ced4da;
--bs-primary-border-subtle: #9ec5fe;
--bs-secondary-border-subtle: #c4c8cb;
--bs-success-border-subtle: #a3cfbb;
--bs-info-border-subtle: #9eeaf9;
--bs-warning-border-subtle: #ffe69c;
--bs-danger-border-subtle: #f1aeb5;
--bs-light-border-subtle: #e9ecef;
--bs-dark-border-subtle: #adb5bd;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 0, 0, 0;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--bs-body-font-family: var(--bs-font-sans-serif);
--bs-body-font-size: 1rem;
--bs-body-font-weight: 400;
--bs-body-line-height: 1.5;
--bs-body-color: #212529;
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg: #fff;
--bs-body-bg-rgb: 255, 255, 255;
--bs-emphasis-color: #000;
--bs-emphasis-color-rgb: 0, 0, 0;
--bs-secondary-color: rgba(33, 37, 41, 0.75);
--bs-secondary-color-rgb: 33, 37, 41;
--bs-secondary-bg: #e9ecef;
--bs-secondary-bg-rgb: 233, 236, 239;
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
--bs-tertiary-color-rgb: 33, 37, 41;
--bs-tertiary-bg: #f8f9fa;
--bs-tertiary-bg-rgb: 248, 249, 250;
--bs-heading-color: inherit;
--bs-link-color: #0d6efd;
--bs-link-color-rgb: 13, 110, 253;
--bs-link-decoration: underline;
--bs-link-hover-color: #0a58ca;
--bs-link-hover-color-rgb: 10, 88, 202;
--bs-code-color: #d63384;
--bs-highlight-bg: #fff3cd;
--bs-border-width: 1px;
--bs-border-style: solid;
--bs-border-color: #dee2e6;
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
--bs-border-radius: 0.375rem;
--bs-border-radius-sm: 0.25rem;
--bs-border-radius-lg: 0.5rem;
--bs-border-radius-xl: 1rem;
--bs-border-radius-xxl: 2rem;
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
--bs-border-radius-pill: 50rem;
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
--bs-focus-ring-width: 0.25rem;
--bs-focus-ring-opacity: 0.25;
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
--bs-form-valid-color: #198754;
--bs-form-valid-border-color: #198754;
--bs-form-invalid-color: #dc3545;
--bs-form-invalid-border-color: #dc3545;
}
[data-bs-theme=dark] {
color-scheme: dark;
--bs-body-color: #adb5bd;
--bs-body-color-rgb: 173, 181, 189;
--bs-body-bg: #212529;
--bs-body-bg-rgb: 33, 37, 41;
--bs-emphasis-color: #fff;
--bs-emphasis-color-rgb: 255, 255, 255;
--bs-secondary-color: rgba(173, 181, 189, 0.75);
--bs-secondary-color-rgb: 173, 181, 189;
--bs-secondary-bg: #343a40;
--bs-secondary-bg-rgb: 52, 58, 64;
--bs-tertiary-color: rgba(173, 181, 189, 0.5);
--bs-tertiary-color-rgb: 173, 181, 189;
--bs-tertiary-bg: #2b3035;
--bs-tertiary-bg-rgb: 43, 48, 53;
--bs-primary-text-emphasis: #6ea8fe;
--bs-secondary-text-emphasis: #a7acb1;
--bs-success-text-emphasis: #75b798;
--bs-info-text-emphasis: #6edff6;
--bs-warning-text-emphasis: #ffda6a;
--bs-danger-text-emphasis: #ea868f;
--bs-light-text-emphasis: #f8f9fa;
--bs-dark-text-emphasis: #dee2e6;
--bs-primary-bg-subtle: #031633;
--bs-secondary-bg-subtle: #161719;
--bs-success-bg-subtle: #051b11;
--bs-info-bg-subtle: #032830;
--bs-warning-bg-subtle: #332701;
--bs-danger-bg-subtle: #2c0b0e;
--bs-light-bg-subtle: #343a40;
--bs-dark-bg-subtle: #1a1d20;
--bs-primary-border-subtle: #084298;
--bs-secondary-border-subtle: #41464b;
--bs-success-border-subtle: #0f5132;
--bs-info-border-subtle: #087990;
--bs-warning-border-subtle: #997404;
--bs-danger-border-subtle: #842029;
--bs-light-border-subtle: #495057;
--bs-dark-border-subtle: #343a40;
--bs-heading-color: inherit;
--bs-link-color: #6ea8fe;
--bs-link-hover-color: #8bb9fe;
--bs-link-color-rgb: 110, 168, 254;
--bs-link-hover-color-rgb: 139, 185, 254;
--bs-code-color: #e685b5;
--bs-border-color: #495057;
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
--bs-form-valid-color: #75b798;
--bs-form-valid-border-color: #75b798;
--bs-form-invalid-color: #ea868f;
--bs-form-invalid-border-color: #ea868f;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
color: var(--bs-body-color);
text-align: var(--bs-body-text-align);
background-color: var(--bs-body-bg);
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
border: 0;
border-top: var(--bs-border-width) solid;
opacity: 0.25;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: var(--bs-heading-color);
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-left: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.1875em;
background-color: var(--bs-highlight-bg);
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
text-decoration: underline;
}
a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: var(--bs-font-monospace);
font-size: 1em;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: var(--bs-code-color);
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.1875rem 0.375rem;
font-size: 0.875em;
color: var(--bs-body-bg);
background-color: var(--bs-body-color);
border-radius: 0.25rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: var(--bs-secondary-color);
text-align: left;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
select {
text-transform: none;
}
[role=button] {
cursor: pointer;
}
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
display: none !important;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
textarea {
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
float: left;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: left;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
outline-offset: -2px;
-webkit-appearance: textfield;
}
/* rtl:raw:
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
::file-selector-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

1
static/bootstrap/css/bootstrap-reboot.css.map

File diff suppressed because one or more lines are too long

6
static/bootstrap/css/bootstrap-reboot.min.css vendored

File diff suppressed because one or more lines are too long

1
static/bootstrap/css/bootstrap-reboot.min.css.map

File diff suppressed because one or more lines are too long

590
static/bootstrap/css/bootstrap-reboot.rtl.css

@ -0,0 +1,590 @@
/*!
* Bootstrap Reboot v5.3.0 (https://getbootstrap.com/)
* Copyright 2011-2023 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
:root,
[data-bs-theme=light] {
--bs-blue: #0d6efd;
--bs-indigo: #6610f2;
--bs-purple: #6f42c1;
--bs-pink: #d63384;
--bs-red: #dc3545;
--bs-orange: #fd7e14;
--bs-yellow: #ffc107;
--bs-green: #198754;
--bs-teal: #20c997;
--bs-cyan: #0dcaf0;
--bs-black: #000;
--bs-white: #fff;
--bs-gray: #6c757d;
--bs-gray-dark: #343a40;
--bs-gray-100: #f8f9fa;
--bs-gray-200: #e9ecef;
--bs-gray-300: #dee2e6;
--bs-gray-400: #ced4da;
--bs-gray-500: #adb5bd;
--bs-gray-600: #6c757d;
--bs-gray-700: #495057;
--bs-gray-800: #343a40;
--bs-gray-900: #212529;
--bs-primary: #0d6efd;
--bs-secondary: #6c757d;
--bs-success: #198754;
--bs-info: #0dcaf0;
--bs-warning: #ffc107;
--bs-danger: #dc3545;
--bs-light: #f8f9fa;
--bs-dark: #212529;
--bs-primary-rgb: 13, 110, 253;
--bs-secondary-rgb: 108, 117, 125;
--bs-success-rgb: 25, 135, 84;
--bs-info-rgb: 13, 202, 240;
--bs-warning-rgb: 255, 193, 7;
--bs-danger-rgb: 220, 53, 69;
--bs-light-rgb: 248, 249, 250;
--bs-dark-rgb: 33, 37, 41;
--bs-primary-text-emphasis: #052c65;
--bs-secondary-text-emphasis: #2b2f32;
--bs-success-text-emphasis: #0a3622;
--bs-info-text-emphasis: #055160;
--bs-warning-text-emphasis: #664d03;
--bs-danger-text-emphasis: #58151c;
--bs-light-text-emphasis: #495057;
--bs-dark-text-emphasis: #495057;
--bs-primary-bg-subtle: #cfe2ff;
--bs-secondary-bg-subtle: #e2e3e5;
--bs-success-bg-subtle: #d1e7dd;
--bs-info-bg-subtle: #cff4fc;
--bs-warning-bg-subtle: #fff3cd;
--bs-danger-bg-subtle: #f8d7da;
--bs-light-bg-subtle: #fcfcfd;
--bs-dark-bg-subtle: #ced4da;
--bs-primary-border-subtle: #9ec5fe;
--bs-secondary-border-subtle: #c4c8cb;
--bs-success-border-subtle: #a3cfbb;
--bs-info-border-subtle: #9eeaf9;
--bs-warning-border-subtle: #ffe69c;
--bs-danger-border-subtle: #f1aeb5;
--bs-light-border-subtle: #e9ecef;
--bs-dark-border-subtle: #adb5bd;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 0, 0, 0;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--bs-body-font-family: var(--bs-font-sans-serif);
--bs-body-font-size: 1rem;
--bs-body-font-weight: 400;
--bs-body-line-height: 1.5;
--bs-body-color: #212529;
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg: #fff;
--bs-body-bg-rgb: 255, 255, 255;
--bs-emphasis-color: #000;
--bs-emphasis-color-rgb: 0, 0, 0;
--bs-secondary-color: rgba(33, 37, 41, 0.75);
--bs-secondary-color-rgb: 33, 37, 41;
--bs-secondary-bg: #e9ecef;
--bs-secondary-bg-rgb: 233, 236, 239;
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
--bs-tertiary-color-rgb: 33, 37, 41;
--bs-tertiary-bg: #f8f9fa;
--bs-tertiary-bg-rgb: 248, 249, 250;
--bs-heading-color: inherit;
--bs-link-color: #0d6efd;
--bs-link-color-rgb: 13, 110, 253;
--bs-link-decoration: underline;
--bs-link-hover-color: #0a58ca;
--bs-link-hover-color-rgb: 10, 88, 202;
--bs-code-color: #d63384;
--bs-highlight-bg: #fff3cd;
--bs-border-width: 1px;
--bs-border-style: solid;
--bs-border-color: #dee2e6;
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
--bs-border-radius: 0.375rem;
--bs-border-radius-sm: 0.25rem;
--bs-border-radius-lg: 0.5rem;
--bs-border-radius-xl: 1rem;
--bs-border-radius-xxl: 2rem;
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
--bs-border-radius-pill: 50rem;
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
--bs-focus-ring-width: 0.25rem;
--bs-focus-ring-opacity: 0.25;
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
--bs-form-valid-color: #198754;
--bs-form-valid-border-color: #198754;
--bs-form-invalid-color: #dc3545;
--bs-form-invalid-border-color: #dc3545;
}
[data-bs-theme=dark] {
color-scheme: dark;
--bs-body-color: #adb5bd;
--bs-body-color-rgb: 173, 181, 189;
--bs-body-bg: #212529;
--bs-body-bg-rgb: 33, 37, 41;
--bs-emphasis-color: #fff;
--bs-emphasis-color-rgb: 255, 255, 255;
--bs-secondary-color: rgba(173, 181, 189, 0.75);
--bs-secondary-color-rgb: 173, 181, 189;
--bs-secondary-bg: #343a40;
--bs-secondary-bg-rgb: 52, 58, 64;
--bs-tertiary-color: rgba(173, 181, 189, 0.5);
--bs-tertiary-color-rgb: 173, 181, 189;
--bs-tertiary-bg: #2b3035;
--bs-tertiary-bg-rgb: 43, 48, 53;
--bs-primary-text-emphasis: #6ea8fe;
--bs-secondary-text-emphasis: #a7acb1;
--bs-success-text-emphasis: #75b798;
--bs-info-text-emphasis: #6edff6;
--bs-warning-text-emphasis: #ffda6a;
--bs-danger-text-emphasis: #ea868f;
--bs-light-text-emphasis: #f8f9fa;
--bs-dark-text-emphasis: #dee2e6;
--bs-primary-bg-subtle: #031633;
--bs-secondary-bg-subtle: #161719;
--bs-success-bg-subtle: #051b11;
--bs-info-bg-subtle: #032830;
--bs-warning-bg-subtle: #332701;
--bs-danger-bg-subtle: #2c0b0e;
--bs-light-bg-subtle: #343a40;
--bs-dark-bg-subtle: #1a1d20;
--bs-primary-border-subtle: #084298;
--bs-secondary-border-subtle: #41464b;
--bs-success-border-subtle: #0f5132;
--bs-info-border-subtle: #087990;
--bs-warning-border-subtle: #997404;
--bs-danger-border-subtle: #842029;
--bs-light-border-subtle: #495057;
--bs-dark-border-subtle: #343a40;
--bs-heading-color: inherit;
--bs-link-color: #6ea8fe;
--bs-link-hover-color: #8bb9fe;
--bs-link-color-rgb: 110, 168, 254;
--bs-link-hover-color-rgb: 139, 185, 254;
--bs-code-color: #e685b5;
--bs-border-color: #495057;
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
--bs-form-valid-color: #75b798;
--bs-form-valid-border-color: #75b798;
--bs-form-invalid-color: #ea868f;
--bs-form-invalid-border-color: #ea868f;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
color: var(--bs-body-color);
text-align: var(--bs-body-text-align);
background-color: var(--bs-body-bg);
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
border: 0;
border-top: var(--bs-border-width) solid;
opacity: 0.25;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: var(--bs-heading-color);
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-right: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-right: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.1875em;
background-color: var(--bs-highlight-bg);
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
text-decoration: underline;
}
a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: var(--bs-font-monospace);
font-size: 1em;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: var(--bs-code-color);
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.1875rem 0.375rem;
font-size: 0.875em;
color: var(--bs-body-bg);
background-color: var(--bs-body-color);
border-radius: 0.25rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: var(--bs-secondary-color);
text-align: right;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
select {
text-transform: none;
}
[role=button] {
cursor: pointer;
}
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
display: none !important;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
textarea {
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
float: right;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: right;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
outline-offset: -2px;
-webkit-appearance: textfield;
}
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
::file-selector-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */

1
static/bootstrap/css/bootstrap-reboot.rtl.css.map

File diff suppressed because one or more lines are too long

6
static/bootstrap/css/bootstrap-reboot.rtl.min.css vendored

File diff suppressed because one or more lines are too long

1
static/bootstrap/css/bootstrap-reboot.rtl.min.css.map

File diff suppressed because one or more lines are too long

5399
static/bootstrap/css/bootstrap-utilities.css vendored

File diff suppressed because it is too large Load Diff

1
static/bootstrap/css/bootstrap-utilities.css.map

File diff suppressed because one or more lines are too long

6
static/bootstrap/css/bootstrap-utilities.min.css vendored

File diff suppressed because one or more lines are too long

1
static/bootstrap/css/bootstrap-utilities.min.css.map

File diff suppressed because one or more lines are too long

5390
static/bootstrap/css/bootstrap-utilities.rtl.css

File diff suppressed because it is too large Load Diff

1
static/bootstrap/css/bootstrap-utilities.rtl.css.map

File diff suppressed because one or more lines are too long

6
static/bootstrap/css/bootstrap-utilities.rtl.min.css vendored

File diff suppressed because one or more lines are too long

1
static/bootstrap/css/bootstrap-utilities.rtl.min.css.map

File diff suppressed because one or more lines are too long

12071
static/bootstrap/css/bootstrap.css vendored

File diff suppressed because it is too large Load Diff

1
static/bootstrap/css/bootstrap.css.map

File diff suppressed because one or more lines are too long

6
static/bootstrap/css/bootstrap.min.css vendored

File diff suppressed because one or more lines are too long

1
static/bootstrap/css/bootstrap.min.css.map

File diff suppressed because one or more lines are too long

12035
static/bootstrap/css/bootstrap.rtl.css

File diff suppressed because it is too large Load Diff

1
static/bootstrap/css/bootstrap.rtl.css.map

File diff suppressed because one or more lines are too long

6
static/bootstrap/css/bootstrap.rtl.min.css vendored

File diff suppressed because one or more lines are too long

1
static/bootstrap/css/bootstrap.rtl.min.css.map

File diff suppressed because one or more lines are too long

6306
static/bootstrap/js/bootstrap.bundle.js

File diff suppressed because it is too large Load Diff

1
static/bootstrap/js/bootstrap.bundle.js.map

File diff suppressed because one or more lines are too long

7
static/bootstrap/js/bootstrap.bundle.min.js vendored

File diff suppressed because one or more lines are too long

1
static/bootstrap/js/bootstrap.bundle.min.js.map

File diff suppressed because one or more lines are too long

4439
static/bootstrap/js/bootstrap.esm.js

File diff suppressed because it is too large Load Diff

1
static/bootstrap/js/bootstrap.esm.js.map

File diff suppressed because one or more lines are too long

7
static/bootstrap/js/bootstrap.esm.min.js vendored

File diff suppressed because one or more lines are too long

1
static/bootstrap/js/bootstrap.esm.min.js.map

File diff suppressed because one or more lines are too long

4486
static/bootstrap/js/bootstrap.js vendored

File diff suppressed because it is too large Load Diff

1
static/bootstrap/js/bootstrap.js.map

File diff suppressed because one or more lines are too long

7
static/bootstrap/js/bootstrap.min.js vendored

File diff suppressed because one or more lines are too long

1
static/bootstrap/js/bootstrap.min.js.map

File diff suppressed because one or more lines are too long

BIN
static/images/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Loading…
Cancel
Save