diff --git a/Makefile b/Makefile index 45a38ff..7144fc9 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ .PHONY: js .PHONY: unit-test-sqlite .PHONY: unit-test-postgres +.PHONY: unit-test-mysql .PHONY: sync-locales .PHONY: find-locales @@ -59,6 +60,9 @@ unit-test-sqlite: unit-test-postgres: @ ./vendor/bin/phpunit -c tests/phpunit.unit.postgres.xml +unit-test-mysql: + @ ./vendor/bin/phpunit -c tests/phpunit.unit.mysql.xml + sync-locales: @ php scripts/sync-locales.php diff --git a/app/check_setup.php b/app/check_setup.php index cbf1a20..070ce56 100644 --- a/app/check_setup.php +++ b/app/check_setup.php @@ -40,6 +40,10 @@ if (DB_DRIVER === 'postgres' && ! extension_loaded('pdo_pgsql')) { die('PHP extension required: pdo_pgsql'); } +if (DB_DRIVER === 'mysql' && ! extension_loaded('pdo_mysql')) { + die('PHP extension required: pdo_mysql'); +} + // Check extension: mbstring (simpleValidator) if (! extension_loaded('mbstring')) { die('PHP extension required: mbstring'); diff --git a/app/core/database.php b/app/core/database.php index dfb8023..55ca8cf 100644 --- a/app/core/database.php +++ b/app/core/database.php @@ -38,6 +38,16 @@ function get_connection_parameters() 'database' => DB_NAME, 'port' => DB_PORT, ); + } elseif (DB_DRIVER === 'mysql') { + require_once __DIR__.'/../schemas/mysql.php'; + $params = array( + 'driver' => 'mysql', + 'hostname' => DB_HOSTNAME, + 'username' => DB_USERNAME, + 'password' => DB_PASSWORD, + 'database' => DB_NAME, + 'port' => DB_PORT, + ); } else { require_once __DIR__.'/../schemas/sqlite.php'; $params = array( diff --git a/app/schemas/mysql.php b/app/schemas/mysql.php new file mode 100644 index 0000000..d666394 --- /dev/null +++ b/app/schemas/mysql.php @@ -0,0 +1,146 @@ +exec('ALTER TABLE feeds ADD COLUMN expiration BIGINT DEFAULT 0'); +} + +function version_2(PDO $pdo) +{ + $pdo->exec('ALTER TABLE feeds ADD COLUMN parsing_error_message VARCHAR(255)'); +} + +function version_1(PDO $pdo) +{ + $pdo->exec("CREATE TABLE users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(50) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + is_admin TINYINT(1) DEFAULT FALSE, + last_login BIGINT, + api_token VARCHAR(255) NOT NULL UNIQUE, + bookmarklet_token VARCHAR(255) NOT NULL UNIQUE, + cronjob_token VARCHAR(255) NOT NULL UNIQUE, + feed_token VARCHAR(255) NOT NULL UNIQUE, + fever_token VARCHAR(255) NOT NULL UNIQUE, + fever_api_key VARCHAR(255) NOT NULL UNIQUE + )"); + + $pdo->exec('CREATE TABLE user_settings ( + "user_id" INT NOT NULL, + "key" VARCHAR(255) NOT NULL, + "value" TEXT NOT NULL, + PRIMARY KEY("user_id", "key"), + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + )'); + + $pdo->exec('CREATE TABLE feeds ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + feed_url VARCHAR(255) NOT NULL, + site_url VARCHAR(255), + title VARCHAR(255) NOT NULL, + last_checked BIGINT DEFAULT 0, + last_modified VARCHAR(255), + etag VARCHAR(255), + enabled TINYINT(1) DEFAULT TRUE, + download_content TINYINT(1) DEFAULT FALSE, + parsing_error INT DEFAULT 0, + rtl TINYINT(1) DEFAULT FALSE, + cloak_referrer TINYINT(1) DEFAULT FALSE, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE(user_id, feed_url) + )'); + + $pdo->exec('CREATE TABLE items ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + feed_id BIGINT NOT NULL, + checksum VARCHAR(255) NOT NULL, + status VARCHAR(10) NOT NULL, + bookmark INT DEFAULT 0, + url TEXT NOT NULL, + title TEXT NOT NULL, + author TEXT, + content TEXT, + updated BIGINT, + enclosure_url TEXT, + enclosure_type VARCHAR(50), + language VARCHAR(50), + rtl TINYINT(1) DEFAULT FALSE, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE, + UNIQUE(feed_id, checksum) + )'); + + $pdo->exec('CREATE TABLE "groups" ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + title VARCHAR(255) NOT NULL, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE(user_id, title) + )'); + + $pdo->exec('CREATE TABLE "feeds_groups" ( + feed_id BIGINT NOT NULL, + group_id INT NOT NULL, + PRIMARY KEY(feed_id, group_id), + FOREIGN KEY(group_id) REFERENCES groups(id) ON DELETE CASCADE, + FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE + )'); + + $pdo->exec('CREATE TABLE favicons ( + id INT AUTO_INCREMENT PRIMARY KEY, + hash VARCHAR(255) UNIQUE, + type VARCHAR(50) + )'); + + $pdo->exec('CREATE TABLE "favicons_feeds" ( + feed_id BIGINT NOT NULL, + favicon_id INT NOT NULL, + PRIMARY KEY(feed_id, favicon_id), + FOREIGN KEY(favicon_id) REFERENCES favicons(id) ON DELETE CASCADE, + FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE + )'); + + $pdo->exec('CREATE TABLE remember_me ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + ip VARCHAR(255), + user_agent VARCHAR(255), + token VARCHAR(255), + sequence VARCHAR(255), + expiration BIGINT, + date_creation BIGINT, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + )'); + + $fever_token = Helper\generate_token(); + $rq = $pdo->prepare(' + INSERT INTO users + (username, password, is_admin, api_token, bookmarklet_token, cronjob_token, feed_token, fever_token, fever_api_key) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + '); + + $rq->execute(array( + 'admin', + password_hash('admin', PASSWORD_BCRYPT), + '1', + Helper\generate_token(), + Helper\generate_token(), + Helper\generate_token(), + Helper\generate_token(), + $fever_token, + md5('admin:'.$fever_token), + )); + + $pdo->exec('CREATE INDEX items_user_status_idx ON items(user_id, status)'); + $pdo->exec('CREATE INDEX items_user_feed_idx ON items(user_id, feed_id)'); +} diff --git a/config.default.php b/config.default.php index 74069f3..8688911 100644 --- a/config.default.php +++ b/config.default.php @@ -15,7 +15,7 @@ define('FAVICON_DIRECTORY', DATA_DIRECTORY.DIRECTORY_SEPARATOR.'favicons'); // FAVICON_URL_PATH => default is data/favicons/ define('FAVICON_URL_PATH', 'data/favicons'); -// Database driver: "sqlite" or "postgres", default is sqlite +// Database driver: "sqlite", "postgres", or "mysql" default is sqlite define('DB_DRIVER', 'sqlite'); // Database connection parameters when Postgres is used diff --git a/docs/config.markdown b/docs/config.markdown index f0bb7bd..62c12a2 100644 --- a/docs/config.markdown +++ b/docs/config.markdown @@ -12,7 +12,7 @@ To override them, rename the file `config.default.php` to `config.php`. Database configuration ---------------------- -By default, Miniflux uses Sqlite but Postgres is also supported. +By default, Miniflux uses Sqlite but Postgres and mysql are also supported. ### Sqlite configuration @@ -43,6 +43,26 @@ define('DB_USERNAME', 'my postgres user'); define('DB_PASSWORD', 'my secret password'); ``` +### MySQL configuration + +Miniflux will creates the schema automatically but not the database itself: + +```sql +CREATE DATABASE miniflux; +``` + +The `config.php` have to be modified as well: + +```php +define('DB_DRIVER', 'mysql'); + +// Replace these values: +define('DB_HOSTNAME', 'localhost'); +define('DB_NAME', 'miniflux'); +define('DB_USERNAME', 'my mysql user'); +define('DB_PASSWORD', 'my secret password'); +``` + List of parameters ------------------ @@ -64,10 +84,10 @@ define('FAVICON_DIRECTORY', DATA_DIRECTORY.DIRECTORY_SEPARATOR.'favicons'); // FAVICON_URL_PATH => default is data/favicons/ define('FAVICON_URL_PATH', 'data/favicons'); -// Database driver: "sqlite" or "postgres", default is sqlite +// Database driver: "sqlite", "postgres", or "mysql" default is sqlite define('DB_DRIVER', 'sqlite'); -// Database connection parameters when Postgres is used +// Database connection parameters when Postgres or Mysql are used define('DB_HOSTNAME', 'localhost'); define('DB_NAME', 'miniflux'); define('DB_USERNAME', 'postgres'); diff --git a/scripts/migrate-db.php b/scripts/migrate-db.php index 31db83f..a02e836 100644 --- a/scripts/migrate-db.php +++ b/scripts/migrate-db.php @@ -32,6 +32,10 @@ function get_last_id(PDO $pdo) $rq = $pdo->prepare('SELECT LASTVAL()'); $rq->execute(); return $rq->fetchColumn(); + } elseif (DB_DRIVER === 'mysql') { + $rq = $pdo->prepare('SELECT LAST_INSERT_ID()'); + $rq->execute(); + return $rq->fetchColumn(); } return $pdo->lastInsertId();