mirror of
https://github.com/writefreely/writefreely
synced 2025-02-17 16:28:23 +00:00
This adds a self-serve password reset page. Users can enter their username and receive an email with a link that will let them create a new password. If they've never set a password, it will send them a one-time login link (building on #776) that will then take them to their Account Settings page. If they don't have an email associated with their account, they'll be instructed to contact the admin, so they can manually reset the password. Includes changes to the stylesheet and database, so run: make ui writefreely db migrate Closes T508
149 lines
4.2 KiB
Go
149 lines
4.2 KiB
Go
/*
|
|
* Copyright © 2019 Musing Studio LLC.
|
|
*
|
|
* This file is part of WriteFreely.
|
|
*
|
|
* WriteFreely is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License, included
|
|
* in the LICENSE file in this source code package.
|
|
*/
|
|
|
|
// Package migrations contains database migrations for WriteFreely
|
|
package migrations
|
|
|
|
import (
|
|
"database/sql"
|
|
|
|
"github.com/writeas/web-core/log"
|
|
)
|
|
|
|
// TODO: refactor to use the datastore struct from writefreely pkg
|
|
type datastore struct {
|
|
*sql.DB
|
|
driverName string
|
|
}
|
|
|
|
func NewDatastore(db *sql.DB, dn string) *datastore {
|
|
return &datastore{db, dn}
|
|
}
|
|
|
|
// TODO: use these consts from writefreely pkg
|
|
const (
|
|
driverMySQL = "mysql"
|
|
driverSQLite = "sqlite3"
|
|
)
|
|
|
|
type Migration interface {
|
|
Description() string
|
|
Migrate(db *datastore) error
|
|
}
|
|
|
|
type migration struct {
|
|
description string
|
|
migrate func(db *datastore) error
|
|
}
|
|
|
|
func New(d string, fn func(db *datastore) error) Migration {
|
|
return &migration{d, fn}
|
|
}
|
|
|
|
func (m *migration) Description() string {
|
|
return m.description
|
|
}
|
|
|
|
func (m *migration) Migrate(db *datastore) error {
|
|
return m.migrate(db)
|
|
}
|
|
|
|
var migrations = []Migration{
|
|
New("support user invites", supportUserInvites), // -> V1 (v0.8.0)
|
|
New("support dynamic instance pages", supportInstancePages), // V1 -> V2 (v0.9.0)
|
|
New("support users suspension", supportUserStatus), // V2 -> V3 (v0.11.0)
|
|
New("support oauth", oauth), // V3 -> V4
|
|
New("support slack oauth", oauthSlack), // V4 -> v5
|
|
New("support ActivityPub mentions", supportActivityPubMentions), // V5 -> V6
|
|
New("support oauth attach", oauthAttach), // V6 -> V7
|
|
New("support oauth via invite", oauthInvites), // V7 -> V8 (v0.12.0)
|
|
New("optimize drafts retrieval", optimizeDrafts), // V8 -> V9
|
|
New("support post signatures", supportPostSignatures), // V9 -> V10 (v0.13.0)
|
|
New("Widen oauth_users.access_token", widenOauthAcceesToken), // V10 -> V11
|
|
New("support verifying fedi profile", fediverseVerifyProfile), // V11 -> V12 (v0.14.0)
|
|
New("support newsletters", supportLetters), // V12 -> V13
|
|
New("support password resetting", supportPassReset), // V13 -> V14
|
|
}
|
|
|
|
// CurrentVer returns the current migration version the application is on
|
|
func CurrentVer() int {
|
|
return len(migrations)
|
|
}
|
|
|
|
func SetInitialMigrations(db *datastore) error {
|
|
// Included schema files represent changes up to V1, so note that in the database
|
|
_, err := db.Exec("INSERT INTO appmigrations (version, migrated, result) VALUES (?, "+db.now()+", ?)", 1, "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func Migrate(db *datastore) error {
|
|
var version int
|
|
var err error
|
|
if db.tableExists("appmigrations") {
|
|
err = db.QueryRow("SELECT MAX(version) FROM appmigrations").Scan(&version)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
log.Info("Initializing appmigrations table...")
|
|
version = 0
|
|
_, err = db.Exec(`CREATE TABLE appmigrations (
|
|
version ` + db.typeInt() + ` NOT NULL,
|
|
migrated ` + db.typeDateTime() + ` NOT NULL,
|
|
result ` + db.typeText() + ` NOT NULL
|
|
) ` + db.engine() + `;`)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if len(migrations[version:]) > 0 {
|
|
for i, m := range migrations[version:] {
|
|
curVer := version + i + 1
|
|
log.Info("Migrating to V%d: %s", curVer, m.Description())
|
|
err = m.Migrate(db)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update migrations table
|
|
_, err = db.Exec("INSERT INTO appmigrations (version, migrated, result) VALUES (?, "+db.now()+", ?)", curVer, "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
} else {
|
|
log.Info("Database up-to-date. No migrations to run.")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (db *datastore) tableExists(t string) bool {
|
|
var dummy string
|
|
var err error
|
|
if db.driverName == driverSQLite {
|
|
err = db.QueryRow("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?", t).Scan(&dummy)
|
|
} else {
|
|
err = db.QueryRow("SHOW TABLES LIKE '" + t + "'").Scan(&dummy)
|
|
}
|
|
switch {
|
|
case err == sql.ErrNoRows:
|
|
return false
|
|
case err != nil:
|
|
log.Error("Couldn't SHOW TABLES: %v", err)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|