Fix database migrations for first-time setup. (#29)
Some checks failed
Run unit tests / run-unit-tests (push) Has been cancelled
Some checks failed
Run unit tests / run-unit-tests (push) Has been cancelled
The database initialization had a number of bugs for the first-time setup. This PR fixes them. Reviewed-on: https://gitea.subcultureofone.org/greg/tkr/pulls/29 Co-authored-by: Greg Sarjeant <greg@subcultureofone.org> Co-committed-by: Greg Sarjeant <greg@subcultureofone.org>
This commit is contained in:
parent
edd7f6effe
commit
4255f46fc7
@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE settings
|
|
||||||
ADD COLUMN strict_accessibility BOOLEAN DEFAULT TRUE;
|
|
@ -15,7 +15,8 @@ CREATE TABLE IF NOT EXISTS settings (
|
|||||||
base_url TEXT NOT NULL,
|
base_url TEXT NOT NULL,
|
||||||
base_path TEXT NOT NULL,
|
base_path TEXT NOT NULL,
|
||||||
items_per_page INTEGER NOT NULL,
|
items_per_page INTEGER NOT NULL,
|
||||||
css_id INTEGER NULL
|
css_id INTEGER NULL,
|
||||||
|
strict_accessibility BOOLEAN DEFAULT TRUE
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS css (
|
CREATE TABLE IF NOT EXISTS css (
|
0
config/migrations/002_add_show_tick_mood_setting.sql
Executable file → Normal file
0
config/migrations/002_add_show_tick_mood_setting.sql
Executable file → Normal file
@ -14,16 +14,19 @@ if (preg_match('/\.php$/', $path)) {
|
|||||||
// Define base paths and load classes
|
// Define base paths and load classes
|
||||||
include_once(dirname(dirname(__FILE__)) . "/config/bootstrap.php");
|
include_once(dirname(dirname(__FILE__)) . "/config/bootstrap.php");
|
||||||
|
|
||||||
|
// validate that necessary directories exist and are writable
|
||||||
|
$fsMgr = new Filesystem();
|
||||||
|
$fsMgr->validate();
|
||||||
|
|
||||||
|
// do any necessary database migrations
|
||||||
|
$dbMgr = new Database();
|
||||||
|
$dbMgr->migrate();
|
||||||
|
|
||||||
// Make sure the initial setup is complete
|
// Make sure the initial setup is complete
|
||||||
// unless we're already heading to setup
|
// unless we're already heading to setup
|
||||||
if (!(preg_match('/setup$/', $path))) {
|
if (!(preg_match('/setup$/', $path))) {
|
||||||
try {
|
try {
|
||||||
// filesystem validation
|
|
||||||
$fsMgr = new Filesystem();
|
|
||||||
$fsMgr->validate();
|
|
||||||
|
|
||||||
// database validation
|
// database validation
|
||||||
$dbMgr = new Database();
|
|
||||||
$dbMgr->validate();
|
$dbMgr->validate();
|
||||||
} catch (SetupException $e) {
|
} catch (SetupException $e) {
|
||||||
$e->handle();
|
$e->handle();
|
||||||
@ -31,7 +34,8 @@ if (!(preg_match('/setup$/', $path))) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize the database
|
// Get a database connection
|
||||||
|
// TODO: Change from static function.
|
||||||
global $db;
|
global $db;
|
||||||
$db = Database::get();
|
$db = Database::get();
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
class Database{
|
class Database{
|
||||||
|
// TODO = Make this not static
|
||||||
public static function get(): PDO {
|
public static function get(): PDO {
|
||||||
try {
|
try {
|
||||||
// SQLite will just create this if it doesn't exist.
|
// SQLite will just create this if it doesn't exist.
|
||||||
@ -19,20 +20,16 @@ class Database{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function validate(): void{
|
public function validate(): void{
|
||||||
$this->validateTables();
|
|
||||||
$this->validateTableContents();
|
$this->validateTableContents();
|
||||||
$this->migrate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The database version will just be an int
|
// The database version will just be an int
|
||||||
// stored as PRAGMA user_version. It will
|
// stored as PRAGMA user_version. It will
|
||||||
// correspond to the most recent migration file applied to the db.
|
// correspond to the most recent migration file applied to the db.
|
||||||
//
|
|
||||||
// I'm starting from 0, so if the user_version is NULL, I'll return -1.
|
|
||||||
private function getVersion(): int {
|
private function getVersion(): int {
|
||||||
$db = self::get();
|
$db = self::get();
|
||||||
|
|
||||||
return $db->query("PRAGMA user_version")->fetchColumn() ?? -1;
|
return $db->query("PRAGMA user_version")->fetchColumn() ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function migrationNumberFromFile(string $filename): int {
|
private function migrationNumberFromFile(string $filename): int {
|
||||||
@ -71,7 +68,7 @@ class Database{
|
|||||||
return $pending;
|
return $pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function migrate(): void {
|
public function migrate(): void {
|
||||||
$migrations = $this->getPendingMigrations();
|
$migrations = $this->getPendingMigrations();
|
||||||
|
|
||||||
if (empty($migrations)) {
|
if (empty($migrations)) {
|
||||||
@ -121,78 +118,6 @@ class Database{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createTables(): void {
|
|
||||||
$db = self::get();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// user table
|
|
||||||
$db->exec("CREATE TABLE IF NOT EXISTS user (
|
|
||||||
id INTEGER PRIMARY KEY,
|
|
||||||
username TEXT NOT NULL,
|
|
||||||
display_name TEXT NOT NULL,
|
|
||||||
password_hash TEXT NULL,
|
|
||||||
about TEXT NULL,
|
|
||||||
website TEXT NULL,
|
|
||||||
mood TEXT NULL
|
|
||||||
)");
|
|
||||||
|
|
||||||
// settings table
|
|
||||||
$db->exec("CREATE TABLE IF NOT EXISTS settings (
|
|
||||||
id INTEGER PRIMARY KEY,
|
|
||||||
site_title TEXT NOT NULL,
|
|
||||||
site_description TEXT NULL,
|
|
||||||
base_url TEXT NOT NULL,
|
|
||||||
base_path TEXT NOT NULL,
|
|
||||||
items_per_page INTEGER NOT NULL,
|
|
||||||
css_id INTEGER NULL
|
|
||||||
)");
|
|
||||||
|
|
||||||
// css table
|
|
||||||
$db->exec("CREATE TABLE IF NOT EXISTS css (
|
|
||||||
id INTEGER PRIMARY KEY,
|
|
||||||
filename TEXT UNIQUE NOT NULL,
|
|
||||||
description TEXT NULL
|
|
||||||
)");
|
|
||||||
|
|
||||||
// mood table
|
|
||||||
$db->exec("CREATE TABLE IF NOT EXISTS emoji(
|
|
||||||
id INTEGER PRIMARY KEY,
|
|
||||||
emoji TEXT UNIQUE NOT NULL,
|
|
||||||
description TEXT NOT NULL
|
|
||||||
)");
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
throw new SetupException(
|
|
||||||
"Table creation failed: " . $e->getMessage(),
|
|
||||||
'table_creation',
|
|
||||||
0,
|
|
||||||
$e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure all tables exist
|
|
||||||
// attempt to create them if they don't
|
|
||||||
private function validateTables(): void {
|
|
||||||
$appTables = array();
|
|
||||||
$appTables[] = "settings";
|
|
||||||
$appTables[] = "user";
|
|
||||||
$appTables[] = "css";
|
|
||||||
$appTables[] = "emoji";
|
|
||||||
|
|
||||||
$db = self::get();
|
|
||||||
|
|
||||||
foreach ($appTables as $appTable){
|
|
||||||
$stmt = $db->prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?");
|
|
||||||
$stmt->execute([$appTable]);
|
|
||||||
if (!$stmt->fetch()){
|
|
||||||
// At least one table doesn't exist.
|
|
||||||
// Try creating tables (hacky, but I have 4 total tables)
|
|
||||||
// Will throw an exception if it fails
|
|
||||||
$this->createTables();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure tables that need to be seeded have been
|
// make sure tables that need to be seeded have been
|
||||||
private function validateTableContents(): void {
|
private function validateTableContents(): void {
|
||||||
$db = self::get();
|
$db = self::get();
|
||||||
@ -201,8 +126,8 @@ class Database{
|
|||||||
$user_count = (int) $db->query("SELECT COUNT(*) FROM user")->fetchColumn();
|
$user_count = (int) $db->query("SELECT COUNT(*) FROM user")->fetchColumn();
|
||||||
$settings_count = (int) $db->query("SELECT COUNT(*) FROM settings")->fetchColumn();
|
$settings_count = (int) $db->query("SELECT COUNT(*) FROM settings")->fetchColumn();
|
||||||
|
|
||||||
// If either required table has no records and we aren't on /admin,
|
// If either required table has no records, throw an exception.
|
||||||
// redirect to /admin to complete setup
|
// This will be caught and redirect to setup.
|
||||||
if ($user_count === 0 || $settings_count === 0){
|
if ($user_count === 0 || $settings_count === 0){
|
||||||
throw new SetupException(
|
throw new SetupException(
|
||||||
"Required tables aren't populated. Please complete setup",
|
"Required tables aren't populated. Please complete setup",
|
||||||
|
@ -67,7 +67,7 @@ class ConfigModel {
|
|||||||
base_path,
|
base_path,
|
||||||
items_per_page,
|
items_per_page,
|
||||||
css_id,
|
css_id,
|
||||||
strict_accessibility,
|
strict_accessibility
|
||||||
)
|
)
|
||||||
VALUES (1, ?, ?, ?, ?, ?, ?, ?)");
|
VALUES (1, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
} else {
|
} else {
|
||||||
|
@ -30,7 +30,7 @@ class UserModel {
|
|||||||
$userCount = (int) $db->query("SELECT COUNT(*) FROM user")->fetchColumn();
|
$userCount = (int) $db->query("SELECT COUNT(*) FROM user")->fetchColumn();
|
||||||
|
|
||||||
if ($userCount === 0){
|
if ($userCount === 0){
|
||||||
$stmt = $db->prepare("INSERT INTO user (id, username, display_name, website, mood) VALUES (1, ?, ?, ?, ?, ?)");
|
$stmt = $db->prepare("INSERT INTO user (id, username, display_name, website, mood) VALUES (1, ?, ?, ?, ?)");
|
||||||
} else {
|
} else {
|
||||||
$stmt = $db->prepare("UPDATE user SET username=?, display_name=?, website=?, mood=? WHERE id=1");
|
$stmt = $db->prepare("UPDATE user SET username=?, display_name=?, website=?, mood=? WHERE id=1");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user