From 6d7217267c76a4d36d9eafe8975b39098e181940 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 06:56:16 +0000 Subject: [PATCH 01/20] build(deps): bump modernc.org/sqlite from 1.32.0 to 1.33.0 Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.32.0 to 1.33.0. - [Commits](https://gitlab.com/cznic/sqlite/compare/v1.32.0...v1.33.0) --- updated-dependencies: - dependency-name: modernc.org/sqlite dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 3 +-- go.sum | 16 ++-------------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 4d7dca9..d94d2bc 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,6 @@ require ( github.com/ncruces/go-strftime v0.1.9 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect - modernc.org/libc v1.55.3 // indirect modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.8.0 // indirect modernc.org/strutil v1.2.0 // indirect @@ -44,5 +43,5 @@ require ( golang.org/x/sys v0.23.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/sqlite v1.32.0 + modernc.org/sqlite v1.33.0 ) diff --git a/go.sum b/go.sum index 4146f65..51c2aa2 100644 --- a/go.sum +++ b/go.sum @@ -94,28 +94,16 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= -modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= -modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= -modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s= 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.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= -modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= -modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= -modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= 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.32.0 h1:6BM4uGza7bWypsw4fdLRsLxut6bHe4c58VeqjRgST8s= -modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA= +modernc.org/sqlite v1.33.0 h1:WWkA/T2G17okiLGgKAj4/RMIvgyMT19yQ038160IeYk= +modernc.org/sqlite v1.33.0/go.mod h1:9uQ9hF/pCZoYZK73D/ud5Z7cIRIILSZI8NdIemVMTX8= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= From 348de419cc2c182c3b910c05861fe3da6cec53c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 06:45:58 +0000 Subject: [PATCH 02/20] build(deps): bump modernc.org/sqlite from 1.33.0 to 1.33.1 Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.33.0 to 1.33.1. - [Commits](https://gitlab.com/cznic/sqlite/compare/v1.33.0...v1.33.1) --- updated-dependencies: - dependency-name: modernc.org/sqlite dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 3 ++- go.sum | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d94d2bc..090c8af 100644 --- a/go.mod +++ b/go.mod @@ -24,6 +24,7 @@ require ( github.com/ncruces/go-strftime v0.1.9 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect + modernc.org/libc v1.55.3 // indirect modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.8.0 // indirect modernc.org/strutil v1.2.0 // indirect @@ -43,5 +44,5 @@ require ( golang.org/x/sys v0.23.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/sqlite v1.33.0 + modernc.org/sqlite v1.33.1 ) diff --git a/go.sum b/go.sum index 51c2aa2..079d87e 100644 --- a/go.sum +++ b/go.sum @@ -94,16 +94,28 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= +modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= +modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s= 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.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= +modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= +modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= -modernc.org/sqlite v1.33.0 h1:WWkA/T2G17okiLGgKAj4/RMIvgyMT19yQ038160IeYk= -modernc.org/sqlite v1.33.0/go.mod h1:9uQ9hF/pCZoYZK73D/ud5Z7cIRIILSZI8NdIemVMTX8= +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.33.1 h1:trb6Z3YYoeM9eDL1O8do81kP+0ejv+YzgyFo+Gwy0nM= +modernc.org/sqlite v1.33.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= From 1fa11e28a3b132c768a14681b3720c5af990f041 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 10 Nov 2024 20:24:02 -0500 Subject: [PATCH 03/20] feat(web): basic reader mode --- internal/server/fetch.go | 68 +- internal/server/web/document.html | 4 +- internal/server/web/reader.html | 85 ++ internal/server/web/static/global.css | 8 + internal/server/web/static/reader.css | 1220 +++++++++++++++++++++++++ 5 files changed, 1359 insertions(+), 26 deletions(-) create mode 100644 internal/server/web/reader.html create mode 100644 internal/server/web/static/reader.css diff --git a/internal/server/fetch.go b/internal/server/fetch.go index a3376ca..901a422 100644 --- a/internal/server/fetch.go +++ b/internal/server/fetch.go @@ -62,37 +62,57 @@ func (s *Server) StaticDocument(w http.ResponseWriter, r *http.Request) { return } - t, err := template.ParseFS(resources, "web/document.html") + // Reader mode or code mode? + if r.URL.Query().Get("reader") != "" { + t, err := template.ParseFS(resources, "web/reader.html") - if err != nil { - util.RenderError(&resources, w, http.StatusInternalServerError, err) - return - } + if err != nil { + util.RenderError(&resources, w, http.StatusInternalServerError, err) + return + } - extension := "" + data := map[string]interface{}{ + "Content": document.Content, + "Analytics": template.HTML(config.Config.Analytics), + } - if len(params) == 2 { - extension = params[1] - } + if err := t.Execute(w, data); err != nil { + util.RenderError(&resources, w, http.StatusInternalServerError, err) + return + } + } else { + t, err := template.ParseFS(resources, "web/document.html") - highlighted, css, err := util.Highlight(document.Content, extension) + if err != nil { + util.RenderError(&resources, w, http.StatusInternalServerError, err) + return + } - if err != nil { - util.RenderError(&resources, w, http.StatusInternalServerError, err) - return - } + extension := "" - data := map[string]interface{}{ - "Stylesheet": template.CSS(css), - "Content": document.Content, - "Highlighted": template.HTML(highlighted), - "Extension": extension, - "Analytics": template.HTML(config.Config.Analytics), - } + if len(params) == 2 { + extension = params[1] + } - if err := t.Execute(w, data); err != nil { - util.RenderError(&resources, w, http.StatusInternalServerError, err) - return + highlighted, css, err := util.Highlight(document.Content, extension) + + if err != nil { + util.RenderError(&resources, w, http.StatusInternalServerError, err) + return + } + + data := map[string]interface{}{ + "Stylesheet": template.CSS(css), + "Content": document.Content, + "Highlighted": template.HTML(highlighted), + "Extension": extension, + "Analytics": template.HTML(config.Config.Analytics), + } + + if err := t.Execute(w, data); err != nil { + util.RenderError(&resources, w, http.StatusInternalServerError, err) + return + } } } diff --git a/internal/server/web/document.html b/internal/server/web/document.html index d91c735..eae81d7 100644 --- a/internal/server/web/document.html +++ b/internal/server/web/document.html @@ -48,7 +48,7 @@ - + @@ -81,4 +81,4 @@ - \ No newline at end of file + diff --git a/internal/server/web/reader.html b/internal/server/web/reader.html new file mode 100644 index 0000000..1d7ab71 --- /dev/null +++ b/internal/server/web/reader.html @@ -0,0 +1,85 @@ + + + + + + + + + Spacebin + + + + + + + + + + + + + + + + + {{.Analytics}} + + + + +
+ Spacebin Logo + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ {{.Content}} +
+ + + diff --git a/internal/server/web/static/global.css b/internal/server/web/static/global.css index 0191c8a..fb771fb 100644 --- a/internal/server/web/static/global.css +++ b/internal/server/web/static/global.css @@ -154,3 +154,11 @@ h1 { .chroma { background-color: transparent !important; } + +.markdown-body { + font-size: 1rem; + line-height: 1.5; + color: var(--color-foreground); + margin: 0 auto; + padding: 0 25%; +} diff --git a/internal/server/web/static/reader.css b/internal/server/web/static/reader.css new file mode 100644 index 0000000..0897612 --- /dev/null +++ b/internal/server/web/static/reader.css @@ -0,0 +1,1220 @@ +.markdown-body { + --base-size-4: 0.25rem; + --base-size-8: 0.5rem; + --base-size-16: 1rem; + --base-size-24: 1.5rem; + --base-size-40: 2.5rem; + --base-text-weight-normal: 400; + --base-text-weight-medium: 500; + --base-text-weight-semibold: 600; + --fontStack-monospace: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; + --fgColor-accent: Highlight; + } + @media (prefers-color-scheme: dark) { + .markdown-body, [data-theme="dark"] { + /* dark */ + color-scheme: dark; + --focus-outlineColor: #1f6feb; + --fgColor-default: #f0f6fc; + --fgColor-muted: #9198a1; + --fgColor-accent: #4493f8; + --fgColor-success: #3fb950; + --fgColor-attention: #d29922; + --fgColor-danger: #f85149; + --fgColor-done: #ab7df8; + --bgColor-default: #0d1117; + --bgColor-muted: #151b23; + --bgColor-neutral-muted: #656c7633; + --bgColor-attention-muted: #bb800926; + --borderColor-default: #3d444d; + --borderColor-muted: #3d444db3; + --borderColor-neutral-muted: #3d444db3; + --borderColor-accent-emphasis: #1f6feb; + --borderColor-success-emphasis: #238636; + --borderColor-attention-emphasis: #9e6a03; + --borderColor-danger-emphasis: #da3633; + --borderColor-done-emphasis: #8957e5; + --color-prettylights-syntax-comment: #9198a1; + --color-prettylights-syntax-constant: #79c0ff; + --color-prettylights-syntax-constant-other-reference-link: #a5d6ff; + --color-prettylights-syntax-entity: #d2a8ff; + --color-prettylights-syntax-storage-modifier-import: #f0f6fc; + --color-prettylights-syntax-entity-tag: #7ee787; + --color-prettylights-syntax-keyword: #ff7b72; + --color-prettylights-syntax-string: #a5d6ff; + --color-prettylights-syntax-variable: #ffa657; + --color-prettylights-syntax-brackethighlighter-unmatched: #f85149; + --color-prettylights-syntax-brackethighlighter-angle: #9198a1; + --color-prettylights-syntax-invalid-illegal-text: #f0f6fc; + --color-prettylights-syntax-invalid-illegal-bg: #8e1519; + --color-prettylights-syntax-carriage-return-text: #f0f6fc; + --color-prettylights-syntax-carriage-return-bg: #b62324; + --color-prettylights-syntax-string-regexp: #7ee787; + --color-prettylights-syntax-markup-list: #f2cc60; + --color-prettylights-syntax-markup-heading: #1f6feb; + --color-prettylights-syntax-markup-italic: #f0f6fc; + --color-prettylights-syntax-markup-bold: #f0f6fc; + --color-prettylights-syntax-markup-deleted-text: #ffdcd7; + --color-prettylights-syntax-markup-deleted-bg: #67060c; + --color-prettylights-syntax-markup-inserted-text: #aff5b4; + --color-prettylights-syntax-markup-inserted-bg: #033a16; + --color-prettylights-syntax-markup-changed-text: #ffdfb6; + --color-prettylights-syntax-markup-changed-bg: #5a1e02; + --color-prettylights-syntax-markup-ignored-text: #f0f6fc; + --color-prettylights-syntax-markup-ignored-bg: #1158c7; + --color-prettylights-syntax-meta-diff-range: #d2a8ff; + --color-prettylights-syntax-sublimelinter-gutter-mark: #3d444d; + } + } + @media (prefers-color-scheme: light) { + .markdown-body, [data-theme="light"] { + /* light */ + color-scheme: light; + --focus-outlineColor: #0969da; + --fgColor-default: #1f2328; + --fgColor-muted: #59636e; + --fgColor-accent: #0969da; + --fgColor-success: #1a7f37; + --fgColor-attention: #9a6700; + --fgColor-danger: #d1242f; + --fgColor-done: #8250df; + --bgColor-default: #ffffff; + --bgColor-muted: #f6f8fa; + --bgColor-neutral-muted: #818b981f; + --bgColor-attention-muted: #fff8c5; + --borderColor-default: #d1d9e0; + --borderColor-muted: #d1d9e0b3; + --borderColor-neutral-muted: #d1d9e0b3; + --borderColor-accent-emphasis: #0969da; + --borderColor-success-emphasis: #1a7f37; + --borderColor-attention-emphasis: #9a6700; + --borderColor-danger-emphasis: #cf222e; + --borderColor-done-emphasis: #8250df; + --color-prettylights-syntax-comment: #59636e; + --color-prettylights-syntax-constant: #0550ae; + --color-prettylights-syntax-constant-other-reference-link: #0a3069; + --color-prettylights-syntax-entity: #6639ba; + --color-prettylights-syntax-storage-modifier-import: #1f2328; + --color-prettylights-syntax-entity-tag: #0550ae; + --color-prettylights-syntax-keyword: #cf222e; + --color-prettylights-syntax-string: #0a3069; + --color-prettylights-syntax-variable: #953800; + --color-prettylights-syntax-brackethighlighter-unmatched: #82071e; + --color-prettylights-syntax-brackethighlighter-angle: #59636e; + --color-prettylights-syntax-invalid-illegal-text: #f6f8fa; + --color-prettylights-syntax-invalid-illegal-bg: #82071e; + --color-prettylights-syntax-carriage-return-text: #f6f8fa; + --color-prettylights-syntax-carriage-return-bg: #cf222e; + --color-prettylights-syntax-string-regexp: #116329; + --color-prettylights-syntax-markup-list: #3b2300; + --color-prettylights-syntax-markup-heading: #0550ae; + --color-prettylights-syntax-markup-italic: #1f2328; + --color-prettylights-syntax-markup-bold: #1f2328; + --color-prettylights-syntax-markup-deleted-text: #82071e; + --color-prettylights-syntax-markup-deleted-bg: #ffebe9; + --color-prettylights-syntax-markup-inserted-text: #116329; + --color-prettylights-syntax-markup-inserted-bg: #dafbe1; + --color-prettylights-syntax-markup-changed-text: #953800; + --color-prettylights-syntax-markup-changed-bg: #ffd8b5; + --color-prettylights-syntax-markup-ignored-text: #d1d9e0; + --color-prettylights-syntax-markup-ignored-bg: #0550ae; + --color-prettylights-syntax-meta-diff-range: #8250df; + --color-prettylights-syntax-sublimelinter-gutter-mark: #818b98; + } + } + + .markdown-body { + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + margin: 0; + color: var(--fgColor-default); + background-color: var(--bgColor-default); + font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"; + font-size: 16px; + line-height: 1.5; + word-wrap: break-word; + scroll-behavior: auto !important; + } + + .markdown-body .octicon { + display: inline-block; + fill: currentColor; + vertical-align: text-bottom; + } + + .markdown-body h1:hover .anchor .octicon-link:before, + .markdown-body h2:hover .anchor .octicon-link:before, + .markdown-body h3:hover .anchor .octicon-link:before, + .markdown-body h4:hover .anchor .octicon-link:before, + .markdown-body h5:hover .anchor .octicon-link:before, + .markdown-body h6:hover .anchor .octicon-link:before { + width: 16px; + height: 16px; + content: ' '; + display: inline-block; + background-color: currentColor; + -webkit-mask-image: url("data:image/svg+xml,"); + mask-image: url("data:image/svg+xml,"); + } + + .markdown-body details, + .markdown-body figcaption, + .markdown-body figure { + display: block; + } + + .markdown-body summary { + display: list-item; + } + + .markdown-body [hidden] { + display: none !important; + } + + .markdown-body a { + background-color: transparent; + color: var(--fgColor-accent); + text-decoration: none; + } + + .markdown-body abbr[title] { + border-bottom: none; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; + } + + .markdown-body b, + .markdown-body strong { + font-weight: var(--base-text-weight-semibold, 600); + } + + .markdown-body dfn { + font-style: italic; + } + + .markdown-body h1 { + margin: .67em 0; + font-weight: var(--base-text-weight-semibold, 600); + padding-bottom: .3em; + font-size: 2em; + border-bottom: 1px solid var(--borderColor-muted); + } + + .markdown-body mark { + background-color: var(--bgColor-attention-muted); + color: var(--fgColor-default); + } + + .markdown-body small { + font-size: 90%; + } + + .markdown-body sub, + .markdown-body sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; + } + + .markdown-body sub { + bottom: -0.25em; + } + + .markdown-body sup { + top: -0.5em; + } + + .markdown-body img { + border-style: none; + max-width: 100%; + box-sizing: content-box; + } + + .markdown-body code, + .markdown-body kbd, + .markdown-body pre, + .markdown-body samp { + font-family: monospace; + font-size: 1em; + } + + .markdown-body figure { + margin: 1em var(--base-size-40); + } + + .markdown-body hr { + box-sizing: content-box; + overflow: hidden; + background: transparent; + border-bottom: 1px solid var(--borderColor-muted); + height: .25em; + padding: 0; + margin: var(--base-size-24) 0; + background-color: var(--borderColor-default); + border: 0; + } + + .markdown-body input { + font: inherit; + margin: 0; + overflow: visible; + font-family: inherit; + font-size: inherit; + line-height: inherit; + } + + .markdown-body [type=button], + .markdown-body [type=reset], + .markdown-body [type=submit] { + -webkit-appearance: button; + appearance: button; + } + + .markdown-body [type=checkbox], + .markdown-body [type=radio] { + box-sizing: border-box; + padding: 0; + } + + .markdown-body [type=number]::-webkit-inner-spin-button, + .markdown-body [type=number]::-webkit-outer-spin-button { + height: auto; + } + + .markdown-body [type=search]::-webkit-search-cancel-button, + .markdown-body [type=search]::-webkit-search-decoration { + -webkit-appearance: none; + appearance: none; + } + + .markdown-body ::-webkit-input-placeholder { + color: inherit; + opacity: .54; + } + + .markdown-body ::-webkit-file-upload-button { + -webkit-appearance: button; + appearance: button; + font: inherit; + } + + .markdown-body a:hover { + text-decoration: underline; + } + + .markdown-body ::placeholder { + color: var(--fgColor-muted); + opacity: 1; + } + + .markdown-body hr::before { + display: table; + content: ""; + } + + .markdown-body hr::after { + display: table; + clear: both; + content: ""; + } + + .markdown-body table { + border-spacing: 0; + border-collapse: collapse; + display: block; + width: max-content; + max-width: 100%; + overflow: auto; + } + + .markdown-body td, + .markdown-body th { + padding: 0; + } + + .markdown-body details summary { + cursor: pointer; + } + + .markdown-body a:focus, + .markdown-body [role=button]:focus, + .markdown-body input[type=radio]:focus, + .markdown-body input[type=checkbox]:focus { + outline: 2px solid var(--focus-outlineColor); + outline-offset: -2px; + box-shadow: none; + } + + .markdown-body a:focus:not(:focus-visible), + .markdown-body [role=button]:focus:not(:focus-visible), + .markdown-body input[type=radio]:focus:not(:focus-visible), + .markdown-body input[type=checkbox]:focus:not(:focus-visible) { + outline: solid 1px transparent; + } + + .markdown-body a:focus-visible, + .markdown-body [role=button]:focus-visible, + .markdown-body input[type=radio]:focus-visible, + .markdown-body input[type=checkbox]:focus-visible { + outline: 2px solid var(--focus-outlineColor); + outline-offset: -2px; + box-shadow: none; + } + + .markdown-body a:not([class]):focus, + .markdown-body a:not([class]):focus-visible, + .markdown-body input[type=radio]:focus, + .markdown-body input[type=radio]:focus-visible, + .markdown-body input[type=checkbox]:focus, + .markdown-body input[type=checkbox]:focus-visible { + outline-offset: 0; + } + + .markdown-body kbd { + display: inline-block; + padding: var(--base-size-4); + font: 11px var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace); + line-height: 10px; + color: var(--fgColor-default); + vertical-align: middle; + background-color: var(--bgColor-muted); + border: solid 1px var(--borderColor-neutral-muted); + border-bottom-color: var(--borderColor-neutral-muted); + border-radius: 6px; + box-shadow: inset 0 -1px 0 var(--borderColor-neutral-muted); + } + + .markdown-body h1, + .markdown-body h2, + .markdown-body h3, + .markdown-body h4, + .markdown-body h5, + .markdown-body h6 { + margin-top: var(--base-size-24); + margin-bottom: var(--base-size-16); + font-weight: var(--base-text-weight-semibold, 600); + line-height: 1.25; + } + + .markdown-body h2 { + font-weight: var(--base-text-weight-semibold, 600); + padding-bottom: .3em; + font-size: 1.5em; + border-bottom: 1px solid var(--borderColor-muted); + } + + .markdown-body h3 { + font-weight: var(--base-text-weight-semibold, 600); + font-size: 1.25em; + } + + .markdown-body h4 { + font-weight: var(--base-text-weight-semibold, 600); + font-size: 1em; + } + + .markdown-body h5 { + font-weight: var(--base-text-weight-semibold, 600); + font-size: .875em; + } + + .markdown-body h6 { + font-weight: var(--base-text-weight-semibold, 600); + font-size: .85em; + color: var(--fgColor-muted); + } + + .markdown-body p { + margin-top: 0; + margin-bottom: 10px; + } + + .markdown-body blockquote { + margin: 0; + padding: 0 1em; + color: var(--fgColor-muted); + border-left: .25em solid var(--borderColor-default); + } + + .markdown-body ul, + .markdown-body ol { + margin-top: 0; + margin-bottom: 0; + padding-left: 2em; + } + + .markdown-body ol ol, + .markdown-body ul ol { + list-style-type: lower-roman; + } + + .markdown-body ul ul ol, + .markdown-body ul ol ol, + .markdown-body ol ul ol, + .markdown-body ol ol ol { + list-style-type: lower-alpha; + } + + .markdown-body dd { + margin-left: 0; + } + + .markdown-body tt, + .markdown-body code, + .markdown-body samp { + font-family: var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace); + font-size: 12px; + } + + .markdown-body pre { + margin-top: 0; + margin-bottom: 0; + font-family: var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace); + font-size: 12px; + word-wrap: normal; + } + + .markdown-body .octicon { + display: inline-block; + overflow: visible !important; + vertical-align: text-bottom; + fill: currentColor; + } + + .markdown-body input::-webkit-outer-spin-button, + .markdown-body input::-webkit-inner-spin-button { + margin: 0; + -webkit-appearance: none; + appearance: none; + } + + .markdown-body .mr-2 { + margin-right: var(--base-size-8, 8px) !important; + } + + .markdown-body::before { + display: table; + content: ""; + } + + .markdown-body::after { + display: table; + clear: both; + content: ""; + } + + .markdown-body>*:first-child { + margin-top: 0 !important; + } + + .markdown-body>*:last-child { + margin-bottom: 0 !important; + } + + .markdown-body a:not([href]) { + color: inherit; + text-decoration: none; + } + + .markdown-body .absent { + color: var(--fgColor-danger); + } + + .markdown-body .anchor { + float: left; + padding-right: var(--base-size-4); + margin-left: -20px; + line-height: 1; + } + + .markdown-body .anchor:focus { + outline: none; + } + + .markdown-body p, + .markdown-body blockquote, + .markdown-body ul, + .markdown-body ol, + .markdown-body dl, + .markdown-body table, + .markdown-body pre, + .markdown-body details { + margin-top: 0; + margin-bottom: var(--base-size-16); + } + + .markdown-body blockquote>:first-child { + margin-top: 0; + } + + .markdown-body blockquote>:last-child { + margin-bottom: 0; + } + + .markdown-body h1 .octicon-link, + .markdown-body h2 .octicon-link, + .markdown-body h3 .octicon-link, + .markdown-body h4 .octicon-link, + .markdown-body h5 .octicon-link, + .markdown-body h6 .octicon-link { + color: var(--fgColor-default); + vertical-align: middle; + visibility: hidden; + } + + .markdown-body h1:hover .anchor, + .markdown-body h2:hover .anchor, + .markdown-body h3:hover .anchor, + .markdown-body h4:hover .anchor, + .markdown-body h5:hover .anchor, + .markdown-body h6:hover .anchor { + text-decoration: none; + } + + .markdown-body h1:hover .anchor .octicon-link, + .markdown-body h2:hover .anchor .octicon-link, + .markdown-body h3:hover .anchor .octicon-link, + .markdown-body h4:hover .anchor .octicon-link, + .markdown-body h5:hover .anchor .octicon-link, + .markdown-body h6:hover .anchor .octicon-link { + visibility: visible; + } + + .markdown-body h1 tt, + .markdown-body h1 code, + .markdown-body h2 tt, + .markdown-body h2 code, + .markdown-body h3 tt, + .markdown-body h3 code, + .markdown-body h4 tt, + .markdown-body h4 code, + .markdown-body h5 tt, + .markdown-body h5 code, + .markdown-body h6 tt, + .markdown-body h6 code { + padding: 0 .2em; + font-size: inherit; + } + + .markdown-body summary h1, + .markdown-body summary h2, + .markdown-body summary h3, + .markdown-body summary h4, + .markdown-body summary h5, + .markdown-body summary h6 { + display: inline-block; + } + + .markdown-body summary h1 .anchor, + .markdown-body summary h2 .anchor, + .markdown-body summary h3 .anchor, + .markdown-body summary h4 .anchor, + .markdown-body summary h5 .anchor, + .markdown-body summary h6 .anchor { + margin-left: -40px; + } + + .markdown-body summary h1, + .markdown-body summary h2 { + padding-bottom: 0; + border-bottom: 0; + } + + .markdown-body ul.no-list, + .markdown-body ol.no-list { + padding: 0; + list-style-type: none; + } + + .markdown-body ol[type="a s"] { + list-style-type: lower-alpha; + } + + .markdown-body ol[type="A s"] { + list-style-type: upper-alpha; + } + + .markdown-body ol[type="i s"] { + list-style-type: lower-roman; + } + + .markdown-body ol[type="I s"] { + list-style-type: upper-roman; + } + + .markdown-body ol[type="1"] { + list-style-type: decimal; + } + + .markdown-body div>ol:not([type]) { + list-style-type: decimal; + } + + .markdown-body ul ul, + .markdown-body ul ol, + .markdown-body ol ol, + .markdown-body ol ul { + margin-top: 0; + margin-bottom: 0; + } + + .markdown-body li>p { + margin-top: var(--base-size-16); + } + + .markdown-body li+li { + margin-top: .25em; + } + + .markdown-body dl { + padding: 0; + } + + .markdown-body dl dt { + padding: 0; + margin-top: var(--base-size-16); + font-size: 1em; + font-style: italic; + font-weight: var(--base-text-weight-semibold, 600); + } + + .markdown-body dl dd { + padding: 0 var(--base-size-16); + margin-bottom: var(--base-size-16); + } + + .markdown-body table th { + font-weight: var(--base-text-weight-semibold, 600); + } + + .markdown-body table th, + .markdown-body table td { + padding: 6px 13px; + border: 1px solid var(--borderColor-default); + } + + .markdown-body table td>:last-child { + margin-bottom: 0; + } + + .markdown-body table tr { + background-color: var(--bgColor-default); + border-top: 1px solid var(--borderColor-muted); + } + + .markdown-body table tr:nth-child(2n) { + background-color: var(--bgColor-muted); + } + + .markdown-body table img { + background-color: transparent; + } + + .markdown-body img[align=right] { + padding-left: 20px; + } + + .markdown-body img[align=left] { + padding-right: 20px; + } + + .markdown-body .emoji { + max-width: none; + vertical-align: text-top; + background-color: transparent; + } + + .markdown-body span.frame { + display: block; + overflow: hidden; + } + + .markdown-body span.frame>span { + display: block; + float: left; + width: auto; + padding: 7px; + margin: 13px 0 0; + overflow: hidden; + border: 1px solid var(--borderColor-default); + } + + .markdown-body span.frame span img { + display: block; + float: left; + } + + .markdown-body span.frame span span { + display: block; + padding: 5px 0 0; + clear: both; + color: var(--fgColor-default); + } + + .markdown-body span.align-center { + display: block; + overflow: hidden; + clear: both; + } + + .markdown-body span.align-center>span { + display: block; + margin: 13px auto 0; + overflow: hidden; + text-align: center; + } + + .markdown-body span.align-center span img { + margin: 0 auto; + text-align: center; + } + + .markdown-body span.align-right { + display: block; + overflow: hidden; + clear: both; + } + + .markdown-body span.align-right>span { + display: block; + margin: 13px 0 0; + overflow: hidden; + text-align: right; + } + + .markdown-body span.align-right span img { + margin: 0; + text-align: right; + } + + .markdown-body span.float-left { + display: block; + float: left; + margin-right: 13px; + overflow: hidden; + } + + .markdown-body span.float-left span { + margin: 13px 0 0; + } + + .markdown-body span.float-right { + display: block; + float: right; + margin-left: 13px; + overflow: hidden; + } + + .markdown-body span.float-right>span { + display: block; + margin: 13px auto 0; + overflow: hidden; + text-align: right; + } + + .markdown-body code, + .markdown-body tt { + padding: .2em .4em; + margin: 0; + font-size: 85%; + white-space: break-spaces; + background-color: var(--bgColor-neutral-muted); + border-radius: 6px; + } + + .markdown-body code br, + .markdown-body tt br { + display: none; + } + + .markdown-body del code { + text-decoration: inherit; + } + + .markdown-body samp { + font-size: 85%; + } + + .markdown-body pre code { + font-size: 100%; + } + + .markdown-body pre>code { + padding: 0; + margin: 0; + word-break: normal; + white-space: pre; + background: transparent; + border: 0; + } + + .markdown-body .highlight { + margin-bottom: var(--base-size-16); + } + + .markdown-body .highlight pre { + margin-bottom: 0; + word-break: normal; + } + + .markdown-body .highlight pre, + .markdown-body pre { + padding: var(--base-size-16); + overflow: auto; + font-size: 85%; + line-height: 1.45; + color: var(--fgColor-default); + background-color: var(--bgColor-muted); + border-radius: 6px; + } + + .markdown-body pre code, + .markdown-body pre tt { + display: inline; + max-width: auto; + padding: 0; + margin: 0; + overflow: visible; + line-height: inherit; + word-wrap: normal; + background-color: transparent; + border: 0; + } + + .markdown-body .csv-data td, + .markdown-body .csv-data th { + padding: 5px; + overflow: hidden; + font-size: 12px; + line-height: 1; + text-align: left; + white-space: nowrap; + } + + .markdown-body .csv-data .blob-num { + padding: 10px var(--base-size-8) 9px; + text-align: right; + background: var(--bgColor-default); + border: 0; + } + + .markdown-body .csv-data tr { + border-top: 0; + } + + .markdown-body .csv-data th { + font-weight: var(--base-text-weight-semibold, 600); + background: var(--bgColor-muted); + border-top: 0; + } + + .markdown-body [data-footnote-ref]::before { + content: "["; + } + + .markdown-body [data-footnote-ref]::after { + content: "]"; + } + + .markdown-body .footnotes { + font-size: 12px; + color: var(--fgColor-muted); + border-top: 1px solid var(--borderColor-default); + } + + .markdown-body .footnotes ol { + padding-left: var(--base-size-16); + } + + .markdown-body .footnotes ol ul { + display: inline-block; + padding-left: var(--base-size-16); + margin-top: var(--base-size-16); + } + + .markdown-body .footnotes li { + position: relative; + } + + .markdown-body .footnotes li:target::before { + position: absolute; + top: calc(var(--base-size-8)*-1); + right: calc(var(--base-size-8)*-1); + bottom: calc(var(--base-size-8)*-1); + left: calc(var(--base-size-24)*-1); + pointer-events: none; + content: ""; + border: 2px solid var(--borderColor-accent-emphasis); + border-radius: 6px; + } + + .markdown-body .footnotes li:target { + color: var(--fgColor-default); + } + + .markdown-body .footnotes .data-footnote-backref g-emoji { + font-family: monospace; + } + + .markdown-body .pl-c { + color: var(--color-prettylights-syntax-comment); + } + + .markdown-body .pl-c1, + .markdown-body .pl-s .pl-v { + color: var(--color-prettylights-syntax-constant); + } + + .markdown-body .pl-e, + .markdown-body .pl-en { + color: var(--color-prettylights-syntax-entity); + } + + .markdown-body .pl-smi, + .markdown-body .pl-s .pl-s1 { + color: var(--color-prettylights-syntax-storage-modifier-import); + } + + .markdown-body .pl-ent { + color: var(--color-prettylights-syntax-entity-tag); + } + + .markdown-body .pl-k { + color: var(--color-prettylights-syntax-keyword); + } + + .markdown-body .pl-s, + .markdown-body .pl-pds, + .markdown-body .pl-s .pl-pse .pl-s1, + .markdown-body .pl-sr, + .markdown-body .pl-sr .pl-cce, + .markdown-body .pl-sr .pl-sre, + .markdown-body .pl-sr .pl-sra { + color: var(--color-prettylights-syntax-string); + } + + .markdown-body .pl-v, + .markdown-body .pl-smw { + color: var(--color-prettylights-syntax-variable); + } + + .markdown-body .pl-bu { + color: var(--color-prettylights-syntax-brackethighlighter-unmatched); + } + + .markdown-body .pl-ii { + color: var(--color-prettylights-syntax-invalid-illegal-text); + background-color: var(--color-prettylights-syntax-invalid-illegal-bg); + } + + .markdown-body .pl-c2 { + color: var(--color-prettylights-syntax-carriage-return-text); + background-color: var(--color-prettylights-syntax-carriage-return-bg); + } + + .markdown-body .pl-sr .pl-cce { + font-weight: bold; + color: var(--color-prettylights-syntax-string-regexp); + } + + .markdown-body .pl-ml { + color: var(--color-prettylights-syntax-markup-list); + } + + .markdown-body .pl-mh, + .markdown-body .pl-mh .pl-en, + .markdown-body .pl-ms { + font-weight: bold; + color: var(--color-prettylights-syntax-markup-heading); + } + + .markdown-body .pl-mi { + font-style: italic; + color: var(--color-prettylights-syntax-markup-italic); + } + + .markdown-body .pl-mb { + font-weight: bold; + color: var(--color-prettylights-syntax-markup-bold); + } + + .markdown-body .pl-md { + color: var(--color-prettylights-syntax-markup-deleted-text); + background-color: var(--color-prettylights-syntax-markup-deleted-bg); + } + + .markdown-body .pl-mi1 { + color: var(--color-prettylights-syntax-markup-inserted-text); + background-color: var(--color-prettylights-syntax-markup-inserted-bg); + } + + .markdown-body .pl-mc { + color: var(--color-prettylights-syntax-markup-changed-text); + background-color: var(--color-prettylights-syntax-markup-changed-bg); + } + + .markdown-body .pl-mi2 { + color: var(--color-prettylights-syntax-markup-ignored-text); + background-color: var(--color-prettylights-syntax-markup-ignored-bg); + } + + .markdown-body .pl-mdr { + font-weight: bold; + color: var(--color-prettylights-syntax-meta-diff-range); + } + + .markdown-body .pl-ba { + color: var(--color-prettylights-syntax-brackethighlighter-angle); + } + + .markdown-body .pl-sg { + color: var(--color-prettylights-syntax-sublimelinter-gutter-mark); + } + + .markdown-body .pl-corl { + text-decoration: underline; + color: var(--color-prettylights-syntax-constant-other-reference-link); + } + + .markdown-body [role=button]:focus:not(:focus-visible), + .markdown-body [role=tabpanel][tabindex="0"]:focus:not(:focus-visible), + .markdown-body button:focus:not(:focus-visible), + .markdown-body summary:focus:not(:focus-visible), + .markdown-body a:focus:not(:focus-visible) { + outline: none; + box-shadow: none; + } + + .markdown-body [tabindex="0"]:focus:not(:focus-visible), + .markdown-body details-dialog:focus:not(:focus-visible) { + outline: none; + } + + .markdown-body g-emoji { + display: inline-block; + min-width: 1ch; + font-family: "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; + font-size: 1em; + font-style: normal !important; + font-weight: var(--base-text-weight-normal, 400); + line-height: 1; + vertical-align: -0.075em; + } + + .markdown-body g-emoji img { + width: 1em; + height: 1em; + } + + .markdown-body .task-list-item { + list-style-type: none; + } + + .markdown-body .task-list-item label { + font-weight: var(--base-text-weight-normal, 400); + } + + .markdown-body .task-list-item.enabled label { + cursor: pointer; + } + + .markdown-body .task-list-item+.task-list-item { + margin-top: var(--base-size-4); + } + + .markdown-body .task-list-item .handle { + display: none; + } + + .markdown-body .task-list-item-checkbox { + margin: 0 .2em .25em -1.4em; + vertical-align: middle; + } + + .markdown-body ul:dir(rtl) .task-list-item-checkbox { + margin: 0 -1.6em .25em .2em; + } + + .markdown-body ol:dir(rtl) .task-list-item-checkbox { + margin: 0 -1.6em .25em .2em; + } + + .markdown-body .contains-task-list:hover .task-list-item-convert-container, + .markdown-body .contains-task-list:focus-within .task-list-item-convert-container { + display: block; + width: auto; + height: 24px; + overflow: visible; + clip: auto; + } + + .markdown-body ::-webkit-calendar-picker-indicator { + filter: invert(50%); + } + + .markdown-body .markdown-alert { + padding: var(--base-size-8) var(--base-size-16); + margin-bottom: var(--base-size-16); + color: inherit; + border-left: .25em solid var(--borderColor-default); + } + + .markdown-body .markdown-alert>:first-child { + margin-top: 0; + } + + .markdown-body .markdown-alert>:last-child { + margin-bottom: 0; + } + + .markdown-body .markdown-alert .markdown-alert-title { + display: flex; + font-weight: var(--base-text-weight-medium, 500); + align-items: center; + line-height: 1; + } + + .markdown-body .markdown-alert.markdown-alert-note { + border-left-color: var(--borderColor-accent-emphasis); + } + + .markdown-body .markdown-alert.markdown-alert-note .markdown-alert-title { + color: var(--fgColor-accent); + } + + .markdown-body .markdown-alert.markdown-alert-important { + border-left-color: var(--borderColor-done-emphasis); + } + + .markdown-body .markdown-alert.markdown-alert-important .markdown-alert-title { + color: var(--fgColor-done); + } + + .markdown-body .markdown-alert.markdown-alert-warning { + border-left-color: var(--borderColor-attention-emphasis); + } + + .markdown-body .markdown-alert.markdown-alert-warning .markdown-alert-title { + color: var(--fgColor-attention); + } + + .markdown-body .markdown-alert.markdown-alert-tip { + border-left-color: var(--borderColor-success-emphasis); + } + + .markdown-body .markdown-alert.markdown-alert-tip .markdown-alert-title { + color: var(--fgColor-success); + } + + .markdown-body .markdown-alert.markdown-alert-caution { + border-left-color: var(--borderColor-danger-emphasis); + } + + .markdown-body .markdown-alert.markdown-alert-caution .markdown-alert-title { + color: var(--fgColor-danger); + } + + .markdown-body>*:first-child>.heading-element:first-child { + margin-top: 0 !important; + } From cae17a084cd3fa663ef2dcb71a145bdc4c7c9d71 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 17 Nov 2024 01:39:05 -0500 Subject: [PATCH 04/20] feat(web): add reader mode --- go.mod | 1 + go.sum | 2 + internal/server/fetch.go | 6 +- internal/server/web/document.html | 8 +- internal/server/web/index.html | 9 - internal/server/web/reader.html | 27 +- internal/server/web/static/app.js | 18 + internal/server/web/static/global.css | 9 +- internal/server/web/static/reader.css | 1465 +++++-------------------- internal/util/markdown.go | 20 + 10 files changed, 337 insertions(+), 1228 deletions(-) create mode 100644 internal/util/markdown.go diff --git a/go.mod b/go.mod index 090c8af..c5dcac7 100644 --- a/go.mod +++ b/go.mod @@ -37,6 +37,7 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-sql-driver/mysql v1.8.1 + github.com/gomarkdown/markdown v0.0.0-20241105142532-d03b89096d81 github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect diff --git a/go.sum b/go.sum index 079d87e..06b8c9a 100644 --- a/go.sum +++ b/go.sum @@ -33,6 +33,8 @@ github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRi github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gomarkdown/markdown v0.0.0-20241105142532-d03b89096d81 h1:5lyLWsV+qCkoYqsKUDuycESh9DEIPVKN6iCFeL7ag50= +github.com/gomarkdown/markdown v0.0.0-20241105142532-d03b89096d81/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= 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= diff --git a/internal/server/fetch.go b/internal/server/fetch.go index 901a422..a85b073 100644 --- a/internal/server/fetch.go +++ b/internal/server/fetch.go @@ -63,7 +63,7 @@ func (s *Server) StaticDocument(w http.ResponseWriter, r *http.Request) { } // Reader mode or code mode? - if r.URL.Query().Get("reader") != "" { + if r.URL.Query().Get("reader") == "true" { t, err := template.ParseFS(resources, "web/reader.html") if err != nil { @@ -71,8 +71,10 @@ func (s *Server) StaticDocument(w http.ResponseWriter, r *http.Request) { return } + content := util.ParseMarkdown([]byte(document.Content)) + data := map[string]interface{}{ - "Content": document.Content, + "Content": template.HTML(string(content)), "Analytics": template.HTML(config.Config.Analytics), } diff --git a/internal/server/web/document.html b/internal/server/web/document.html index eae81d7..0993b8c 100644 --- a/internal/server/web/document.html +++ b/internal/server/web/document.html @@ -49,10 +49,10 @@ - - - + + + diff --git a/internal/server/web/index.html b/internal/server/web/index.html index 787c3e5..da72b9d 100644 --- a/internal/server/web/index.html +++ b/internal/server/web/index.html @@ -45,15 +45,6 @@ - - - - - - - -
- {{.Content}} +
+
+ + +
+ +
+ {{.Content}} +
+ + diff --git a/internal/server/web/static/app.js b/internal/server/web/static/app.js index 741d9dd..8fbc21f 100644 --- a/internal/server/web/static/app.js +++ b/internal/server/web/static/app.js @@ -13,3 +13,21 @@ document.querySelector('textarea')?.addEventListener('keydown', function (e) { this.selectionStart = this.selectionEnd = start + 1; } }); + +function switchFont(to) { + const main = document.querySelector('.wysiwyg'); + + if (to === 'sans') { + main.classList.remove('font-serif', 'font-sans'); + main.classList.add('font-sans'); + + document.querySelector('#serif').classList.remove('active'); + document.querySelector('#sans').classList.add('active'); + } else if (to === 'serif') { + main.classList.remove('font-serif', 'font-sans'); + main.classList.add('font-serif'); + + document.querySelector('#sans').classList.remove('active'); + document.querySelector('#serif').classList.add('active'); + } +} diff --git a/internal/server/web/static/global.css b/internal/server/web/static/global.css index fb771fb..4e146c4 100644 --- a/internal/server/web/static/global.css +++ b/internal/server/web/static/global.css @@ -9,6 +9,7 @@ --color-links-dark: #7a98d8; --color-foreground: #dedede; --color-background: #121212; + --color-buttons: #1d1c1c; } * { @@ -154,11 +155,3 @@ h1 { .chroma { background-color: transparent !important; } - -.markdown-body { - font-size: 1rem; - line-height: 1.5; - color: var(--color-foreground); - margin: 0 auto; - padding: 0 25%; -} diff --git a/internal/server/web/static/reader.css b/internal/server/web/static/reader.css index 0897612..23b184e 100644 --- a/internal/server/web/static/reader.css +++ b/internal/server/web/static/reader.css @@ -1,1220 +1,289 @@ -.markdown-body { - --base-size-4: 0.25rem; - --base-size-8: 0.5rem; - --base-size-16: 1rem; - --base-size-24: 1.5rem; - --base-size-40: 2.5rem; - --base-text-weight-normal: 400; - --base-text-weight-medium: 500; - --base-text-weight-semibold: 600; - --fontStack-monospace: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; - --fgColor-accent: Highlight; - } - @media (prefers-color-scheme: dark) { - .markdown-body, [data-theme="dark"] { - /* dark */ - color-scheme: dark; - --focus-outlineColor: #1f6feb; - --fgColor-default: #f0f6fc; - --fgColor-muted: #9198a1; - --fgColor-accent: #4493f8; - --fgColor-success: #3fb950; - --fgColor-attention: #d29922; - --fgColor-danger: #f85149; - --fgColor-done: #ab7df8; - --bgColor-default: #0d1117; - --bgColor-muted: #151b23; - --bgColor-neutral-muted: #656c7633; - --bgColor-attention-muted: #bb800926; - --borderColor-default: #3d444d; - --borderColor-muted: #3d444db3; - --borderColor-neutral-muted: #3d444db3; - --borderColor-accent-emphasis: #1f6feb; - --borderColor-success-emphasis: #238636; - --borderColor-attention-emphasis: #9e6a03; - --borderColor-danger-emphasis: #da3633; - --borderColor-done-emphasis: #8957e5; - --color-prettylights-syntax-comment: #9198a1; - --color-prettylights-syntax-constant: #79c0ff; - --color-prettylights-syntax-constant-other-reference-link: #a5d6ff; - --color-prettylights-syntax-entity: #d2a8ff; - --color-prettylights-syntax-storage-modifier-import: #f0f6fc; - --color-prettylights-syntax-entity-tag: #7ee787; - --color-prettylights-syntax-keyword: #ff7b72; - --color-prettylights-syntax-string: #a5d6ff; - --color-prettylights-syntax-variable: #ffa657; - --color-prettylights-syntax-brackethighlighter-unmatched: #f85149; - --color-prettylights-syntax-brackethighlighter-angle: #9198a1; - --color-prettylights-syntax-invalid-illegal-text: #f0f6fc; - --color-prettylights-syntax-invalid-illegal-bg: #8e1519; - --color-prettylights-syntax-carriage-return-text: #f0f6fc; - --color-prettylights-syntax-carriage-return-bg: #b62324; - --color-prettylights-syntax-string-regexp: #7ee787; - --color-prettylights-syntax-markup-list: #f2cc60; - --color-prettylights-syntax-markup-heading: #1f6feb; - --color-prettylights-syntax-markup-italic: #f0f6fc; - --color-prettylights-syntax-markup-bold: #f0f6fc; - --color-prettylights-syntax-markup-deleted-text: #ffdcd7; - --color-prettylights-syntax-markup-deleted-bg: #67060c; - --color-prettylights-syntax-markup-inserted-text: #aff5b4; - --color-prettylights-syntax-markup-inserted-bg: #033a16; - --color-prettylights-syntax-markup-changed-text: #ffdfb6; - --color-prettylights-syntax-markup-changed-bg: #5a1e02; - --color-prettylights-syntax-markup-ignored-text: #f0f6fc; - --color-prettylights-syntax-markup-ignored-bg: #1158c7; - --color-prettylights-syntax-meta-diff-range: #d2a8ff; - --color-prettylights-syntax-sublimelinter-gutter-mark: #3d444d; - } - } - @media (prefers-color-scheme: light) { - .markdown-body, [data-theme="light"] { - /* light */ - color-scheme: light; - --focus-outlineColor: #0969da; - --fgColor-default: #1f2328; - --fgColor-muted: #59636e; - --fgColor-accent: #0969da; - --fgColor-success: #1a7f37; - --fgColor-attention: #9a6700; - --fgColor-danger: #d1242f; - --fgColor-done: #8250df; - --bgColor-default: #ffffff; - --bgColor-muted: #f6f8fa; - --bgColor-neutral-muted: #818b981f; - --bgColor-attention-muted: #fff8c5; - --borderColor-default: #d1d9e0; - --borderColor-muted: #d1d9e0b3; - --borderColor-neutral-muted: #d1d9e0b3; - --borderColor-accent-emphasis: #0969da; - --borderColor-success-emphasis: #1a7f37; - --borderColor-attention-emphasis: #9a6700; - --borderColor-danger-emphasis: #cf222e; - --borderColor-done-emphasis: #8250df; - --color-prettylights-syntax-comment: #59636e; - --color-prettylights-syntax-constant: #0550ae; - --color-prettylights-syntax-constant-other-reference-link: #0a3069; - --color-prettylights-syntax-entity: #6639ba; - --color-prettylights-syntax-storage-modifier-import: #1f2328; - --color-prettylights-syntax-entity-tag: #0550ae; - --color-prettylights-syntax-keyword: #cf222e; - --color-prettylights-syntax-string: #0a3069; - --color-prettylights-syntax-variable: #953800; - --color-prettylights-syntax-brackethighlighter-unmatched: #82071e; - --color-prettylights-syntax-brackethighlighter-angle: #59636e; - --color-prettylights-syntax-invalid-illegal-text: #f6f8fa; - --color-prettylights-syntax-invalid-illegal-bg: #82071e; - --color-prettylights-syntax-carriage-return-text: #f6f8fa; - --color-prettylights-syntax-carriage-return-bg: #cf222e; - --color-prettylights-syntax-string-regexp: #116329; - --color-prettylights-syntax-markup-list: #3b2300; - --color-prettylights-syntax-markup-heading: #0550ae; - --color-prettylights-syntax-markup-italic: #1f2328; - --color-prettylights-syntax-markup-bold: #1f2328; - --color-prettylights-syntax-markup-deleted-text: #82071e; - --color-prettylights-syntax-markup-deleted-bg: #ffebe9; - --color-prettylights-syntax-markup-inserted-text: #116329; - --color-prettylights-syntax-markup-inserted-bg: #dafbe1; - --color-prettylights-syntax-markup-changed-text: #953800; - --color-prettylights-syntax-markup-changed-bg: #ffd8b5; - --color-prettylights-syntax-markup-ignored-text: #d1d9e0; - --color-prettylights-syntax-markup-ignored-bg: #0550ae; - --color-prettylights-syntax-meta-diff-range: #8250df; - --color-prettylights-syntax-sublimelinter-gutter-mark: #818b98; - } - } - .markdown-body { - -ms-text-size-adjust: 100%; - -webkit-text-size-adjust: 100%; - margin: 0; - color: var(--fgColor-default); - background-color: var(--bgColor-default); - font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"; - font-size: 16px; - line-height: 1.5; - word-wrap: break-word; - scroll-behavior: auto !important; - } - - .markdown-body .octicon { - display: inline-block; - fill: currentColor; - vertical-align: text-bottom; - } +.wysiwyg { + font-size: 1rem; + line-height: 2; + color: var(--color-foreground); +} - .markdown-body h1:hover .anchor .octicon-link:before, - .markdown-body h2:hover .anchor .octicon-link:before, - .markdown-body h3:hover .anchor .octicon-link:before, - .markdown-body h4:hover .anchor .octicon-link:before, - .markdown-body h5:hover .anchor .octicon-link:before, - .markdown-body h6:hover .anchor .octicon-link:before { - width: 16px; - height: 16px; - content: ' '; - display: inline-block; - background-color: currentColor; - -webkit-mask-image: url("data:image/svg+xml,"); - mask-image: url("data:image/svg+xml,"); - } +#reader { + margin: 0 auto; + padding: 0 25%; +} - .markdown-body details, - .markdown-body figcaption, - .markdown-body figure { - display: block; - } +.font-sans { + font-family: -apple-system, BlinkMacSystemFont, "Avenir Next", "Avenir", "Segoe UI", "Helvetica Neue", "Helvetica", "Cantarell", "Ubuntu", "Roboto", "Noto", "Arial", sans-serif; +} - .markdown-body summary { - display: list-item; - } +.font-serif { + font-family: "Iowan Old Style", "Apple Garamond", "Baskerville", "Times New Roman", "Droid Serif", "Times", "Source Serif Pro", serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; +} - .markdown-body [hidden] { - display: none !important; - } +#font-button-group { + display: flex; + width: fit-content; + gap: 1px; + background-color: var(--color-buttons); + padding: 5px; + margin-bottom: 24px; +} - .markdown-body a { - background-color: transparent; - color: var(--fgColor-accent); +#font-button-group button { + padding: 5px 10px; + margin: 0; + border: none; + color: var(--color-links); + font-size: var(--font-size); + cursor: pointer; text-decoration: none; - } - - .markdown-body abbr[title] { - border-bottom: none; - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - } +} - .markdown-body b, - .markdown-body strong { - font-weight: var(--base-text-weight-semibold, 600); - } +button.active { + background-color: var(--color-background); +} - .markdown-body dfn { +/*! wysiwyg.css v0.0.3 | MIT License | github.com/jgthms/wysiwyg.css */ +.wysiwyg { + line-height: 1.6; +} +.wysiwyg a { + text-decoration: none; +} +.wysiwyg a:hover { + border-bottom: 1px solid; +} +.wysiwyg abbr { + border-bottom: 1px dotted; + cursor: help; +} +.wysiwyg cite { font-style: italic; - } - - .markdown-body h1 { - margin: .67em 0; - font-weight: var(--base-text-weight-semibold, 600); - padding-bottom: .3em; - font-size: 2em; - border-bottom: 1px solid var(--borderColor-muted); - } - - .markdown-body mark { - background-color: var(--bgColor-attention-muted); - color: var(--fgColor-default); - } - - .markdown-body small { - font-size: 90%; - } - - .markdown-body sub, - .markdown-body sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; - } - - .markdown-body sub { - bottom: -0.25em; - } - - .markdown-body sup { - top: -0.5em; - } - - .markdown-body img { - border-style: none; - max-width: 100%; - box-sizing: content-box; - } - - .markdown-body code, - .markdown-body kbd, - .markdown-body pre, - .markdown-body samp { - font-family: monospace; - font-size: 1em; - } - - .markdown-body figure { - margin: 1em var(--base-size-40); - } - - .markdown-body hr { - box-sizing: content-box; - overflow: hidden; - background: transparent; - border-bottom: 1px solid var(--borderColor-muted); - height: .25em; - padding: 0; - margin: var(--base-size-24) 0; - background-color: var(--borderColor-default); - border: 0; - } - - .markdown-body input { - font: inherit; - margin: 0; - overflow: visible; - font-family: inherit; - font-size: inherit; - line-height: inherit; - } - - .markdown-body [type=button], - .markdown-body [type=reset], - .markdown-body [type=submit] { - -webkit-appearance: button; - appearance: button; - } - - .markdown-body [type=checkbox], - .markdown-body [type=radio] { - box-sizing: border-box; - padding: 0; - } - - .markdown-body [type=number]::-webkit-inner-spin-button, - .markdown-body [type=number]::-webkit-outer-spin-button { - height: auto; - } - - .markdown-body [type=search]::-webkit-search-cancel-button, - .markdown-body [type=search]::-webkit-search-decoration { - -webkit-appearance: none; - appearance: none; - } - - .markdown-body ::-webkit-input-placeholder { - color: inherit; - opacity: .54; - } - - .markdown-body ::-webkit-file-upload-button { - -webkit-appearance: button; - appearance: button; - font: inherit; - } - - .markdown-body a:hover { - text-decoration: underline; - } - - .markdown-body ::placeholder { - color: var(--fgColor-muted); - opacity: 1; - } - - .markdown-body hr::before { - display: table; - content: ""; - } - - .markdown-body hr::after { - display: table; - clear: both; - content: ""; - } - - .markdown-body table { - border-spacing: 0; - border-collapse: collapse; +} +.wysiwyg hr { + background: #e6e6e6; + border: none; display: block; - width: max-content; - max-width: 100%; - overflow: auto; - } - - .markdown-body td, - .markdown-body th { - padding: 0; - } - - .markdown-body details summary { - cursor: pointer; - } - - .markdown-body a:focus, - .markdown-body [role=button]:focus, - .markdown-body input[type=radio]:focus, - .markdown-body input[type=checkbox]:focus { - outline: 2px solid var(--focus-outlineColor); - outline-offset: -2px; - box-shadow: none; - } - - .markdown-body a:focus:not(:focus-visible), - .markdown-body [role=button]:focus:not(:focus-visible), - .markdown-body input[type=radio]:focus:not(:focus-visible), - .markdown-body input[type=checkbox]:focus:not(:focus-visible) { - outline: solid 1px transparent; - } - - .markdown-body a:focus-visible, - .markdown-body [role=button]:focus-visible, - .markdown-body input[type=radio]:focus-visible, - .markdown-body input[type=checkbox]:focus-visible { - outline: 2px solid var(--focus-outlineColor); - outline-offset: -2px; - box-shadow: none; - } - - .markdown-body a:not([class]):focus, - .markdown-body a:not([class]):focus-visible, - .markdown-body input[type=radio]:focus, - .markdown-body input[type=radio]:focus-visible, - .markdown-body input[type=checkbox]:focus, - .markdown-body input[type=checkbox]:focus-visible { - outline-offset: 0; - } - - .markdown-body kbd { - display: inline-block; - padding: var(--base-size-4); - font: 11px var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace); - line-height: 10px; - color: var(--fgColor-default); - vertical-align: middle; - background-color: var(--bgColor-muted); - border: solid 1px var(--borderColor-neutral-muted); - border-bottom-color: var(--borderColor-neutral-muted); - border-radius: 6px; - box-shadow: inset 0 -1px 0 var(--borderColor-neutral-muted); - } - - .markdown-body h1, - .markdown-body h2, - .markdown-body h3, - .markdown-body h4, - .markdown-body h5, - .markdown-body h6 { - margin-top: var(--base-size-24); - margin-bottom: var(--base-size-16); - font-weight: var(--base-text-weight-semibold, 600); - line-height: 1.25; - } - - .markdown-body h2 { - font-weight: var(--base-text-weight-semibold, 600); - padding-bottom: .3em; - font-size: 1.5em; - border-bottom: 1px solid var(--borderColor-muted); - } - - .markdown-body h3 { - font-weight: var(--base-text-weight-semibold, 600); - font-size: 1.25em; - } - - .markdown-body h4 { - font-weight: var(--base-text-weight-semibold, 600); - font-size: 1em; - } - - .markdown-body h5 { - font-weight: var(--base-text-weight-semibold, 600); - font-size: .875em; - } - - .markdown-body h6 { - font-weight: var(--base-text-weight-semibold, 600); - font-size: .85em; - color: var(--fgColor-muted); - } - - .markdown-body p { - margin-top: 0; - margin-bottom: 10px; - } - - .markdown-body blockquote { - margin: 0; - padding: 0 1em; - color: var(--fgColor-muted); - border-left: .25em solid var(--borderColor-default); - } - - .markdown-body ul, - .markdown-body ol { - margin-top: 0; - margin-bottom: 0; - padding-left: 2em; - } - - .markdown-body ol ol, - .markdown-body ul ol { - list-style-type: lower-roman; - } - - .markdown-body ul ul ol, - .markdown-body ul ol ol, - .markdown-body ol ul ol, - .markdown-body ol ol ol { - list-style-type: lower-alpha; - } - - .markdown-body dd { - margin-left: 0; - } - - .markdown-body tt, - .markdown-body code, - .markdown-body samp { - font-family: var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace); - font-size: 12px; - } - - .markdown-body pre { - margin-top: 0; - margin-bottom: 0; - font-family: var(--fontStack-monospace, ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace); - font-size: 12px; - word-wrap: normal; - } - - .markdown-body .octicon { - display: inline-block; - overflow: visible !important; + height: 1px; + margin-bottom: 1.4em; + margin-top: 1.4em; +} +.wysiwyg img { vertical-align: text-bottom; - fill: currentColor; - } - - .markdown-body input::-webkit-outer-spin-button, - .markdown-body input::-webkit-inner-spin-button { - margin: 0; - -webkit-appearance: none; - appearance: none; - } - - .markdown-body .mr-2 { - margin-right: var(--base-size-8, 8px) !important; - } - - .markdown-body::before { - display: table; - content: ""; - } - - .markdown-body::after { - display: table; - clear: both; - content: ""; - } - - .markdown-body>*:first-child { - margin-top: 0 !important; - } - - .markdown-body>*:last-child { - margin-bottom: 0 !important; - } - - .markdown-body a:not([href]) { - color: inherit; +} +.wysiwyg ins { + background-color: lime; text-decoration: none; - } - - .markdown-body .absent { - color: var(--fgColor-danger); - } - - .markdown-body .anchor { - float: left; - padding-right: var(--base-size-4); - margin-left: -20px; - line-height: 1; - } - - .markdown-body .anchor:focus { - outline: none; - } - - .markdown-body p, - .markdown-body blockquote, - .markdown-body ul, - .markdown-body ol, - .markdown-body dl, - .markdown-body table, - .markdown-body pre, - .markdown-body details { - margin-top: 0; - margin-bottom: var(--base-size-16); - } - - .markdown-body blockquote>:first-child { - margin-top: 0; - } - - .markdown-body blockquote>:last-child { +} +.wysiwyg mark { + background-color: #ff0; +} +.wysiwyg small { + font-size: 0.8em; +} +.wysiwyg strong { + font-weight: 700; +} +.wysiwyg sub, +.wysiwyg sup { + font-size: 0.8em; +} +.wysiwyg sub { + vertical-align: sub; +} +.wysiwyg sup { + vertical-align: super; +} +.wysiwyg p, +.wysiwyg dl, +.wysiwyg ol, +.wysiwyg ul, +.wysiwyg blockquote, +.wysiwyg pre, +.wysiwyg table { + margin-bottom: 1.4em; +} +.wysiwyg p:last-child, +.wysiwyg dl:last-child, +.wysiwyg ol:last-child, +.wysiwyg ul:last-child, +.wysiwyg blockquote:last-child, +.wysiwyg pre:last-child, +.wysiwyg table:last-child { margin-bottom: 0; - } - - .markdown-body h1 .octicon-link, - .markdown-body h2 .octicon-link, - .markdown-body h3 .octicon-link, - .markdown-body h4 .octicon-link, - .markdown-body h5 .octicon-link, - .markdown-body h6 .octicon-link { - color: var(--fgColor-default); - vertical-align: middle; - visibility: hidden; - } - - .markdown-body h1:hover .anchor, - .markdown-body h2:hover .anchor, - .markdown-body h3:hover .anchor, - .markdown-body h4:hover .anchor, - .markdown-body h5:hover .anchor, - .markdown-body h6:hover .anchor { - text-decoration: none; - } - - .markdown-body h1:hover .anchor .octicon-link, - .markdown-body h2:hover .anchor .octicon-link, - .markdown-body h3:hover .anchor .octicon-link, - .markdown-body h4:hover .anchor .octicon-link, - .markdown-body h5:hover .anchor .octicon-link, - .markdown-body h6:hover .anchor .octicon-link { - visibility: visible; - } - - .markdown-body h1 tt, - .markdown-body h1 code, - .markdown-body h2 tt, - .markdown-body h2 code, - .markdown-body h3 tt, - .markdown-body h3 code, - .markdown-body h4 tt, - .markdown-body h4 code, - .markdown-body h5 tt, - .markdown-body h5 code, - .markdown-body h6 tt, - .markdown-body h6 code { - padding: 0 .2em; - font-size: inherit; - } - - .markdown-body summary h1, - .markdown-body summary h2, - .markdown-body summary h3, - .markdown-body summary h4, - .markdown-body summary h5, - .markdown-body summary h6 { - display: inline-block; - } - - .markdown-body summary h1 .anchor, - .markdown-body summary h2 .anchor, - .markdown-body summary h3 .anchor, - .markdown-body summary h4 .anchor, - .markdown-body summary h5 .anchor, - .markdown-body summary h6 .anchor { - margin-left: -40px; - } - - .markdown-body summary h1, - .markdown-body summary h2 { - padding-bottom: 0; - border-bottom: 0; - } - - .markdown-body ul.no-list, - .markdown-body ol.no-list { - padding: 0; - list-style-type: none; - } - - .markdown-body ol[type="a s"] { +} +.wysiwyg p:empty { + display: none; +} +.wysiwyg h1, +.wysiwyg h2, +.wysiwyg h3, +.wysiwyg h4, +.wysiwyg h5, +.wysiwyg h6 { + font-weight: 700; + line-height: 1.2; +} +.wysiwyg h1:first-child, +.wysiwyg h2:first-child, +.wysiwyg h3:first-child, +.wysiwyg h4:first-child, +.wysiwyg h5:first-child, +.wysiwyg h6:first-child { + margin-top: 0; +} +.wysiwyg h1 { + font-size: 2.4em; + margin-bottom: 0.58333em; + margin-top: 0.58333em; + line-height: 1; +} +.wysiwyg h2 { + font-size: 1.6em; + margin-bottom: 0.875em; + margin-top: 1.75em; + line-height: 1.1; +} +.wysiwyg h3 { + font-size: 1.3em; + margin-bottom: 1.07692em; + margin-top: 1.07692em; +} +.wysiwyg h4 { + font-size: 1.2em; + margin-bottom: 1.16667em; + margin-top: 1.16667em; +} +.wysiwyg h5 { + font-size: 1.1em; + margin-bottom: 1.27273em; + margin-top: 1.27273em; +} +.wysiwyg h6 { + font-size: 1em; + margin-bottom: 1.4em; + margin-top: 1.4em; +} +.wysiwyg dd { + margin-left: 1.4em; +} +.wysiwyg ol, +.wysiwyg ul { + list-style-position: outside; + margin-left: 1.4em; +} +.wysiwyg ol { + list-style-type: decimal; +} +.wysiwyg ol ol { list-style-type: lower-alpha; - } - - .markdown-body ol[type="A s"] { - list-style-type: upper-alpha; - } - - .markdown-body ol[type="i s"] { +} +.wysiwyg ol ol ol { list-style-type: lower-roman; - } - - .markdown-body ol[type="I s"] { - list-style-type: upper-roman; - } - - .markdown-body ol[type="1"] { - list-style-type: decimal; - } - - .markdown-body div>ol:not([type]) { +} +.wysiwyg ol ol ol ol { + list-style-type: lower-greek; +} +.wysiwyg ol ol ol ol ol { list-style-type: decimal; - } - - .markdown-body ul ul, - .markdown-body ul ol, - .markdown-body ol ol, - .markdown-body ol ul { - margin-top: 0; - margin-bottom: 0; - } - - .markdown-body li>p { - margin-top: var(--base-size-16); - } - - .markdown-body li+li { - margin-top: .25em; - } - - .markdown-body dl { - padding: 0; - } - - .markdown-body dl dt { - padding: 0; - margin-top: var(--base-size-16); - font-size: 1em; - font-style: italic; - font-weight: var(--base-text-weight-semibold, 600); - } - - .markdown-body dl dd { - padding: 0 var(--base-size-16); - margin-bottom: var(--base-size-16); - } - - .markdown-body table th { - font-weight: var(--base-text-weight-semibold, 600); - } - - .markdown-body table th, - .markdown-body table td { - padding: 6px 13px; - border: 1px solid var(--borderColor-default); - } - - .markdown-body table td>:last-child { - margin-bottom: 0; - } - - .markdown-body table tr { - background-color: var(--bgColor-default); - border-top: 1px solid var(--borderColor-muted); - } - - .markdown-body table tr:nth-child(2n) { - background-color: var(--bgColor-muted); - } - - .markdown-body table img { - background-color: transparent; - } - - .markdown-body img[align=right] { - padding-left: 20px; - } - - .markdown-body img[align=left] { - padding-right: 20px; - } - - .markdown-body .emoji { - max-width: none; - vertical-align: text-top; - background-color: transparent; - } - - .markdown-body span.frame { - display: block; - overflow: hidden; - } - - .markdown-body span.frame>span { - display: block; - float: left; - width: auto; - padding: 7px; - margin: 13px 0 0; - overflow: hidden; - border: 1px solid var(--borderColor-default); - } - - .markdown-body span.frame span img { - display: block; - float: left; - } - - .markdown-body span.frame span span { - display: block; - padding: 5px 0 0; - clear: both; - color: var(--fgColor-default); - } - - .markdown-body span.align-center { - display: block; - overflow: hidden; - clear: both; - } - - .markdown-body span.align-center>span { - display: block; - margin: 13px auto 0; - overflow: hidden; - text-align: center; - } - - .markdown-body span.align-center span img { - margin: 0 auto; - text-align: center; - } - - .markdown-body span.align-right { - display: block; - overflow: hidden; - clear: both; - } - - .markdown-body span.align-right>span { - display: block; - margin: 13px 0 0; - overflow: hidden; - text-align: right; - } - - .markdown-body span.align-right span img { - margin: 0; - text-align: right; - } - - .markdown-body span.float-left { - display: block; - float: left; - margin-right: 13px; - overflow: hidden; - } - - .markdown-body span.float-left span { - margin: 13px 0 0; - } - - .markdown-body span.float-right { - display: block; - float: right; - margin-left: 13px; - overflow: hidden; - } - - .markdown-body span.float-right>span { - display: block; - margin: 13px auto 0; - overflow: hidden; - text-align: right; - } - - .markdown-body code, - .markdown-body tt { - padding: .2em .4em; - margin: 0; - font-size: 85%; - white-space: break-spaces; - background-color: var(--bgColor-neutral-muted); - border-radius: 6px; - } - - .markdown-body code br, - .markdown-body tt br { - display: none; - } - - .markdown-body del code { - text-decoration: inherit; - } - - .markdown-body samp { - font-size: 85%; - } - - .markdown-body pre code { - font-size: 100%; - } - - .markdown-body pre>code { - padding: 0; - margin: 0; - word-break: normal; - white-space: pre; - background: transparent; - border: 0; - } - - .markdown-body .highlight { - margin-bottom: var(--base-size-16); - } - - .markdown-body .highlight pre { +} +.wysiwyg ol ol ol ol ol ol { + list-style-type: lower-alpha; +} +.wysiwyg ul { + list-style-type: disc; +} +.wysiwyg ul ul { + list-style-type: circle; +} +.wysiwyg ul ul ul { + list-style-type: square; +} +.wysiwyg ul ul ul ul { + list-style-type: circle; +} +.wysiwyg ul ul ul ul ul { + list-style-type: disc; +} +.wysiwyg ul ul ul ul ul ul { + list-style-type: circle; +} +.wysiwyg blockquote { + border-left: 4px solid #e6e6e6; + padding: 0.6em 1.2em; +} +.wysiwyg blockquote p { margin-bottom: 0; - word-break: normal; - } - - .markdown-body .highlight pre, - .markdown-body pre { - padding: var(--base-size-16); +} +.wysiwyg code, +.wysiwyg kbd, +.wysiwyg samp, +.wysiwyg pre { + -moz-osx-font-smoothing: auto; + -webkit-font-smoothing: auto; + background-color: #f2f2f2; + color: #333; + font-size: 0.9em; +} +.wysiwyg code, +.wysiwyg kbd, +.wysiwyg samp { + border-radius: 3px; + line-height: 1.77778; + padding: 0.1em 0.4em 0.2em; + vertical-align: baseline; +} +.wysiwyg pre { overflow: auto; - font-size: 85%; - line-height: 1.45; - color: var(--fgColor-default); - background-color: var(--bgColor-muted); - border-radius: 6px; - } - - .markdown-body pre code, - .markdown-body pre tt { - display: inline; - max-width: auto; - padding: 0; - margin: 0; - overflow: visible; - line-height: inherit; - word-wrap: normal; - background-color: transparent; - border: 0; - } - - .markdown-body .csv-data td, - .markdown-body .csv-data th { - padding: 5px; - overflow: hidden; - font-size: 12px; - line-height: 1; - text-align: left; - white-space: nowrap; - } - - .markdown-body .csv-data .blob-num { - padding: 10px var(--base-size-8) 9px; - text-align: right; - background: var(--bgColor-default); - border: 0; - } - - .markdown-body .csv-data tr { - border-top: 0; - } - - .markdown-body .csv-data th { - font-weight: var(--base-text-weight-semibold, 600); - background: var(--bgColor-muted); - border-top: 0; - } - - .markdown-body [data-footnote-ref]::before { - content: "["; - } - - .markdown-body [data-footnote-ref]::after { - content: "]"; - } - - .markdown-body .footnotes { - font-size: 12px; - color: var(--fgColor-muted); - border-top: 1px solid var(--borderColor-default); - } - - .markdown-body .footnotes ol { - padding-left: var(--base-size-16); - } - - .markdown-body .footnotes ol ul { - display: inline-block; - padding-left: var(--base-size-16); - margin-top: var(--base-size-16); - } - - .markdown-body .footnotes li { - position: relative; - } - - .markdown-body .footnotes li:target::before { - position: absolute; - top: calc(var(--base-size-8)*-1); - right: calc(var(--base-size-8)*-1); - bottom: calc(var(--base-size-8)*-1); - left: calc(var(--base-size-24)*-1); - pointer-events: none; - content: ""; - border: 2px solid var(--borderColor-accent-emphasis); - border-radius: 6px; - } - - .markdown-body .footnotes li:target { - color: var(--fgColor-default); - } - - .markdown-body .footnotes .data-footnote-backref g-emoji { - font-family: monospace; - } - - .markdown-body .pl-c { - color: var(--color-prettylights-syntax-comment); - } - - .markdown-body .pl-c1, - .markdown-body .pl-s .pl-v { - color: var(--color-prettylights-syntax-constant); - } - - .markdown-body .pl-e, - .markdown-body .pl-en { - color: var(--color-prettylights-syntax-entity); - } - - .markdown-body .pl-smi, - .markdown-body .pl-s .pl-s1 { - color: var(--color-prettylights-syntax-storage-modifier-import); - } - - .markdown-body .pl-ent { - color: var(--color-prettylights-syntax-entity-tag); - } - - .markdown-body .pl-k { - color: var(--color-prettylights-syntax-keyword); - } - - .markdown-body .pl-s, - .markdown-body .pl-pds, - .markdown-body .pl-s .pl-pse .pl-s1, - .markdown-body .pl-sr, - .markdown-body .pl-sr .pl-cce, - .markdown-body .pl-sr .pl-sre, - .markdown-body .pl-sr .pl-sra { - color: var(--color-prettylights-syntax-string); - } - - .markdown-body .pl-v, - .markdown-body .pl-smw { - color: var(--color-prettylights-syntax-variable); - } - - .markdown-body .pl-bu { - color: var(--color-prettylights-syntax-brackethighlighter-unmatched); - } - - .markdown-body .pl-ii { - color: var(--color-prettylights-syntax-invalid-illegal-text); - background-color: var(--color-prettylights-syntax-invalid-illegal-bg); - } - - .markdown-body .pl-c2 { - color: var(--color-prettylights-syntax-carriage-return-text); - background-color: var(--color-prettylights-syntax-carriage-return-bg); - } - - .markdown-body .pl-sr .pl-cce { - font-weight: bold; - color: var(--color-prettylights-syntax-string-regexp); - } - - .markdown-body .pl-ml { - color: var(--color-prettylights-syntax-markup-list); - } - - .markdown-body .pl-mh, - .markdown-body .pl-mh .pl-en, - .markdown-body .pl-ms { - font-weight: bold; - color: var(--color-prettylights-syntax-markup-heading); - } - - .markdown-body .pl-mi { - font-style: italic; - color: var(--color-prettylights-syntax-markup-italic); - } - - .markdown-body .pl-mb { - font-weight: bold; - color: var(--color-prettylights-syntax-markup-bold); - } - - .markdown-body .pl-md { - color: var(--color-prettylights-syntax-markup-deleted-text); - background-color: var(--color-prettylights-syntax-markup-deleted-bg); - } - - .markdown-body .pl-mi1 { - color: var(--color-prettylights-syntax-markup-inserted-text); - background-color: var(--color-prettylights-syntax-markup-inserted-bg); - } - - .markdown-body .pl-mc { - color: var(--color-prettylights-syntax-markup-changed-text); - background-color: var(--color-prettylights-syntax-markup-changed-bg); - } - - .markdown-body .pl-mi2 { - color: var(--color-prettylights-syntax-markup-ignored-text); - background-color: var(--color-prettylights-syntax-markup-ignored-bg); - } - - .markdown-body .pl-mdr { - font-weight: bold; - color: var(--color-prettylights-syntax-meta-diff-range); - } - - .markdown-body .pl-ba { - color: var(--color-prettylights-syntax-brackethighlighter-angle); - } - - .markdown-body .pl-sg { - color: var(--color-prettylights-syntax-sublimelinter-gutter-mark); - } - - .markdown-body .pl-corl { - text-decoration: underline; - color: var(--color-prettylights-syntax-constant-other-reference-link); - } - - .markdown-body [role=button]:focus:not(:focus-visible), - .markdown-body [role=tabpanel][tabindex="0"]:focus:not(:focus-visible), - .markdown-body button:focus:not(:focus-visible), - .markdown-body summary:focus:not(:focus-visible), - .markdown-body a:focus:not(:focus-visible) { - outline: none; - box-shadow: none; - } - - .markdown-body [tabindex="0"]:focus:not(:focus-visible), - .markdown-body details-dialog:focus:not(:focus-visible) { - outline: none; - } - - .markdown-body g-emoji { - display: inline-block; - min-width: 1ch; - font-family: "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; + padding: 1em 1.2em; +} +.wysiwyg pre code { + background: none; font-size: 1em; - font-style: normal !important; - font-weight: var(--base-text-weight-normal, 400); - line-height: 1; - vertical-align: -0.075em; - } - - .markdown-body g-emoji img { - width: 1em; - height: 1em; - } - - .markdown-body .task-list-item { - list-style-type: none; - } - - .markdown-body .task-list-item label { - font-weight: var(--base-text-weight-normal, 400); - } - - .markdown-body .task-list-item.enabled label { - cursor: pointer; - } - - .markdown-body .task-list-item+.task-list-item { - margin-top: var(--base-size-4); - } - - .markdown-body .task-list-item .handle { - display: none; - } - - .markdown-body .task-list-item-checkbox { - margin: 0 .2em .25em -1.4em; - vertical-align: middle; - } - - .markdown-body ul:dir(rtl) .task-list-item-checkbox { - margin: 0 -1.6em .25em .2em; - } - - .markdown-body ol:dir(rtl) .task-list-item-checkbox { - margin: 0 -1.6em .25em .2em; - } - - .markdown-body .contains-task-list:hover .task-list-item-convert-container, - .markdown-body .contains-task-list:focus-within .task-list-item-convert-container { - display: block; - width: auto; - height: 24px; - overflow: visible; - clip: auto; - } - - .markdown-body ::-webkit-calendar-picker-indicator { - filter: invert(50%); - } - - .markdown-body .markdown-alert { - padding: var(--base-size-8) var(--base-size-16); - margin-bottom: var(--base-size-16); - color: inherit; - border-left: .25em solid var(--borderColor-default); - } - - .markdown-body .markdown-alert>:first-child { + line-height: 1em; +} +.wysiwyg figure { + margin-bottom: 2.8em; + text-align: center; +} +.wysiwyg figure:first-child { margin-top: 0; - } - - .markdown-body .markdown-alert>:last-child { +} +.wysiwyg figure:last-child { margin-bottom: 0; - } - - .markdown-body .markdown-alert .markdown-alert-title { - display: flex; - font-weight: var(--base-text-weight-medium, 500); - align-items: center; - line-height: 1; - } - - .markdown-body .markdown-alert.markdown-alert-note { - border-left-color: var(--borderColor-accent-emphasis); - } - - .markdown-body .markdown-alert.markdown-alert-note .markdown-alert-title { - color: var(--fgColor-accent); - } - - .markdown-body .markdown-alert.markdown-alert-important { - border-left-color: var(--borderColor-done-emphasis); - } - - .markdown-body .markdown-alert.markdown-alert-important .markdown-alert-title { - color: var(--fgColor-done); - } - - .markdown-body .markdown-alert.markdown-alert-warning { - border-left-color: var(--borderColor-attention-emphasis); - } - - .markdown-body .markdown-alert.markdown-alert-warning .markdown-alert-title { - color: var(--fgColor-attention); - } - - .markdown-body .markdown-alert.markdown-alert-tip { - border-left-color: var(--borderColor-success-emphasis); - } - - .markdown-body .markdown-alert.markdown-alert-tip .markdown-alert-title { - color: var(--fgColor-success); - } - - .markdown-body .markdown-alert.markdown-alert-caution { - border-left-color: var(--borderColor-danger-emphasis); - } - - .markdown-body .markdown-alert.markdown-alert-caution .markdown-alert-title { - color: var(--fgColor-danger); - } - - .markdown-body>*:first-child>.heading-element:first-child { - margin-top: 0 !important; - } +} +.wysiwyg figcaption { + font-size: 0.8em; + margin-top: 0.875em; +} +.wysiwyg table { + width: 100%; +} +.wysiwyg table pre { + white-space: pre-wrap; +} +.wysiwyg th, +.wysiwyg td { + font-size: 1em; + padding: 0.7em; + border: 1px solid #e6e6e6; + line-height: 1.4; +} +.wysiwyg thead tr, +.wysiwyg tfoot tr { + background-color: #f5f5f5; +} +.wysiwyg thead th, +.wysiwyg thead td, +.wysiwyg tfoot th, +.wysiwyg tfoot td { + font-size: 0.9em; + padding: 0.77778em; +} +.wysiwyg thead th code, +.wysiwyg thead td code, +.wysiwyg tfoot th code, +.wysiwyg tfoot td code { + background-color: #fff; +} +.wysiwyg tbody tr { + background-color: #fff; +} diff --git a/internal/util/markdown.go b/internal/util/markdown.go new file mode 100644 index 0000000..2ed5243 --- /dev/null +++ b/internal/util/markdown.go @@ -0,0 +1,20 @@ +package util + +import ( + "github.com/gomarkdown/markdown" + "github.com/gomarkdown/markdown/html" + "github.com/gomarkdown/markdown/parser" +) + +func ParseMarkdown(md []byte) []byte { + extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock + p := parser.NewWithExtensions(extensions) + doc := p.Parse(md) + + // create HTML renderer with extensions + htmlFlags := html.CommonFlags | html.HrefTargetBlank + opts := html.RendererOptions{Flags: htmlFlags} + renderer := html.NewRenderer(opts) + + return markdown.Render(doc, renderer) +} From 03e52f1627f1c8476ed2d1acf7807c5598113c03 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 17 Nov 2024 14:05:18 -0500 Subject: [PATCH 05/20] fix(authentication): db stubs --- go.mod | 12 +- go.sum | 14 + internal/database/database.go | 21 + internal/database/database_mysql.go | 8 + internal/database/database_pg.go | 8 + internal/database/database_sqlite.go | 8 + .../database/databasefakes/fake_database.go | 476 ++++++++++++++++++ internal/server/web/account.html | 0 8 files changed, 545 insertions(+), 2 deletions(-) create mode 100644 internal/server/web/account.html diff --git a/go.mod b/go.mod index c5dcac7..08ac896 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/lukewhrit/spacebin -go 1.22.4 +go 1.23 + +toolchain go1.23.3 require ( github.com/caarlos0/env/v9 v9.0.0 @@ -21,8 +23,13 @@ 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/maxbrunsfeld/counterfeiter/v6 v6.10.0 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + golang.org/x/mod v0.21.0 // indirect + golang.org/x/sync v0.9.0 // indirect + golang.org/x/text v0.20.0 // indirect + golang.org/x/tools v0.26.0 // indirect modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect modernc.org/libc v1.55.3 // indirect modernc.org/mathutil v1.6.0 // indirect @@ -42,7 +49,8 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/sys v0.23.0 // indirect + golang.org/x/crypto v0.29.0 + golang.org/x/sys v0.27.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/sqlite v1.33.1 diff --git a/go.sum b/go.sum index 06b8c9a..239a9cd 100644 --- a/go.sum +++ b/go.sum @@ -60,6 +60,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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/maxbrunsfeld/counterfeiter/v6 v6.10.0 h1:9WsegDYiSKtZXru+NcOB4z7iqb00n4atjmQlyy5TRXI= +github.com/maxbrunsfeld/counterfeiter/v6 v6.10.0/go.mod h1:TeVdzh+5QB5IpWDJAU/uviXA6kOg9yXzLrrjeLKJXqY= 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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -77,19 +79,31 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/internal/database/database.go b/internal/database/database.go index 6f2cd50..8f1c746 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -30,6 +30,19 @@ type Document struct { UpdatedAt time.Time `db:"updated_at" json:"updated_at"` } +type Account struct { + ID string `db:"id" json:"id"` + Username string `db:"username" json:"username"` + Password string `db:"password" json:"password"` + Documents []Document `db:"documents" json:"documents"` +} + +type Session struct { + Public string `db:"public" json:"public"` + Token string `db:"token" json:"token"` + Secret string `db:"secret" json:"secret"` +} + //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . Database type Database interface { Migrate(ctx context.Context) error @@ -37,4 +50,12 @@ type Database interface { GetDocument(ctx context.Context, id string) (Document, error) CreateDocument(ctx context.Context, id, content string) error + + GetAccount(ctx context.Context, id string) (Account, error) + CreateAccount(ctx context.Context, username, password string) error + UpdateAccount(ctx context.Context, id, username, password string) error + DeleteAccount(ctx context.Context, id string) error + + GetSession(ctx context.Context, id string) (Session, error) + CreateSession(ctx context.Context, public, token, secret string) error } diff --git a/internal/database/database_mysql.go b/internal/database/database_mysql.go index b97392a..936b7a7 100644 --- a/internal/database/database_mysql.go +++ b/internal/database/database_mysql.go @@ -77,3 +77,11 @@ func (m *MySQL) CreateDocument(ctx context.Context, id, content string) error { return tx.Commit() } + +func (m *MySQL) GetAccount(ctx context.Context, id string) (Account, error) +func (m *MySQL) CreateAccount(ctx context.Context, username, password string) error +func (m *MySQL) UpdateAccount(ctx context.Context, id, username, password string) error +func (m *MySQL) DeleteAccount(ctx context.Context, id string) error + +func (m *MySQL) GetSession(ctx context.Context, id string) (Session, error) +func (m *MySQL) CreateSession(ctx context.Context, public, token, secret string) error diff --git a/internal/database/database_pg.go b/internal/database/database_pg.go index 83ed2fd..fc60cae 100644 --- a/internal/database/database_pg.go +++ b/internal/database/database_pg.go @@ -70,3 +70,11 @@ func (p *Postgres) CreateDocument(ctx context.Context, id, content string) error return tx.Commit() } + +func (p *Postgres) GetAccount(ctx context.Context, id string) (Account, error) +func (p *Postgres) CreateAccount(ctx context.Context, username, password string) error +func (p *Postgres) UpdateAccount(ctx context.Context, id, username, password string) error +func (p *Postgres) DeleteAccount(ctx context.Context, id string) error + +func (p *Postgres) GetSession(ctx context.Context, id string) (Session, error) +func (p *Postgres) CreateSession(ctx context.Context, public, token, secret string) error diff --git a/internal/database/database_sqlite.go b/internal/database/database_sqlite.go index 288bbcf..246a19b 100644 --- a/internal/database/database_sqlite.go +++ b/internal/database/database_sqlite.go @@ -78,3 +78,11 @@ func (s *SQLite) CreateDocument(ctx context.Context, id, content string) error { return tx.Commit() } + +func (s *SQLite) GetAccount(ctx context.Context, id string) (Account, error) +func (s *SQLite) CreateAccount(ctx context.Context, username, password string) error +func (s *SQLite) UpdateAccount(ctx context.Context, id, username, password string) error +func (s *SQLite) DeleteAccount(ctx context.Context, id string) error + +func (s *SQLite) GetSession(ctx context.Context, id string) (Session, error) +func (s *SQLite) CreateSession(ctx context.Context, public, token, secret string) error diff --git a/internal/database/databasefakes/fake_database.go b/internal/database/databasefakes/fake_database.go index 2421414..9342c60 100644 --- a/internal/database/databasefakes/fake_database.go +++ b/internal/database/databasefakes/fake_database.go @@ -19,6 +19,19 @@ type FakeDatabase struct { closeReturnsOnCall map[int]struct { result1 error } + CreateAccountStub func(context.Context, string, string) error + createAccountMutex sync.RWMutex + createAccountArgsForCall []struct { + arg1 context.Context + arg2 string + arg3 string + } + createAccountReturns struct { + result1 error + } + createAccountReturnsOnCall map[int]struct { + result1 error + } CreateDocumentStub func(context.Context, string, string) error createDocumentMutex sync.RWMutex createDocumentArgsForCall []struct { @@ -32,6 +45,46 @@ type FakeDatabase struct { createDocumentReturnsOnCall map[int]struct { result1 error } + CreateSessionStub func(context.Context, string, string, string) error + createSessionMutex sync.RWMutex + createSessionArgsForCall []struct { + arg1 context.Context + arg2 string + arg3 string + arg4 string + } + createSessionReturns struct { + result1 error + } + createSessionReturnsOnCall map[int]struct { + result1 error + } + DeleteAccountStub func(context.Context, string) error + deleteAccountMutex sync.RWMutex + deleteAccountArgsForCall []struct { + arg1 context.Context + arg2 string + } + deleteAccountReturns struct { + result1 error + } + deleteAccountReturnsOnCall map[int]struct { + result1 error + } + GetAccountStub func(context.Context, string) (database.Account, error) + getAccountMutex sync.RWMutex + getAccountArgsForCall []struct { + arg1 context.Context + arg2 string + } + getAccountReturns struct { + result1 database.Account + result2 error + } + getAccountReturnsOnCall map[int]struct { + result1 database.Account + result2 error + } GetDocumentStub func(context.Context, string) (database.Document, error) getDocumentMutex sync.RWMutex getDocumentArgsForCall []struct { @@ -46,6 +99,20 @@ type FakeDatabase struct { result1 database.Document result2 error } + GetSessionStub func(context.Context, string) (database.Session, error) + getSessionMutex sync.RWMutex + getSessionArgsForCall []struct { + arg1 context.Context + arg2 string + } + getSessionReturns struct { + result1 database.Session + result2 error + } + getSessionReturnsOnCall map[int]struct { + result1 database.Session + result2 error + } MigrateStub func(context.Context) error migrateMutex sync.RWMutex migrateArgsForCall []struct { @@ -57,6 +124,20 @@ type FakeDatabase struct { migrateReturnsOnCall map[int]struct { result1 error } + UpdateAccountStub func(context.Context, string, string, string) error + updateAccountMutex sync.RWMutex + updateAccountArgsForCall []struct { + arg1 context.Context + arg2 string + arg3 string + arg4 string + } + updateAccountReturns struct { + result1 error + } + updateAccountReturnsOnCall map[int]struct { + result1 error + } invocations map[string][][]interface{} invocationsMutex sync.RWMutex } @@ -114,6 +195,69 @@ func (fake *FakeDatabase) CloseReturnsOnCall(i int, result1 error) { }{result1} } +func (fake *FakeDatabase) CreateAccount(arg1 context.Context, arg2 string, arg3 string) error { + fake.createAccountMutex.Lock() + ret, specificReturn := fake.createAccountReturnsOnCall[len(fake.createAccountArgsForCall)] + fake.createAccountArgsForCall = append(fake.createAccountArgsForCall, struct { + arg1 context.Context + arg2 string + arg3 string + }{arg1, arg2, arg3}) + stub := fake.CreateAccountStub + fakeReturns := fake.createAccountReturns + fake.recordInvocation("CreateAccount", []interface{}{arg1, arg2, arg3}) + fake.createAccountMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeDatabase) CreateAccountCallCount() int { + fake.createAccountMutex.RLock() + defer fake.createAccountMutex.RUnlock() + return len(fake.createAccountArgsForCall) +} + +func (fake *FakeDatabase) CreateAccountCalls(stub func(context.Context, string, string) error) { + fake.createAccountMutex.Lock() + defer fake.createAccountMutex.Unlock() + fake.CreateAccountStub = stub +} + +func (fake *FakeDatabase) CreateAccountArgsForCall(i int) (context.Context, string, string) { + fake.createAccountMutex.RLock() + defer fake.createAccountMutex.RUnlock() + argsForCall := fake.createAccountArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3 +} + +func (fake *FakeDatabase) CreateAccountReturns(result1 error) { + fake.createAccountMutex.Lock() + defer fake.createAccountMutex.Unlock() + fake.CreateAccountStub = nil + fake.createAccountReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeDatabase) CreateAccountReturnsOnCall(i int, result1 error) { + fake.createAccountMutex.Lock() + defer fake.createAccountMutex.Unlock() + fake.CreateAccountStub = nil + if fake.createAccountReturnsOnCall == nil { + fake.createAccountReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.createAccountReturnsOnCall[i] = struct { + result1 error + }{result1} +} + func (fake *FakeDatabase) CreateDocument(arg1 context.Context, arg2 string, arg3 string) error { fake.createDocumentMutex.Lock() ret, specificReturn := fake.createDocumentReturnsOnCall[len(fake.createDocumentArgsForCall)] @@ -177,6 +321,197 @@ func (fake *FakeDatabase) CreateDocumentReturnsOnCall(i int, result1 error) { }{result1} } +func (fake *FakeDatabase) CreateSession(arg1 context.Context, arg2 string, arg3 string, arg4 string) error { + fake.createSessionMutex.Lock() + ret, specificReturn := fake.createSessionReturnsOnCall[len(fake.createSessionArgsForCall)] + fake.createSessionArgsForCall = append(fake.createSessionArgsForCall, struct { + arg1 context.Context + arg2 string + arg3 string + arg4 string + }{arg1, arg2, arg3, arg4}) + stub := fake.CreateSessionStub + fakeReturns := fake.createSessionReturns + fake.recordInvocation("CreateSession", []interface{}{arg1, arg2, arg3, arg4}) + fake.createSessionMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3, arg4) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeDatabase) CreateSessionCallCount() int { + fake.createSessionMutex.RLock() + defer fake.createSessionMutex.RUnlock() + return len(fake.createSessionArgsForCall) +} + +func (fake *FakeDatabase) CreateSessionCalls(stub func(context.Context, string, string, string) error) { + fake.createSessionMutex.Lock() + defer fake.createSessionMutex.Unlock() + fake.CreateSessionStub = stub +} + +func (fake *FakeDatabase) CreateSessionArgsForCall(i int) (context.Context, string, string, string) { + fake.createSessionMutex.RLock() + defer fake.createSessionMutex.RUnlock() + argsForCall := fake.createSessionArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4 +} + +func (fake *FakeDatabase) CreateSessionReturns(result1 error) { + fake.createSessionMutex.Lock() + defer fake.createSessionMutex.Unlock() + fake.CreateSessionStub = nil + fake.createSessionReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeDatabase) CreateSessionReturnsOnCall(i int, result1 error) { + fake.createSessionMutex.Lock() + defer fake.createSessionMutex.Unlock() + fake.CreateSessionStub = nil + if fake.createSessionReturnsOnCall == nil { + fake.createSessionReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.createSessionReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakeDatabase) DeleteAccount(arg1 context.Context, arg2 string) error { + fake.deleteAccountMutex.Lock() + ret, specificReturn := fake.deleteAccountReturnsOnCall[len(fake.deleteAccountArgsForCall)] + fake.deleteAccountArgsForCall = append(fake.deleteAccountArgsForCall, struct { + arg1 context.Context + arg2 string + }{arg1, arg2}) + stub := fake.DeleteAccountStub + fakeReturns := fake.deleteAccountReturns + fake.recordInvocation("DeleteAccount", []interface{}{arg1, arg2}) + fake.deleteAccountMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeDatabase) DeleteAccountCallCount() int { + fake.deleteAccountMutex.RLock() + defer fake.deleteAccountMutex.RUnlock() + return len(fake.deleteAccountArgsForCall) +} + +func (fake *FakeDatabase) DeleteAccountCalls(stub func(context.Context, string) error) { + fake.deleteAccountMutex.Lock() + defer fake.deleteAccountMutex.Unlock() + fake.DeleteAccountStub = stub +} + +func (fake *FakeDatabase) DeleteAccountArgsForCall(i int) (context.Context, string) { + fake.deleteAccountMutex.RLock() + defer fake.deleteAccountMutex.RUnlock() + argsForCall := fake.deleteAccountArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeDatabase) DeleteAccountReturns(result1 error) { + fake.deleteAccountMutex.Lock() + defer fake.deleteAccountMutex.Unlock() + fake.DeleteAccountStub = nil + fake.deleteAccountReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeDatabase) DeleteAccountReturnsOnCall(i int, result1 error) { + fake.deleteAccountMutex.Lock() + defer fake.deleteAccountMutex.Unlock() + fake.DeleteAccountStub = nil + if fake.deleteAccountReturnsOnCall == nil { + fake.deleteAccountReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.deleteAccountReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakeDatabase) GetAccount(arg1 context.Context, arg2 string) (database.Account, error) { + fake.getAccountMutex.Lock() + ret, specificReturn := fake.getAccountReturnsOnCall[len(fake.getAccountArgsForCall)] + fake.getAccountArgsForCall = append(fake.getAccountArgsForCall, struct { + arg1 context.Context + arg2 string + }{arg1, arg2}) + stub := fake.GetAccountStub + fakeReturns := fake.getAccountReturns + fake.recordInvocation("GetAccount", []interface{}{arg1, arg2}) + fake.getAccountMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeDatabase) GetAccountCallCount() int { + fake.getAccountMutex.RLock() + defer fake.getAccountMutex.RUnlock() + return len(fake.getAccountArgsForCall) +} + +func (fake *FakeDatabase) GetAccountCalls(stub func(context.Context, string) (database.Account, error)) { + fake.getAccountMutex.Lock() + defer fake.getAccountMutex.Unlock() + fake.GetAccountStub = stub +} + +func (fake *FakeDatabase) GetAccountArgsForCall(i int) (context.Context, string) { + fake.getAccountMutex.RLock() + defer fake.getAccountMutex.RUnlock() + argsForCall := fake.getAccountArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeDatabase) GetAccountReturns(result1 database.Account, result2 error) { + fake.getAccountMutex.Lock() + defer fake.getAccountMutex.Unlock() + fake.GetAccountStub = nil + fake.getAccountReturns = struct { + result1 database.Account + result2 error + }{result1, result2} +} + +func (fake *FakeDatabase) GetAccountReturnsOnCall(i int, result1 database.Account, result2 error) { + fake.getAccountMutex.Lock() + defer fake.getAccountMutex.Unlock() + fake.GetAccountStub = nil + if fake.getAccountReturnsOnCall == nil { + fake.getAccountReturnsOnCall = make(map[int]struct { + result1 database.Account + result2 error + }) + } + fake.getAccountReturnsOnCall[i] = struct { + result1 database.Account + result2 error + }{result1, result2} +} + func (fake *FakeDatabase) GetDocument(arg1 context.Context, arg2 string) (database.Document, error) { fake.getDocumentMutex.Lock() ret, specificReturn := fake.getDocumentReturnsOnCall[len(fake.getDocumentArgsForCall)] @@ -242,6 +577,71 @@ func (fake *FakeDatabase) GetDocumentReturnsOnCall(i int, result1 database.Docum }{result1, result2} } +func (fake *FakeDatabase) GetSession(arg1 context.Context, arg2 string) (database.Session, error) { + fake.getSessionMutex.Lock() + ret, specificReturn := fake.getSessionReturnsOnCall[len(fake.getSessionArgsForCall)] + fake.getSessionArgsForCall = append(fake.getSessionArgsForCall, struct { + arg1 context.Context + arg2 string + }{arg1, arg2}) + stub := fake.GetSessionStub + fakeReturns := fake.getSessionReturns + fake.recordInvocation("GetSession", []interface{}{arg1, arg2}) + fake.getSessionMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeDatabase) GetSessionCallCount() int { + fake.getSessionMutex.RLock() + defer fake.getSessionMutex.RUnlock() + return len(fake.getSessionArgsForCall) +} + +func (fake *FakeDatabase) GetSessionCalls(stub func(context.Context, string) (database.Session, error)) { + fake.getSessionMutex.Lock() + defer fake.getSessionMutex.Unlock() + fake.GetSessionStub = stub +} + +func (fake *FakeDatabase) GetSessionArgsForCall(i int) (context.Context, string) { + fake.getSessionMutex.RLock() + defer fake.getSessionMutex.RUnlock() + argsForCall := fake.getSessionArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeDatabase) GetSessionReturns(result1 database.Session, result2 error) { + fake.getSessionMutex.Lock() + defer fake.getSessionMutex.Unlock() + fake.GetSessionStub = nil + fake.getSessionReturns = struct { + result1 database.Session + result2 error + }{result1, result2} +} + +func (fake *FakeDatabase) GetSessionReturnsOnCall(i int, result1 database.Session, result2 error) { + fake.getSessionMutex.Lock() + defer fake.getSessionMutex.Unlock() + fake.GetSessionStub = nil + if fake.getSessionReturnsOnCall == nil { + fake.getSessionReturnsOnCall = make(map[int]struct { + result1 database.Session + result2 error + }) + } + fake.getSessionReturnsOnCall[i] = struct { + result1 database.Session + result2 error + }{result1, result2} +} + func (fake *FakeDatabase) Migrate(arg1 context.Context) error { fake.migrateMutex.Lock() ret, specificReturn := fake.migrateReturnsOnCall[len(fake.migrateArgsForCall)] @@ -303,17 +703,93 @@ func (fake *FakeDatabase) MigrateReturnsOnCall(i int, result1 error) { }{result1} } +func (fake *FakeDatabase) UpdateAccount(arg1 context.Context, arg2 string, arg3 string, arg4 string) error { + fake.updateAccountMutex.Lock() + ret, specificReturn := fake.updateAccountReturnsOnCall[len(fake.updateAccountArgsForCall)] + fake.updateAccountArgsForCall = append(fake.updateAccountArgsForCall, struct { + arg1 context.Context + arg2 string + arg3 string + arg4 string + }{arg1, arg2, arg3, arg4}) + stub := fake.UpdateAccountStub + fakeReturns := fake.updateAccountReturns + fake.recordInvocation("UpdateAccount", []interface{}{arg1, arg2, arg3, arg4}) + fake.updateAccountMutex.Unlock() + if stub != nil { + return stub(arg1, arg2, arg3, arg4) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeDatabase) UpdateAccountCallCount() int { + fake.updateAccountMutex.RLock() + defer fake.updateAccountMutex.RUnlock() + return len(fake.updateAccountArgsForCall) +} + +func (fake *FakeDatabase) UpdateAccountCalls(stub func(context.Context, string, string, string) error) { + fake.updateAccountMutex.Lock() + defer fake.updateAccountMutex.Unlock() + fake.UpdateAccountStub = stub +} + +func (fake *FakeDatabase) UpdateAccountArgsForCall(i int) (context.Context, string, string, string) { + fake.updateAccountMutex.RLock() + defer fake.updateAccountMutex.RUnlock() + argsForCall := fake.updateAccountArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4 +} + +func (fake *FakeDatabase) UpdateAccountReturns(result1 error) { + fake.updateAccountMutex.Lock() + defer fake.updateAccountMutex.Unlock() + fake.UpdateAccountStub = nil + fake.updateAccountReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeDatabase) UpdateAccountReturnsOnCall(i int, result1 error) { + fake.updateAccountMutex.Lock() + defer fake.updateAccountMutex.Unlock() + fake.UpdateAccountStub = nil + if fake.updateAccountReturnsOnCall == nil { + fake.updateAccountReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.updateAccountReturnsOnCall[i] = struct { + result1 error + }{result1} +} + func (fake *FakeDatabase) Invocations() map[string][][]interface{} { fake.invocationsMutex.RLock() defer fake.invocationsMutex.RUnlock() fake.closeMutex.RLock() defer fake.closeMutex.RUnlock() + fake.createAccountMutex.RLock() + defer fake.createAccountMutex.RUnlock() fake.createDocumentMutex.RLock() defer fake.createDocumentMutex.RUnlock() + fake.createSessionMutex.RLock() + defer fake.createSessionMutex.RUnlock() + fake.deleteAccountMutex.RLock() + defer fake.deleteAccountMutex.RUnlock() + fake.getAccountMutex.RLock() + defer fake.getAccountMutex.RUnlock() fake.getDocumentMutex.RLock() defer fake.getDocumentMutex.RUnlock() + fake.getSessionMutex.RLock() + defer fake.getSessionMutex.RUnlock() fake.migrateMutex.RLock() defer fake.migrateMutex.RUnlock() + fake.updateAccountMutex.RLock() + defer fake.updateAccountMutex.RUnlock() copiedInvocations := map[string][][]interface{}{} for key, value := range fake.invocations { copiedInvocations[key] = value diff --git a/internal/server/web/account.html b/internal/server/web/account.html new file mode 100644 index 0000000..e69de29 From 6361f7dab61367a031e0e92673f7258de3630c95 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 17 Nov 2024 15:58:54 -0500 Subject: [PATCH 06/20] feat(database): add authentication methods (untested) --- internal/database/database.go | 10 +-- internal/database/database_mysql.go | 87 +++++++++++++++++++++-- internal/database/database_pg.go | 87 +++++++++++++++++++++-- internal/database/database_sqlite.go | 100 +++++++++++++++++++++++++-- internal/util/authentication.go | 76 ++++++++++++++++++++ 5 files changed, 335 insertions(+), 25 deletions(-) create mode 100644 internal/util/authentication.go diff --git a/internal/database/database.go b/internal/database/database.go index 8f1c746..099da02 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -31,10 +31,10 @@ type Document struct { } type Account struct { - ID string `db:"id" json:"id"` - Username string `db:"username" json:"username"` - Password string `db:"password" json:"password"` - Documents []Document `db:"documents" json:"documents"` + ID string `db:"id" json:"id"` + Username string `db:"username" json:"username"` + Password string `db:"password" json:"password"` + // Documents []Document `db:"documents" json:"documents"` } type Session struct { @@ -53,7 +53,7 @@ type Database interface { GetAccount(ctx context.Context, id string) (Account, error) CreateAccount(ctx context.Context, username, password string) error - UpdateAccount(ctx context.Context, id, username, password string) error + // UpdateAccount(ctx context.Context, id, username, password string) error DeleteAccount(ctx context.Context, id string) error GetSession(ctx context.Context, id string) (Session, error) diff --git a/internal/database/database_mysql.go b/internal/database/database_mysql.go index 936b7a7..08f0501 100644 --- a/internal/database/database_mysql.go +++ b/internal/database/database_mysql.go @@ -24,6 +24,7 @@ import ( "time" _ "github.com/go-sql-driver/mysql" + "github.com/lukewhrit/spacebin/internal/util" ) type MySQL struct { @@ -48,7 +49,19 @@ CREATE TABLE IF NOT EXISTS documents ( content TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -)`) +); + +CREATE TABLE IF NOT EXISTS accounts ( + id SERIAL PRIMARY KEY, + username varchar(255) NOT NULL, + password varchar(255) NOT NULL +); + +CREATE TABLE IF NOT EXISTS sessions ( + public varchar(255) PRIMARY KEY, + token varchar(255) NOT NULL, + secret varchar +);`) return err } @@ -78,10 +91,70 @@ func (m *MySQL) CreateDocument(ctx context.Context, id, content string) error { return tx.Commit() } -func (m *MySQL) GetAccount(ctx context.Context, id string) (Account, error) -func (m *MySQL) CreateAccount(ctx context.Context, username, password string) error -func (m *MySQL) UpdateAccount(ctx context.Context, id, username, password string) error -func (m *MySQL) DeleteAccount(ctx context.Context, id string) error +func (m *MySQL) GetAccount(ctx context.Context, id string) (Account, error) { + acc := new(Account) + row := m.QueryRow("SELECT * FROM accounts WHERE id=?", id) + err := row.Scan(&acc.ID, &acc.Username, &acc.Password) + + return *acc, err +} + +func (m *MySQL) CreateAccount(ctx context.Context, username, password string) error { + tx, err := m.Begin() + + if err != nil { + return err + } + + // Add account to database + // Hash and salt the password + _, err = tx.Exec("INSERT INTO accounts (username, password) VALUES ($1, $2)", + username, util.HashAndSalt([]byte(password))) + + if err != nil { + return err + } + + return tx.Commit() +} + +func (m *MySQL) DeleteAccount(ctx context.Context, id string) error { + tx, err := m.Begin() + + if err != nil { + return err + } + + _, err = tx.Exec("DELETE FROM accounts WHERE id=$1", id) + + if err != nil { + return err + } + + return tx.Commit() +} + +func (m *MySQL) GetSession(ctx context.Context, id string) (Session, error) { + session := new(Session) + row := m.QueryRow("SELECT * FROM sessions WHERE id=$1", id) + err := row.Scan(&session.Public, &session.Token, &session.Secret) + + return *session, err +} + +func (m *MySQL) CreateSession(ctx context.Context, public, token, secret string) error { + tx, err := m.Begin() + + if err != nil { + return err + } + + _, err = tx.Exec("INSERT INTO sessions (public, token, secret) VALUES ($1, $2, $3)", + public, token, secret) -func (m *MySQL) GetSession(ctx context.Context, id string) (Session, error) -func (m *MySQL) CreateSession(ctx context.Context, public, token, secret string) error + if err != nil { + return err + } + + return tx.Commit() +} diff --git a/internal/database/database_pg.go b/internal/database/database_pg.go index fc60cae..1f4e72a 100644 --- a/internal/database/database_pg.go +++ b/internal/database/database_pg.go @@ -22,6 +22,7 @@ import ( "net/url" _ "github.com/lib/pq" + "github.com/lukewhrit/spacebin/internal/util" ) type Postgres struct { @@ -41,7 +42,19 @@ CREATE TABLE IF NOT EXISTS documents ( content text NOT NULL, created_at timestamp with time zone DEFAULT now(), updated_at timestamp with time zone DEFAULT now() -)`) +); + +CREATE TABLE IF NOT EXISTS accounts ( + id SERIAL PRIMARY KEY, + username varchar(255) NOT NULL, + password varchar(255) NOT NULL +); + +CREATE TABLE IF NOT EXISTS sessions ( + public varchar(255) PRIMARY KEY, + token varchar(255) NOT NULL, + secret varchar +);`) return err } @@ -71,10 +84,70 @@ func (p *Postgres) CreateDocument(ctx context.Context, id, content string) error return tx.Commit() } -func (p *Postgres) GetAccount(ctx context.Context, id string) (Account, error) -func (p *Postgres) CreateAccount(ctx context.Context, username, password string) error -func (p *Postgres) UpdateAccount(ctx context.Context, id, username, password string) error -func (p *Postgres) DeleteAccount(ctx context.Context, id string) error +func (p *Postgres) GetAccount(ctx context.Context, id string) (Account, error) { + account := new(Account) + row := p.QueryRow("SELECT * FROM accounts WHERE id=$1", id) + err := row.Scan(&account.ID, &account.Username, &account.Password) + + return *account, err +} + +func (p *Postgres) CreateAccount(ctx context.Context, username, password string) error { + tx, err := p.Begin() + + if err != nil { + return err + } + + // Add account to database + // Hash and salt the password + _, err = tx.Exec("INSERT INTO accounts (username, password) VALUES ($1, $2)", + username, util.HashAndSalt([]byte(password))) + + if err != nil { + return err + } + + return tx.Commit() +} + +func (p *Postgres) DeleteAccount(ctx context.Context, id string) error { + tx, err := p.Begin() + + if err != nil { + return err + } + + _, err = tx.Exec("DELETE FROM accounts WHERE id=$1", id) + + if err != nil { + return err + } + + return tx.Commit() +} + +func (p *Postgres) GetSession(ctx context.Context, id string) (Session, error) { + session := new(Session) + row := p.QueryRow("SELECT * FROM sessions WHERE id=$1", id) + err := row.Scan(&session.Public, &session.Token, &session.Secret) + + return *session, err +} + +func (p *Postgres) CreateSession(ctx context.Context, public, token, secret string) error { + tx, err := p.Begin() + + if err != nil { + return err + } + + _, err = tx.Exec("INSERT INTO sessions (public, token, secret) VALUES ($1, $2, $3)", + public, token, secret) -func (p *Postgres) GetSession(ctx context.Context, id string) (Session, error) -func (p *Postgres) CreateSession(ctx context.Context, public, token, secret string) error + if err != nil { + return err + } + + return tx.Commit() +} diff --git a/internal/database/database_sqlite.go b/internal/database/database_sqlite.go index 246a19b..5b6dff5 100644 --- a/internal/database/database_sqlite.go +++ b/internal/database/database_sqlite.go @@ -22,6 +22,7 @@ import ( "net/url" "sync" + "github.com/lukewhrit/spacebin/internal/util" _ "modernc.org/sqlite" ) @@ -43,6 +44,18 @@ CREATE TABLE IF NOT EXISTS documents ( content TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, usdated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS accounts ( + id SERIAL PRIMARY KEY, + username varchar(255) NOT NULL, + password varchar(255) NOT NULL +); + +CREATE TABLE IF NOT EXISTS sessions ( + public varchar(255) PRIMARY KEY, + token varchar(255) NOT NULL, + secret varchar );`) return err @@ -79,10 +92,85 @@ func (s *SQLite) CreateDocument(ctx context.Context, id, content string) error { return tx.Commit() } -func (s *SQLite) GetAccount(ctx context.Context, id string) (Account, error) -func (s *SQLite) CreateAccount(ctx context.Context, username, password string) error -func (s *SQLite) UpdateAccount(ctx context.Context, id, username, password string) error -func (s *SQLite) DeleteAccount(ctx context.Context, id string) error +func (s *SQLite) GetAccount(ctx context.Context, id string) (Account, error) { + s.RLock() + defer s.RUnlock() + + acc := new(Account) + row := s.QueryRow("SELECT * FROM accounts WHERE id=?", id) + err := row.Scan(&acc.ID, &acc.Username, &acc.Password) + + return *acc, err +} + +func (s *SQLite) CreateAccount(ctx context.Context, username, password string) error { + s.Lock() + defer s.Unlock() + + tx, err := s.Begin() + + if err != nil { + return err + } + + // Add account to database + // Hash and salt the password + _, err = tx.Exec("INSERT INTO accounts (username, password) VALUES ($1, $2)", + username, util.HashAndSalt([]byte(password))) + + if err != nil { + return err + } + + return tx.Commit() +} + +func (s *SQLite) DeleteAccount(ctx context.Context, id string) error { + s.Lock() + defer s.Unlock() + + tx, err := s.Begin() + + if err != nil { + return err + } -func (s *SQLite) GetSession(ctx context.Context, id string) (Session, error) -func (s *SQLite) CreateSession(ctx context.Context, public, token, secret string) error + _, err = tx.Exec("DELETE FROM accounts WHERE id=$1", id) + + if err != nil { + return err + } + + return tx.Commit() +} + +func (s *SQLite) GetSession(ctx context.Context, id string) (Session, error) { + s.RLock() + defer s.RUnlock() + + session := new(Session) + row := s.QueryRow("SELECT * FROM sessions WHERE id=?", id) + err := row.Scan(&session.Public, &session.Token, &session.Secret) + + return *session, err +} + +func (s *SQLite) CreateSession(ctx context.Context, public, token, secret string) error { + s.Lock() + defer s.Unlock() + + tx, err := s.Begin() + + if err != nil { + return err + } + + _, err = tx.Exec("INSERT INTO sessions (public, token, secret) VALUES ($1, $2, $3)", + public, token, secret) + + if err != nil { + return err + } + + return tx.Commit() +} diff --git a/internal/util/authentication.go b/internal/util/authentication.go new file mode 100644 index 0000000..47c733f --- /dev/null +++ b/internal/util/authentication.go @@ -0,0 +1,76 @@ +package util + +import ( + "crypto/rand" + "fmt" + "log" + "strings" + + "golang.org/x/crypto/bcrypt" +) + +type Token struct { + Version string + Public string + Secret string + Salt string +} + +func HashAndSalt(pwd []byte) string { + // Use GenerateFromPassword to hash & salt pwd. + // MinCost is just an integer constant provided by the bcrypt + // package along with DefaultCost & MaxCost. + // The cost can be any value you want provided it isn't lower + // than the MinCost (4) + hash, err := bcrypt.GenerateFromPassword(pwd, bcrypt.MinCost) + if err != nil { + log.Fatalln(err) + } // GenerateFromPassword returns a byte slice so we need to + // convert the bytes to a string and return it + return string(hash) +} + +func PrngString() (string, error) { + b := make([]byte, 10) + _, err := rand.Read(b) + if err != nil { + return "", err + } + + return fmt.Sprintf("%x", b), nil +} + +func GenerateStrings(bits []int) (a, b, c string, err error) { + if a, err = PrngString(); err != nil { + return "", "", "", err + } + + if b, err = PrngString(); err != nil { + return "", "", "", err + } + + if c, err = PrngString(); err != nil { + return "", "", "", err + } + + return a, b, c, err +} + +func ParseToken(token string) (Token, error) { + var tok Token + toks := strings.Split(token, ".") + + tok.Version = toks[0] + tok.Public = toks[1] + tok.Secret = toks[2] + + if len(toks) == 4 { + tok.Salt = toks[3] + } + + return tok, nil +} + +func MakeToken(token Token) string { + return fmt.Sprintf("%s.%s.%s.%s", token.Version, token.Public, token.Secret, token.Salt) +} From 8edf35c51f69dfd73e36ec9e29136d013eaf24da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 06:47:14 +0000 Subject: [PATCH 07/20] build(deps): bump modernc.org/sqlite from 1.33.1 to 1.34.1 Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.33.1 to 1.34.1. - [Commits](https://gitlab.com/cznic/sqlite/compare/v1.33.1...v1.34.1) --- updated-dependencies: - dependency-name: modernc.org/sqlite dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 5 +---- go.sum | 16 ++-------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 08ac896..67a28b0 100644 --- a/go.mod +++ b/go.mod @@ -23,12 +23,9 @@ 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/maxbrunsfeld/counterfeiter/v6 v6.10.0 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - golang.org/x/mod v0.21.0 // indirect golang.org/x/sync v0.9.0 // indirect - golang.org/x/text v0.20.0 // indirect golang.org/x/tools v0.26.0 // indirect modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect modernc.org/libc v1.55.3 // indirect @@ -53,5 +50,5 @@ require ( golang.org/x/sys v0.27.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/sqlite v1.33.1 + modernc.org/sqlite v1.34.1 ) diff --git a/go.sum b/go.sum index 239a9cd..09c852b 100644 --- a/go.sum +++ b/go.sum @@ -60,8 +60,6 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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/maxbrunsfeld/counterfeiter/v6 v6.10.0 h1:9WsegDYiSKtZXru+NcOB4z7iqb00n4atjmQlyy5TRXI= -github.com/maxbrunsfeld/counterfeiter/v6 v6.10.0/go.mod h1:TeVdzh+5QB5IpWDJAU/uviXA6kOg9yXzLrrjeLKJXqY= 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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -83,25 +81,15 @@ golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -130,8 +118,8 @@ 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.33.1 h1:trb6Z3YYoeM9eDL1O8do81kP+0ejv+YzgyFo+Gwy0nM= -modernc.org/sqlite v1.33.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k= +modernc.org/sqlite v1.34.1 h1:u3Yi6M0N8t9yKRDwhXcyp1eS5/ErhPTBggxWFuR6Hfk= +modernc.org/sqlite v1.34.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= From c027545b86a8686336c15951c62d47b86b437d7c Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 18 Nov 2024 21:17:23 -0500 Subject: [PATCH 08/20] feat(authentication): signup and signin routes --- internal/database/database.go | 3 +- internal/database/database_mysql.go | 29 +++- internal/database/database_pg.go | 8 + internal/database/database_sqlite.go | 22 ++- .../database/databasefakes/fake_database.go | 161 +++++++++--------- internal/server/authentication.go | 106 ++++++++++++ internal/server/server.go | 6 + internal/util/authentication.go | 7 - internal/util/domain.go | 25 +++ internal/util/helpers.go | 4 - 10 files changed, 264 insertions(+), 107 deletions(-) create mode 100644 internal/server/authentication.go diff --git a/internal/database/database.go b/internal/database/database.go index 099da02..0b4cec0 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -31,7 +31,7 @@ type Document struct { } type Account struct { - ID string `db:"id" json:"id"` + ID int `db:"id" json:"id"` Username string `db:"username" json:"username"` Password string `db:"password" json:"password"` // Documents []Document `db:"documents" json:"documents"` @@ -52,6 +52,7 @@ type Database interface { CreateDocument(ctx context.Context, id, content string) error GetAccount(ctx context.Context, id string) (Account, error) + GetAccountByUsername(ctx context.Context, username string) (Account, error) CreateAccount(ctx context.Context, username, password string) error // UpdateAccount(ctx context.Context, id, username, password string) error DeleteAccount(ctx context.Context, id string) error diff --git a/internal/database/database_mysql.go b/internal/database/database_mysql.go index 08f0501..fb332fa 100644 --- a/internal/database/database_mysql.go +++ b/internal/database/database_mysql.go @@ -45,22 +45,27 @@ func NewMySQL(uri *url.URL) (Database, error) { func (m *MySQL) Migrate(ctx context.Context) error { _, err := m.Exec(` CREATE TABLE IF NOT EXISTS documents ( - id VARCHAR(255) PRIMARY KEY, + id VARCHAR(255) NOT NULL, content TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + + PRIMARY KEY (id) ); CREATE TABLE IF NOT EXISTS accounts ( - id SERIAL PRIMARY KEY, - username varchar(255) NOT NULL, - password varchar(255) NOT NULL + id INT NOT NULL AUTO_INCREMENT, + username VARCHAR(255) NOT NULL, + password VARCHAR(255) NOT NULL, + + PRIMARY_KEY(id) ); CREATE TABLE IF NOT EXISTS sessions ( - public varchar(255) PRIMARY KEY, - token varchar(255) NOT NULL, - secret varchar + public VARCHAR(255) NOT NULL, + token VARCHAR(255) NOT NULL, + secret TEXT NOT NULL, + PRIMARY_KEY(public) );`) return err @@ -99,6 +104,14 @@ func (m *MySQL) GetAccount(ctx context.Context, id string) (Account, error) { return *acc, err } +func (m *MySQL) GetAccountByUsername(ctx context.Context, username string) (Account, error) { + account := new(Account) + row := m.QueryRow("SELECT * FROM accounts WHERE username=$1", username) + err := row.Scan(&account.ID, &account.Username, &account.Password) + + return *account, err +} + func (m *MySQL) CreateAccount(ctx context.Context, username, password string) error { tx, err := m.Begin() diff --git a/internal/database/database_pg.go b/internal/database/database_pg.go index 1f4e72a..27b5cab 100644 --- a/internal/database/database_pg.go +++ b/internal/database/database_pg.go @@ -92,6 +92,14 @@ func (p *Postgres) GetAccount(ctx context.Context, id string) (Account, error) { return *account, err } +func (p *Postgres) GetAccountByUsername(ctx context.Context, username string) (Account, error) { + account := new(Account) + row := p.QueryRow("SELECT * FROM accounts WHERE username=$1", username) + err := row.Scan(&account.ID, &account.Username, &account.Password) + + return *account, err +} + func (p *Postgres) CreateAccount(ctx context.Context, username, password string) error { tx, err := p.Begin() diff --git a/internal/database/database_sqlite.go b/internal/database/database_sqlite.go index 5b6dff5..e9fbd5c 100644 --- a/internal/database/database_sqlite.go +++ b/internal/database/database_sqlite.go @@ -47,15 +47,15 @@ CREATE TABLE IF NOT EXISTS documents ( ); CREATE TABLE IF NOT EXISTS accounts ( - id SERIAL PRIMARY KEY, - username varchar(255) NOT NULL, - password varchar(255) NOT NULL + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT NOT NULL, + password TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS sessions ( - public varchar(255) PRIMARY KEY, - token varchar(255) NOT NULL, - secret varchar + public TEXT PRIMARY KEY, + token TEXT NOT NULL, + secret TEXT NOT NULL );`) return err @@ -97,12 +97,20 @@ func (s *SQLite) GetAccount(ctx context.Context, id string) (Account, error) { defer s.RUnlock() acc := new(Account) - row := s.QueryRow("SELECT * FROM accounts WHERE id=?", id) + row := s.QueryRow("SELECT * FROM accounts WHERE id=$1", id) err := row.Scan(&acc.ID, &acc.Username, &acc.Password) return *acc, err } +func (s *SQLite) GetAccountByUsername(ctx context.Context, username string) (Account, error) { + account := new(Account) + row := s.QueryRow("SELECT * FROM accounts WHERE username=$1", username) + err := row.Scan(&account.ID, &account.Username, &account.Password) + + return *account, err +} + func (s *SQLite) CreateAccount(ctx context.Context, username, password string) error { s.Lock() defer s.Unlock() diff --git a/internal/database/databasefakes/fake_database.go b/internal/database/databasefakes/fake_database.go index 9342c60..0932906 100644 --- a/internal/database/databasefakes/fake_database.go +++ b/internal/database/databasefakes/fake_database.go @@ -85,6 +85,20 @@ type FakeDatabase struct { result1 database.Account result2 error } + GetAccountByUsernameStub func(context.Context, string) (database.Account, error) + getAccountByUsernameMutex sync.RWMutex + getAccountByUsernameArgsForCall []struct { + arg1 context.Context + arg2 string + } + getAccountByUsernameReturns struct { + result1 database.Account + result2 error + } + getAccountByUsernameReturnsOnCall map[int]struct { + result1 database.Account + result2 error + } GetDocumentStub func(context.Context, string) (database.Document, error) getDocumentMutex sync.RWMutex getDocumentArgsForCall []struct { @@ -124,20 +138,6 @@ type FakeDatabase struct { migrateReturnsOnCall map[int]struct { result1 error } - UpdateAccountStub func(context.Context, string, string, string) error - updateAccountMutex sync.RWMutex - updateAccountArgsForCall []struct { - arg1 context.Context - arg2 string - arg3 string - arg4 string - } - updateAccountReturns struct { - result1 error - } - updateAccountReturnsOnCall map[int]struct { - result1 error - } invocations map[string][][]interface{} invocationsMutex sync.RWMutex } @@ -512,6 +512,71 @@ func (fake *FakeDatabase) GetAccountReturnsOnCall(i int, result1 database.Accoun }{result1, result2} } +func (fake *FakeDatabase) GetAccountByUsername(arg1 context.Context, arg2 string) (database.Account, error) { + fake.getAccountByUsernameMutex.Lock() + ret, specificReturn := fake.getAccountByUsernameReturnsOnCall[len(fake.getAccountByUsernameArgsForCall)] + fake.getAccountByUsernameArgsForCall = append(fake.getAccountByUsernameArgsForCall, struct { + arg1 context.Context + arg2 string + }{arg1, arg2}) + stub := fake.GetAccountByUsernameStub + fakeReturns := fake.getAccountByUsernameReturns + fake.recordInvocation("GetAccountByUsername", []interface{}{arg1, arg2}) + fake.getAccountByUsernameMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeDatabase) GetAccountByUsernameCallCount() int { + fake.getAccountByUsernameMutex.RLock() + defer fake.getAccountByUsernameMutex.RUnlock() + return len(fake.getAccountByUsernameArgsForCall) +} + +func (fake *FakeDatabase) GetAccountByUsernameCalls(stub func(context.Context, string) (database.Account, error)) { + fake.getAccountByUsernameMutex.Lock() + defer fake.getAccountByUsernameMutex.Unlock() + fake.GetAccountByUsernameStub = stub +} + +func (fake *FakeDatabase) GetAccountByUsernameArgsForCall(i int) (context.Context, string) { + fake.getAccountByUsernameMutex.RLock() + defer fake.getAccountByUsernameMutex.RUnlock() + argsForCall := fake.getAccountByUsernameArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeDatabase) GetAccountByUsernameReturns(result1 database.Account, result2 error) { + fake.getAccountByUsernameMutex.Lock() + defer fake.getAccountByUsernameMutex.Unlock() + fake.GetAccountByUsernameStub = nil + fake.getAccountByUsernameReturns = struct { + result1 database.Account + result2 error + }{result1, result2} +} + +func (fake *FakeDatabase) GetAccountByUsernameReturnsOnCall(i int, result1 database.Account, result2 error) { + fake.getAccountByUsernameMutex.Lock() + defer fake.getAccountByUsernameMutex.Unlock() + fake.GetAccountByUsernameStub = nil + if fake.getAccountByUsernameReturnsOnCall == nil { + fake.getAccountByUsernameReturnsOnCall = make(map[int]struct { + result1 database.Account + result2 error + }) + } + fake.getAccountByUsernameReturnsOnCall[i] = struct { + result1 database.Account + result2 error + }{result1, result2} +} + func (fake *FakeDatabase) GetDocument(arg1 context.Context, arg2 string) (database.Document, error) { fake.getDocumentMutex.Lock() ret, specificReturn := fake.getDocumentReturnsOnCall[len(fake.getDocumentArgsForCall)] @@ -703,70 +768,6 @@ func (fake *FakeDatabase) MigrateReturnsOnCall(i int, result1 error) { }{result1} } -func (fake *FakeDatabase) UpdateAccount(arg1 context.Context, arg2 string, arg3 string, arg4 string) error { - fake.updateAccountMutex.Lock() - ret, specificReturn := fake.updateAccountReturnsOnCall[len(fake.updateAccountArgsForCall)] - fake.updateAccountArgsForCall = append(fake.updateAccountArgsForCall, struct { - arg1 context.Context - arg2 string - arg3 string - arg4 string - }{arg1, arg2, arg3, arg4}) - stub := fake.UpdateAccountStub - fakeReturns := fake.updateAccountReturns - fake.recordInvocation("UpdateAccount", []interface{}{arg1, arg2, arg3, arg4}) - fake.updateAccountMutex.Unlock() - if stub != nil { - return stub(arg1, arg2, arg3, arg4) - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeDatabase) UpdateAccountCallCount() int { - fake.updateAccountMutex.RLock() - defer fake.updateAccountMutex.RUnlock() - return len(fake.updateAccountArgsForCall) -} - -func (fake *FakeDatabase) UpdateAccountCalls(stub func(context.Context, string, string, string) error) { - fake.updateAccountMutex.Lock() - defer fake.updateAccountMutex.Unlock() - fake.UpdateAccountStub = stub -} - -func (fake *FakeDatabase) UpdateAccountArgsForCall(i int) (context.Context, string, string, string) { - fake.updateAccountMutex.RLock() - defer fake.updateAccountMutex.RUnlock() - argsForCall := fake.updateAccountArgsForCall[i] - return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4 -} - -func (fake *FakeDatabase) UpdateAccountReturns(result1 error) { - fake.updateAccountMutex.Lock() - defer fake.updateAccountMutex.Unlock() - fake.UpdateAccountStub = nil - fake.updateAccountReturns = struct { - result1 error - }{result1} -} - -func (fake *FakeDatabase) UpdateAccountReturnsOnCall(i int, result1 error) { - fake.updateAccountMutex.Lock() - defer fake.updateAccountMutex.Unlock() - fake.UpdateAccountStub = nil - if fake.updateAccountReturnsOnCall == nil { - fake.updateAccountReturnsOnCall = make(map[int]struct { - result1 error - }) - } - fake.updateAccountReturnsOnCall[i] = struct { - result1 error - }{result1} -} - func (fake *FakeDatabase) Invocations() map[string][][]interface{} { fake.invocationsMutex.RLock() defer fake.invocationsMutex.RUnlock() @@ -782,14 +783,14 @@ func (fake *FakeDatabase) Invocations() map[string][][]interface{} { defer fake.deleteAccountMutex.RUnlock() fake.getAccountMutex.RLock() defer fake.getAccountMutex.RUnlock() + fake.getAccountByUsernameMutex.RLock() + defer fake.getAccountByUsernameMutex.RUnlock() fake.getDocumentMutex.RLock() defer fake.getDocumentMutex.RUnlock() fake.getSessionMutex.RLock() defer fake.getSessionMutex.RUnlock() fake.migrateMutex.RLock() defer fake.migrateMutex.RUnlock() - fake.updateAccountMutex.RLock() - defer fake.updateAccountMutex.RUnlock() copiedInvocations := map[string][][]interface{}{} for key, value := range fake.invocations { copiedInvocations[key] = value diff --git a/internal/server/authentication.go b/internal/server/authentication.go new file mode 100644 index 0000000..972e2aa --- /dev/null +++ b/internal/server/authentication.go @@ -0,0 +1,106 @@ +package server + +import ( + "encoding/base64" + "errors" + "fmt" + "log" + "net/http" + + "github.com/lukewhrit/spacebin/internal/util" + "golang.org/x/crypto/bcrypt" + "golang.org/x/crypto/sha3" +) + +func (s *Server) SignUp(w http.ResponseWriter, r *http.Request) { + body := util.SignupRequest{ + Username: "luke", + Password: "password", + } + // Do validation + // Make sure password is secure, make sure username does not exist + + // Create account + // Encryption handled in Database function + err := s.Database.CreateAccount(r.Context(), body.Username, body.Password) + + if err != nil { + util.WriteError(w, http.StatusInternalServerError, err) + } + + // Respond on success with account ID and username + account, err := s.Database.GetAccountByUsername(r.Context(), body.Username) + + if err != nil { + util.WriteError(w, http.StatusInternalServerError, err) + } + + util.WriteJSON(w, http.StatusOK, map[string]interface{}{ + "id": account.ID, + "username": account.Username, + }) + +} + +func (s *Server) SignIn(w http.ResponseWriter, r *http.Request) { + body := &util.SigninRequest{ + Username: "luke", + Password: "password", + } + + // if err != nil { + // util.WriteError(w, http.StatusBadRequest, err) + // } + + // Get user from database + acc, err := s.Database.GetAccountByUsername(r.Context(), body.Username) + + if err != nil { + util.WriteError(w, http.StatusInternalServerError, err) + } + + // Compare passwords + if bcrypt.CompareHashAndPassword([]byte(acc.Password), []byte(body.Password)) == nil { + // Generate public, secret keys and salt + pub, sec, salt, err := util.GenerateStrings([]int{64, 64, 32}) + + if err != nil { + log.Fatal(err) + } + + // Salt secret key + buf := []byte(sec + salt) + secret := make([]byte, 64) + sha3.ShakeSum256(secret, buf) + + // Create user and server tokens for later comparison + userToken := util.MakeToken(util.Token{ + Version: "v1", + Public: pub, + Secret: base64.URLEncoding.EncodeToString([]byte(sec)), + Salt: salt, + }) + + serverToken := util.MakeToken(util.Token{ + Version: "v1", + Public: pub, + Secret: fmt.Sprintf("%x", secret), + Salt: salt, + }) + + if err != nil { + util.WriteError(w, http.StatusInternalServerError, err) + } + + // Add session to Postgres + if err := s.Database.CreateSession(r.Context(), pub, userToken, serverToken); err != nil { + util.WriteError(w, http.StatusInternalServerError, err) + } + + util.WriteJSON(w, http.StatusOK, map[string]string{ + "token": userToken, + }) + } else { + util.WriteError(w, http.StatusUnauthorized, errors.New("invalid username or password")) + } +} diff --git a/internal/server/server.go b/internal/server/server.go index cb4e82d..73c8c2c 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -173,10 +173,16 @@ func (s *Server) MountHandlers() { // Register routes s.Router.Get("/config", s.GetConfig) + // Document routes s.Router.Post("/api/", s.CreateDocument) s.Router.Get("/api/{document}", s.FetchDocument) s.Router.Get("/api/{document}/raw", s.FetchRawDocument) + // Account routes + s.Router.Post("/api/signin", s.SignIn) + s.Router.Post("/api/signup", s.SignUp) + + // Static routes s.Router.Post("/", s.StaticCreateDocument) s.Router.Get("/{document}", s.StaticDocument) s.Router.Get("/{document}/raw", s.FetchRawDocument) diff --git a/internal/util/authentication.go b/internal/util/authentication.go index 47c733f..08a576f 100644 --- a/internal/util/authentication.go +++ b/internal/util/authentication.go @@ -9,13 +9,6 @@ import ( "golang.org/x/crypto/bcrypt" ) -type Token struct { - Version string - Public string - Secret string - Salt string -} - func HashAndSalt(pwd []byte) string { // Use GenerateFromPassword to hash & salt pwd. // MinCost is just an integer constant provided by the bcrypt diff --git a/internal/util/domain.go b/internal/util/domain.go index efbf6ad..52e5ac9 100644 --- a/internal/util/domain.go +++ b/internal/util/domain.go @@ -28,3 +28,28 @@ type DocumentResponse struct { UpdatedAt int64 `json:"updated_at,omitempty"` // The Unix timestamp of when the document was last modified. Exists bool `json:"exists,omitempty"` // Whether the document does or does not exist. } + +// Token is an authentication token object +type Token struct { + Version string + Public string + Secret string + Salt string +} + +// CreateRequest represents a POST request to create a document +type CreateRequest struct { + Content string +} + +// SigninRequest represents a POST request to authenticate an account +type SigninRequest struct { + Username string + Password string +} + +// SignupRequest represents a POST request to register an account +type SignupRequest struct { + Username string + Password string +} diff --git a/internal/util/helpers.go b/internal/util/helpers.go index 7c70fc8..03b562a 100644 --- a/internal/util/helpers.go +++ b/internal/util/helpers.go @@ -29,10 +29,6 @@ import ( "github.com/rs/zerolog/log" ) -type CreateRequest struct { - Content string -} - func ValidateBody(maxSize int, body CreateRequest) error { return validation.ValidateStruct(&body, validation.Field(&body.Content, validation.Required, From c62343cb6da0862f8e8b7a9e1ae095d906b7ca9d Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 18 Nov 2024 21:35:39 -0500 Subject: [PATCH 09/20] feat(validation): signin/signup request validation --- internal/server/authentication.go | 1 + internal/util/helpers.go | 25 ++++++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/internal/server/authentication.go b/internal/server/authentication.go index 972e2aa..b0482b6 100644 --- a/internal/server/authentication.go +++ b/internal/server/authentication.go @@ -17,6 +17,7 @@ func (s *Server) SignUp(w http.ResponseWriter, r *http.Request) { Username: "luke", Password: "password", } + // Do validation // Make sure password is secure, make sure username does not exist diff --git a/internal/util/helpers.go b/internal/util/helpers.go index 03b562a..2bc125f 100644 --- a/internal/util/helpers.go +++ b/internal/util/helpers.go @@ -29,11 +29,26 @@ import ( "github.com/rs/zerolog/log" ) -func ValidateBody(maxSize int, body CreateRequest) error { - return validation.ValidateStruct(&body, - validation.Field(&body.Content, validation.Required, - validation.Length(2, maxSize)), - ) +func ValidateBody[T CreateRequest | SigninRequest | SignupRequest](maxSize int, body T) error { + switch v := any(body).(type) { + case CreateRequest: + return validation.ValidateStruct(&v, + validation.Field(&v.Content, validation.Required, validation.Length(2, maxSize)), + ) + case SigninRequest: + return validation.ValidateStruct(&v, + validation.Field(&v.Username, validation.Required), + validation.Field(&v.Password, validation.Required, validation.Length(16, 128)), + ) + case SignupRequest: + return validation.ValidateStruct(&v, + validation.Field(&v.Username, validation.Required), + validation.Field(&v.Password, validation.Required, validation.Length(16, 128)), + ) + default: + return validation.Errors{"body": validation.NewError("validation_error", "unsupported request type")} + } + } // HandleBody figures out whether a incoming request is in JSON or multipart/form-data and decodes it appropriately From 5175ffe13d9d83ff716cbb4fa18093ff3848fe53 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 18 Nov 2024 21:39:14 -0500 Subject: [PATCH 10/20] fix(database/sqlite): fix typo in schema --- internal/database/database_sqlite.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/database/database_sqlite.go b/internal/database/database_sqlite.go index e9fbd5c..82a502d 100644 --- a/internal/database/database_sqlite.go +++ b/internal/database/database_sqlite.go @@ -43,7 +43,7 @@ CREATE TABLE IF NOT EXISTS documents ( id TEXT PRIMARY KEY, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - usdated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS accounts ( From 5c2da8bcf1be8b7f281f0ef9600c10868a386c7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 07:00:36 +0000 Subject: [PATCH 11/20] build(deps): bump github.com/stretchr/testify from 1.9.0 to 1.10.0 Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.9.0 to 1.10.0. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.9.0...v1.10.0) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 67a28b0..5705131 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/lib/pq v1.10.9 github.com/lukewhrit/phrase v1.0.0 github.com/rs/zerolog v1.33.0 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 ) diff --git a/go.sum b/go.sum index 09c852b..65be6a2 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= From c378de68773c361adc541385bdcff337e2764e72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 06:06:49 +0000 Subject: [PATCH 12/20] build(deps): bump modernc.org/sqlite from 1.34.1 to 1.34.2 Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.34.1 to 1.34.2. - [Commits](https://gitlab.com/cznic/sqlite/compare/v1.34.1...v1.34.2) --- updated-dependencies: - dependency-name: modernc.org/sqlite dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 67a28b0..09e8f8a 100644 --- a/go.mod +++ b/go.mod @@ -50,5 +50,5 @@ require ( golang.org/x/sys v0.27.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/sqlite v1.34.1 + modernc.org/sqlite v1.34.2 ) diff --git a/go.sum b/go.sum index 09c852b..b0aeda8 100644 --- a/go.sum +++ b/go.sum @@ -118,8 +118,8 @@ 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.1 h1:u3Yi6M0N8t9yKRDwhXcyp1eS5/ErhPTBggxWFuR6Hfk= -modernc.org/sqlite v1.34.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k= +modernc.org/sqlite v1.34.2 h1:J9n76TPsfYYkFkZ9Uy1QphILYifiVEwwOT7yP5b++2Y= +modernc.org/sqlite v1.34.2/go.mod h1:dnR723UrTtjKpoHCAMN0Q/gZ9MT4r+iRvIBb9umWFkU= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= From ef319815771ca438c7aac596409cd6b303fe7722 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 14 Dec 2024 07:00:14 +0000 Subject: [PATCH 13/20] build(deps): bump golang.org/x/crypto from 0.29.0 to 0.31.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.29.0 to 0.31.0. - [Commits](https://github.com/golang/crypto/compare/v0.29.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index adacab1..fc514b9 100644 --- a/go.mod +++ b/go.mod @@ -46,8 +46,8 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/crypto v0.29.0 - golang.org/x/sys v0.27.0 // indirect + golang.org/x/crypto v0.31.0 + golang.org/x/sys v0.28.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/sqlite v1.34.2 diff --git a/go.sum b/go.sum index be3d3ea..837194a 100644 --- a/go.sum +++ b/go.sum @@ -77,8 +77,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= @@ -88,8 +88,8 @@ golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 9dc3c9cbbd322cdae03b9d0fa700bf4b1f258d46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 06:53:20 +0000 Subject: [PATCH 14/20] build(deps): bump github.com/go-chi/chi/v5 from 5.1.0 to 5.2.0 Bumps [github.com/go-chi/chi/v5](https://github.com/go-chi/chi) from 5.1.0 to 5.2.0. - [Release notes](https://github.com/go-chi/chi/releases) - [Changelog](https://github.com/go-chi/chi/blob/master/CHANGELOG.md) - [Commits](https://github.com/go-chi/chi/compare/v5.1.0...v5.2.0) --- updated-dependencies: - dependency-name: github.com/go-chi/chi/v5 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fc514b9..8e93e8f 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.23.3 require ( github.com/caarlos0/env/v9 v9.0.0 - github.com/go-chi/chi/v5 v5.1.0 + github.com/go-chi/chi/v5 v5.2.0 github.com/go-chi/cors v1.2.1 github.com/go-chi/httprate v0.14.1 github.com/go-ozzo/ozzo-validation/v4 v4.3.0 diff --git a/go.sum b/go.sum index 837194a..4c83e7e 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,8 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 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.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= -github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +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/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/httprate v0.14.1 h1:EKZHYEZ58Cg6hWcYzoZILsv7ppb46Wt4uQ738IRtpZs= From dc2fc50d1c0adad79b0ef856266fed96b9a74921 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 07:00:38 +0000 Subject: [PATCH 15/20] build(deps): bump modernc.org/sqlite from 1.34.2 to 1.34.4 Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.34.2 to 1.34.4. - [Commits](https://gitlab.com/cznic/sqlite/compare/v1.34.2...v1.34.4) --- updated-dependencies: - dependency-name: modernc.org/sqlite dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fc514b9..17406a0 100644 --- a/go.mod +++ b/go.mod @@ -50,5 +50,5 @@ require ( golang.org/x/sys v0.28.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/sqlite v1.34.2 + modernc.org/sqlite v1.34.4 ) diff --git a/go.sum b/go.sum index 837194a..7e2c401 100644 --- a/go.sum +++ b/go.sum @@ -118,8 +118,8 @@ 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.2 h1:J9n76TPsfYYkFkZ9Uy1QphILYifiVEwwOT7yP5b++2Y= -modernc.org/sqlite v1.34.2/go.mod h1:dnR723UrTtjKpoHCAMN0Q/gZ9MT4r+iRvIBb9umWFkU= +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.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= From 47a2e7d8ce151ef04ef6119981de700370c9c165 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 06:31:25 +0000 Subject: [PATCH 16/20] build(deps): bump github.com/alecthomas/chroma/v2 from 2.14.0 to 2.15.0 Bumps [github.com/alecthomas/chroma/v2](https://github.com/alecthomas/chroma) from 2.14.0 to 2.15.0. - [Release notes](https://github.com/alecthomas/chroma/releases) - [Changelog](https://github.com/alecthomas/chroma/blob/master/.goreleaser.yml) - [Commits](https://github.com/alecthomas/chroma/compare/v2.14.0...v2.15.0) --- updated-dependencies: - dependency-name: github.com/alecthomas/chroma/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index fc514b9..82eab39 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect - github.com/dlclark/regexp2 v1.11.0 // indirect + github.com/dlclark/regexp2 v1.11.4 // indirect 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 @@ -36,7 +36,7 @@ require ( ) require ( - github.com/alecthomas/chroma/v2 v2.14.0 + github.com/alecthomas/chroma/v2 v2.15.0 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.sum b/go.sum index 837194a..28f0883 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE= -github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= -github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E= -github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I= +github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= +github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc= +github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= @@ -18,8 +18,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= -github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= +github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 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.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= From a128eabe3abb0f5dd9485148b7da2ca15d0167d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 06:25:50 +0000 Subject: [PATCH 17/20] build(deps): bump golang.org/x/crypto from 0.31.0 to 0.32.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.31.0 to 0.32.0. - [Commits](https://github.com/golang/crypto/compare/v0.31.0...v0.32.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index fc514b9..88fe7ea 100644 --- a/go.mod +++ b/go.mod @@ -46,8 +46,8 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/crypto v0.31.0 - golang.org/x/sys v0.28.0 // indirect + golang.org/x/crypto v0.32.0 + golang.org/x/sys v0.29.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/sqlite v1.34.2 diff --git a/go.sum b/go.sum index 837194a..8f6a958 100644 --- a/go.sum +++ b/go.sum @@ -77,8 +77,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +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-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= @@ -88,8 +88,8 @@ golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 994b5177638d72266087beed4fe8f0c528d4972c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 06:59:40 +0000 Subject: [PATCH 18/20] build(deps): bump modernc.org/sqlite from 1.34.4 to 1.34.5 Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.34.4 to 1.34.5. - [Commits](https://gitlab.com/cznic/sqlite/compare/v1.34.4...v1.34.5) --- updated-dependencies: - dependency-name: modernc.org/sqlite dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 7 ++----- go.sum | 8 ++------ 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 4e22ce3..e33526d 100644 --- a/go.mod +++ b/go.mod @@ -22,17 +22,14 @@ require ( github.com/dlclark/regexp2 v1.11.4 // indirect 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/ncruces/go-strftime v0.1.9 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + golang.org/x/mod v0.21.0 // indirect golang.org/x/sync v0.9.0 // indirect golang.org/x/tools v0.26.0 // indirect - modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect modernc.org/libc v1.55.3 // indirect modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.8.0 // indirect - modernc.org/strutil v1.2.0 // indirect - modernc.org/token v1.1.0 // indirect ) require ( @@ -50,5 +47,5 @@ require ( golang.org/x/sys v0.29.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/sqlite v1.34.4 + modernc.org/sqlite v1.34.5 ) diff --git a/go.sum b/go.sum index dc4a175..f38e9e8 100644 --- a/go.sum +++ b/go.sum @@ -39,8 +39,6 @@ github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlG 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/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -106,8 +104,6 @@ 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.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= -modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= -modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= @@ -118,8 +114,8 @@ 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/sqlite v1.34.5 h1:Bb6SR13/fjp15jt70CL4f18JIN7p7dnMExd+UFnF15g= +modernc.org/sqlite v1.34.5/go.mod h1:YLuNmX9NKs8wRNK2ko1LW1NGYcc9FkBO69JOt1AR9JE= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= From da7163a84412bdf2177846c41ba1cd36c408b71a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 06:10:55 +0000 Subject: [PATCH 19/20] build(deps): bump github.com/go-chi/chi/v5 from 5.2.0 to 5.2.1 Bumps [github.com/go-chi/chi/v5](https://github.com/go-chi/chi) from 5.2.0 to 5.2.1. - [Release notes](https://github.com/go-chi/chi/releases) - [Changelog](https://github.com/go-chi/chi/blob/master/CHANGELOG.md) - [Commits](https://github.com/go-chi/chi/compare/v5.2.0...v5.2.1) --- updated-dependencies: - dependency-name: github.com/go-chi/chi/v5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e33526d..46313ff 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.23.3 require ( github.com/caarlos0/env/v9 v9.0.0 - github.com/go-chi/chi/v5 v5.2.0 + github.com/go-chi/chi/v5 v5.2.1 github.com/go-chi/cors v1.2.1 github.com/go-chi/httprate v0.14.1 github.com/go-ozzo/ozzo-validation/v4 v4.3.0 diff --git a/go.sum b/go.sum index f38e9e8..442bd79 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,8 @@ github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yA github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 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/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= +github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/httprate v0.14.1 h1:EKZHYEZ58Cg6hWcYzoZILsv7ppb46Wt4uQ738IRtpZs= From 3fd3c6d30fdacb6b1045dc23e376c2f51d55dd4a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 06:11:00 +0000 Subject: [PATCH 20/20] build(deps): bump golang.org/x/crypto from 0.32.0 to 0.33.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.32.0 to 0.33.0. - [Commits](https://github.com/golang/crypto/compare/v0.32.0...v0.33.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index e33526d..761e176 100644 --- a/go.mod +++ b/go.mod @@ -43,8 +43,8 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/crypto v0.32.0 - golang.org/x/sys v0.29.0 // indirect + golang.org/x/crypto v0.33.0 + golang.org/x/sys v0.30.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/sqlite v1.34.5 diff --git a/go.sum b/go.sum index f38e9e8..1f5efae 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= @@ -86,8 +86,8 @@ golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.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/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=