mirror of
https://github.com/dnote/dnote
synced 2026-03-14 14:35:50 +01:00
Customize app URLs in the emails (#290)
* Allow to customize app url in emails * Validate env var * Test * Add license * Add guide
This commit is contained in:
parent
47d4a8f6fc
commit
5d6ad342f3
27 changed files with 427 additions and 197 deletions
11
CHANGELOG.md
11
CHANGELOG.md
|
|
@ -12,7 +12,13 @@ The following log documentes the history of the server project.
|
|||
|
||||
### [Unreleased]
|
||||
|
||||
- N/A
|
||||
#### Upgrade Guide
|
||||
|
||||
* Please define a new environment variable `WebURL` whose value is the URL to your Dnote server, without the trailing slash. (e.g. `https://my-server.com`) (Please see #290)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Allow to customize the app URL in the emails (#290)
|
||||
|
||||
### 0.2.0 - 2019-10-28
|
||||
|
||||
|
|
@ -24,6 +30,9 @@ The following log documentes the history of the server project.
|
|||
|
||||
- Treat a linebreak as a new line in the preview (#261)
|
||||
- Allow to have multiple editor states for adding and editing notes (#260)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Fix jumping focus on editor (#265)
|
||||
|
||||
### 0.1.1 - 2019-09-30
|
||||
|
|
|
|||
|
|
@ -23,10 +23,13 @@ DBPort=5432 \
|
|||
DBName=dnote \
|
||||
DBUser=$user \
|
||||
DBPassword=$password \
|
||||
WebURL=$webURL
|
||||
dnote-server start
|
||||
```
|
||||
|
||||
Replace $user and $password with the credentials of the Postgres user that owns the `dnote` database.
|
||||
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`).
|
||||
|
||||
By default, dnote server will run on the port 3000.
|
||||
|
||||
|
|
@ -91,6 +94,7 @@ Environment=GO_ENV=PRODUCTION
|
|||
Environment=DBHost=localhost
|
||||
Environment=DBPort=5432
|
||||
Environment=DBName=dnote
|
||||
Environment=WebURL=$WebURL
|
||||
Environment=DBUser=$DBUser
|
||||
Environment=DBPassword=$DBPassword
|
||||
Environment=SmtpHost=
|
||||
|
|
@ -101,9 +105,9 @@ Environment=SmtpPassword=
|
|||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Replace `$user`, `$DBUser`, and `$DBPassword` with the actual values.
|
||||
Replace `$user`, `$WebURL`, `$DBUser`, and `$DBPassword` with the actual values.
|
||||
|
||||
Optionally, if you would like to send email digests, populate `SmtpHost`, `SmtpUsername`, and `SmtpPassword`.
|
||||
Optionally, if you would like to send spaced repetitions throught email, populate `SmtpHost`, `SmtpUsername`, and `SmtpPassword`.
|
||||
|
||||
2. Reload the change by running `sudo systemctl daemon-reload`.
|
||||
3. Enable the Daemon by running `sudo systemctl enable dnote`.`
|
||||
|
|
@ -144,3 +148,7 @@ 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'.
|
||||
|
|
|
|||
|
|
@ -9,3 +9,5 @@ DBPassword=
|
|||
SmtpUsername=mock-SmtpUsername
|
||||
SmtpPassword=mock-SmtpPassword
|
||||
SmtpHost=mock-SmtpHost
|
||||
|
||||
WebURL=http://localhost:3000
|
||||
|
|
|
|||
|
|
@ -9,3 +9,5 @@ DBPassword=
|
|||
SmtpUsername=mock-SmtpUsername
|
||||
SmtpPassword=mock-SmtpPassword
|
||||
SmtpHost=mock-SmtpHost
|
||||
|
||||
WebURL=http://localhost:3000
|
||||
|
|
|
|||
|
|
@ -133,12 +133,10 @@ func (a *App) createResetToken(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
subject := "Reset your password"
|
||||
data := struct {
|
||||
Subject string
|
||||
Token string
|
||||
}{
|
||||
subject,
|
||||
resetToken,
|
||||
data := mailer.EmailResetPasswordTmplData{
|
||||
Subject: subject,
|
||||
Token: resetToken,
|
||||
WebURL: a.WebURL,
|
||||
}
|
||||
email := mailer.NewEmail("noreply@getdnote.com", []string{params.Email}, subject)
|
||||
if err := email.ParseTemplate(mailer.EmailTypeResetPassword, data); err != nil {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ package handlers
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -36,9 +35,9 @@ func TestGetMe(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -64,9 +63,9 @@ func TestCreateResetToken(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -97,9 +96,9 @@ func TestCreateResetToken(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -126,9 +125,9 @@ func TestResetPassword(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -172,9 +171,9 @@ func TestResetPassword(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -210,9 +209,9 @@ func TestResetPassword(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -247,9 +246,9 @@ func TestResetPassword(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -295,9 +294,9 @@ func TestResetPassword(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/dnote/dnote/pkg/assert"
|
||||
|
|
@ -78,9 +77,9 @@ func TestClassicPresignin(t *testing.T) {
|
|||
t.Run(fmt.Sprintf("presignin %s", tc.email), func(t *testing.T) {
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
endpoint := fmt.Sprintf("/classic/presignin?email=%s", tc.email)
|
||||
|
|
@ -106,9 +105,9 @@ func TestClassicPresignin_MissingParams(t *testing.T) {
|
|||
defer testutils.ClearData()
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
req := testutils.MakeReq(server, "GET", "/classic/presignin", "")
|
||||
|
|
@ -129,9 +128,9 @@ func TestClassicSignin(t *testing.T) {
|
|||
testutils.MustExec(t, db.Save(&alice), "saving alice")
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
dat := fmt.Sprintf(`{"email": "%s", "auth_key": "%s"}`, "alice@example.com", "/XCYisXJ6/o+vf6NUEtmrdYzJYPz+T9oAUCtMpOjhzc=")
|
||||
|
|
@ -227,9 +226,9 @@ func TestClassicSignin_Failure(t *testing.T) {
|
|||
t.Run(fmt.Sprintf("signin %s %s", tc.email, tc.authKey), func(t *testing.T) {
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
dat := fmt.Sprintf(`{"email": "%s", "auth_key": "%s"}`, tc.email, tc.authKey)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ package handlers
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/dnote/dnote/pkg/assert"
|
||||
|
|
@ -32,9 +31,9 @@ func TestCheckHealth(t *testing.T) {
|
|||
defer testutils.ClearData()
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
// Execute
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -44,9 +43,9 @@ func TestGetNotes(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -187,9 +186,9 @@ func TestGetNote(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -43,9 +42,9 @@ func TestGetRepetitionRule(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -117,9 +116,9 @@ func TestGetRepetitionRules(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -225,9 +224,10 @@ func TestCreateRepetitionRules(t *testing.T) {
|
|||
c := clock.NewMock()
|
||||
t0 := time.Date(2009, time.November, 1, 2, 3, 4, 5, time.UTC)
|
||||
c.SetNow(t0)
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: c,
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -282,9 +282,10 @@ func TestCreateRepetitionRules(t *testing.T) {
|
|||
c := clock.NewMock()
|
||||
t0 := time.Date(2009, time.November, 1, 2, 3, 4, 5, time.UTC)
|
||||
c.SetNow(t0)
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: c,
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -345,9 +346,9 @@ func TestUpdateRepetitionRules(t *testing.T) {
|
|||
c := clock.NewMock()
|
||||
t0 := time.Date(2009, time.November, 1, 2, 3, 4, 5, time.UTC)
|
||||
c.SetNow(t0)
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: c,
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -419,9 +420,9 @@ func TestDeleteRepetitionRules(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -544,9 +545,9 @@ func TestCreateUpdateRepetitionRules_BadRequest(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -588,9 +589,9 @@ func TestCreateUpdateRepetitionRules_BadRequest(t *testing.T) {
|
|||
}
|
||||
testutils.MustExec(t, db.Save(&b1), "preparing book1")
|
||||
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
// Execute
|
||||
|
|
@ -627,9 +628,9 @@ func TestCreateRepetitionRules_BadRequest(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
|
|||
|
|
@ -351,20 +351,37 @@ func applyMiddleware(h http.HandlerFunc, rateLimit bool) http.Handler {
|
|||
type App struct {
|
||||
Clock clock.Clock
|
||||
StripeAPIBackend stripe.Backend
|
||||
WebURL string
|
||||
}
|
||||
|
||||
func (a *App) validate() error {
|
||||
if a.WebURL == "" {
|
||||
return errors.New("WebURL is empty")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// init sets up the application based on the configuration
|
||||
func (a *App) init() {
|
||||
func (a *App) init() error {
|
||||
if err := a.validate(); err != nil {
|
||||
return errors.Wrap(err, "validating the app parameters")
|
||||
}
|
||||
|
||||
stripe.Key = os.Getenv("StripeSecretKey")
|
||||
|
||||
if a.StripeAPIBackend != nil {
|
||||
stripe.SetBackend(stripe.APIBackend, a.StripeAPIBackend)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewRouter creates and returns a new router
|
||||
func NewRouter(app *App) *mux.Router {
|
||||
app.init()
|
||||
func NewRouter(app *App) (*mux.Router, error) {
|
||||
if err := app.init(); err != nil {
|
||||
return nil, errors.Wrap(err, "initializing app")
|
||||
}
|
||||
|
||||
proOnly := authMiddlewareParams{ProOnly: true}
|
||||
|
||||
|
|
@ -436,5 +453,5 @@ func NewRouter(app *App) *mux.Router {
|
|||
Handler(applyMiddleware(handler, route.RateLimit))
|
||||
}
|
||||
|
||||
return router
|
||||
return router, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -681,9 +681,9 @@ func TestNotSupportedVersions(t *testing.T) {
|
|||
}
|
||||
|
||||
// setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
|
|
|||
42
pkg/server/api/handlers/testutils.go
Normal file
42
pkg/server/api/handlers/testutils.go
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/* Copyright (C) 2019 Monomax Software Pty Ltd
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// mustNewServer is a test utility function to initialize a new server
|
||||
// with the given app paratmers
|
||||
func mustNewServer(t *testing.T, app *App) *httptest.Server {
|
||||
app.WebURL = os.Getenv("WebURL")
|
||||
|
||||
r, err := NewRouter(app)
|
||||
if err != nil {
|
||||
t.Fatal(errors.Wrap(err, "initializing server"))
|
||||
}
|
||||
|
||||
server := httptest.NewServer(r)
|
||||
|
||||
return server
|
||||
}
|
||||
|
|
@ -188,12 +188,10 @@ func (a *App) createVerificationToken(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
subject := "Verify your email"
|
||||
data := struct {
|
||||
Subject string
|
||||
Token string
|
||||
}{
|
||||
subject,
|
||||
tokenValue,
|
||||
data := mailer.EmailVerificationTmplData{
|
||||
Subject: subject,
|
||||
Token: tokenValue,
|
||||
WebURL: a.WebURL,
|
||||
}
|
||||
email := mailer.NewEmail("noreply@getdnote.com", []string{account.Email.String}, subject)
|
||||
if err := email.ParseTemplate(mailer.EmailTypeEmailVerification, data); err != nil {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -47,9 +46,9 @@ func TestUpdatePassword(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -75,9 +74,9 @@ func TestUpdatePassword(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -101,9 +100,9 @@ func TestUpdatePassword(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -135,9 +134,9 @@ func TestCreateVerificationToken(t *testing.T) {
|
|||
templatePath := fmt.Sprintf("%s/mailer/templates/src", testutils.ServerPath)
|
||||
mailer.InitTemplates(&templatePath)
|
||||
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -168,9 +167,9 @@ func TestCreateVerificationToken(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -201,9 +200,9 @@ func TestVerifyEmail(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -242,9 +241,9 @@ func TestVerifyEmail(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -286,9 +285,9 @@ func TestVerifyEmail(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -328,9 +327,9 @@ func TestVerifyEmail(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -373,9 +372,9 @@ func TestUpdateEmail(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -407,9 +406,9 @@ func TestUpdateEmailPreference(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -433,9 +432,9 @@ func TestUpdateEmailPreference(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -473,9 +472,9 @@ func TestUpdateEmailPreference(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -507,9 +506,9 @@ func TestUpdateEmailPreference(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -543,9 +542,9 @@ func TestUpdateEmailPreference(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -579,9 +578,9 @@ func TestUpdateEmailPreference(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -605,9 +604,9 @@ func TestUpdateEmailPreference(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -641,9 +640,9 @@ func TestGetEmailPreference(t *testing.T) {
|
|||
defer testutils.ClearData()
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -91,9 +90,9 @@ func TestRegister(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
dat := fmt.Sprintf(`{"email": "%s", "password": "%s"}`, tc.email, tc.password)
|
||||
|
|
@ -134,9 +133,9 @@ func TestRegisterMissingParams(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
dat := fmt.Sprintf(`{"password": %s}`, "SLMZFM5RmSjA5vfXnG5lPOnrpZSbtmV76cnAcrlr2yU")
|
||||
|
|
@ -161,9 +160,9 @@ func TestRegisterMissingParams(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
dat := fmt.Sprintf(`{"email": "%s"}`, "alice@example.com")
|
||||
|
|
@ -189,9 +188,9 @@ func TestRegisterDuplicateEmail(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -226,9 +225,9 @@ func TestSignIn(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -256,9 +255,9 @@ func TestSignIn(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -287,9 +286,9 @@ func TestSignIn(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
u := testutils.SetupUserData()
|
||||
|
|
@ -318,9 +317,9 @@ func TestSignIn(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
dat := `{"email": "nonexistent@example.com", "password": "pass1234"}`
|
||||
|
|
@ -361,9 +360,9 @@ func TestSignout(t *testing.T) {
|
|||
testutils.MustExec(t, db.Save(&session2), "preparing session2")
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
// Execute
|
||||
|
|
@ -412,9 +411,9 @@ func TestSignout(t *testing.T) {
|
|||
testutils.MustExec(t, db.Save(&session2), "preparing session2")
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
// Execute
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
|
|
@ -43,9 +42,9 @@ func TestGetBooks(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -122,9 +121,9 @@ func TestGetBooksByName(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -207,9 +206,9 @@ func TestDeleteBook(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -357,9 +356,9 @@ func TestCreateBook(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -418,9 +417,9 @@ func TestCreateBookDuplicate(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -497,9 +496,9 @@ func TestUpdateBook(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ package handlers
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/dnote/dnote/pkg/assert"
|
||||
|
|
@ -39,9 +38,9 @@ func TestCreateNote(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -172,9 +171,9 @@ func TestUpdateNote(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
@ -265,9 +264,9 @@ func TestDeleteNote(t *testing.T) {
|
|||
db := database.DBConn
|
||||
|
||||
// Setup
|
||||
server := httptest.NewServer(NewRouter(&App{
|
||||
server := mustNewServer(t, &App{
|
||||
Clock: clock.NewMock(),
|
||||
}))
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
user := testutils.SetupUserData()
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ package job
|
|||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/dnote/dnote/pkg/clock"
|
||||
"github.com/dnote/dnote/pkg/server/job/repetition"
|
||||
|
|
@ -36,10 +37,15 @@ func scheduleJob(c *cron.Cron, spec string, cmd func()) {
|
|||
c.Schedule(s, cron.FuncJob(cmd))
|
||||
}
|
||||
|
||||
// Run starts the background tasks and blocks forever.
|
||||
func Run() {
|
||||
log.Println("Started background tasks")
|
||||
func checkEnvironment() error {
|
||||
if os.Getenv("WebURL") == "" {
|
||||
return errors.New("WebURL is empty")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func schedule(ch chan error) {
|
||||
cl := clock.New()
|
||||
|
||||
// Schedule jobs
|
||||
|
|
@ -47,6 +53,25 @@ func Run() {
|
|||
scheduleJob(c, "* * * * *", func() { repetition.Do(cl) })
|
||||
c.Start()
|
||||
|
||||
ch <- nil
|
||||
|
||||
// Block forever
|
||||
select {}
|
||||
}
|
||||
|
||||
// Run starts the background tasks in a separate goroutine that runs forever
|
||||
func Run() error {
|
||||
if err := checkEnvironment(); err != nil {
|
||||
return errors.Wrap(err, "checking environment variables")
|
||||
}
|
||||
|
||||
ch := make(chan error)
|
||||
go schedule(ch)
|
||||
if err := <-ch; err != nil {
|
||||
return errors.Wrap(err, "scheduling jobs")
|
||||
}
|
||||
|
||||
log.Println("Started background tasks")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ package repetition
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/dnote/dnote/pkg/clock"
|
||||
|
|
@ -74,6 +75,7 @@ func BuildEmail(now time.Time, user database.User, emailAddr string, digest data
|
|||
EmailSessionToken: tok.Value,
|
||||
RuleUUID: rule.UUID,
|
||||
RuleTitle: rule.Title,
|
||||
WebURL: os.Getenv("WebURL"),
|
||||
}
|
||||
|
||||
email := mailer.NewEmail("noreply@getdnote.com", []string{emailAddr}, subject)
|
||||
|
|
|
|||
|
|
@ -161,12 +161,12 @@ func (e *Email) ParseTemplate(templateName string, data interface{}) error {
|
|||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := t.Execute(buf, data); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "executing the template")
|
||||
}
|
||||
|
||||
html, err := inliner.Inline(buf.String())
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "inlining the css rules")
|
||||
}
|
||||
|
||||
e.Body = html
|
||||
|
|
|
|||
113
pkg/server/mailer/mailer_test.go
Normal file
113
pkg/server/mailer/mailer_test.go
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/* Copyright (C) 2019 Monomax Software Pty Ltd
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package mailer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/dnote/dnote/pkg/server/testutils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func init() {
|
||||
testutils.InitTestDB()
|
||||
|
||||
templatePath := fmt.Sprintf("%s/mailer/templates/src", testutils.ServerPath)
|
||||
InitTemplates(&templatePath)
|
||||
}
|
||||
|
||||
func TestEmailVerificationEmail(t *testing.T) {
|
||||
testCases := []struct {
|
||||
token string
|
||||
webURL string
|
||||
}{
|
||||
{
|
||||
token: "someRandomToken1",
|
||||
webURL: "http://localhost:3000",
|
||||
},
|
||||
{
|
||||
token: "someRandomToken2",
|
||||
webURL: "http://localhost:3001",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("with WebURL %s", tc.webURL), func(t *testing.T) {
|
||||
m := NewEmail("alice@example.com", []string{"bob@example.com"}, "Test email")
|
||||
|
||||
dat := EmailVerificationTmplData{
|
||||
Subject: "Test email verification email",
|
||||
Token: tc.token,
|
||||
WebURL: tc.webURL,
|
||||
}
|
||||
err := m.ParseTemplate(EmailTypeEmailVerification, dat)
|
||||
if err != nil {
|
||||
t.Fatal(errors.Wrap(err, "executing"))
|
||||
}
|
||||
|
||||
if ok := strings.Contains(m.Body, tc.webURL); !ok {
|
||||
t.Errorf("email body did not contain %s", tc.webURL)
|
||||
}
|
||||
if ok := strings.Contains(m.Body, tc.token); !ok {
|
||||
t.Errorf("email body did not contain %s", tc.token)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetPasswordEmail(t *testing.T) {
|
||||
testCases := []struct {
|
||||
token string
|
||||
webURL string
|
||||
}{
|
||||
{
|
||||
token: "someRandomToken1",
|
||||
webURL: "http://localhost:3000",
|
||||
},
|
||||
{
|
||||
token: "someRandomToken2",
|
||||
webURL: "http://localhost:3001",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("with WebURL %s", tc.webURL), func(t *testing.T) {
|
||||
m := NewEmail("alice@example.com", []string{"bob@example.com"}, "Test email")
|
||||
|
||||
dat := EmailVerificationTmplData{
|
||||
Subject: "Test reset passowrd email",
|
||||
Token: tc.token,
|
||||
WebURL: tc.webURL,
|
||||
}
|
||||
err := m.ParseTemplate(EmailTypeResetPassword, dat)
|
||||
if err != nil {
|
||||
t.Fatal(errors.Wrap(err, "executing"))
|
||||
}
|
||||
|
||||
if ok := strings.Contains(m.Body, tc.webURL); !ok {
|
||||
t.Errorf("email body did not contain %s", tc.webURL)
|
||||
}
|
||||
if ok := strings.Contains(m.Body, tc.token); !ok {
|
||||
t.Errorf("email body did not contain %s", tc.token)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -392,7 +392,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td class="note-meta">
|
||||
<a target="_blank" rel="noreferrer noopener" href="https://app.getdnote.com/notes/{{ .UUID }}"><span class="meta-label">Open</span></a>
|
||||
<a target="_blank" rel="noreferrer noopener" href="{{ $.WebURL }}/notes/{{ .UUID }}"><span class="meta-label">Open</span></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
@ -413,9 +413,7 @@
|
|||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
|
@ -436,7 +434,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://app.getdnote.com/preferences/repetitions/{{ .RuleUUID }}?token={{ .EmailSessionToken }}">Change digest settings</a>
|
||||
<a href="{{ .WebURL }}/preferences/repetitions/{{ .RuleUUID }}?token={{ .EmailSessionToken }}">Change digest settings</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -318,7 +318,7 @@
|
|||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://app.getdnote.com/verify-email/{{ .Token }}" target="_blank">Verify email</a>
|
||||
<a href="{{ .WebURL }}/{{ .Token }}" target="_blank">Verify email</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
@ -332,7 +332,7 @@
|
|||
|
||||
<tr>
|
||||
<td class="content-block">
|
||||
Alternatively you can manually go to the following URL: <a href="https://app.getdnote.com/verify-email/{{ .Token }}">https://app.getdnote.com/verify-email/{{ .Token }}</a>
|
||||
Alternatively you can manually go to the following URL: <a href="{{ .WebURL }}/{{ .Token }}">{{ .WebURL }}/verify-email/{{ .Token }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
|
|
|||
|
|
@ -319,7 +319,7 @@
|
|||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://app.getdnote.com/password-reset/{{ .Token }}" target="_blank">Reset Password</a>
|
||||
<a href="{{ .WebURL }}/password-reset/{{ .Token }}" target="_blank">Reset Password</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -34,17 +34,6 @@ type DigestNoteInfo struct {
|
|||
Stage int
|
||||
}
|
||||
|
||||
// DigestTmplData is a template data for digest emails
|
||||
type DigestTmplData struct {
|
||||
Subject string
|
||||
NoteInfo []DigestNoteInfo
|
||||
ActiveBookCount int
|
||||
ActiveNoteCount int
|
||||
EmailSessionToken string
|
||||
RuleUUID string
|
||||
RuleTitle string
|
||||
}
|
||||
|
||||
// NewNoteInfo returns a new NoteInfo
|
||||
func NewNoteInfo(note database.Note, stage int) DigestNoteInfo {
|
||||
tm := time.Unix(0, int64(note.AddedOn))
|
||||
|
|
@ -57,3 +46,29 @@ func NewNoteInfo(note database.Note, stage int) DigestNoteInfo {
|
|||
Stage: stage,
|
||||
}
|
||||
}
|
||||
|
||||
// DigestTmplData is a template data for digest emails
|
||||
type DigestTmplData struct {
|
||||
Subject string
|
||||
NoteInfo []DigestNoteInfo
|
||||
ActiveBookCount int
|
||||
ActiveNoteCount int
|
||||
EmailSessionToken string
|
||||
RuleUUID string
|
||||
RuleTitle string
|
||||
WebURL string
|
||||
}
|
||||
|
||||
// EmailVerificationTmplData is a template data for email verification emails
|
||||
type EmailVerificationTmplData struct {
|
||||
Subject string
|
||||
Token string
|
||||
WebURL string
|
||||
}
|
||||
|
||||
// EmailResetPasswordTmplData is a template data for reset password emails
|
||||
type EmailResetPasswordTmplData struct {
|
||||
Subject string
|
||||
Token string
|
||||
WebURL string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ import (
|
|||
|
||||
var versionTag = "master"
|
||||
var port = flag.String("port", "3000", "port to connect to")
|
||||
|
||||
var rootBox *packr.Box
|
||||
|
||||
func init() {
|
||||
|
|
@ -89,13 +88,17 @@ func getSWHandler() http.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
func initServer() *mux.Router {
|
||||
func initServer() (*mux.Router, error) {
|
||||
srv := mux.NewRouter()
|
||||
|
||||
apiRouter := handlers.NewRouter(&handlers.App{
|
||||
apiRouter, err := handlers.NewRouter(&handlers.App{
|
||||
Clock: clock.New(),
|
||||
StripeAPIBackend: nil,
|
||||
WebURL: os.Getenv("WebURL"),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "initializing router")
|
||||
}
|
||||
|
||||
srv.PathPrefix("/api").Handler(http.StripPrefix("/api", apiRouter))
|
||||
srv.PathPrefix("/static").Handler(getStaticHandler())
|
||||
|
|
@ -105,49 +108,45 @@ func initServer() *mux.Router {
|
|||
// For all other requests, serve the index.html file
|
||||
srv.PathPrefix("/").Handler(getRootHandler())
|
||||
|
||||
return srv
|
||||
return srv, nil
|
||||
}
|
||||
|
||||
func startCmd() {
|
||||
c := database.Config{
|
||||
mailer.InitTemplates(nil)
|
||||
|
||||
database.Open(database.Config{
|
||||
Host: os.Getenv("DBHost"),
|
||||
Port: os.Getenv("DBPort"),
|
||||
Name: os.Getenv("DBName"),
|
||||
User: os.Getenv("DBUser"),
|
||||
Password: os.Getenv("DBPassword"),
|
||||
}
|
||||
database.Open(c)
|
||||
})
|
||||
database.InitSchema()
|
||||
defer database.Close()
|
||||
|
||||
mailer.InitTemplates(nil)
|
||||
|
||||
// Perform database migration
|
||||
if err := database.Migrate(); err != nil {
|
||||
panic(errors.Wrap(err, "running migrations"))
|
||||
}
|
||||
if err := job.Run(); err != nil {
|
||||
panic(errors.Wrap(err, "running job"))
|
||||
}
|
||||
|
||||
// Run job in the background
|
||||
go job.Run()
|
||||
|
||||
srv := initServer()
|
||||
srv, err := initServer()
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "initializing server"))
|
||||
}
|
||||
|
||||
log.Printf("Dnote version %s is running on port %s", versionTag, *port)
|
||||
addr := fmt.Sprintf(":%s", *port)
|
||||
log.Println(http.ListenAndServe(addr, srv))
|
||||
http.ListenAndServe(addr, srv)
|
||||
}
|
||||
|
||||
func versionCmd() {
|
||||
fmt.Printf("dnote-server-%s\n", versionTag)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
cmd := flag.Arg(0)
|
||||
|
||||
switch cmd {
|
||||
case "":
|
||||
fmt.Printf(`Dnote Server - A simple notebook for developers
|
||||
func rootCmd() {
|
||||
fmt.Printf(`Dnote Server - A simple notebook for developers
|
||||
|
||||
Usage:
|
||||
dnote-server [command]
|
||||
|
|
@ -156,6 +155,15 @@ Available commands:
|
|||
start: Start the server
|
||||
version: Print the version
|
||||
`)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
cmd := flag.Arg(0)
|
||||
|
||||
switch cmd {
|
||||
case "":
|
||||
rootCmd()
|
||||
case "start":
|
||||
startCmd()
|
||||
case "version":
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue