diff --git a/.gitignore b/.gitignore
index f1abba6b..57d82ddc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ node_modules
/test
tmp
*.db
+server
diff --git a/SELF_HOSTING.md b/SELF_HOSTING.md
index 1c080bed..5d3db690 100644
--- a/SELF_HOSTING.md
+++ b/SELF_HOSTING.md
@@ -4,45 +4,30 @@ This guide documents the steps for installing the Dnote server on your own machi
## Overview
-Dnote server comes as a single binary file that you can simply download and run. It uses Postgres as the database.
+Dnote server comes as a single binary file that you can simply download and run. It uses SQLite as the database.
## Installation
-1. Install Postgres 11+.
-2. Create a `dnote` database by running `createdb dnote`
-3. Download the official Dnote server release from the [release page](https://github.com/dnote/dnote/releases).
-4. Extract the archive and move the `dnote-server` executable to `/usr/local/bin`.
+1. Download the official Dnote server release from the [release page](https://github.com/dnote/dnote/releases).
+2. Extract the archive and move the `dnote-server` executable to `/usr/local/bin`.
```bash
tar -xzf dnote-server-$version-$os.tar.gz
mv ./dnote-server /usr/local/bin
```
-4. Run Dnote
+3. Run Dnote
```bash
-GO_ENV=PRODUCTION \
-DBHost=localhost \
-DBPort=5432 \
-DBName=dnote \
-DBUser=$user \
-DBPassword=$password \
+APP_ENV=PRODUCTION \
WebURL=$webURL \
-SmtpHost=$SmtpHost \
-SmtpPort=$SmtpPort \
-SmtpUsername=$SmtpUsername \
-SmtpPassword=$SmtpPassword \
DisableRegistration=false \
dnote-server start
```
-Replace `$user`, `$password` with the credentials of the Postgres user that owns the `dnote` database.
-
Replace `$webURL` with the full URL to your server, without a trailing slash (e.g. `https://your.server`).
-Replace `$SmtpHost`, `SmtpPort`, `$SmtpUsername`, `$SmtpPassword` with actual values, if you would like to receive spaced repetition through email.
-
-Replace `DisableRegistration` to `true` if you would like to disable user registrations.
+Set `DisableRegistration=true` if you would like to disable user registrations.
By default, dnote server will run on the port 3000.
@@ -127,25 +112,32 @@ Restart=always
RestartSec=3
WorkingDirectory=/home/$user
ExecStart=/usr/local/bin/dnote-server start
-Environment=GO_ENV=PRODUCTION
+Environment=APP_ENV=PRODUCTION
Environment=WebURL=$WebURL
-Environment=SmtpHost=
-Environment=SmtpPort=
-Environment=SmtpUsername=
-Environment=SmtpPassword=
[Install]
WantedBy=multi-user.target
```
-Replace `$user`, `$WebURL`, `$DBUser`, and `$DBPassword` with the actual values.
+Replace `$user` and `$WebURL` with the actual values.
-Optionally, if you would like to send spaced repetitions throught email, populate `SmtpHost`, `SmtpPort`, `SmtpUsername`, and `SmtpPassword`.
+By default, the database will be stored at `$XDG_DATA_HOME/dnote/server.db` (typically `~/.local/share/dnote/server.db`). To use a custom location, add `Environment=DBPath=/path/to/database.db` to the service file.
2. Reload the change by running `sudo systemctl daemon-reload`.
3. Enable the Daemon by running `sudo systemctl enable dnote`.`
4. Start the Daemon by running `sudo systemctl start dnote`
+### Optional: Email Support
+
+To enable sending emails, add the following environment variables to your configuration. But they are not required.
+
+- `SmtpHost` - SMTP server hostname
+- `SmtpPort` - SMTP server port
+- `SmtpUsername` - SMTP username
+- `SmtpPassword` - SMTP password
+
+For systemd, add these as additional `Environment=` lines in `/etc/systemd/system/dnote.service`.
+
### Configure clients
Let's configure Dnote clients to connect to the self-hosted web API endpoint.
@@ -169,7 +161,3 @@ e.g.
editor: nvim
apiEndpoint: my-dnote-server.com/api
```
-
-#### Browser extension
-
-Navigate into the 'Settings' tab and set the values for 'API URL', and 'Web URL'.
diff --git a/host/docker/compose.yml b/host/docker/compose.yml
index 8860084a..d2c2e5ed 100644
--- a/host/docker/compose.yml
+++ b/host/docker/compose.yml
@@ -1,28 +1,14 @@
version: "3"
services:
- postgres:
- image: postgres:14-alpine
- environment:
- POSTGRES_USER: dnote
- POSTGRES_PASSWORD: dnote
- POSTGRES_DB: dnote
- volumes:
- - ./dnote_data:/var/lib/postgresql/data
- restart: always
-
dnote:
image: dnote/dnote:latest
environment:
- GO_ENV: PRODUCTION
+ APP_ENV: PRODUCTION
WebURL: localhost:3000
- SmtpHost:
- SmtpPort:
- SmtpUsername:
- SmtpPassword:
DisableRegistration: "false"
ports:
- 3000:3000
- depends_on:
- - postgres
+ volumes:
+ - ./dnote_data:/data
restart: always
diff --git a/host/docker/entrypoint.sh b/host/docker/entrypoint.sh
index 8fb62de9..0fee185c 100755
--- a/host/docker/entrypoint.sh
+++ b/host/docker/entrypoint.sh
@@ -1,25 +1,6 @@
#!/bin/sh
-wait_for_db() {
- HOST=${DBHost:-postgres}
- PORT=${DBPort:-5432}
- echo "Waiting for the database connection..."
-
- attempts=0
- max_attempts=10
- while [ $attempts -lt $max_attempts ]; do
- nc -z "${HOST}" "${PORT}" 2>/dev/null && break
- echo "Waiting for db at ${HOST}:${PORT}..."
- sleep 5
- attempts=$((attempts+1))
- done
-
- if [ $attempts -eq $max_attempts ]; then
- echo "Timed out while waiting for db at ${HOST}:${PORT}"
- exit 1
- fi
-}
-
-wait_for_db
+# Set default DBPath to /data if not specified
+export DBPath=${DBPath:-/data/dnote.db}
exec "$@"
diff --git a/host/smoketest/testsuite.sh b/host/smoketest/testsuite.sh
index 4fa05af3..a32ad09f 100755
--- a/host/smoketest/testsuite.sh
+++ b/host/smoketest/testsuite.sh
@@ -14,7 +14,7 @@ cd /vagrant
tar -xvf dnote_server_integration_test_linux_amd64.tar.gz
-GO_ENV=PRODUCTION \
+APP_ENV=PRODUCTION \
DBHost=localhost \
DBPort=5432 \
DBName=dnote \
diff --git a/pkg/e2e/server_test.go b/pkg/e2e/server_test.go
new file mode 100644
index 00000000..3891cdb9
--- /dev/null
+++ b/pkg/e2e/server_test.go
@@ -0,0 +1,114 @@
+/* Copyright (C) 2019, 2020, 2021, 2022, 2023, 2024, 2025 Dnote contributors
+ *
+ * This file is part of Dnote.
+ *
+ * Dnote is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Dnote is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Dnote. If not, see .
+ */
+
+package main
+
+import (
+ "fmt"
+ "net/http"
+ "os"
+ "os/exec"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/dnote/dnote/pkg/assert"
+ "gorm.io/driver/sqlite"
+ "gorm.io/gorm"
+)
+
+func TestServerStart(t *testing.T) {
+ tmpDB := t.TempDir() + "/test.db"
+ port := "3456" // Use non-standard port to avoid conflicts
+
+ // Start server in background
+ cmd := exec.Command("go", "run", "-tags", "fts5", "../server", "-port", port, "start")
+ cmd.Env = append(os.Environ(),
+ "DBPath="+tmpDB,
+ "WebURL=http://localhost:"+port,
+ "APP_ENV=PRODUCTION",
+ )
+ // Capture output for debugging
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+
+ if err := cmd.Start(); err != nil {
+ t.Fatalf("failed to start server: %v", err)
+ }
+ defer func() {
+ if cmd.Process != nil {
+ cmd.Process.Kill()
+ }
+ }()
+
+ // Wait for server to start and migrations to run
+ time.Sleep(3 * time.Second)
+
+ // Verify server responds to health check
+ resp, err := http.Get(fmt.Sprintf("http://localhost:%s/health", port))
+ if err != nil {
+ t.Fatalf("failed to reach server health endpoint: %v", err)
+ }
+ defer resp.Body.Close()
+
+ assert.Equal(t, resp.StatusCode, 200, "health endpoint should return 200")
+
+ // Kill server before checking database to avoid locks
+ if cmd.Process != nil {
+ cmd.Process.Kill()
+ cmd.Wait() // Clean up zombie process
+ }
+
+ // Verify database file was created
+ if _, err := os.Stat(tmpDB); os.IsNotExist(err) {
+ t.Fatalf("database file was not created at %s", tmpDB)
+ }
+
+ // Verify migrations ran by checking database
+ db, err := gorm.Open(sqlite.Open(tmpDB), &gorm.Config{})
+ if err != nil {
+ t.Fatalf("failed to open test database: %v", err)
+ }
+
+ // Verify migrations ran
+ var count int64
+ if err := db.Raw("SELECT COUNT(*) FROM schema_migrations").Scan(&count).Error; err != nil {
+ t.Fatalf("schema_migrations table not found: %v", err)
+ }
+ if count == 0 {
+ t.Fatal("no migrations were run")
+ }
+
+ // Verify FTS table exists and is functional
+ if err := db.Exec("SELECT * FROM notes_fts LIMIT 1").Error; err != nil {
+ t.Fatalf("notes_fts table not found or not functional: %v", err)
+ }
+}
+
+func TestServerVersion(t *testing.T) {
+ cmd := exec.Command("go", "run", "-tags", "fts5", "../server", "version")
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("version command failed: %v", err)
+ }
+
+ outputStr := string(output)
+ if !strings.Contains(outputStr, "dnote-server-") {
+ t.Errorf("expected version output to contain 'dnote-server-', got: %s", outputStr)
+ }
+}
diff --git a/pkg/server/.env.dev b/pkg/server/.env.dev
index b7268daa..334b1196 100644
--- a/pkg/server/.env.dev
+++ b/pkg/server/.env.dev
@@ -1,4 +1,4 @@
-GO_ENV=DEVELOPMENT
+APP_ENV=DEVELOPMENT
SmtpUsername=mock-SmtpUsername
SmtpPassword=mock-SmtpPassword
diff --git a/pkg/server/.env.test b/pkg/server/.env.test
index 8ab39d3d..d633f83c 100644
--- a/pkg/server/.env.test
+++ b/pkg/server/.env.test
@@ -1,4 +1,4 @@
-GO_ENV=TEST
+APP_ENV=TEST
SmtpUsername=mock-SmtpUsername
SmtpPassword=mock-SmtpPassword
diff --git a/pkg/server/app/testutils.go b/pkg/server/app/testutils.go
index fc0be26b..d31f8918 100644
--- a/pkg/server/app/testutils.go
+++ b/pkg/server/app/testutils.go
@@ -35,7 +35,7 @@ func NewTest(appParams *App) App {
WebURL: "http://127.0.0.0.1",
Port: "3000",
DisableRegistration: false,
- DB: config.LoadDBConfig(),
+ DBPath: config.LoadDBPath(),
AssetBaseURL: "",
HTTP500Page: assets.MustGetHTTP500ErrorPage(),
}
diff --git a/pkg/server/app/users.go b/pkg/server/app/users.go
index 707fcd42..5c9d7f1f 100644
--- a/pkg/server/app/users.go
+++ b/pkg/server/app/users.go
@@ -24,7 +24,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/token"
pkgErrors "github.com/pkg/errors"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
@@ -40,17 +39,6 @@ func (a *App) TouchLastLoginAt(user database.User, tx *gorm.DB) error {
return nil
}
-func createEmailPreference(user database.User, tx *gorm.DB) error {
- p := database.EmailPreference{
- UserID: user.ID,
- }
- if err := tx.Save(&p).Error; err != nil {
- return pkgErrors.Wrap(err, "inserting email preference")
- }
-
- return nil
-}
-
// CreateUser creates a user
func (a *App) CreateUser(email, password string, passwordConfirmation string) (database.User, error) {
if email == "" {
@@ -104,14 +92,6 @@ func (a *App) CreateUser(email, password string, passwordConfirmation string) (d
return database.User{}, pkgErrors.Wrap(err, "saving account")
}
- if _, err := token.Create(tx, user.ID, database.TokenTypeEmailPreference); err != nil {
- tx.Rollback()
- return database.User{}, pkgErrors.Wrap(err, "creating email verificaiton token")
- }
- if err := createEmailPreference(user, tx); err != nil {
- tx.Rollback()
- return database.User{}, pkgErrors.Wrap(err, "creating email preference")
- }
if err := a.TouchLastLoginAt(user, tx); err != nil {
tx.Rollback()
return database.User{}, pkgErrors.Wrap(err, "updating last login")
diff --git a/pkg/server/config/config.go b/pkg/server/config/config.go
index 56947483..a9a4b7de 100644
--- a/pkg/server/config/config.go
+++ b/pkg/server/config/config.go
@@ -19,18 +19,24 @@
package config
import (
+ "fmt"
"net/url"
"os"
"path/filepath"
"github.com/dnote/dnote/pkg/dirs"
"github.com/dnote/dnote/pkg/server/assets"
+ "github.com/dnote/dnote/pkg/server/log"
"github.com/pkg/errors"
)
const (
// AppEnvProduction represents an app environment for production.
AppEnvProduction string = "PRODUCTION"
+ // DefaultDBDir is the default directory name for Dnote data
+ DefaultDBDir = "dnote"
+ // DefaultDBFilename is the default database filename
+ DefaultDBFilename = "server.db"
)
var (
@@ -42,11 +48,6 @@ var (
ErrPortInvalid = errors.New("Invalid Port")
)
-// DBConfig holds the database connection configuration.
-type DBConfig struct {
- Path string
-}
-
func readBoolEnv(name string) bool {
if os.Getenv(name) == "true" {
return true
@@ -55,15 +56,13 @@ func readBoolEnv(name string) bool {
return false
}
-func LoadDBConfig() DBConfig {
+func LoadDBPath() string {
path := os.Getenv("DBPath")
if path == "" {
- path = filepath.Join(dirs.DataHome, "dnote", "server.db")
+ path = filepath.Join(dirs.DataHome, DefaultDBDir, DefaultDBFilename)
}
- return DBConfig{
- Path: path,
- }
+ return path
}
// Config is an application configuration
@@ -72,14 +71,25 @@ type Config struct {
WebURL string
DisableRegistration bool
Port string
- DB DBConfig
+ DBPath string
AssetBaseURL string
HTTP500Page []byte
}
+func getDeprecatedEnvVar(deprecated, current string) string {
+ val := os.Getenv(deprecated)
+ if val != "" {
+ log.WithFields(log.Fields{
+ "deprecated": deprecated,
+ "current": current,
+ }).Warn(fmt.Sprintf("%s is deprecated. Please use %s instead.", deprecated, current))
+ return val
+ }
+ return ""
+}
+
func getAppEnv() string {
- // DEPRECATED
- goEnv := os.Getenv("GO_ENV")
+ goEnv := getDeprecatedEnvVar("GO_ENV", "APP_ENV")
if goEnv != "" {
return goEnv
}
@@ -87,9 +97,6 @@ func getAppEnv() string {
return os.Getenv("APP_ENV")
}
-func checkDeprecatedEnvVars() {
-}
-
// Load constructs and returns a new config based on the environment variables.
func Load() Config {
port := os.Getenv("PORT")
@@ -97,14 +104,12 @@ func Load() Config {
port = "3000"
}
- checkDeprecatedEnvVars()
-
c := Config{
AppEnv: getAppEnv(),
WebURL: os.Getenv("WebURL"),
Port: port,
DisableRegistration: readBoolEnv("DisableRegistration"),
- DB: LoadDBConfig(),
+ DBPath: LoadDBPath(),
AssetBaseURL: "",
HTTP500Page: assets.MustGetHTTP500ErrorPage(),
}
@@ -134,7 +139,7 @@ func validate(c Config) error {
return ErrPortInvalid
}
- if c.DB.Path == "" {
+ if c.DBPath == "" {
return ErrDBMissingPath
}
diff --git a/pkg/server/config/config_test.go b/pkg/server/config/config_test.go
index 96ab3a55..802a3add 100644
--- a/pkg/server/config/config_test.go
+++ b/pkg/server/config/config_test.go
@@ -33,9 +33,7 @@ func TestValidate(t *testing.T) {
}{
{
config: Config{
- DB: DBConfig{
- Path: "test.db",
- },
+ DBPath: "test.db",
WebURL: "http://mock.url",
Port: "3000",
},
@@ -43,9 +41,7 @@ func TestValidate(t *testing.T) {
},
{
config: Config{
- DB: DBConfig{
- Path: "",
- },
+ DBPath: "",
WebURL: "http://mock.url",
Port: "3000",
},
@@ -53,17 +49,13 @@ func TestValidate(t *testing.T) {
},
{
config: Config{
- DB: DBConfig{
- Path: "test.db",
- },
+ DBPath: "test.db",
},
expectedErr: ErrWebURLInvalid,
},
{
config: Config{
- DB: DBConfig{
- Path: "test.db",
- },
+ DBPath: "test.db",
WebURL: "http://mock.url",
},
expectedErr: ErrPortInvalid,
diff --git a/pkg/server/database/consts.go b/pkg/server/database/consts.go
index 38c4d536..b4a1db03 100644
--- a/pkg/server/database/consts.go
+++ b/pkg/server/database/consts.go
@@ -23,8 +23,6 @@ const (
TokenTypeResetPassword = "reset_password"
// TokenTypeEmailVerification is a type of a token for verifying email
TokenTypeEmailVerification = "email_verification"
- // TokenTypeEmailPreference is a type of a token for updating email preference
- TokenTypeEmailPreference = "email_preference"
)
const (
diff --git a/pkg/server/database/database.go b/pkg/server/database/database.go
index 0795fc3d..eaab7c50 100644
--- a/pkg/server/database/database.go
+++ b/pkg/server/database/database.go
@@ -39,9 +39,7 @@ func InitSchema(db *gorm.DB) {
&Account{},
&Book{},
&Note{},
- &Notification{},
&Token{},
- &EmailPreference{},
&Session{},
); err != nil {
panic(err)
@@ -56,9 +54,7 @@ func Open(dbPath string) *gorm.DB {
panic(errors.Wrapf(err, "creating database directory at %s", dir))
}
- // Enable FTS5 extension for full-text search
- dsn := dbPath + "?_fts5=1"
- db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
+ db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
if err != nil {
panic(errors.Wrap(err, "opening database conection"))
}
diff --git a/pkg/server/database/models.go b/pkg/server/database/models.go
index 595e1260..ac3e4e56 100644
--- a/pkg/server/database/models.go
+++ b/pkg/server/database/models.go
@@ -88,21 +88,6 @@ type Token struct {
UsedAt *time.Time
}
-// Notification is the learning notification sent to the user
-type Notification struct {
- Model
- Type string
- UserID int `gorm:"index"`
-}
-
-// EmailPreference is a preference per user for receiving email communication
-type EmailPreference struct {
- Model
- UserID int `gorm:"index" json:"-"`
- InactiveReminder bool `json:"inactive_reminder" gorm:"default:false"`
- ProductUpdate bool `json:"product_update" gorm:"default:true"`
-}
-
// Session represents a user session
type Session struct {
Model
diff --git a/pkg/server/job/job.go b/pkg/server/job/job.go
deleted file mode 100644
index 12efd823..00000000
--- a/pkg/server/job/job.go
+++ /dev/null
@@ -1,127 +0,0 @@
-/* Copyright (C) 2019, 2020, 2021, 2022, 2023, 2024, 2025 Dnote contributors
- *
- * This file is part of Dnote.
- *
- * Dnote is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Dnote is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with Dnote. If not, see .
- */
-
-package job
-
-import (
- slog "log"
-
- "github.com/dnote/dnote/pkg/clock"
- "github.com/dnote/dnote/pkg/server/config"
- "github.com/dnote/dnote/pkg/server/mailer"
- "gorm.io/gorm"
- "github.com/pkg/errors"
- "github.com/robfig/cron"
-)
-
-var (
- // ErrEmptyDB is an error for missing database connection in the app configuration
- ErrEmptyDB = errors.New("No database connection was provided")
- // ErrEmptyClock is an error for missing clock in the app configuration
- ErrEmptyClock = errors.New("No clock was provided")
- // ErrEmptyWebURL is an error for missing WebURL content in the app configuration
- ErrEmptyWebURL = errors.New("No WebURL was provided")
- // ErrEmptyEmailTemplates is an error for missing EmailTemplates content in the app configuration
- ErrEmptyEmailTemplates = errors.New("No EmailTemplate store was provided")
- // ErrEmptyEmailBackend is an error for missing EmailBackend content in the app configuration
- ErrEmptyEmailBackend = errors.New("No EmailBackend was provided")
-)
-
-// Runner is a configuration for job
-type Runner struct {
- DB *gorm.DB
- Clock clock.Clock
- EmailTmpl mailer.Templates
- EmailBackend mailer.Backend
- Config config.Config
-}
-
-// NewRunner returns a new runner
-func NewRunner(db *gorm.DB, c clock.Clock, t mailer.Templates, b mailer.Backend, config config.Config) (Runner, error) {
- ret := Runner{
- DB: db,
- EmailTmpl: t,
- EmailBackend: b,
- Clock: c,
- Config: config,
- }
-
- if err := ret.validate(); err != nil {
- return Runner{}, errors.Wrap(err, "validating runner configuration")
- }
-
- return ret, nil
-}
-
-func (r *Runner) validate() error {
- if r.DB == nil {
- return ErrEmptyDB
- }
- if r.Clock == nil {
- return ErrEmptyClock
- }
- if r.EmailTmpl == nil {
- return ErrEmptyEmailTemplates
- }
- if r.EmailBackend == nil {
- return ErrEmptyEmailBackend
- }
- if r.Config.WebURL == "" {
- return ErrEmptyWebURL
- }
-
- return nil
-}
-
-func scheduleJob(c *cron.Cron, spec string, cmd func()) {
- s, err := cron.ParseStandard(spec)
- if err != nil {
- panic(errors.Wrap(err, "parsing schedule"))
- }
-
- c.Schedule(s, cron.FuncJob(cmd))
-}
-
-func (r *Runner) schedule(ch chan error) {
- // Schedule jobs
- cr := cron.New()
- cr.Start()
-
- ch <- nil
-
- // Block forever
- select {}
-}
-
-// Do starts the background tasks in a separate goroutine that runs forever
-func (r *Runner) Do() error {
- // validate
- if err := r.validate(); err != nil {
- return errors.Wrap(err, "validating job configurations")
- }
-
- ch := make(chan error)
- go r.schedule(ch)
- if err := <-ch; err != nil {
- return errors.Wrap(err, "scheduling jobs")
- }
-
- slog.Println("Started background tasks")
-
- return nil
-}
diff --git a/pkg/server/job/job_test.go b/pkg/server/job/job_test.go
deleted file mode 100644
index 58b362ca..00000000
--- a/pkg/server/job/job_test.go
+++ /dev/null
@@ -1,105 +0,0 @@
-/* Copyright (C) 2019, 2020, 2021, 2022, 2023, 2024, 2025 Dnote contributors
- *
- * This file is part of Dnote.
- *
- * Dnote is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Dnote is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with Dnote. If not, see .
- */
-
-package job
-
-import (
- "fmt"
- "testing"
-
- "github.com/dnote/dnote/pkg/assert"
- "github.com/dnote/dnote/pkg/clock"
- "github.com/dnote/dnote/pkg/server/config"
- "github.com/dnote/dnote/pkg/server/mailer"
- "github.com/dnote/dnote/pkg/server/testutils"
- "gorm.io/gorm"
- "github.com/pkg/errors"
-)
-
-func TestNewRunner(t *testing.T) {
- testCases := []struct {
- db *gorm.DB
- clock clock.Clock
- emailTmpl mailer.Templates
- emailBackend mailer.Backend
- webURL string
- expectedErr error
- }{
- {
- db: &gorm.DB{},
- clock: clock.NewMock(),
- emailTmpl: mailer.Templates{},
- emailBackend: &testutils.MockEmailbackendImplementation{},
- webURL: "http://mock.url",
- expectedErr: nil,
- },
- {
- db: nil,
- clock: clock.NewMock(),
- emailTmpl: mailer.Templates{},
- emailBackend: &testutils.MockEmailbackendImplementation{},
- webURL: "http://mock.url",
- expectedErr: ErrEmptyDB,
- },
- {
- db: &gorm.DB{},
- clock: nil,
- emailTmpl: mailer.Templates{},
- emailBackend: &testutils.MockEmailbackendImplementation{},
- webURL: "http://mock.url",
- expectedErr: ErrEmptyClock,
- },
- {
- db: &gorm.DB{},
- clock: clock.NewMock(),
- emailTmpl: nil,
- emailBackend: &testutils.MockEmailbackendImplementation{},
- webURL: "http://mock.url",
- expectedErr: ErrEmptyEmailTemplates,
- },
- {
- db: &gorm.DB{},
- clock: clock.NewMock(),
- emailTmpl: mailer.Templates{},
- emailBackend: nil,
- webURL: "http://mock.url",
- expectedErr: ErrEmptyEmailBackend,
- },
- {
- db: &gorm.DB{},
- clock: clock.NewMock(),
- emailTmpl: mailer.Templates{},
- emailBackend: &testutils.MockEmailbackendImplementation{},
- webURL: "",
- expectedErr: ErrEmptyWebURL,
- },
- }
-
- for idx, tc := range testCases {
- t.Run(fmt.Sprintf("test case %d", idx), func(t *testing.T) {
-
- c := config.Config{
- WebURL: tc.webURL,
- }
-
- _, err := NewRunner(tc.db, tc.clock, tc.emailTmpl, tc.emailBackend, c)
-
- assert.Equal(t, errors.Cause(err), tc.expectedErr, "error mismatch")
- })
- }
-}
diff --git a/pkg/server/main.go b/pkg/server/main.go
index 4585a03e..02936cc8 100644
--- a/pkg/server/main.go
+++ b/pkg/server/main.go
@@ -23,7 +23,6 @@ import (
"fmt"
"log"
"net/http"
- "os"
"github.com/dnote/dnote/pkg/clock"
"github.com/dnote/dnote/pkg/server/app"
@@ -31,7 +30,6 @@ import (
"github.com/dnote/dnote/pkg/server/config"
"github.com/dnote/dnote/pkg/server/controllers"
"github.com/dnote/dnote/pkg/server/database"
- "github.com/dnote/dnote/pkg/server/job"
"github.com/dnote/dnote/pkg/server/mailer"
"github.com/pkg/errors"
"gorm.io/gorm"
@@ -40,7 +38,7 @@ import (
var port = flag.String("port", "3000", "port to connect to")
func initDB(c config.Config) *gorm.DB {
- db := database.Open(c.DB.Path)
+ db := database.Open(c.DBPath)
database.InitSchema(db)
database.Migrate(db)
@@ -50,11 +48,11 @@ func initDB(c config.Config) *gorm.DB {
func initApp(cfg config.Config) app.App {
db := initDB(cfg)
- isProduction := os.Getenv("GO_ENV") == "PRODUCTION"
- emailBackend, err := mailer.NewDefaultBackend(isProduction)
+ emailBackend, err := mailer.NewDefaultBackend(cfg.IsProd())
if err != nil {
- log.Printf("Email backend not fully configured: %v. Emails will only be logged.", err)
emailBackend = &mailer.DefaultBackend{Enabled: false}
+ } else {
+ log.Printf("Email backend configured")
}
return app.App{
@@ -67,18 +65,6 @@ func initApp(cfg config.Config) app.App {
}
}
-func runJob(a app.App) error {
- runner, err := job.NewRunner(a.DB, a.Clock, a.EmailTemplates, a.EmailBackend, a.Config)
- if err != nil {
- return errors.Wrap(err, "getting a job runner")
- }
- if err := runner.Do(); err != nil {
- return errors.Wrap(err, "running job")
- }
-
- return nil
-}
-
func startCmd() {
cfg := config.Load()
cfg.SetAssetBaseURL("/static")
@@ -91,13 +77,6 @@ func startCmd() {
}
}()
- if err := database.Migrate(app.DB); err != nil {
- panic(errors.Wrap(err, "running migrations"))
- }
- if err := runJob(app); err != nil {
- panic(errors.Wrap(err, "running job"))
- }
-
ctl := controllers.New(&app)
rc := controllers.RouteConfig{
WebRoutes: controllers.NewWebRoutes(&app, ctl),
@@ -119,7 +98,7 @@ func versionCmd() {
}
func rootCmd() {
- fmt.Printf(`Dnote server - a simple personal knowledge base
+ fmt.Printf(`Dnote server - a simple command line notebook
Usage:
dnote-server [command]
diff --git a/pkg/server/middleware/auth_test.go b/pkg/server/middleware/auth_test.go
index d8dc8802..8451ae5d 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.TokenTypeEmailPreference,
+ Type: database.TokenTypeEmailVerification,
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.TokenTypeEmailPreference, nil))
+ server := httptest.NewServer(TokenAuth(db, handler, database.TokenTypeEmailVerification, nil))
defer server.Close()
t.Run("with token", func(t *testing.T) {
diff --git a/pkg/server/middleware/limit.go b/pkg/server/middleware/limit.go
index b186c6a9..3b3c3987 100644
--- a/pkg/server/middleware/limit.go
+++ b/pkg/server/middleware/limit.go
@@ -128,7 +128,7 @@ func Limit(next http.Handler) http.HandlerFunc {
func ApplyLimit(h http.HandlerFunc, rateLimit bool) http.Handler {
ret := h
- if rateLimit && os.Getenv("GO_ENV") != "TEST" {
+ if rateLimit && os.Getenv("APP_ENV") != "TEST" {
ret = Limit(ret)
}
diff --git a/pkg/server/testutils/main.go b/pkg/server/testutils/main.go
index b8519581..e07f41e8 100644
--- a/pkg/server/testutils/main.go
+++ b/pkg/server/testutils/main.go
@@ -45,7 +45,7 @@ import (
// the environment variable configuration and initalizes a new schema
func InitTestDB() *gorm.DB {
c := config.Load()
- return database.Open(c.DB.Path)
+ return database.Open(c.DBPath)
}
// InitMemoryDB creates an in-memory SQLite database with the schema initialized
@@ -76,15 +76,9 @@ func ClearData(db *gorm.DB) {
if err := db.Where("1 = 1").Delete(&database.Book{}).Error; err != nil {
panic(errors.Wrap(err, "Failed to clear books"))
}
- if err := db.Where("1 = 1").Delete(&database.Notification{}).Error; err != nil {
- panic(errors.Wrap(err, "Failed to clear notifications"))
- }
if err := db.Where("1 = 1").Delete(&database.Token{}).Error; err != nil {
panic(errors.Wrap(err, "Failed to clear tokens"))
}
- if err := db.Where("1 = 1").Delete(&database.EmailPreference{}).Error; err != nil {
- panic(errors.Wrap(err, "Failed to clear email preferences"))
- }
if err := db.Where("1 = 1").Delete(&database.Session{}).Error; err != nil {
panic(errors.Wrap(err, "Failed to clear sessions"))
}
diff --git a/pkg/server/token/token_test.go b/pkg/server/token/token_test.go
index cdc114c0..922cc93d 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.TokenTypeEmailPreference,
+ kind: database.TokenTypeEmailVerification,
},
}