diff --git a/internal/db/user.go b/internal/db/user.go index fc4a0e6..52cf314 100644 --- a/internal/db/user.go +++ b/internal/db/user.go @@ -1,23 +1,25 @@ package db import ( + "encoding/json" "github.com/thomiceli/opengist/internal/git" "gorm.io/gorm" ) type User struct { - ID uint `gorm:"primaryKey"` - Username string `gorm:"uniqueIndex,size:191"` - Password string - IsAdmin bool - CreatedAt int64 - Email string - MD5Hash string // for gravatar, if no Email is specified, the value is random - AvatarURL string - GithubID string - GitlabID string - GiteaID string - OIDCID string `gorm:"column:oidc_id"` + ID uint `gorm:"primaryKey"` + Username string `gorm:"uniqueIndex,size:191"` + Password string + IsAdmin bool + CreatedAt int64 + Email string + MD5Hash string // for gravatar, if no Email is specified, the value is random + AvatarURL string + GithubID string + GitlabID string + GiteaID string + OIDCID string `gorm:"column:oidc_id"` + StylePreferences string Gists []Gist `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;foreignKey:UserID"` SSHKeys []SSHKey `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;foreignKey:UserID"` @@ -234,6 +236,15 @@ func (user *User) HasMFA() (bool, bool, error) { return webauthn, totp, err } +func (user *User) GetStyle() *UserStyleDTO { + style := new(UserStyleDTO) + err := json.Unmarshal([]byte(user.StylePreferences), style) + if err != nil { + return nil + } + return style +} + // -- DTO -- // type UserDTO struct { @@ -251,3 +262,18 @@ func (dto *UserDTO) ToUser() *User { type UserUsernameDTO struct { Username string `form:"username" validate:"required,max=24,alphanumdash,notreserved"` } + +type UserStyleDTO struct { + SoftWrap bool `form:"softwrap" json:"soft_wrap"` + RemovedLineColor string `form:"removedlinecolor" json:"removed_line_color" validate:"min=0,max=7"` + AddedLineColor string `form:"addedlinecolor" json:"added_line_color" validate:"min=0,max=7"` + GitLineColor string `form:"gitlinecolor" json:"git_line_color" validate:"min=0,max=7"` +} + +func (dto *UserStyleDTO) ToJson() string { + data, err := json.Marshal(dto) + if err != nil { + return "{}" + } + return string(data) +} diff --git a/internal/i18n/locales/en-US.yml b/internal/i18n/locales/en-US.yml index cad30cf..30801fb 100644 --- a/internal/i18n/locales/en-US.yml +++ b/internal/i18n/locales/en-US.yml @@ -148,6 +148,17 @@ settings.create-password-help: Create your password to login to Opengist via HTT settings.change-password: Change password settings.change-password-help: Change your password to login to Opengist via HTTP settings.password-label-title: Password +settings.header.account: Account +settings.header.mfa: MFA +settings.header.ssh: SSH +settings.header.style: Style +settings.style.gist-code: Gist code +settings.style.no-soft-wrap: No Soft Wrap +settings.style.soft-wrap: Soft Wrap +settings.style.removed-lines-color: Removed lines color +settings.style.added-lines-color: Added lines color +settings.style.git-lines-color: Git lines color +settings.style.save-style: Save style auth.signup-disabled: Administrator has disabled signing up auth.login: Login diff --git a/internal/web/handlers/auth/totp.go b/internal/web/handlers/auth/totp.go index 8be704c..c594c15 100644 --- a/internal/web/handlers/auth/totp.go +++ b/internal/web/handlers/auth/totp.go @@ -14,7 +14,7 @@ func BeginTotp(ctx *context.Context) error { return ctx.ErrorRes(500, "Cannot check for user MFA", err) } else if hasTotp { ctx.AddFlash(ctx.Tr("auth.totp.already-enabled"), "error") - return ctx.RedirectTo("/settings") + return ctx.RedirectTo("/settings/mfa") } ogUrl, err := url.Parse(ctx.GetData("baseHttpUrl").(string)) @@ -47,7 +47,7 @@ func FinishTotp(ctx *context.Context) error { return ctx.ErrorRes(500, "Cannot check for user MFA", err) } else if hasTotp { ctx.AddFlash(ctx.Tr("auth.totp.already-enabled"), "error") - return ctx.RedirectTo("/settings") + return ctx.RedirectTo("/settings/mfa") } dto := &db.TOTPDTO{} @@ -134,7 +134,7 @@ func AssertTotp(ctx *context.Context) error { } ctx.AddFlash(ctx.Tr("auth.totp.code-used", dto.Code), "warning") - redirectUrl = "/settings" + redirectUrl = "/settings/mfa" } sess.Values["user"] = userId @@ -157,7 +157,7 @@ func DisableTotp(ctx *context.Context) error { } ctx.AddFlash(ctx.Tr("auth.totp.disabled"), "success") - return ctx.RedirectTo("/settings") + return ctx.RedirectTo("/settings/mfa") } func RegenerateTotpRecoveryCodes(ctx *context.Context) error { diff --git a/internal/web/handlers/settings/settings.go b/internal/web/handlers/settings/settings.go index 06b3d4c..918e481 100644 --- a/internal/web/handlers/settings/settings.go +++ b/internal/web/handlers/settings/settings.go @@ -5,13 +5,19 @@ import ( "github.com/thomiceli/opengist/internal/web/context" ) -func UserSettings(ctx *context.Context) error { +func UserAccount(ctx *context.Context) error { user := ctx.User - keys, err := db.GetSSHKeysByUserID(user.ID) - if err != nil { - return ctx.ErrorRes(500, "Cannot get SSH keys", err) - } + ctx.SetData("email", user.Email) + ctx.SetData("hasPassword", user.Password != "") + ctx.SetData("disableForm", ctx.GetData("DisableLoginForm")) + ctx.SetData("settingsHeaderPage", "account") + ctx.SetData("htmlTitle", ctx.TrH("settings")) + return ctx.Html("settings_account.html") +} + +func UserMFA(ctx *context.Context) error { + user := ctx.User passkeys, err := db.GetAllCredentialsForUser(user.ID) if err != nil { @@ -23,12 +29,48 @@ func UserSettings(ctx *context.Context) error { return ctx.ErrorRes(500, "Cannot get MFA status", err) } - ctx.SetData("email", user.Email) - ctx.SetData("sshKeys", keys) ctx.SetData("passkeys", passkeys) ctx.SetData("hasTotp", hasTotp) - ctx.SetData("hasPassword", user.Password != "") - ctx.SetData("disableForm", ctx.GetData("DisableLoginForm")) + ctx.SetData("settingsHeaderPage", "mfa") ctx.SetData("htmlTitle", ctx.TrH("settings")) - return ctx.Html("settings.html") + return ctx.Html("settings_mfa.html") +} + +func UserSSHKeys(ctx *context.Context) error { + user := ctx.User + + keys, err := db.GetSSHKeysByUserID(user.ID) + if err != nil { + return ctx.ErrorRes(500, "Cannot get SSH keys", err) + } + + ctx.SetData("sshKeys", keys) + ctx.SetData("settingsHeaderPage", "ssh") + ctx.SetData("htmlTitle", ctx.TrH("settings")) + return ctx.Html("settings_ssh.html") +} + +func UserStyle(ctx *context.Context) error { + ctx.SetData("settingsHeaderPage", "style") + ctx.SetData("htmlTitle", ctx.TrH("settings")) + return ctx.Html("settings_style.html") +} + +func ProcessUserStyle(ctx *context.Context) error { + styleDto := new(db.UserStyleDTO) + if err := ctx.Bind(styleDto); err != nil { + return ctx.ErrorRes(400, ctx.Tr("error.cannot-bind-data"), err) + } + + if err := ctx.Validate(styleDto); err != nil { + return ctx.ErrorRes(400, "Invalid data", err) + } + user := ctx.User + user.StylePreferences = styleDto.ToJson() + if err := user.Update(); err != nil { + return ctx.ErrorRes(500, "Cannot update user styles", err) + } + + ctx.AddFlash("Updated style", "success") + return ctx.RedirectTo("/settings/style") } diff --git a/internal/web/handlers/settings/sshkey.go b/internal/web/handlers/settings/sshkey.go index db8bdc3..fd0eee7 100644 --- a/internal/web/handlers/settings/sshkey.go +++ b/internal/web/handlers/settings/sshkey.go @@ -20,7 +20,7 @@ func SshKeysProcess(ctx *context.Context) error { if err := ctx.Validate(dto); err != nil { ctx.AddFlash(validator.ValidationMessages(&err, ctx.GetData("locale").(*i18n.Locale)), "error") - return ctx.RedirectTo("/settings") + return ctx.RedirectTo("/settings/ssh") } key := dto.ToSSHKey() @@ -29,7 +29,7 @@ func SshKeysProcess(ctx *context.Context) error { pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key.Content)) if err != nil { ctx.AddFlash(ctx.Tr("flash.user.invalid-ssh-key"), "error") - return ctx.RedirectTo("/settings") + return ctx.RedirectTo("/settings/ssh") } key.Content = strings.TrimSpace(string(ssh.MarshalAuthorizedKey(pubKey))) @@ -38,7 +38,7 @@ func SshKeysProcess(ctx *context.Context) error { return ctx.ErrorRes(500, "Cannot check if SSH key exists", err) } ctx.AddFlash(ctx.Tr("settings.ssh-key-exists"), "error") - return ctx.RedirectTo("/settings") + return ctx.RedirectTo("/settings/ssh") } if err := key.Create(); err != nil { @@ -46,20 +46,20 @@ func SshKeysProcess(ctx *context.Context) error { } ctx.AddFlash(ctx.Tr("flash.user.ssh-key-added"), "success") - return ctx.RedirectTo("/settings") + return ctx.RedirectTo("/settings/ssh") } func SshKeysDelete(ctx *context.Context) error { user := ctx.User keyId, err := strconv.Atoi(ctx.Param("id")) if err != nil { - return ctx.RedirectTo("/settings") + return ctx.RedirectTo("/settings/ssh") } key, err := db.GetSSHKeyByID(uint(keyId)) if err != nil || key.UserID != user.ID { - return ctx.RedirectTo("/settings") + return ctx.RedirectTo("/settings/ssh") } if err := key.Delete(); err != nil { @@ -67,5 +67,5 @@ func SshKeysDelete(ctx *context.Context) error { } ctx.AddFlash(ctx.Tr("flash.user.ssh-key-deleted"), "success") - return ctx.RedirectTo("/settings") + return ctx.RedirectTo("/settings/ssh") } diff --git a/internal/web/server/middlewares.go b/internal/web/server/middlewares.go index e5b1cc9..54e7946 100644 --- a/internal/web/server/middlewares.go +++ b/internal/web/server/middlewares.go @@ -275,6 +275,7 @@ func sessionInit(next Handler) Handler { if user != nil { ctx.User = user ctx.SetData("userLogged", user) + ctx.SetData("currentStyle", user.GetStyle()) } return next(ctx) } diff --git a/internal/web/server/renderer.go b/internal/web/server/renderer.go index 3c8e4de..daab68f 100644 --- a/internal/web/server/renderer.go +++ b/internal/web/server/renderer.go @@ -186,6 +186,10 @@ func (s *Server) setFuncMap() { } return str }, + "hexToRgb": func(hex string) string { + h, _ := strconv.ParseUint(strings.TrimPrefix(hex, "#"), 16, 32) + return fmt.Sprintf("%d, %d, %d,", (h>>16)&0xFF, (h>>8)&0xFF, h&0xFF) + }, } t := template.Must(template.New("t").Funcs(fm).ParseFS(templates.Files, "*/*.html")) diff --git a/internal/web/server/router.go b/internal/web/server/router.go index 05267d2..9bd513f 100644 --- a/internal/web/server/router.go +++ b/internal/web/server/router.go @@ -56,7 +56,11 @@ func (s *Server) registerRoutes() { sA := r.SubGroup("/settings") { sA.Use(logged) - sA.GET("", settings.UserSettings) + sA.GET("", settings.UserAccount) + sA.GET("/mfa", settings.UserMFA) + sA.GET("/ssh", settings.UserSSHKeys) + sA.GET("/style", settings.UserStyle) + sA.POST("/style", settings.ProcessUserStyle) sA.POST("/email", settings.EmailProcess) sA.DELETE("/account", settings.AccountDeleteProcess) sA.POST("/ssh-keys", settings.SshKeysProcess) diff --git a/public/style.css b/public/style.css index 4659e5c..37ef9b4 100644 --- a/public/style.css +++ b/public/style.css @@ -10,6 +10,12 @@ } } +:root { + --red-diff: rgba(255, 0, 0, .1); + --green-diff: rgba(0, 255, 128, .1); + --git-diff: rgba(143, 143, 143, 0.38); +} + html { @apply bg-gray-50 dark:bg-gray-800; } @@ -41,18 +47,19 @@ pre { .code .line-num { width: 4%; text-align: right; + vertical-align: top; } .red-diff { - background-color: rgba(255, 0, 0, .1); + background-color: var(--red-diff); } .green-diff { - background-color: rgba(0, 255, 128, .1); + background-color: var(--green-diff); } .gray-diff { - background-color: rgba(143, 143, 143, 0.38); + background-color: var(--git-diff); @apply py-4 !important } diff --git a/public/style_preferences.ts b/public/style_preferences.ts new file mode 100644 index 0000000..b99583a --- /dev/null +++ b/public/style_preferences.ts @@ -0,0 +1,45 @@ +document.addEventListener('DOMContentLoaded', () => { + const noSoftWrapRadio = document.getElementById('no-soft-wrap'); + const softWrapRadio = document.getElementById('soft-wrap'); + + function updateRootClass() { + const table = document.querySelector("table"); + + if (softWrapRadio.checked) { + table.classList.remove('whitespace-pre'); + table.classList.add('whitespace-pre-wrap'); + } else { + table.classList.remove('whitespace-pre-wrap'); + table.classList.add('whitespace-pre'); + } + } + + noSoftWrapRadio.addEventListener('change', updateRootClass); + softWrapRadio.addEventListener('change', updateRootClass); + + + document.getElementById('removedlinecolor').addEventListener('change', function(event) { + const color = hexToRgba(event.target.value, 0.1); + document.documentElement.style.setProperty('--red-diff', color); + }); + + document.getElementById('addedlinecolor').addEventListener('change', function(event) { + const color = hexToRgba(event.target.value, 0.1); + document.documentElement.style.setProperty('--green-diff', color); + }); + + document.getElementById('gitlinecolor').addEventListener('change', function(event) { + const color = hexToRgba(event.target.value, 0.38); + document.documentElement.style.setProperty('--git-diff', color); + }); +}); + +function hexToRgba(hex, opacity) { + hex = hex.replace('#', ''); + + const r = parseInt(hex.substring(0, 2), 16); + const g = parseInt(hex.substring(2, 4), 16); + const b = parseInt(hex.substring(4, 6), 16); + + return `rgba(${r}, ${g}, ${b}, ${opacity})`; +} \ No newline at end of file diff --git a/public/vite.config.js b/public/vite.config.js index 3fb9176..455c055 100644 --- a/public/vite.config.js +++ b/public/vite.config.js @@ -15,7 +15,8 @@ export default defineConfig({ './public/admin.ts', './public/gist.ts', './public/embed.ts', - './public/webauthn.ts' + './public/webauthn.ts', + './public/style_preferences.ts' ] }, assetsInlineLimit: 0, diff --git a/templates/base/base_header.html b/templates/base/base_header.html index b36c325..695f22d 100644 --- a/templates/base/base_header.html +++ b/templates/base/base_header.html @@ -50,6 +50,16 @@ {{ else }} {{ if $.c.CustomName }}{{ $.c.CustomName }}{{ else }}Opengist{{ end }} {{ end }} + + {{ if .currentStyle }} + + {{ end }}
diff --git a/templates/base/settings_footer.html b/templates/base/settings_footer.html new file mode 100644 index 0000000..6277c3f --- /dev/null +++ b/templates/base/settings_footer.html @@ -0,0 +1,8 @@ +{{ if false }}{{/* prevent IDE errors */}} +
+{{ end }} + +{{ define "settings_footer" }} +
+
+{{ end }} diff --git a/templates/base/settings_header.html b/templates/base/settings_header.html new file mode 100644 index 0000000..e056bdb --- /dev/null +++ b/templates/base/settings_header.html @@ -0,0 +1,28 @@ +{{ define "settings_header" }} +
+
+
+

{{ .locale.Tr "settings" }}

+
+
+
+ +{{ end }} + +{{ if false }} +{{/* prevent IDE errors */}} +
+{{ end }} diff --git a/templates/pages/gist.html b/templates/pages/gist.html index f5a6b3c..55a58c0 100644 --- a/templates/pages/gist.html +++ b/templates/pages/gist.html @@ -74,11 +74,11 @@
{{ $fileslug := slug $file.Filename }} {{ if ne $file.Content "" }} - +
{{ $ii := "1" }} {{ $i := toInt $ii }} - {{ range $line := $file.Lines }}{{ $i = inc $i }}{{ end }} + {{ range $line := $file.Lines }}{{ $i = inc $i }}{{ end }}
{{$i}}{{ $line | safe }}
{{$i}}{{ $line | safe }}
{{ end }} diff --git a/templates/pages/revisions.html b/templates/pages/revisions.html index 9846c8f..28ee83d 100644 --- a/templates/pages/revisions.html +++ b/templates/pages/revisions.html @@ -54,7 +54,7 @@ {{ else if eq $file.Content "" }}

{{ $.locale.Tr "gist.revision.empty-file" }}

{{ else }} - +
{{ $left := 0 }} {{ $right := 0 }} @@ -83,7 +83,7 @@ {{ $right = inc $right }} {{ end }} {{ end }} - + {{end}} diff --git a/templates/pages/settings.html b/templates/pages/settings.html deleted file mode 100644 index fc86543..0000000 --- a/templates/pages/settings.html +++ /dev/null @@ -1,316 +0,0 @@ -{{ template "header" .}} -
-
-
-

{{ .locale.Tr "settings" }}

-
-
-
-
-
-
-
-

- {{ .locale.Tr "settings.change-username" }} -

-
-
-
- -
-
- - - - {{ .csrfHtml }} - -
-
- {{ if not .disableForm }} -
-
-

- {{if .hasPassword}} - {{ .locale.Tr "settings.change-password" }} - {{else}} - {{ .locale.Tr "settings.create-password" }} - {{end}} -

-

- {{if .hasPassword}} - {{ .locale.Tr "settings.change-password-help" }} - {{else}} - {{ .locale.Tr "settings.create-password-help" }} - {{end}} -

-
-
- -
- -
-
- - - - {{ .csrfHtml }} - -
-
- {{ end }} -
-
-
-

- {{ .locale.Tr "settings.email" }} -

-

- {{ .locale.Tr "settings.email-help" }} -

-
-
-
- -
-
- - {{ .csrfHtml }} - -
-
- {{ if or .githubOauth .gitlabOauth .giteaOauth .oidcOauth }} -
-
-

- {{ .locale.Tr "settings.link-accounts" }} -

-
- - {{ if .githubOauth }} - {{ if .userLogged.GithubID }} - - {{ .locale.Tr "settings.unlink-github-account" }} - - {{ else }} - - {{ .locale.Tr "settings.link-github-account" }} - - {{ end }} - {{ end }} - - {{ if .gitlabOauth }} - {{ if .userLogged.GitlabID }} - - {{ .locale.Tr "settings.unlink-gitlab-account" }} - - {{ else }} - - {{ .locale.Tr "settings.link-gitlab-account" }} - - {{ end }} - {{ end }} - - {{ if .giteaOauth }} - {{ if .userLogged.GiteaID }} - - {{ .locale.Tr "settings.unlink-gitea-account" }} - - {{ else }} - - {{ .locale.Tr "settings.link-gitea-account" }} - - {{ end }} - {{ end }} - {{ if .oidcOauth }} - {{ if .userLogged.OIDCID }} - - Unlink OpenID account - - {{ else }} - - Link OpenID account - - {{ end }} - {{ end }} -
-
-
- {{ end }} - -
-
-

- {{ .locale.Tr "auth.totp" }} -

-

- {{ .locale.Tr "auth.totp.help" }} -

- {{ if .hasTotp }} -
-
- - {{ .csrfHtml }} - - -
- {{ .csrfHtml }} - - -
- {{ else }} - {{ .locale.Tr "auth.totp.use" }} - {{ end }} -
-
- -
-
-
-

- {{ .locale.Tr "auth.mfa.passkeys" }} -

-

- {{ .locale.Tr "auth.mfa.passkeys-help" }} -

-
-
- -
- -
-
- {{ .csrfHtml }} - - -
- -
-
-
-
-
-
    - {{ if .passkeys }} - {{ range $passkey := .passkeys }} -
  • -
    - - - -
    -

    {{ .Name }}

    -

    {{ $.locale.Tr "auth.mfa.passkey-added-at" }} {{ .CreatedAt }}

    - {{ if eq .LastUsedAt 0 }} -

    {{ $.locale.Tr "auth.mfa.passkey-never-used" }}

    - {{ else }} -

    {{ $.locale.Tr "auth.mfa.passkey-last-used" }} {{ .LastUsedAt }}

    - {{ end }} -
    -
    - - {{ $.csrfHtml }} - - -
    -
  • - {{ end }} - {{ end }} -
-
-
-
- -
-
-
-

- {{ .locale.Tr "settings.add-ssh-key" }} -

-

- {{ .locale.Tr "settings.add-ssh-key-help" }} -

-
-
- -
- -
-
- -
- -
- -
-
- - {{ .csrfHtml }} - -
-
-
-
-
    - {{ if .sshKeys }} - {{ range $key := .sshKeys }} -
  • -
    - - - -
    -

    {{ .Title }}

    -

    SHA256:{{.SHA}}

    -

    {{ $.locale.Tr "settings.ssh-key-added-at" }} {{ .CreatedAt }}

    - {{ if eq .LastUsedAt 0 }} -

    {{ $.locale.Tr "settings.ssh-key-never-used" }}

    - {{ else }} -

    {{ $.locale.Tr "settings.ssh-key-last-used" }} {{ .LastUsedAt }}

    - {{ end }} -
    -
    - - {{ $.csrfHtml }} - - - -
    -
  • - {{ end }} - {{ end }} -
-
-
-
-
-
-

- {{ .locale.Tr "settings.delete-account" }} -

-
- - - {{ .csrfHtml }} - -
-
-
-
-
- - - - -{{ template "footer" .}} diff --git a/templates/pages/settings_account.html b/templates/pages/settings_account.html new file mode 100644 index 0000000..9ace800 --- /dev/null +++ b/templates/pages/settings_account.html @@ -0,0 +1,162 @@ +{{ template "header" .}} +{{ template "settings_header" .}} +
+
+
+
+

+ {{ .locale.Tr "settings.change-username" }} +

+
+
+
+ +
+
+ + + + {{ .csrfHtml }} + +
+
+ {{ if not .disableForm }} +
+
+

+ {{if .hasPassword}} + {{ .locale.Tr "settings.change-password" }} + {{else}} + {{ .locale.Tr "settings.create-password" }} + {{end}} +

+

+ {{if .hasPassword}} + {{ .locale.Tr "settings.change-password-help" }} + {{else}} + {{ .locale.Tr "settings.create-password-help" }} + {{end}} +

+
+
+ +
+ +
+
+ + + + {{ .csrfHtml }} + +
+
+ {{ end }} +
+
+
+

+ {{ .locale.Tr "settings.email" }} +

+

+ {{ .locale.Tr "settings.email-help" }} +

+
+
+
+ +
+
+ + {{ .csrfHtml }} + +
+
+ {{ if or .githubOauth .gitlabOauth .giteaOauth .oidcOauth }} +
+
+

+ {{ .locale.Tr "settings.link-accounts" }} +

+
+ + {{ if .githubOauth }} + {{ if .userLogged.GithubID }} + + {{ .locale.Tr "settings.unlink-github-account" }} + + {{ else }} + + {{ .locale.Tr "settings.link-github-account" }} + + {{ end }} + {{ end }} + + {{ if .gitlabOauth }} + {{ if .userLogged.GitlabID }} + + {{ .locale.Tr "settings.unlink-gitlab-account" }} + + {{ else }} + + {{ .locale.Tr "settings.link-gitlab-account" }} + + {{ end }} + {{ end }} + + {{ if .giteaOauth }} + {{ if .userLogged.GiteaID }} + + {{ .locale.Tr "settings.unlink-gitea-account" }} + + {{ else }} + + {{ .locale.Tr "settings.link-gitea-account" }} + + {{ end }} + {{ end }} + {{ if .oidcOauth }} + {{ if .userLogged.OIDCID }} + + Unlink OpenID account + + {{ else }} + + Link OpenID account + + {{ end }} + {{ end }} +
+
+
+ {{ end }} + + +
+
+

+ {{ .locale.Tr "settings.delete-account" }} +

+
+ + + {{ .csrfHtml }} + +
+
+
+ +{{ template "settings_footer" .}} +{{ template "footer" .}} diff --git a/templates/pages/settings_mfa.html b/templates/pages/settings_mfa.html new file mode 100644 index 0000000..16ec2c1 --- /dev/null +++ b/templates/pages/settings_mfa.html @@ -0,0 +1,91 @@ +{{ template "header" .}} +{{ template "settings_header" .}} +
+
+
+

+ {{ .locale.Tr "auth.totp" }} +

+

+ {{ .locale.Tr "auth.totp.help" }} +

+ {{ if .hasTotp }} +
+
+ + {{ .csrfHtml }} + + +
+ {{ .csrfHtml }} + + +
+ {{ else }} + {{ .locale.Tr "auth.totp.use" }} + {{ end }} +
+
+ +
+
+
+

+ {{ .locale.Tr "auth.mfa.passkeys" }} +

+

+ {{ .locale.Tr "auth.mfa.passkeys-help" }} +

+
+
+ +
+ +
+
+ {{ .csrfHtml }} + + +
+ +
+
+
+
+
+
    + {{ if .passkeys }} + {{ range $passkey := .passkeys }} +
  • +
    + + + +
    +

    {{ .Name }}

    +

    {{ $.locale.Tr "auth.mfa.passkey-added-at" }} {{ .CreatedAt }}

    + {{ if eq .LastUsedAt 0 }} +

    {{ $.locale.Tr "auth.mfa.passkey-never-used" }}

    + {{ else }} +

    {{ $.locale.Tr "auth.mfa.passkey-last-used" }} {{ .LastUsedAt }}

    + {{ end }} +
    +
    + + {{ $.csrfHtml }} + + +
    +
  • + {{ end }} + {{ end }} +
+
+
+
+ + +
+ +{{ template "settings_footer" .}} +{{ template "footer" .}} diff --git a/templates/pages/settings_ssh.html b/templates/pages/settings_ssh.html new file mode 100644 index 0000000..18cf773 --- /dev/null +++ b/templates/pages/settings_ssh.html @@ -0,0 +1,69 @@ +{{ template "header" .}} +{{ template "settings_header" .}} +
+
+
+
+

+ {{ .locale.Tr "settings.add-ssh-key" }} +

+

+ {{ .locale.Tr "settings.add-ssh-key-help" }} +

+
+
+ +
+ +
+
+ +
+ +
+ +
+
+ + {{ .csrfHtml }} + +
+
+
+
+
    + {{ if .sshKeys }} + {{ range $key := .sshKeys }} +
  • +
    + + + +
    +

    {{ .Title }}

    +

    SHA256:{{.SHA}}

    +

    {{ $.locale.Tr "settings.ssh-key-added-at" }} {{ .CreatedAt }}

    + {{ if eq .LastUsedAt 0 }} +

    {{ $.locale.Tr "settings.ssh-key-never-used" }}

    + {{ else }} +

    {{ $.locale.Tr "settings.ssh-key-last-used" }} {{ .LastUsedAt }}

    + {{ end }} +
    +
    + + {{ $.csrfHtml }} + + + +
    +
  • + {{ end }} + {{ end }} +
+
+
+
+
+ +{{ template "settings_footer" .}} +{{ template "footer" .}} diff --git a/templates/pages/settings_style.html b/templates/pages/settings_style.html new file mode 100644 index 0000000..7a6b77d --- /dev/null +++ b/templates/pages/settings_style.html @@ -0,0 +1,110 @@ +{{ template "header" .}} +{{ template "settings_header" .}} +
+
+
+

+ {{ .locale.Tr "settings.style.gist-code" }} +

+
+
+
+ + + + + + file.txt + + + + + + + +
+
+
+
+
{{ if ne (index $line 0) 64 }}{{ slice $line 0 1 }}{{ end }}{{ if ne (index $line 0) 64 }}{{ slice $line 0 1 }}{{ end }} {{ if ne (index $line 0) 64 }}{{ slice $line 1 }}{{ else }}{{ $line }}{{ end }}
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
1This is a string
2This is a really really really really really really really really long string
3
4- code removed
5- another pretty pretty pretty pretty pretty pretty long code removed
6+ code added
7+ added a line which help to demonstrate the difference between enabling and disabling soft wrap
8
+
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ {{ .csrfHtml }} + +
+
+ + + + + + +{{ template "settings_footer" .}} +{{ template "footer" .}} diff --git a/templates/pages/totp.html b/templates/pages/totp.html index 738393f..a27b0aa 100644 --- a/templates/pages/totp.html +++ b/templates/pages/totp.html @@ -23,7 +23,7 @@
- {{ .locale.Tr "auth.totp.proceed" }} + {{ .locale.Tr "auth.totp.proceed" }}
{{ else }} diff --git a/templates/partials/_gist_preview.html b/templates/partials/_gist_preview.html index dc57511..fe83eeb 100644 --- a/templates/partials/_gist_preview.html +++ b/templates/partials/_gist_preview.html @@ -63,7 +63,7 @@ {{ if isMarkdown .gist.PreviewFilename }}
{{ .gist.HTML | safe }}
{{ else }} - +
{{ $ii := "1" }} {{ $i := toInt $ii }} @@ -71,7 +71,7 @@ - + {{ $i = inc $i }} {{ end }}
{{$i}}{{ $line | safe }}{{ $line | safe }}