beta4 changes (#26)

These changes are mostly in underlying libraries; however, this now uses the [inspired by F#](https://git.bitbadger.solutions/bit-badger/inspired-by-fsharp) library to handle the feed parsing pipeline and optional return values

Reviewed-on: #26
This commit was merged in pull request #26.
This commit is contained in:
2024-08-06 23:20:17 +00:00
parent dfd9a873f8
commit d06249aecd
35 changed files with 867 additions and 535 deletions
+18 -7
View File
@@ -1,4 +1,16 @@
<?php declare(strict_types=1);
<?php
/**
* Alpha 7 -> Beta 1 Database Update Utility
*
* Between these two versions, the data format changed; this utility migrates existing data to its new format.
*
* _It will be removed in v1.1._
*
* @author Daniel J. Summers <daniel@bitbadger.solutions>
* @license MIT
*/
declare(strict_types=1);
use BitBadger\PDODocument\{Custom, Document, DocumentException};
use BitBadger\PDODocument\Mapper\{ArrayMapper, ExistsMapper};
@@ -8,8 +20,6 @@ require __DIR__ . '/../cli-start.php';
cli_title('DATABASE UPDATE');
//const PDO_DOC_DEBUG_SQL = true;
if ($argc < 2) display_help();
switch ($argv[1]) {
@@ -41,7 +51,8 @@ function display_help(): never
function json_column_exists(): bool
{
try {
$table = Custom::single("SELECT sql FROM sqlite_master WHERE tbl_name='frc_user'", [], new ArrayMapper());
$table = Custom::single("SELECT sql FROM sqlite_master WHERE tbl_name='frc_user'", [], new ArrayMapper())
->getOrDefault(['sql' => '']);
return $table && substr_compare(strtolower($table['sql']), 'data text not null', 0) >= 0;
} catch (DocumentException $ex) {
printfn("ERR $ex");
@@ -80,13 +91,13 @@ function run_update(): void
$users = Custom::list('SELECT * FROM old_user', [], new ArrayMapper());
if (!$users->hasItems()) throw new DocumentException('Could not retrieve users');
foreach ($users->items() as $user) {
Document::insert(Table::USER, new User($user['id'], $user['email'], $user['password']));
Document::insert(Table::User, new User($user['id'], $user['email'], $user['password']));
}
printfn('Migrating feeds...');
$feeds = Custom::list('SELECT * FROM old_feed', [], new ArrayMapper());
if (!$feeds->hasItems()) throw new DocumentException('Could not retrieve feeds');
foreach ($feeds->items() as $feed) {
Document::insert(Table::FEED,
Document::insert(Table::Feed,
new Feed($feed['id'], $feed['user_id'], $feed['url'], $feed['title'], $feed['updated_on'],
$feed['checked_on']));
}
@@ -94,7 +105,7 @@ function run_update(): void
$items = Custom::list('SELECT * FROM old_item', [], new ArrayMapper());
if (!$items->hasItems()) throw new DocumentException('Could not retrieve items');
foreach ($items->items() as $item) {
Document::insert(Table::ITEM,
Document::insert(Table::Item,
new Item($item['id'], $item['feed_id'], $item['title'], $item['item_guid'], $item['item_link'],
$item['published_on'], $item['updated_on'], $item['content'], $item['is_read'],
$item['is_bookmarked']));
+20 -6
View File
@@ -1,5 +1,16 @@
<?php declare(strict_types=1);
<?php
/**
* Feed Refresh Utility
*
* This will refresh all known feeds in the database; it is suitable for executing via cron or as a scheduled task
*
* @author Daniel J. Summers <daniel@bitbadger.solutions>
* @license MIT
*/
declare(strict_types=1);
use BitBadger\InspiredByFSharp\Result;
use BitBadger\PDODocument\{DocumentException, Find};
use FeedReaderCentral\{Feed, Table, User};
@@ -34,17 +45,20 @@ function refresh_all(): void
{
try {
$users = [];
foreach (Feed::retrieveAll()->items() as /** @var Feed $feed */ $feed) {
Feed::retrieveAll()->iter(function (Feed $feed) use (&$users) {
$result = Feed::refreshFeed($feed->id, $feed->url);
$userKey = "$feed->user_id";
if (!key_exists($userKey, $users)) $users[$userKey] = Find::byId(Table::USER, $feed->user_id, User::class);
if (array_key_exists('error', $result)) {
if (!key_exists($userKey, $users)) {
$users[$userKey] = Find::byId(Table::User, $feed->user_id, User::class)
->getOrDefault(new User(email: 'user-not-found'));
}
if ($result->isError()) {
printfn('ERR (%s) %s', $users[$userKey]->email, $feed->url);
printfn(' %s', $result['error']);
printfn(' %s', $result->getError());
} else {
printfn('OK (%s) %s', $users[$userKey]->email, $feed->url);
}
}
});
printfn(PHP_EOL . 'All feeds refreshed');
} catch (DocumentException $ex) {
printfn("ERR $ex");
+11 -1
View File
@@ -1,4 +1,14 @@
<?php declare(strict_types=1);
<?php
/**
* Search Maintenance Utility
*
* This allows on-demand refreshing of the search index (should be unnecessary in normal use)
*
* @author Daniel J. Summers <daniel@bitbadger.solutions>
* @license MIT
*/
declare(strict_types=1);
use BitBadger\PDODocument\{Custom, DocumentException};
use BitBadger\PDODocument\Mapper\ExistsMapper;
+31 -17
View File
@@ -1,5 +1,17 @@
<?php declare(strict_types=1);
<?php
/**
* User Maintenance Utility
*
* This provides several user maintenance functions for Feed Reader Central; none of these are available through the web
* interface
*
* @author Daniel J. Summers <daniel@bitbadger.solutions>
* @license MIT
*/
declare(strict_types=1);
use BitBadger\InspiredByFSharp\Option;
use BitBadger\PDODocument\{Count, Custom, Delete, DocumentException, Field, Parameters, Patch, Query};
use FeedReaderCentral\{Security, Table, User};
@@ -36,10 +48,10 @@ switch ($argv[1]) {
printfn('Missing parameters: set-single-password requires a new password');
exit(-1);
}
set_password(Security::SINGLE_USER_EMAIL, $argv[2]);
set_password(Security::SingleUserEmail, $argv[2]);
break;
case 'reset-single-password':
set_password(Security::SINGLE_USER_EMAIL, Security::SINGLE_USER_PASSWORD);
set_password(Security::SingleUserEmail, Security::SingleUserPassword);
break;
case 'migrate-single-user':
if ($argc < 4) {
@@ -49,7 +61,7 @@ switch ($argv[1]) {
migrate_single_user();
break;
case 'remove-single-user':
delete_user(Security::SINGLE_USER_EMAIL);
delete_user(Security::SingleUserEmail);
break;
default:
printfn('Unrecognized option "%s"', $argv[1]);
@@ -92,7 +104,7 @@ function add_user(): void
try {
// Ensure there is not already a user with this e-mail address
$user = User::findByEmail($argv[2]);
if ($user) {
if ($user->isSome()) {
printfn('A user with e-mail address "%s" already exists', $argv[2]);
return;
}
@@ -113,7 +125,7 @@ function add_user(): void
*/
function display_user(string $email): string
{
return $email == Security::SINGLE_USER_EMAIL ? 'single-user mode user' : "user \"$email\"";
return $email === Security::SingleUserEmail ? 'single-user mode user' : "user \"$email\"";
}
/**
@@ -126,14 +138,14 @@ function set_password(string $email, string $password): void
// Ensure this user exists
$user = User::findByEmail($email);
if (!$user) {
if ($user->isNone()) {
printfn('No %s exists', $displayUser);
return;
}
Security::updatePassword($email, $password);
$msg = $email == Security::SINGLE_USER_EMAIL && $password == Security::SINGLE_USER_PASSWORD
$msg = $email === Security::SingleUserEmail && $password === Security::SingleUserPassword
? 'reset' : sprintf('set to "%s"', $password);
printfn('%s password %s successfully', init_cap($displayUser), $msg);
} catch (DocumentException $ex) {
@@ -152,14 +164,15 @@ function delete_user(string $email): void
$displayUser = display_user($email);
// Get the user for the provided e-mail address
$user = User::findByEmail($email);
if (!$user) {
$tryUser = User::findByEmail($email);
if ($tryUser->isNone()) {
printfn('No %s exists', $displayUser);
return;
}
$user = $tryUser->get();
try {
$feedCount = Count::byFields(Table::FEED, [Field::EQ('user_id', $user->id)]);
$feedCount = Count::byFields(Table::Feed, [Field::EQ('user_id', $user->id)]);
} catch (DocumentException $ex) {
printfn("$ex");
return;
@@ -174,10 +187,10 @@ function delete_user(string $email): void
try {
$fields = [Field::EQ('user_id', $user->id, ':user')];
Custom::nonQuery(
'DELETE FROM ' . Table::ITEM . " WHERE data->>'feed_id' IN (SELECT data->>'id' FROM " . Table::FEED
'DELETE FROM ' . Table::Item . " WHERE data->>'feed_id' IN (SELECT data->>'id' FROM " . Table::Feed
. ' WHERE ' . Query::whereByFields($fields) . ')', Parameters::addFields($fields, []));
Delete::byFields(Table::FEED, $fields);
Delete::byId(Table::USER, $user->id);
Delete::byFields(Table::Feed, $fields);
Delete::byId(Table::User, $user->id);
printfn('%s deleted successfully', init_cap($displayUser));
} catch (DocumentException $ex) {
@@ -196,13 +209,14 @@ function migrate_single_user(): void
global $argv;
try {
if (!$single = User::findByEmail(Security::SINGLE_USER_EMAIL)) {
$single = User::findByEmail(Security::SingleUserEmail);
if ($single->isNone()) {
printfn('There is no single-user mode user to be migrated');
return;
}
Patch::byId(Table::USER, $single->id,
['email' => $argv[2], 'password' => password_hash($argv[3], Security::PW_ALGORITHM)]);
Patch::byId(Table::User, $single->get()->id,
['email' => $argv[2], 'password' => password_hash($argv[3], Security::PasswordAlgorithm)]);
printfn('The single user has been moved to "%s", with password "%s"', $argv[2], $argv[3]);
} catch (DocumentException $ex) {