From fd7b2a78b27fc48dde5cbfb0c0b4b94d0ade51d6 Mon Sep 17 00:00:00 2001
From: Sung <8265228+sungwoncho@users.noreply.github.com>
Date: Sat, 11 Oct 2025 13:35:39 -0700
Subject: [PATCH] Remove email verification (#688)
* Remove email verified flag
* Fix sass deprecation warnings
---
pkg/server/app/email.go | 22 -
pkg/server/app/email_test.go | 16 -
pkg/server/app/errors.go | 3 -
pkg/server/assets/package-lock.json | 529 ++++++++++++++----
pkg/server/assets/styles/src/_books.scss | 11 +-
pkg/server/assets/styles/src/_buttons.scss | 56 +-
pkg/server/assets/styles/src/_font.scss | 6 +-
pkg/server/assets/styles/src/_global.scss | 36 +-
pkg/server/assets/styles/src/_header.scss | 56 +-
pkg/server/assets/styles/src/_home.scss | 66 +--
pkg/server/assets/styles/src/_login.scss | 25 +-
pkg/server/assets/styles/src/_note.scss | 43 +-
pkg/server/assets/styles/src/_rem.scss | 39 +-
pkg/server/assets/styles/src/_responsive.scss | 18 +-
pkg/server/assets/styles/src/_settings.scss | 68 +--
pkg/server/assets/styles/src/_shared.scss | 60 +-
pkg/server/assets/styles/src/_theme.scss | 5 +-
pkg/server/assets/styles/src/main.scss | 42 +-
pkg/server/controllers/helpers.go | 2 -
pkg/server/controllers/routes.go | 2 -
pkg/server/controllers/users.go | 126 -----
pkg/server/controllers/users_test.go | 291 +---------
pkg/server/database/consts.go | 2 -
pkg/server/database/models.go | 7 +-
pkg/server/mailer/mailer.go | 7 -
pkg/server/mailer/mailer_test.go | 39 --
pkg/server/mailer/templates/verify_email.txt | 5 -
pkg/server/mailer/types.go | 6 -
pkg/server/middleware/auth_test.go | 4 +-
pkg/server/session/session.go | 10 +-
pkg/server/session/session_test.go | 9 +-
pkg/server/token/token_test.go | 2 +-
.../templates/users/email_verification.gohtml | 2 -
.../views/templates/users/settings.gohtml | 28 -
pkg/server/views/view.go | 2 -
35 files changed, 731 insertions(+), 914 deletions(-)
delete mode 100644 pkg/server/mailer/templates/verify_email.txt
delete mode 100644 pkg/server/views/templates/users/email_verification.gohtml
diff --git a/pkg/server/app/email.go b/pkg/server/app/email.go
index 0897e415..2f721a6f 100644
--- a/pkg/server/app/email.go
+++ b/pkg/server/app/email.go
@@ -65,28 +65,6 @@ func getNoreplySender(webURL string) (string, error) {
return addr, nil
}
-// SendVerificationEmail sends verification email
-func (a *App) SendVerificationEmail(email, tokenValue string) error {
- body, err := a.EmailTemplates.Execute(mailer.EmailTypeEmailVerification, mailer.EmailKindText, mailer.EmailVerificationTmplData{
- Token: tokenValue,
- WebURL: a.WebURL,
- })
- if err != nil {
- return errors.Wrapf(err, "executing reset verification template for %s", email)
- }
-
- from, err := GetSenderEmail(a.WebURL, defaultSender)
- if err != nil {
- return errors.Wrap(err, "getting the sender email")
- }
-
- if err := a.EmailBackend.Queue("Verify your Dnote email address", from, []string{email}, mailer.EmailKindText, body); err != nil {
- return errors.Wrapf(err, "queueing email for %s", email)
- }
-
- return nil
-}
-
// SendWelcomeEmail sends welcome email
func (a *App) SendWelcomeEmail(email string) error {
body, err := a.EmailTemplates.Execute(mailer.EmailTypeWelcome, mailer.EmailKindText, mailer.WelcomeTmplData{
diff --git a/pkg/server/app/email_test.go b/pkg/server/app/email_test.go
index 1c68a96f..56270604 100644
--- a/pkg/server/app/email_test.go
+++ b/pkg/server/app/email_test.go
@@ -26,22 +26,6 @@ import (
"github.com/dnote/dnote/pkg/server/testutils"
)
-func TestSendVerificationEmail(t *testing.T) {
- emailBackend := testutils.MockEmailbackendImplementation{}
- a := NewTest()
- a.EmailBackend = &emailBackend
- a.WebURL = "http://example.com"
-
- if err := a.SendVerificationEmail("alice@example.com", "mockTokenValue"); err != nil {
- t.Fatal(err, "failed to perform")
- }
-
- assert.Equalf(t, len(emailBackend.Emails), 1, "email queue count mismatch")
- assert.Equal(t, emailBackend.Emails[0].From, "noreply@example.com", "email sender mismatch")
- assert.DeepEqual(t, emailBackend.Emails[0].To, []string{"alice@example.com"}, "email sender mismatch")
-
-}
-
func TestSendWelcomeEmail(t *testing.T) {
emailBackend := testutils.MockEmailbackendImplementation{}
a := NewTest()
diff --git a/pkg/server/app/errors.go b/pkg/server/app/errors.go
index 250de5d1..895fc40f 100644
--- a/pkg/server/app/errors.go
+++ b/pkg/server/app/errors.go
@@ -79,7 +79,4 @@ var (
ErrInvalidPassword appError = "Invalid currnet password."
// ErrEmailTooLong is an error for email length exceeding the limit
ErrEmailTooLong appError = "Email is too long."
-
- // ErrEmailAlreadyVerified is an error for trying to verify email that is already verified
- ErrEmailAlreadyVerified appError = "Email is already verified."
)
diff --git a/pkg/server/assets/package-lock.json b/pkg/server/assets/package-lock.json
index a37c4305..ec63082c 100644
--- a/pkg/server/assets/package-lock.json
+++ b/pkg/server/assets/package-lock.json
@@ -1,158 +1,493 @@
{
"name": "assets",
"version": "1.0.0",
- "lockfileVersion": 1,
+ "lockfileVersion": 3,
"requires": true,
- "dependencies": {
- "anymatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
- "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
- "dev": true,
- "requires": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
+ "packages": {
+ "": {
+ "name": "assets",
+ "version": "1.0.0",
+ "license": "AGPL-3.0-or-later",
+ "devDependencies": {
+ "sass": "^1.50.1"
}
},
- "binary-extensions": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
- "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
- "dev": true
+ "node_modules/@parcel/watcher": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
+ "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "dependencies": {
+ "detect-libc": "^1.0.3",
+ "is-glob": "^4.0.3",
+ "micromatch": "^4.0.5",
+ "node-addon-api": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "@parcel/watcher-android-arm64": "2.5.1",
+ "@parcel/watcher-darwin-arm64": "2.5.1",
+ "@parcel/watcher-darwin-x64": "2.5.1",
+ "@parcel/watcher-freebsd-x64": "2.5.1",
+ "@parcel/watcher-linux-arm-glibc": "2.5.1",
+ "@parcel/watcher-linux-arm-musl": "2.5.1",
+ "@parcel/watcher-linux-arm64-glibc": "2.5.1",
+ "@parcel/watcher-linux-arm64-musl": "2.5.1",
+ "@parcel/watcher-linux-x64-glibc": "2.5.1",
+ "@parcel/watcher-linux-x64-musl": "2.5.1",
+ "@parcel/watcher-win32-arm64": "2.5.1",
+ "@parcel/watcher-win32-ia32": "2.5.1",
+ "@parcel/watcher-win32-x64": "2.5.1"
+ }
},
- "braces": {
+ "node_modules/@parcel/watcher-android-arm64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
+ "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-darwin-arm64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
+ "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-darwin-x64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
+ "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-freebsd-x64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
+ "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm-glibc": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
+ "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm-musl": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
+ "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm64-glibc": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
+ "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm64-musl": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
+ "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-x64-glibc": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
+ "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-x64-musl": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
+ "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-arm64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
+ "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-ia32": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
+ "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-x64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
+ "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
- "requires": {
+ "optional": true,
+ "dependencies": {
"fill-range": "^7.1.1"
},
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "dev": true,
"dependencies": {
- "fill-range": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
- "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
- "dev": true,
- "requires": {
- "to-regex-range": "^5.0.1"
- }
- }
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
}
},
- "chokidar": {
- "version": "3.5.3",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
- "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "node_modules/detect-libc": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+ "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"dev": true,
- "requires": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "fsevents": "~2.3.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
+ "optional": true,
+ "bin": {
+ "detect-libc": "bin/detect-libc.js"
+ },
+ "engines": {
+ "node": ">=0.10"
}
},
- "fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
- "optional": true
- },
- "glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "requires": {
- "is-glob": "^4.0.1"
+ "optional": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "immutable": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz",
- "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==",
+ "node_modules/immutable": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz",
+ "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==",
"dev": true
},
- "is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dev": true,
- "requires": {
- "binary-extensions": "^2.0.0"
- }
- },
- "is-extglob": {
+ "node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
- "dev": true
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "is-glob": {
+ "node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
- "requires": {
+ "optional": true,
+ "dependencies": {
"is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
}
},
- "is-number": {
+ "node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
},
- "normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
},
- "picomatch": {
+ "node_modules/node-addon-api": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
+ "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true
- },
- "readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
- "requires": {
- "picomatch": "^2.2.1"
+ "optional": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
}
},
- "sass": {
- "version": "1.50.1",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.50.1.tgz",
- "integrity": "sha512-noTnY41KnlW2A9P8sdwESpDmo+KBNkukI1i8+hOK3footBUcohNHtdOJbckp46XO95nuvcHDDZ+4tmOnpK3hjw==",
+ "node_modules/readdirp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+ "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"dev": true,
- "requires": {
- "chokidar": ">=3.0.0 <4.0.0",
- "immutable": "^4.0.0",
+ "engines": {
+ "node": ">= 14.18.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/sass": {
+ "version": "1.93.2",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.2.tgz",
+ "integrity": "sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==",
+ "dev": true,
+ "dependencies": {
+ "chokidar": "^4.0.0",
+ "immutable": "^5.0.2",
"source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "optionalDependencies": {
+ "@parcel/watcher": "^2.4.1"
}
},
- "source-map-js": {
+ "node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
- "dev": true
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "to-regex-range": {
+ "node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
- "requires": {
+ "optional": true,
+ "dependencies": {
"is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
}
}
}
diff --git a/pkg/server/assets/styles/src/_books.scss b/pkg/server/assets/styles/src/_books.scss
index 6f58ef94..c94c4347 100644
--- a/pkg/server/assets/styles/src/_books.scss
+++ b/pkg/server/assets/styles/src/_books.scss
@@ -1,3 +1,6 @@
+@use "rem";
+@use "theme";
+
/* Copyright (C) 2019, 2020, 2021, 2022, 2023, 2024, 2025 Dnote contributors
*
* This file is part of Dnote.
@@ -18,12 +21,12 @@
.books-page {
.books-content {
- padding: rem(16px) rem(24px);
- margin-top: rem(16px);
+ padding: rem.rem(16px) rem.rem(24px);
+ margin-top: rem.rem(16px);
h1 {
- border-bottom: 1px solid $lighter-gray;
- margin-bottom: rem(12px);
+ border-bottom: 1px solid theme.$lighter-gray;
+ margin-bottom: rem.rem(12px);
}
}
}
diff --git a/pkg/server/assets/styles/src/_buttons.scss b/pkg/server/assets/styles/src/_buttons.scss
index ef1095d5..16059864 100644
--- a/pkg/server/assets/styles/src/_buttons.scss
+++ b/pkg/server/assets/styles/src/_buttons.scss
@@ -16,9 +16,11 @@
* along with Dnote. If not, see .
*/
-@import './theme';
-@import './rem';
-@import './font';
+@use "sass:color";
+@use 'theme';
+@use 'rem';
+@use 'font';
+@use "responsive";
@mixin button($text-color, $background-color) {
color: $text-color;
@@ -26,7 +28,7 @@
&:not(:disabled):hover {
color: $text-color;
- background-color: darken($background-color, 5%);
+ background-color: color.adjust($background-color, $lightness: -5%);
box-shadow: 0px 0px 4px 2px #cacaca;
}
}
@@ -87,40 +89,40 @@ button:disabled {
}
.button-small {
- @include font-size('small');
- padding: rem(4px) rem(12px);
+ @include font.font-size('small');
+ padding: rem.rem(4px) rem.rem(12px);
}
.button-normal {
// @include font-size('small');
- padding: rem(8px) rem(16px);
+ padding: rem.rem(8px) rem.rem(16px);
}
.button-large {
- @include font-size('medium');
+ @include font.font-size('medium');
- padding: rem(8px) rem(24px);
+ padding: rem.rem(8px) rem.rem(24px);
- @include breakpoint(md) {
- padding: rem(12px) rem(36px);
+ @include responsive.breakpoint(md) {
+ padding: rem.rem(12px) rem.rem(36px);
}
- @include breakpoint(lg) {
- padding: rem(12px) rem(48px);
+ @include responsive.breakpoint(lg) {
+ padding: rem.rem(12px) rem.rem(48px);
}
}
.button-xlarge {
- @include font-size('x-large');
+ @include font.font-size('x-large');
- padding: rem(16px) rem(24px);
+ padding: rem.rem(16px) rem.rem(24px);
- @include breakpoint(md) {
- padding: rem(12px) rem(36px);
+ @include responsive.breakpoint(md) {
+ padding: rem.rem(12px) rem.rem(36px);
}
- @include breakpoint(lg) {
- padding: rem(16px) rem(48px);
+ @include responsive.breakpoint(lg) {
+ padding: rem.rem(16px) rem.rem(48px);
}
}
@@ -133,23 +135,23 @@ button:disabled {
}
.button-second {
- @include button($black, $second);
+ @include button(theme.$black, theme.$second);
}
.button-second-outline {
- @include button-outline($black, $second);
+ @include button-outline(theme.$black, theme.$second);
}
.button-third {
- @include button(#ffffff, $third);
+ @include button(#ffffff, theme.$third);
}
.button-third-outline {
- @include button-outline($third, $third);
+ @include button-outline(theme.$third, theme.$third);
}
.button-danger {
- @include button-outline($danger-text, $danger-text);
+ @include button-outline(theme.$danger-text, theme.$danger-text);
font-weight: 600;
}
@@ -158,7 +160,7 @@ button:disabled {
}
.button ~ .button {
- margin-left: rem(12px);
+ margin-left: rem.rem(12px);
}
.button-no-ui {
@@ -173,10 +175,10 @@ button:disabled {
}
.button-link {
- color: $link;
+ color: theme.$link;
&:hover {
- color: $link-hover;
+ color: theme.$link-hover;
text-decoration: underline;
}
}
diff --git a/pkg/server/assets/styles/src/_font.scss b/pkg/server/assets/styles/src/_font.scss
index f64b7236..37bc3163 100644
--- a/pkg/server/assets/styles/src/_font.scss
+++ b/pkg/server/assets/styles/src/_font.scss
@@ -16,7 +16,7 @@
* along with Dnote. If not, see .
*/
-@import './responsive';
+@use 'responsive';
$lowDecay: 0.1;
$medDecay: 0.15;
@@ -95,12 +95,12 @@ $highDecay: 0.2;
font-size: $smSizeValue * 1px;
font-size: $smSizeValue * 0.1rem;
- @include breakpoint(md) {
+ @include responsive.breakpoint(md) {
font-size: $mdSizeValue * 1px;
font-size: $mdSizeValue * 0.1rem;
}
- @include breakpoint(lg) {
+ @include responsive.breakpoint(lg) {
font-size: $lgSizeValue * 1px;
font-size: $lgSizeValue * 0.1rem;
}
diff --git a/pkg/server/assets/styles/src/_global.scss b/pkg/server/assets/styles/src/_global.scss
index a91a9a95..8c497658 100644
--- a/pkg/server/assets/styles/src/_global.scss
+++ b/pkg/server/assets/styles/src/_global.scss
@@ -1,3 +1,9 @@
+@use "font";
+@use "rem";
+@use "responsive";
+@use "theme";
+@use "variables";
+
/* Copyright (C) 2019, 2020, 2021, 2022, 2023, 2024, 2025 Dnote contributors
*
* This file is part of Dnote.
@@ -20,8 +26,8 @@
position: relative;
display: flex;
flex-direction: column;
- background: $lighter-gray;
- min-height: calc(100vh - #{$header-height});
+ background: theme.$lighter-gray;
+ min-height: calc(100vh - #{variables.$header-height});
// margin-bottom: $footer-height;
&.nofooter {
@@ -29,49 +35,49 @@
}
&.noheader:not(.nofooter) {
- min-height: calc(100vh - #{$footer-height});
+ min-height: calc(100vh - #{variables.$footer-height});
}
&.nofooter:not(.noheader) {
- min-height: calc(100vh - #{$header-height});
+ min-height: calc(100vh - #{variables.$header-height});
}
&.nofooter.noheader {
min-height: 100vh;
}
- @include breakpoint(lg) {
+ @include responsive.breakpoint(lg) {
margin-bottom: 0;
- min-height: calc(100vh - #{$header-height});
+ min-height: calc(100vh - #{variables.$header-height});
}
}
/* partials */
.partial--time {
- color: $gray;
- @include font-size('small');
+ color: theme.$gray;
+ @include font.font-size('small');
.mobile-text {
- @include breakpoint(md) {
+ @include responsive.breakpoint(md) {
display: none;
}
}
.text {
display: none;
- @include breakpoint(md) {
+ @include responsive.breakpoint(md) {
display: inherit;
}
}
}
.partial--page-toolbar {
- @include breakpoint(lg) {
- height: rem(48px);
- border-radius: rem(4px);
- background: $light;
+ @include responsive.breakpoint(lg) {
+ height: rem.rem(48px);
+ border-radius: rem.rem(4px);
+ background: theme.$light;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.14);
&.bottom {
- margin-top: rem(12px);
+ margin-top: rem.rem(12px);
}
}
}
diff --git a/pkg/server/assets/styles/src/_header.scss b/pkg/server/assets/styles/src/_header.scss
index 61c7b5bf..c952599e 100644
--- a/pkg/server/assets/styles/src/_header.scss
+++ b/pkg/server/assets/styles/src/_header.scss
@@ -16,8 +16,12 @@
* along with Dnote. If not, see .
*/
-@import './theme';
-@import './variables';
+@use "sass:color";
+@use 'theme';
+@use 'variables';
+@use "font";
+@use "rem";
+@use "responsive";
.header-wrapper {
padding: 0;
@@ -25,7 +29,7 @@
position: relative;
display: flex;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
- background: $first;
+ background: theme.$first;
align-items: stretch;
justify-content: space-between;
flex: 1;
@@ -33,13 +37,13 @@
position: sticky;
top: 0;
z-index: 4;
- height: $header-height;
+ height: variables.$header-height;
.container {
height: 100%;
}
- @include breakpoint(md) {
+ @include responsive.breakpoint(md) {
flex-direction: row;
}
@@ -60,15 +64,15 @@
.search-wrapper {
align-items: center;
display: flex;
- margin-left: rem(32px);
+ margin-left: rem.rem(32px);
}
.search-input {
- width: rem(356px);
+ width: rem.rem(356px);
border: 0;
padding: 4px 12px;
- border-radius: rem(4px);
- @include font-size('small');
+ border-radius: rem.rem(4px);
+ @include font.font-size('small');
}
.brand {
@@ -81,7 +85,7 @@
}
.main-nav {
- margin-left: rem(32px);
+ margin-left: rem.rem(32px);
display: flex;
.list {
@@ -94,22 +98,22 @@
}
.nav-link {
- @include font-size('small');
+ @include font.font-size('small');
display: flex;
font-weight: 600;
align-items: center;
- padding: 0 rem(16px);
- color: $white;
+ padding: 0 rem.rem(16px);
+ color: theme.$white;
&:hover {
- color: $white;
+ color: theme.$white;
text-decoration: none;
- background: lighten($first, 10%);
+ background: color.adjust(theme.$first, $lightness: 10%);
}
}
.nav-item {
- @include font-size('small');
+ @include font.font-size('small');
font-weight: 600;
}
}
@@ -131,7 +135,7 @@
display: none;
position: absolute;
background-color: #f1f1f1;
- width: rem(240px);
+ width: rem.rem(240px);
background: #fff;
border: 1px solid #d8d8d8;
border-radius: 4px;
@@ -154,15 +158,15 @@
}
.account-dropdown-header {
- @include font-size('small');
- color: $light-gray;
- padding: rem(8px) rem(12px);
+ @include font.font-size('small');
+ color: theme.$light-gray;
+ padding: rem.rem(8px) rem.rem(12px);
display: block;
margin-bottom: 0;
white-space: nowrap;
svg {
- fill: $light-gray;
+ fill: theme.$light-gray;
}
.email {
@@ -173,15 +177,15 @@
}
.dropdown-link {
- @include font-size('small');
+ @include font.font-size('small');
white-space: pre;
- padding: rem(8px) rem(14px);
+ padding: rem.rem(8px) rem.rem(14px);
width: 100%;
display: block;
color: black;
&:hover {
- background: $lighter-gray;
+ background: theme.$lighter-gray;
text-decoration: none;
color: #0056b3;
}
@@ -192,7 +196,7 @@
}
&:not(.disabled):focus {
- background: $lighter-gray;
+ background: theme.$lighter-gray;
color: #0056b3;
outline: 1px dotted gray;
}
@@ -204,7 +208,7 @@
}
.session-notice {
- margin-left: rem(4px);
+ margin-left: rem.rem(4px);
}
}
}
diff --git a/pkg/server/assets/styles/src/_home.scss b/pkg/server/assets/styles/src/_home.scss
index b1eac511..ce72a9f3 100644
--- a/pkg/server/assets/styles/src/_home.scss
+++ b/pkg/server/assets/styles/src/_home.scss
@@ -16,21 +16,23 @@
* along with Dnote. If not, see .
*/
-@import './theme';
-@import './font';
+@use 'theme';
+@use 'font';
+@use "rem";
+@use "responsive";
.home-page {
.note-group-list {
flex-grow: 1;
- @include breakpoint(lg) {
- margin-top: rem(16px);
+ @include responsive.breakpoint(lg) {
+ margin-top: rem.rem(16px);
}
.note-group-list-empty {
- padding: rem(40px) rem(16px);
+ padding: rem.rem(40px) rem.rem(16px);
text-align: center;
- color: $gray;
+ color: theme.$gray;
}
}
@@ -40,29 +42,29 @@
box-shadow: 0 0 8px rgba(0, 0, 0, 0.14);
&:not(:first-of-type) {
- margin-top: rem(20px);
+ margin-top: rem.rem(20px);
- @include breakpoint(md) {
- margin-top: rem(24px);
+ @include responsive.breakpoint(md) {
+ margin-top: rem.rem(24px);
}
}
.note-group-header {
- @include font-size('small');
+ @include font.font-size('small');
display: flex;
justify-content: space-between;
color: white;
- padding: rem(12px) rem(16px);
- background: $light;
- color: $black;
- border-bottom: 1px solid $border-color;
+ padding: rem.rem(12px) rem.rem(16px);
+ background: theme.$light;
+ color: theme.$black;
+ border-bottom: 1px solid theme.$border-color;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.date {
font-weight: 600;
- @include font-size('small');
+ @include font.font-size('small');
}
.mask {
@@ -78,7 +80,7 @@
.header-date {
font-weight: 600;
- @include font-size('regular');
+ @include font.font-size('regular');
}
.header-count {
font-weight: 300;
@@ -101,23 +103,23 @@
background: white;
position: relative;
- border-bottom: 1px solid $border-color;
+ border-bottom: 1px solid theme.$border-color;
.link {
- color: $black;
+ color: theme.$black;
display: block;
- padding: rem(12px) rem(16px);
+ padding: rem.rem(12px) rem.rem(16px);
border: 2px solid transparent;
&:hover {
text-decoration: none;
- background: $light-blue;
+ background: theme.$light-blue;
color: inherit;
}
}
.meta {
- line-height: rem(16px);
+ line-height: rem.rem(16px);
}
.body {
@@ -131,11 +133,11 @@
}
.note-content {
- margin-top: rem(12px);
+ margin-top: rem.rem(12px);
line-height: 1.6rem;
overflow: hidden;
text-overflow: ellipsis;
- color: $gray;
+ color: theme.$gray;
}
.book-label {
@@ -143,11 +145,11 @@
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 700;
- @include font-size('small');
+ @include font.font-size('small');
width: 212px;
- @include breakpoint('md') {
+ @include responsive.breakpoint('md') {
width: 320px;
}
}
@@ -155,7 +157,7 @@
.match {
display: inline-block;
background: #f7f77d;
- padding: rem(4px) rem(4px);
+ padding: rem.rem(4px) rem.rem(4px);
}
}
@@ -168,12 +170,12 @@
align-items: center;
.paginator-info {
- @include font-size('small');
- color: $gray;
+ @include font.font-size('small');
+ color: theme.$gray;
}
.paginator-link {
- padding: rem(12px) rem(12px);
+ padding: rem.rem(12px) rem.rem(12px);
&.disabled {
cursor: not-allowed;
@@ -181,10 +183,10 @@
}
.paginator-link-prev {
- margin-left: rem(8px);
+ margin-left: rem.rem(8px);
- @include breakpoint(md) {
- margin-left: rem(20px);
+ @include responsive.breakpoint(md) {
+ margin-left: rem.rem(20px);
}
}
diff --git a/pkg/server/assets/styles/src/_login.scss b/pkg/server/assets/styles/src/_login.scss
index 49d5c42c..9200a911 100644
--- a/pkg/server/assets/styles/src/_login.scss
+++ b/pkg/server/assets/styles/src/_login.scss
@@ -16,11 +16,12 @@
* along with Dnote. If not, see .
*/
-@import './theme';
-@import './font';
+@use 'theme';
+@use 'font';
+@use "rem";
.auth-page {
- background: $lighter-gray;
+ background: theme.$lighter-gray;
text-align: center;
min-height: 100vh;
padding: 50px 0;
@@ -30,8 +31,8 @@
}
.heading {
- color: $black;
- @include font-size('2x-large');
+ color: theme.$black;
+ @include font.font-size('2x-large');
font-weight: 300;
margin-top: 12px;
margin-bottom: 0;
@@ -58,15 +59,15 @@
.callout {
color: #7c7c7c;
- @include font-size('small');
+ @include font.font-size('small');
}
.cta {
- @include font-size('small');
+ @include font.font-size('small');
}
.panel {
- border: 1px solid $border-color;
- background: $white;
+ border: 1px solid theme.$border-color;
+ background: theme.$white;
border-radius: 2px;
padding: 20px;
text-align: left;
@@ -82,21 +83,21 @@
}
}
.label {
- @include font-size('small');
+ @include font.font-size('small');
font-weight: 600;
width: 100%;
margin-bottom: 0;
}
.forgot {
- @include font-size('small');
+ @include font.font-size('small');
float: right;
font-weight: 400;
}
&.password-reset-page {
.email-input {
- margin-top: rem(16px);
+ margin-top: rem.rem(16px);
}
}
diff --git a/pkg/server/assets/styles/src/_note.scss b/pkg/server/assets/styles/src/_note.scss
index 623d7969..edc83a34 100644
--- a/pkg/server/assets/styles/src/_note.scss
+++ b/pkg/server/assets/styles/src/_note.scss
@@ -1,3 +1,8 @@
+@use "font";
+@use "rem";
+@use "responsive";
+@use "theme";
+
/* Copyright (C) 2019, 2020, 2021, 2022, 2023, 2024, 2025 Dnote contributors
*
* This file is part of Dnote.
@@ -18,7 +23,7 @@
.note-page {
// min-height: calc(100vh - 57px);
- background: $lighter-gray;
+ background: theme.$lighter-gray;
flex-grow: 1;
flex-basis: 0;
@@ -38,8 +43,8 @@
display: flex;
align-items: center;
justify-content: space-between;
- padding: rem(12px) rem(16px);
- border-bottom: 1px solid $border-color;
+ padding: rem.rem(12px) rem.rem(16px);
+ border-bottom: 1px solid theme.$border-color;
}
.header-left,
.header-right {
@@ -52,27 +57,27 @@
}
.content-wrapper {
- padding: rem(12px) rem(16px);
+ padding: rem.rem(12px) rem.rem(16px);
}
.collapsed-content {
- color: $light-gray;
+ color: theme.$light-gray;
}
.footer {
display: flex;
justify-content: space-between;
align-items: center;
- @include font-size('small');
- padding: rem(12px) rem(16px);
+ @include font.font-size('small');
+ padding: rem.rem(12px) rem.rem(16px);
}
.ts {
- color: $light-gray;
+ color: theme.$light-gray;
}
.ts-lead {
display: none;
- @include breakpoint(md) {
+ @include responsive.breakpoint(md) {
display: inline;
}
}
@@ -83,13 +88,13 @@
}
.book-label {
- @include font-size('medium');
+ @include font.font-size('medium');
font-weight: 600;
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
- color: $black;
+ color: theme.$black;
a {
color: inherit;
@@ -103,17 +108,17 @@
// header
.header {
.book-label {
- max-width: rem(200px);
- margin-left: rem(12px);
+ max-width: rem.rem(200px);
+ margin-left: rem.rem(12px);
- @include breakpoint(sm) {
- max-width: rem(200px);
+ @include responsive.breakpoint(sm) {
+ max-width: rem.rem(200px);
}
- @include breakpoint(md) {
- max-width: rem(420px);
+ @include responsive.breakpoint(md) {
+ max-width: rem.rem(420px);
}
- @include breakpoint(lg) {
- max-width: rem(600px);
+ @include responsive.breakpoint(lg) {
+ max-width: rem.rem(600px);
}
}
}
diff --git a/pkg/server/assets/styles/src/_rem.scss b/pkg/server/assets/styles/src/_rem.scss
index e875d0cd..cd736925 100644
--- a/pkg/server/assets/styles/src/_rem.scss
+++ b/pkg/server/assets/styles/src/_rem.scss
@@ -1,3 +1,6 @@
+@use "sass:list";
+@use "sass:map";
+@use "sass:meta";
/* Copyright (C) 2019, 2020, 2021, 2022, 2023, 2024, 2025 Dnote contributors
*
* This file is part of Dnote.
@@ -24,6 +27,8 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+@use "sass:math";
+
// assume 1 rem = 10 px
// achieved by body { font-size: 62.5%; )
$rem-baseline: 10px !default;
@@ -32,24 +37,24 @@ $rem-px-only: false !default;
@function rem-separator($list, $separator: false) {
@if $separator == "comma" or $separator == "space" {
- @return append($list, null, $separator);
+ @return list.append($list, null, $separator);
}
- @if function-exists("list-separator") == true {
- @return list-separator($list);
+ @if meta.function-exists("list-separator") == true {
+ @return list.separator($list);
}
// list-separator polyfill by Hugo Giraudel (https://sass-compatibility.github.io/#list_separator_function)
$test-list: ();
@each $item in $list {
- $test-list: append($test-list, $item, space);
+ $test-list: list.append($test-list, $item, space);
}
@return if($test-list == $list, space, comma);
}
@mixin rem-baseline($zoom: 100%) {
- font-size: $zoom / 16px * $rem-baseline;
+ font-size: math.div($zoom, 16px) * $rem-baseline;
}
@function rem-convert($to, $values...) {
@@ -57,28 +62,28 @@ $rem-px-only: false !default;
$separator: rem-separator($values);
@each $value in $values {
- @if type-of($value) == "number" and unit($value) == "rem" and $to == "px" {
- $result: append($result, $value / 1rem * $rem-baseline, $separator);
+ @if meta.type-of($value) == "number" and math.unit($value) == "rem" and $to == "px" {
+ $result: list.append($result, math.div($value, 1rem) * $rem-baseline, $separator);
} @else if
- type-of($value) ==
+ meta.type-of($value) ==
"number" and
- unit($value) ==
+ math.unit($value) ==
"px" and
$to ==
"rem"
{
- $result: append($result, $value / $rem-baseline * 1rem, $separator);
- } @else if type-of($value) == "list" {
+ $result: list.append($result, math.div($value, $rem-baseline) * 1rem, $separator);
+ } @else if meta.type-of($value) == "list" {
$value-separator: rem-separator($value);
$value: rem-convert($to, $value...);
$value: rem-separator($value, $value-separator);
- $result: append($result, $value, $separator);
+ $result: list.append($result, $value, $separator);
} @else {
- $result: append($result, $value, $separator);
+ $result: list.append($result, $value, $separator);
}
}
- @return if(length($result) == 1, nth($result, 1), $result);
+ @return if(list.length($result) == 1, list.nth($result, 1), $result);
}
@function rem($values...) {
@@ -90,9 +95,9 @@ $rem-px-only: false !default;
}
@mixin rem($properties, $values...) {
- @if type-of($properties) == "map" {
- @each $property in map-keys($properties) {
- @include rem($property, map-get($properties, $property));
+ @if meta.type-of($properties) == "map" {
+ @each $property in map.keys($properties) {
+ @include rem($property, map.get($properties, $property));
}
} @else {
@each $property in $properties {
diff --git a/pkg/server/assets/styles/src/_responsive.scss b/pkg/server/assets/styles/src/_responsive.scss
index 05188e4c..9aaf9a7f 100644
--- a/pkg/server/assets/styles/src/_responsive.scss
+++ b/pkg/server/assets/styles/src/_responsive.scss
@@ -16,39 +16,39 @@
* along with Dnote. If not, see .
*/
-@import './variables';
+@use 'variables';
@mixin breakpoint($point) {
@if $point == xl {
- @media (min-width: $xl-breakpoint) {
+ @media (min-width: variables.$xl-breakpoint) {
@content;
}
} @else if $point == lg {
- @media (min-width: $lg-breakpoint) {
+ @media (min-width: variables.$lg-breakpoint) {
@content;
}
} @else if $point == md {
- @media (min-width: $md-breakpoint) {
+ @media (min-width: variables.$md-breakpoint) {
@content;
}
} @else if $point == sm {
- @media (min-width: $sm-breakpoint) {
+ @media (min-width: variables.$sm-breakpoint) {
@content;
}
} @else if $point == smonly {
- @media (min-width: $sm-breakpoint) and (max-width: $md-breakpoint - 1px) {
+ @media (min-width: variables.$sm-breakpoint) and (max-width: variables.$md-breakpoint - 1px) {
@content;
}
} @else if $point == smdown {
- @media (max-width: $md-breakpoint - 1px) {
+ @media (max-width: variables.$md-breakpoint - 1px) {
@content;
}
} @else if $point == mdonly {
- @media (min-width: $md-breakpoint) and (max-width: $lg-breakpoint - 1px) {
+ @media (min-width: variables.$md-breakpoint) and (max-width: variables.$lg-breakpoint - 1px) {
@content;
}
} @else if $point == mddown {
- @media (max-width: $lg-breakpoint - 1px) {
+ @media (max-width: variables.$lg-breakpoint - 1px) {
@content;
}
}
diff --git a/pkg/server/assets/styles/src/_settings.scss b/pkg/server/assets/styles/src/_settings.scss
index 6b89ed7a..2f5d439d 100644
--- a/pkg/server/assets/styles/src/_settings.scss
+++ b/pkg/server/assets/styles/src/_settings.scss
@@ -16,17 +16,19 @@
* along with Dnote. If not, see .
*/
-@import './theme';
-@import './font';
+@use 'theme';
+@use 'font';
+@use "rem";
+@use "responsive";
.settings-page {
.sidebar {
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
background: white;
- margin-bottom: rem(20px);
- margin-top: rem(20px);
+ margin-bottom: rem.rem(20px);
+ margin-top: rem.rem(20px);
- @include breakpoint(lg) {
+ @include responsive.breakpoint(lg) {
margin-bottom: 0;
margin-top: 0;
}
@@ -34,30 +36,30 @@
.sidebar-item {
display: block;
- padding: rem(12px) rem(16px);
+ padding: rem.rem(12px) rem.rem(16px);
border-left: 4px solid transparent;
- @include font-size('regular');
+ @include font.font-size('regular');
&:hover {
text-decoration: none;
- background: $light;
+ background: theme.$light;
}
&.active {
font-weight: 600;
- border-left-color: $first;
+ border-left-color: theme.$first;
}
}
.setting-section-wrapper {
.header {
- @include breakpoint(lg) {
+ @include responsive.breakpoint(lg) {
display: none;
}
}
.setting-section {
- margin-top: rem(24px);
+ margin-top: rem.rem(24px);
background: white;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.14);
@@ -67,27 +69,27 @@
}
.section-heading {
- @include font-size('regular');
+ @include font.font-size('regular');
font-weight: 600;
- padding-bottom: rem(4px);
- background: $light;
- padding: rem(16px) rem(20px);
+ padding-bottom: rem.rem(4px);
+ background: theme.$light;
+ padding: rem.rem(16px) rem.rem(20px);
}
.section-content {
- margin-top: rem(20px);
+ margin-top: rem.rem(20px);
}
.actions {
- margin-top: rem(18px);
+ margin-top: rem.rem(18px);
text-align: right;
}
}
.setting-row {
- padding: rem(16px) rem(20px);
+ padding: rem.rem(16px) rem.rem(20px);
&:not(:last-child) {
- border-bottom: 1px solid $border-color;
+ border-bottom: 1px solid theme.$border-color;
}
.setting-row-summary {
@@ -95,7 +97,7 @@
flex-direction: column;
// align-items: flex-start;
- @include breakpoint(md) {
+ @include responsive.breakpoint(md) {
flex-direction: row;
justify-content: space-between;
align-items: center;
@@ -103,24 +105,24 @@
}
.setting-row-main {
- padding-top: rem(24px);
+ padding-top: rem.rem(24px);
}
.setting-name {
font-weight: 400;
- @include font-size('regular');
+ @include font.font-size('regular');
margin-bottom: 0;
}
.setting-desc {
margin-bottom: 0;
- @include font-size('small');
- color: $gray;
+ @include font.font-size('small');
+ color: theme.$gray;
}
.setting-action {
display: flex;
flex-direction: column;
- @include breakpoint(md) {
+ @include responsive.breakpoint(md) {
flex-direction: row;
}
}
@@ -130,9 +132,9 @@
word-break: break-all;
justify-content: space-between;
align-items: center;
- margin-top: rem(4px);
+ margin-top: rem.rem(4px);
- @include breakpoint(md) {
+ @include responsive.breakpoint(md) {
flex-direction: row;
align-items: center;
margin-top: 0;
@@ -140,26 +142,26 @@
}
.setting-edit {
- color: $link;
+ color: theme.$link;
padding: 0;
&:hover {
- color: $link-hover;
+ color: theme.$link-hover;
}
- @include breakpoint(md) {
- margin-left: rem(16px);
+ @include responsive.breakpoint(md) {
+ margin-left: rem.rem(16px);
}
}
.input-row {
& ~ .input-row,
.input-row {
- margin-top: rem(12px);
+ margin-top: rem.rem(12px);
}
}
}
.email-verification-form {
- margin-left: rem(12px);
+ margin-left: rem.rem(12px);
}
}
diff --git a/pkg/server/assets/styles/src/_shared.scss b/pkg/server/assets/styles/src/_shared.scss
index 78d35e03..1b7697c1 100644
--- a/pkg/server/assets/styles/src/_shared.scss
+++ b/pkg/server/assets/styles/src/_shared.scss
@@ -16,8 +16,10 @@
* along with Dnote. If not, see .
*/
-@import './font';
-@import './responsive';
+@use 'font';
+@use 'responsive';
+@use "rem";
+@use "theme";
@keyframes holderPulse {
0% {
@@ -46,7 +48,7 @@ input[type='email']:disabled,
input[type='number']:disabled,
input[type='password']:disabled,
textarea:disabled {
- background-color: $lighter-gray;
+ background-color: theme.$lighter-gray;
cursor: not-allowed;
}
@@ -76,17 +78,17 @@ button {
}
.text-input {
- border: 1px solid $border-color;
- padding: rem(8px) rem(12px);
+ border: 1px solid theme.$border-color;
+ padding: rem.rem(8px) rem.rem(12px);
position: relative;
- border-radius: rem(4px);
+ border-radius: rem.rem(4px);
display: block;
&::placeholder {
- color: $gray;
+ color: theme.$gray;
}
&:focus {
- border-color: $light-blue;
+ border-color: theme.$light-blue;
box-shadow: inset 0 1px 2px rgba(24, 31, 35, 0.075),
0 0 0 0.2em rgba(4, 100, 210, 0.3);
outline: none;
@@ -94,11 +96,11 @@ button {
}
.text-input-small {
- padding: rem(4px) rem(12px);
+ padding: rem.rem(4px) rem.rem(12px);
}
.text-input-medium {
- padding: rem(8px) rem(12px);
+ padding: rem.rem(8px) rem.rem(12px);
}
.text-input-stretch {
@@ -110,10 +112,10 @@ button {
}
a {
- color: $link;
+ color: theme.$link;
&:hover {
- color: $link-hover;
+ color: theme.$link-hover;
}
}
@@ -129,12 +131,12 @@ h6 {
// grid
.container.mobile-fw {
- @include breakpoint(mddown) {
+ @include responsive.breakpoint(mddown) {
max-width: 100%;
}
}
.container.mobile-nopadding {
- @include breakpoint(mddown) {
+ @include responsive.breakpoint(mddown) {
padding-left: 0;
padding-right: 0;
@@ -154,30 +156,30 @@ html body {
}
.page {
- padding-top: rem(20px);
- padding-bottom: rem(20px);
+ padding-top: rem.rem(20px);
+ padding-bottom: rem.rem(20px);
&.page-mobile-full {
padding-top: 0;
padding-bottom: 0;
- @include breakpoint(lg) {
- padding-top: rem(32px);
- padding-bottom: rem(32px);
+ @include responsive.breakpoint(lg) {
+ padding-top: rem.rem(32px);
+ padding-bottom: rem.rem(32px);
}
}
}
.page-header {
- margin-top: rem(20px);
+ margin-top: rem.rem(20px);
&.page-header-full {
- margin-bottom: rem(20px);
+ margin-bottom: rem.rem(20px);
}
- @include breakpoint(lg) {
+ @include responsive.breakpoint(lg) {
// padding: 0;
- margin-bottom: rem(20px);
+ margin-bottom: rem.rem(20px);
margin-top: 0;
}
}
@@ -189,7 +191,7 @@ html body {
background-repeat: no-repeat;
background-position: right 8px center;
background-size: 8px 10px;
- border: 1px solid $border-color;
+ border: 1px solid theme.$border-color;
min-height: 34px;
padding: 6px 8px;
padding-right: 24px;
@@ -207,7 +209,7 @@ html body {
&:disabled,
&.form-select-disabled {
background-image: url('data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAABAAAAAUCAYAAACEYr13AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAEKSURBVHgBzVTNDYIwFC4NB46OwAi4gY7gETgoE6gTGCcwTgAJ4efGCLCBjMAIXrmA3yOhQazQhJj4JQ0v7fte3/e1hbFfIk3TYxzHp6kc7dtCFEUW5/xBcdM0a9d1S1kel00mSWKCnIkkxDSnXADIMYYEU9O0zPf91WwB6L6NyB3atrUMw7hNFkCbFyROmXYYmypMDMNwo+t6ztSwtW27oEAXrXBuwu2rCht+WPgU7C8gPCBzYOBKhQS5FTwIKBYeQFeJoWyiKNYH5Co6OCuQr/0JdBuPVyElQCd7GRMb3B3HebsHHzexrmvyQvZwqjFZWsDzvCc62BFhSGYD3UMsfs6ToKOd+6EsxgtrtWLW4gUN3AAAAABJRU5ErkJggg==');
- background-color: $lighter-gray;
+ background-color: theme.$lighter-gray;
}
}
@@ -215,12 +217,12 @@ html body {
// width: 100%;
width: auto;
font-weight: 600;
- margin-bottom: rem(4px);
- @include font-size('small');
+ margin-bottom: rem.rem(4px);
+ @include font.font-size('small');
}
.page-heading {
- @include font-size('x-large');
+ @include font.font-size('x-large');
}
.dropdown-caret {
@@ -231,7 +233,7 @@ html body {
border-right: 4px solid transparent;
border-bottom: 0 solid transparent;
border-left: 4px solid transparent;
- margin-left: rem(8px);
+ margin-left: rem.rem(8px);
}
.divider {
diff --git a/pkg/server/assets/styles/src/_theme.scss b/pkg/server/assets/styles/src/_theme.scss
index e17497a6..f37d81a5 100644
--- a/pkg/server/assets/styles/src/_theme.scss
+++ b/pkg/server/assets/styles/src/_theme.scss
@@ -1,3 +1,4 @@
+@use "sass:color";
/* Copyright (C) 2019, 2020, 2021, 2022, 2023, 2024, 2025 Dnote contributors
*
* This file is part of Dnote.
@@ -26,7 +27,7 @@ $lighter-gray: #f3f3f3;
$dark-gray: #637283;
// primary colors
-$first: #072a40;
+$first: #333745;
$second: #e7e7e7;
$third: #0a4b73;
@@ -35,7 +36,7 @@ $border-color: #d8d8d8;
$border-color-light: $lighter-gray;
$link: #6f53c0;
-$link-hover: darken($link, 5%);
+$link-hover: color.adjust($link, $lightness: -5%);
$danger-text: #cb2431;
$danger-background: #f8d7da;
diff --git a/pkg/server/assets/styles/src/main.scss b/pkg/server/assets/styles/src/main.scss
index df45971a..4a5a7379 100644
--- a/pkg/server/assets/styles/src/main.scss
+++ b/pkg/server/assets/styles/src/main.scss
@@ -16,25 +16,25 @@
* along with Dnote. If not, see .
*/
-@import './reboot';
-@import './grid';
-@import './bootstrap';
-@import './buttons';
-@import './responsive';
-@import './select';
-@import './shared';
-@import './marker';
-@import './rem';
-@import './markdown';
-@import './hljs';
+@use 'reboot';
+@use 'grid';
+@use 'bootstrap';
+@use 'buttons';
+@use 'responsive';
+@use 'select';
+@use 'shared';
+@use 'marker';
+@use 'rem';
+@use 'markdown';
+@use 'hljs';
-@import './login';
-@import './home';
-@import './note';
-@import './books';
-@import './settings';
-@import './header';
-@import './global';
+@use 'login';
+@use 'home';
+@use 'note';
+@use 'books';
+@use 'settings';
+@use 'header';
+@use 'global';
html {
font-size: 62.5%; /* 1.0 rem = 10px */
@@ -74,11 +74,11 @@ img {
}
.container.mobile-nopadding {
- @include breakpoint(mdonly) {
+ @include responsive.breakpoint(mdonly) {
max-width: 100%;
}
- @include breakpoint(mddown) {
+ @include responsive.breakpoint(mddown) {
padding-left: 0;
padding-right: 0;
@@ -137,7 +137,7 @@ img {
}
.input {
- border-radius: rem(4px);
+ border-radius: rem.rem(4px);
background-clip: padding-box;
border: 1px solid #ced4da;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
diff --git a/pkg/server/controllers/helpers.go b/pkg/server/controllers/helpers.go
index 0e88034b..72100c8b 100644
--- a/pkg/server/controllers/helpers.go
+++ b/pkg/server/controllers/helpers.go
@@ -232,8 +232,6 @@ func getStatusCode(err error) int {
return http.StatusUnauthorized
case app.ErrEmailTooLong:
return http.StatusBadRequest
- case app.ErrEmailAlreadyVerified:
- return http.StatusConflict
case app.ErrMissingToken:
return http.StatusBadRequest
case app.ErrExpiredToken:
diff --git a/pkg/server/controllers/routes.go b/pkg/server/controllers/routes.go
index 0c873fa3..db8b706d 100644
--- a/pkg/server/controllers/routes.go
+++ b/pkg/server/controllers/routes.go
@@ -58,8 +58,6 @@ func NewWebRoutes(a *app.App, c *Controllers) []Route {
{"PATCH", "/password-reset", c.Users.PasswordReset, true},
{"GET", "/password-reset/{token}", c.Users.PasswordResetConfirm, true},
{"POST", "/reset-token", c.Users.CreateResetToken, true},
- {"POST", "/verification-token", mw.Auth(a.DB, c.Users.CreateEmailVerificationToken, redirectGuest), true},
- {"GET", "/verify-email/{token}", mw.Auth(a.DB, c.Users.VerifyEmail, redirectGuest), true},
{"PATCH", "/account/profile", mw.Auth(a.DB, c.Users.ProfileUpdate, nil), true},
{"PATCH", "/account/password", mw.Auth(a.DB, c.Users.PasswordUpdate, nil), true},
diff --git a/pkg/server/controllers/users.go b/pkg/server/controllers/users.go
index 4643f599..67baca6a 100644
--- a/pkg/server/controllers/users.go
+++ b/pkg/server/controllers/users.go
@@ -30,7 +30,6 @@ import (
"github.com/dnote/dnote/pkg/server/database"
"github.com/dnote/dnote/pkg/server/helpers"
"github.com/dnote/dnote/pkg/server/log"
- "github.com/dnote/dnote/pkg/server/mailer"
"github.com/dnote/dnote/pkg/server/token"
"github.com/dnote/dnote/pkg/server/views"
"github.com/gorilla/mux"
@@ -80,10 +79,6 @@ func NewUsers(app *app.App, viewEngine *views.Engine) *Users {
views.Config{Title: "About", Layout: "base", HelperFuncs: commonHelpers, HeaderTemplate: "navbar"},
"users/settings_about",
),
- EmailVerificationView: viewEngine.NewView(app,
- views.Config{Layout: "base", HelperFuncs: commonHelpers, HeaderTemplate: "navbar"},
- "users/email_verification",
- ),
app: app,
}
}
@@ -96,7 +91,6 @@ type Users struct {
AboutView *views.View
PasswordResetView *views.View
PasswordResetConfirmView *views.View
- EmailVerificationView *views.View
app *app.App
}
@@ -599,10 +593,6 @@ func (u *Users) ProfileUpdate(w http.ResponseWriter, r *http.Request) {
return
}
- // check if email was changed
- if form.Email != account.Email.String {
- account.EmailVerified = false
- }
account.Email.String = form.Email
if err := tx.Save(&account).Error; err != nil {
@@ -620,119 +610,3 @@ func (u *Users) ProfileUpdate(w http.ResponseWriter, r *http.Request) {
views.RedirectAlert(w, r, "/", http.StatusFound, alert)
}
-func (u *Users) VerifyEmail(w http.ResponseWriter, r *http.Request) {
- vd := views.Data{}
-
- vars := mux.Vars(r)
- tokenValue := vars["token"]
-
- if tokenValue == "" {
- handleHTMLError(w, r, app.ErrMissingToken, "Missing email verification token", u.EmailVerificationView, vd)
- return
- }
-
- var token database.Token
- if err := u.app.DB.
- Where("value = ? AND type = ?", tokenValue, database.TokenTypeEmailVerification).
- First(&token).Error; err != nil {
- handleHTMLError(w, r, app.ErrInvalidToken, "Finding token", u.EmailVerificationView, vd)
- return
- }
-
- if token.UsedAt != nil {
- handleHTMLError(w, r, app.ErrInvalidToken, "Token has already been used.", u.EmailVerificationView, vd)
- return
- }
-
- // Expire after ttl
- if time.Since(token.CreatedAt).Minutes() > 30 {
- handleHTMLError(w, r, app.ErrExpiredToken, "Token has expired.", u.EmailVerificationView, vd)
- return
- }
-
- var account database.Account
- if err := u.app.DB.Where("user_id = ?", token.UserID).First(&account).Error; err != nil {
- handleHTMLError(w, r, err, "finding account", u.EmailVerificationView, vd)
- return
- }
- if account.EmailVerified {
- handleHTMLError(w, r, app.ErrEmailAlreadyVerified, "Already verified", u.EmailVerificationView, vd)
- return
- }
-
- tx := u.app.DB.Begin()
- account.EmailVerified = true
- if err := tx.Save(&account).Error; err != nil {
- tx.Rollback()
- handleHTMLError(w, r, err, "updating email_verified", u.EmailVerificationView, vd)
- return
- }
- if err := tx.Model(&token).Update("used_at", time.Now()).Error; err != nil {
- tx.Rollback()
- handleHTMLError(w, r, err, "updating reset token", u.EmailVerificationView, vd)
- return
- }
- tx.Commit()
-
- var user database.User
- if err := u.app.DB.Where("id = ?", token.UserID).First(&user).Error; err != nil {
- handleHTMLError(w, r, err, "finding user", u.EmailVerificationView, vd)
- return
- }
-
- session, err := u.app.SignIn(&user)
- if err != nil {
- handleHTMLError(w, r, err, "Creating session", u.EmailVerificationView, vd)
- }
-
- setSessionCookie(w, session.Key, session.ExpiresAt)
- http.Redirect(w, r, "/", http.StatusFound)
-}
-
-func (u *Users) CreateEmailVerificationToken(w http.ResponseWriter, r *http.Request) {
- vd := views.Data{}
-
- user := context.User(r.Context())
- if user == nil {
- handleHTMLError(w, r, app.ErrLoginRequired, "No authenticated user found", u.SettingView, vd)
- return
- }
-
- var account database.Account
- err := u.app.DB.Where("user_id = ?", user.ID).First(&account).Error
- if err != nil {
- handleHTMLError(w, r, err, "finding account", u.SettingView, vd)
- return
- }
-
- if account.EmailVerified {
- handleHTMLError(w, r, app.ErrEmailAlreadyVerified, "email is already verified.", u.SettingView, vd)
- return
- }
- if account.Email.String == "" {
- handleHTMLError(w, r, app.ErrEmailRequired, "email is empty.", u.SettingView, vd)
- return
- }
-
- tok, err := token.Create(u.app.DB, account.UserID, database.TokenTypeEmailVerification)
- if err != nil {
- handleHTMLError(w, r, err, "saving token", u.SettingView, vd)
- return
- }
-
- if err := u.app.SendVerificationEmail(account.Email.String, tok.Value); err != nil {
- if pkgErrors.Cause(err) == mailer.ErrSMTPNotConfigured {
- handleHTMLError(w, r, app.ErrInvalidSMTPConfig, "SMTP config is not configured correctly.", u.SettingView, vd)
- } else {
- handleHTMLError(w, r, err, "sending verification email", u.SettingView, vd)
- }
-
- return
- }
-
- alert := views.Alert{
- Level: views.AlertLvlSuccess,
- Message: "Please check your email for the verification",
- }
- views.RedirectAlert(w, r, "/", http.StatusFound, alert)
-}
diff --git a/pkg/server/controllers/users_test.go b/pkg/server/controllers/users_test.go
index e4c9ff2d..643cb016 100644
--- a/pkg/server/controllers/users_test.go
+++ b/pkg/server/controllers/users_test.go
@@ -578,12 +578,6 @@ func TestResetPassword(t *testing.T) {
Type: database.TokenTypeResetPassword,
}
testutils.MustExec(t, db.Save(&tok), "preparing token")
- otherTok := database.Token{
- UserID: u.ID,
- Value: "somerandomvalue",
- Type: database.TokenTypeEmailVerification,
- }
- testutils.MustExec(t, db.Save(&otherTok), "preparing another token")
s1 := database.Session{
Key: "some-session-key-1",
@@ -618,16 +612,14 @@ func TestResetPassword(t *testing.T) {
// Test
assert.StatusCodeEquals(t, res, http.StatusFound, "Status code mismatch")
- var resetToken, verificationToken database.Token
+ var resetToken database.Token
var account database.Account
testutils.MustExec(t, db.Where("value = ?", "MivFxYiSMMA4An9dP24DNQ==").First(&resetToken), "finding reset token")
- testutils.MustExec(t, db.Where("value = ?", "somerandomvalue").First(&verificationToken), "finding reset token")
testutils.MustExec(t, db.Where("id = ?", acc.ID).First(&account), "finding account")
assert.NotEqual(t, resetToken.UsedAt, nil, "reset_token UsedAt mismatch")
passwordErr := bcrypt.CompareHashAndPassword([]byte(account.Password.String), []byte("newpassword"))
assert.Equal(t, passwordErr, nil, "Password mismatch")
- assert.Equal(t, verificationToken.UsedAt, (*time.Time)(nil), "verificationToken UsedAt mismatch")
var s1Count, s2Count int64
testutils.MustExec(t, db.Model(&database.Session{}).Where("id = ?", s1.ID).Count(&s1Count), "counting s1")
@@ -777,46 +769,6 @@ func TestResetPassword(t *testing.T) {
}
})
- t.Run("using wrong type token: email_verification", func(t *testing.T) {
- db := testutils.InitMemoryDB(t)
-
- // Setup
- a := app.NewTest()
- a.Clock = clock.NewMock()
- a.DB = db
- server := MustNewServer(t, &a)
- defer server.Close()
-
- u := testutils.SetupUserData(db)
- acc := testutils.SetupAccountData(db, u, "alice@example.com", "somepassword")
- tok := database.Token{
- UserID: u.ID,
- Value: "MivFxYiSMMA4An9dP24DNQ==",
- Type: database.TokenTypeEmailVerification,
- }
- testutils.MustExec(t, db.Save(&tok), "Failed to prepare reset_token")
- testutils.MustExec(t, db.Model(&tok).Update("created_at", time.Now().Add(time.Minute*-11)), "Failed to prepare reset_token created_at")
-
- dat := url.Values{}
- dat.Set("token", "MivFxYiSMMA4An9dP24DNQ==")
- dat.Set("password", "oldpassword")
- dat.Set("password_confirmation", "oldpassword")
- req := testutils.MakeFormReq(server.URL, "PATCH", "/password-reset", dat)
-
- // Execute
- res := testutils.HTTPDo(t, req)
-
- // Test
- assert.StatusCodeEquals(t, res, http.StatusBadRequest, "Status code mismatch")
-
- var resetToken database.Token
- var account database.Account
- testutils.MustExec(t, db.Where("value = ?", "MivFxYiSMMA4An9dP24DNQ==").First(&resetToken), "failed to find reset_token")
- testutils.MustExec(t, db.Where("id = ?", acc.ID).First(&account), "failed to find account")
-
- assert.Equal(t, acc.Password, account.Password, "password should not have been updated")
- assert.Equal(t, resetToken.UsedAt, (*time.Time)(nil), "used_at should be nil")
- })
}
func TestCreateResetToken(t *testing.T) {
@@ -1018,9 +970,7 @@ func TestUpdateEmail(t *testing.T) {
defer server.Close()
u := testutils.SetupUserData(db)
- acc := testutils.SetupAccountData(db, u, "alice@example.com", "pass1234")
- acc.EmailVerified = true
- testutils.MustExec(t, db.Save(&acc), "updating email_verified")
+ testutils.SetupAccountData(db, u, "alice@example.com", "pass1234")
// Execute
dat := url.Values{}
@@ -1039,7 +989,6 @@ func TestUpdateEmail(t *testing.T) {
testutils.MustExec(t, db.Where("user_id = ?", u.ID).First(&account), "finding account")
assert.Equal(t, account.Email.String, "alice-new@example.com", "email mismatch")
- assert.Equal(t, account.EmailVerified, false, "EmailVerified mismatch")
})
t.Run("password mismatch", func(t *testing.T) {
@@ -1053,9 +1002,7 @@ func TestUpdateEmail(t *testing.T) {
defer server.Close()
u := testutils.SetupUserData(db)
- acc := testutils.SetupAccountData(db, u, "alice@example.com", "pass1234")
- acc.EmailVerified = true
- testutils.MustExec(t, db.Save(&acc), "updating email_verified")
+ testutils.SetupAccountData(db, u, "alice@example.com", "pass1234")
// Execute
dat := url.Values{}
@@ -1074,238 +1021,6 @@ func TestUpdateEmail(t *testing.T) {
testutils.MustExec(t, db.Where("user_id = ?", u.ID).First(&account), "finding account")
assert.Equal(t, account.Email.String, "alice@example.com", "email mismatch")
- assert.Equal(t, account.EmailVerified, true, "EmailVerified mismatch")
})
}
-func TestVerifyEmail(t *testing.T) {
- t.Run("success", func(t *testing.T) {
- db := testutils.InitMemoryDB(t)
-
- // Setup
- a := app.NewTest()
- a.Clock = clock.NewMock()
- a.DB = db
- server := MustNewServer(t, &a)
- defer server.Close()
-
- user := testutils.SetupUserData(db)
- testutils.SetupAccountData(db, user, "alice@example.com", "pass1234")
- tok := database.Token{
- UserID: user.ID,
- Type: database.TokenTypeEmailVerification,
- Value: "someTokenValue",
- }
- testutils.MustExec(t, db.Save(&tok), "preparing token")
-
- // Execute
- req := testutils.MakeReq(server.URL, "GET", fmt.Sprintf("/verify-email/%s", "someTokenValue"), "")
- res := testutils.HTTPAuthDo(t, db, req, user)
-
- // Test
- assert.StatusCodeEquals(t, res, http.StatusFound, "Status code mismatch")
-
- var account database.Account
- var token database.Token
- var tokenCount int64
- testutils.MustExec(t, db.Where("user_id = ?", user.ID).First(&account), "finding account")
- testutils.MustExec(t, db.Where("user_id = ? AND type = ?", user.ID, database.TokenTypeEmailVerification).First(&token), "finding token")
- testutils.MustExec(t, db.Model(&database.Token{}).Count(&tokenCount), "counting token")
-
- assert.Equal(t, account.EmailVerified, true, "email_verified mismatch")
- assert.NotEqual(t, token.Value, "", "token value should not have been updated")
- assert.Equal(t, tokenCount, int64(1), "token count mismatch")
- assert.NotEqual(t, token.UsedAt, (*time.Time)(nil), "token should have been used")
- })
-
- t.Run("used token", func(t *testing.T) {
- db := testutils.InitMemoryDB(t)
-
- // Setup
- a := app.NewTest()
- a.Clock = clock.NewMock()
- a.DB = db
- server := MustNewServer(t, &a)
- defer server.Close()
-
- user := testutils.SetupUserData(db)
- testutils.SetupAccountData(db, user, "alice@example.com", "pass1234")
-
- usedAt := time.Now().Add(time.Hour * -11).UTC()
- tok := database.Token{
- UserID: user.ID,
- Type: database.TokenTypeEmailVerification,
- Value: "someTokenValue",
- UsedAt: &usedAt,
- }
- testutils.MustExec(t, db.Save(&tok), "preparing token")
-
- // Execute
- req := testutils.MakeReq(server.URL, "GET", fmt.Sprintf("/verify-email/%s", "someTokenValue"), "")
- res := testutils.HTTPAuthDo(t, db, req, user)
-
- // Test
- assert.StatusCodeEquals(t, res, http.StatusBadRequest, "")
-
- var account database.Account
- var token database.Token
- var tokenCount int64
- testutils.MustExec(t, db.Where("user_id = ?", user.ID).First(&account), "finding account")
- testutils.MustExec(t, db.Where("user_id = ? AND type = ?", user.ID, database.TokenTypeEmailVerification).First(&token), "finding token")
- testutils.MustExec(t, db.Model(&database.Token{}).Count(&tokenCount), "counting token")
-
- assert.Equal(t, account.EmailVerified, false, "email_verified mismatch")
- assert.NotEqual(t, token.UsedAt, nil, "token used_at mismatch")
- assert.Equal(t, tokenCount, int64(1), "token count mismatch")
- assert.NotEqual(t, token.UsedAt, (*time.Time)(nil), "token should have been used")
- })
-
- t.Run("expired token", func(t *testing.T) {
- db := testutils.InitMemoryDB(t)
-
- // Setup
- a := app.NewTest()
- a.Clock = clock.NewMock()
- a.DB = db
- server := MustNewServer(t, &a)
- defer server.Close()
-
- user := testutils.SetupUserData(db)
- testutils.SetupAccountData(db, user, "alice@example.com", "pass1234")
-
- tok := database.Token{
- UserID: user.ID,
- Type: database.TokenTypeEmailVerification,
- Value: "someTokenValue",
- }
- testutils.MustExec(t, db.Save(&tok), "preparing token")
- testutils.MustExec(t, db.Model(&tok).Update("created_at", time.Now().Add(time.Minute*-31)), "Failed to prepare token created_at")
-
- // Execute
- req := testutils.MakeReq(server.URL, "GET", fmt.Sprintf("/verify-email/%s", "someTokenValue"), "")
- res := testutils.HTTPAuthDo(t, db, req, user)
-
- // Test
- assert.StatusCodeEquals(t, res, http.StatusGone, "")
-
- var account database.Account
- var token database.Token
- var tokenCount int64
- testutils.MustExec(t, db.Where("user_id = ?", user.ID).First(&account), "finding account")
- testutils.MustExec(t, db.Where("user_id = ? AND type = ?", user.ID, database.TokenTypeEmailVerification).First(&token), "finding token")
- testutils.MustExec(t, db.Model(&database.Token{}).Count(&tokenCount), "counting token")
-
- assert.Equal(t, account.EmailVerified, false, "email_verified mismatch")
- assert.Equal(t, tokenCount, int64(1), "token count mismatch")
- assert.Equal(t, token.UsedAt, (*time.Time)(nil), "token should have not been used")
- })
-
- t.Run("already verified", func(t *testing.T) {
- db := testutils.InitMemoryDB(t)
-
- // Setup
- a := app.NewTest()
- a.Clock = clock.NewMock()
- a.DB = db
- server := MustNewServer(t, &a)
- defer server.Close()
-
- user := testutils.SetupUserData(db)
- acc := testutils.SetupAccountData(db, user, "alice@example.com", "oldpass1234")
- acc.EmailVerified = true
- testutils.MustExec(t, db.Save(&acc), "preparing account")
-
- tok := database.Token{
- UserID: user.ID,
- Type: database.TokenTypeEmailVerification,
- Value: "someTokenValue",
- }
- testutils.MustExec(t, db.Save(&tok), "preparing token")
-
- // Execute
- req := testutils.MakeReq(server.URL, "GET", fmt.Sprintf("/verify-email/%s", "someTokenValue"), "")
- res := testutils.HTTPAuthDo(t, db, req, user)
-
- // Test
- assert.StatusCodeEquals(t, res, http.StatusConflict, "")
-
- var account database.Account
- var token database.Token
- var tokenCount int64
- testutils.MustExec(t, db.Where("user_id = ?", user.ID).First(&account), "finding account")
- testutils.MustExec(t, db.Where("user_id = ? AND type = ?", user.ID, database.TokenTypeEmailVerification).First(&token), "finding token")
- testutils.MustExec(t, db.Model(&database.Token{}).Count(&tokenCount), "counting token")
-
- assert.Equal(t, account.EmailVerified, true, "email_verified mismatch")
- assert.Equal(t, tokenCount, int64(1), "token count mismatch")
- assert.Equal(t, token.UsedAt, (*time.Time)(nil), "token should have not been used")
- })
-}
-
-func TestCreateVerificationToken(t *testing.T) {
- t.Run("success", func(t *testing.T) {
- db := testutils.InitMemoryDB(t)
-
- // Setup
- emailBackend := testutils.MockEmailbackendImplementation{}
- a := app.NewTest()
- a.Clock = clock.NewMock()
- a.DB = db
- a.EmailBackend = &emailBackend
- server := MustNewServer(t, &a)
- defer server.Close()
-
- user := testutils.SetupUserData(db)
- testutils.SetupAccountData(db, user, "alice@example.com", "pass1234")
-
- // Execute
- req := testutils.MakeReq(server.URL, "POST", "/verification-token", "")
- res := testutils.HTTPAuthDo(t, db, req, user)
-
- // Test
- assert.StatusCodeEquals(t, res, http.StatusFound, "status code mismatch")
-
- var account database.Account
- var token database.Token
- var tokenCount int64
- testutils.MustExec(t, db.Where("user_id = ?", user.ID).First(&account), "finding account")
- testutils.MustExec(t, db.Where("user_id = ? AND type = ?", user.ID, database.TokenTypeEmailVerification).First(&token), "finding token")
- testutils.MustExec(t, db.Model(&database.Token{}).Count(&tokenCount), "counting token")
-
- assert.Equal(t, account.EmailVerified, false, "email_verified should not have been updated")
- assert.NotEqual(t, token.Value, "", "token Value mismatch")
- assert.Equal(t, tokenCount, int64(1), "token count mismatch")
- assert.Equal(t, token.UsedAt, (*time.Time)(nil), "token UsedAt mismatch")
- assert.Equal(t, len(emailBackend.Emails), 1, "email queue count mismatch")
- })
-
- t.Run("already verified", func(t *testing.T) {
- db := testutils.InitMemoryDB(t)
- // Setup
- a := app.NewTest()
- a.Clock = clock.NewMock()
- a.DB = db
- server := MustNewServer(t, &a)
- defer server.Close()
-
- user := testutils.SetupUserData(db)
- acc := testutils.SetupAccountData(db, user, "alice@example.com", "pass1234")
- acc.EmailVerified = true
- testutils.MustExec(t, db.Save(&acc), "preparing account")
-
- // Execute
- req := testutils.MakeReq(server.URL, "POST", "/verification-token", "")
- res := testutils.HTTPAuthDo(t, db, req, user)
-
- // Test
- assert.StatusCodeEquals(t, res, http.StatusConflict, "Status code mismatch")
-
- var account database.Account
- var tokenCount int64
- testutils.MustExec(t, db.Where("user_id = ?", user.ID).First(&account), "finding account")
- testutils.MustExec(t, db.Model(&database.Token{}).Count(&tokenCount), "counting token")
-
- assert.Equal(t, account.EmailVerified, true, "email_verified should not have been updated")
- assert.Equal(t, tokenCount, int64(0), "token count mismatch")
- })
-}
diff --git a/pkg/server/database/consts.go b/pkg/server/database/consts.go
index b4a1db03..6a9abc0c 100644
--- a/pkg/server/database/consts.go
+++ b/pkg/server/database/consts.go
@@ -21,8 +21,6 @@ package database
const (
// TokenTypeResetPassword is a type of a token for reseting password
TokenTypeResetPassword = "reset_password"
- // TokenTypeEmailVerification is a type of a token for verifying email
- TokenTypeEmailVerification = "email_verification"
)
const (
diff --git a/pkg/server/database/models.go b/pkg/server/database/models.go
index ac3e4e56..98571e10 100644
--- a/pkg/server/database/models.go
+++ b/pkg/server/database/models.go
@@ -73,10 +73,9 @@ type User struct {
// Account is a model for an account
type Account struct {
Model
- UserID int `gorm:"index"`
- Email NullString
- EmailVerified bool `gorm:"default:false"`
- Password NullString
+ UserID int `gorm:"index"`
+ Email NullString
+ Password NullString
}
// Token is a model for a token
diff --git a/pkg/server/mailer/mailer.go b/pkg/server/mailer/mailer.go
index d02d8911..1cf786a7 100644
--- a/pkg/server/mailer/mailer.go
+++ b/pkg/server/mailer/mailer.go
@@ -34,8 +34,6 @@ var (
EmailTypeResetPassword = "reset_password"
// EmailTypeResetPasswordAlert represents a password change notification email
EmailTypeResetPasswordAlert = "reset_password_alert"
- // EmailTypeEmailVerification represents an email verification email
- EmailTypeEmailVerification = "verify_email"
// EmailTypeWelcome represents an welcome email
EmailTypeWelcome = "welcome"
)
@@ -79,10 +77,6 @@ func NewTemplates() Templates {
if err != nil {
panic(errors.Wrap(err, "initializing welcome template"))
}
- verifyEmailText, err := initTextTmpl(EmailTypeEmailVerification)
- if err != nil {
- panic(errors.Wrap(err, "initializing email verification template"))
- }
passwordResetText, err := initTextTmpl(EmailTypeResetPassword)
if err != nil {
panic(errors.Wrap(err, "initializing password reset template"))
@@ -95,7 +89,6 @@ func NewTemplates() Templates {
T := Templates{}
T.set(EmailTypeResetPassword, EmailKindText, passwordResetText)
T.set(EmailTypeResetPasswordAlert, EmailKindText, passwordResetAlertText)
- T.set(EmailTypeEmailVerification, EmailKindText, verifyEmailText)
T.set(EmailTypeWelcome, EmailKindText, welcomeText)
return T
diff --git a/pkg/server/mailer/mailer_test.go b/pkg/server/mailer/mailer_test.go
index df95b1f9..5837192a 100644
--- a/pkg/server/mailer/mailer_test.go
+++ b/pkg/server/mailer/mailer_test.go
@@ -32,7 +32,6 @@ func TestAllTemplatesInitialized(t *testing.T) {
emailTypes := []string{
EmailTypeResetPassword,
EmailTypeResetPasswordAlert,
- EmailTypeEmailVerification,
EmailTypeWelcome,
}
@@ -46,44 +45,6 @@ func TestAllTemplatesInitialized(t *testing.T) {
}
}
-func TestEmailVerificationEmail(t *testing.T) {
- testCases := []struct {
- token string
- webURL string
- }{
- {
- token: "someRandomToken1",
- webURL: "http://localhost:3000",
- },
- {
- token: "someRandomToken2",
- webURL: "http://localhost:3001",
- },
- }
-
- tmpl := NewTemplates()
-
- for _, tc := range testCases {
- t.Run(fmt.Sprintf("with WebURL %s", tc.webURL), func(t *testing.T) {
- dat := EmailVerificationTmplData{
- Token: tc.token,
- WebURL: tc.webURL,
- }
- body, err := tmpl.Execute(EmailTypeEmailVerification, EmailKindText, dat)
- if err != nil {
- t.Fatal(errors.Wrap(err, "executing"))
- }
-
- if ok := strings.Contains(body, tc.webURL); !ok {
- t.Errorf("email body did not contain %s", tc.webURL)
- }
- if ok := strings.Contains(body, tc.token); !ok {
- t.Errorf("email body did not contain %s", tc.token)
- }
- })
- }
-}
-
func TestResetPasswordEmail(t *testing.T) {
testCases := []struct {
token string
diff --git a/pkg/server/mailer/templates/verify_email.txt b/pkg/server/mailer/templates/verify_email.txt
deleted file mode 100644
index c21af88d..00000000
--- a/pkg/server/mailer/templates/verify_email.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Hi,
-
-Welcome to Dnote! To verify your email, visit the following link:
-
- {{ .WebURL }}/verify-email/{{ .Token }}
diff --git a/pkg/server/mailer/types.go b/pkg/server/mailer/types.go
index 3a371911..6ad862de 100644
--- a/pkg/server/mailer/types.go
+++ b/pkg/server/mailer/types.go
@@ -18,12 +18,6 @@
package mailer
-// EmailVerificationTmplData is a template data for email verification emails
-type EmailVerificationTmplData struct {
- Token string
- WebURL string
-}
-
// EmailResetPasswordTmplData is a template data for reset password emails
type EmailResetPasswordTmplData struct {
AccountEmail string
diff --git a/pkg/server/middleware/auth_test.go b/pkg/server/middleware/auth_test.go
index 8451ae5d..1485befa 100644
--- a/pkg/server/middleware/auth_test.go
+++ b/pkg/server/middleware/auth_test.go
@@ -178,7 +178,7 @@ func TestTokenAuth(t *testing.T) {
user := testutils.SetupUserData(db)
tok := database.Token{
UserID: user.ID,
- Type: database.TokenTypeEmailVerification,
+ Type: database.TokenTypeResetPassword,
Value: "xpwFnc0MdllFUePDq9DLeQ==",
}
testutils.MustExec(t, db.Save(&tok), "preparing token")
@@ -193,7 +193,7 @@ func TestTokenAuth(t *testing.T) {
w.WriteHeader(http.StatusOK)
}
- server := httptest.NewServer(TokenAuth(db, handler, database.TokenTypeEmailVerification, nil))
+ server := httptest.NewServer(TokenAuth(db, handler, database.TokenTypeResetPassword, nil))
defer server.Close()
t.Run("with token", func(t *testing.T) {
diff --git a/pkg/server/session/session.go b/pkg/server/session/session.go
index 8c55549c..a9494c58 100644
--- a/pkg/server/session/session.go
+++ b/pkg/server/session/session.go
@@ -24,16 +24,14 @@ import (
// Session represents user session
type Session struct {
- UUID string `json:"uuid"`
- Email string `json:"email"`
- EmailVerified bool `json:"email_verified"`
+ UUID string `json:"uuid"`
+ Email string `json:"email"`
}
// New returns a new session for the given user
func New(user database.User, account database.Account) Session {
return Session{
- UUID: user.UUID,
- Email: account.Email.String,
- EmailVerified: account.EmailVerified,
+ UUID: user.UUID,
+ Email: account.Email.String,
}
}
diff --git a/pkg/server/session/session_test.go b/pkg/server/session/session_test.go
index 107dacfe..dec674b7 100644
--- a/pkg/server/session/session_test.go
+++ b/pkg/server/session/session_test.go
@@ -28,10 +28,10 @@ import (
func TestNew(t *testing.T) {
u1 := database.User{UUID: "0f5f0054-d23f-4be1-b5fb-57673109e9cb"}
- a1 := database.Account{Email: database.ToNullString("alice@example.com"), EmailVerified: false}
+ a1 := database.Account{Email: database.ToNullString("alice@example.com")}
u2 := database.User{UUID: "718a1041-bbe6-496e-bbe4-ea7e572c295e"}
- a2 := database.Account{Email: database.ToNullString("bob@example.com"), EmailVerified: false}
+ a2 := database.Account{Email: database.ToNullString("bob@example.com")}
testCases := []struct {
user database.User
@@ -52,9 +52,8 @@ func TestNew(t *testing.T) {
// Execute
got := New(tc.user, tc.account)
expected := Session{
- UUID: tc.user.UUID,
- Email: tc.account.Email.String,
- EmailVerified: tc.account.EmailVerified,
+ UUID: tc.user.UUID,
+ Email: tc.account.Email.String,
}
assert.DeepEqual(t, got, expected, "result mismatch")
diff --git a/pkg/server/token/token_test.go b/pkg/server/token/token_test.go
index 922cc93d..426ff3d1 100644
--- a/pkg/server/token/token_test.go
+++ b/pkg/server/token/token_test.go
@@ -33,7 +33,7 @@ func TestCreate(t *testing.T) {
kind string
}{
{
- kind: database.TokenTypeEmailVerification,
+ kind: database.TokenTypeResetPassword,
},
}
diff --git a/pkg/server/views/templates/users/email_verification.gohtml b/pkg/server/views/templates/users/email_verification.gohtml
deleted file mode 100644
index 969688a4..00000000
--- a/pkg/server/views/templates/users/email_verification.gohtml
+++ /dev/null
@@ -1,2 +0,0 @@
-{{define "yield"}}
-{{end}}
diff --git a/pkg/server/views/templates/users/settings.gohtml b/pkg/server/views/templates/users/settings.gohtml
index a9639468..9f747b3e 100644
--- a/pkg/server/views/templates/users/settings.gohtml
+++ b/pkg/server/views/templates/users/settings.gohtml
@@ -144,34 +144,6 @@
-
-
-
-
Email Verified
-
-
-
- {{ if eq true false }} b{{end}}
-
- {{if .EmailVerified}}
- Yes
- {{else}}
- No
-
-
- {{end}}
-
-
-
-
diff --git a/pkg/server/views/view.go b/pkg/server/views/view.go
index 2b0e57a9..3484dca8 100644
--- a/pkg/server/views/view.go
+++ b/pkg/server/views/view.go
@@ -116,8 +116,6 @@ func (v *View) Render(w http.ResponseWriter, r *http.Request, data *Data, status
}
if vd.Account != nil {
vd.Yield["Email"] = vd.Account.Email.String
- vd.Yield["EmailVerified"] = vd.Account.EmailVerified
- vd.Yield["EmailVerified"] = vd.Account.EmailVerified
}
vd.Yield["CurrentPath"] = r.URL.Path
vd.Yield["Standalone"] = buildinfo.Standalone