Add SQL schema, CLI commands, and initialization logic

This commit is contained in:
netkas 2024-09-25 00:40:46 -04:00
parent bc6e814c42
commit ff1363c63f
12 changed files with 327 additions and 1 deletions

3
.idea/sqldialects.xml generated
View file

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/password_authentication.sql" dialect="MariaDB" />
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/registered_peers.sql" dialect="MariaDB" />
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/sessions.sql" dialect="MariaDB" />
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/SessionManager.php" dialect="MariaDB" />
<file url="file:///var/ncc/packages/net.nosial.socialbox=1.0.0/bin/src/Socialbox/Managers/SessionManager.php" dialect="MariaDB" />
</component>

View file

@ -6,6 +6,7 @@
<entryData>
<resourceRoots>
<path value="file://$PROJECT_DIR$/examples" />
<path value="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources" />
</resourceRoots>
</entryData>
</entry>

View file

@ -0,0 +1,85 @@
<?php
namespace Socialbox\Classes\CliCommands;
use LogLib\Log;
use PDOException;
use Socialbox\Classes\Configuration;
use Socialbox\Classes\Database;
use Socialbox\Classes\Resources;
use Socialbox\Enums\DatabaseObjects;
use Socialbox\Interfaces\CliCommandInterface;
class InitializeCommand implements CliCommandInterface
{
/**
* Executes the command with the given arguments.
*
* @param array $args An array of arguments to be processed.
* @return int The result of the execution as an integer.
*/
public static function execute(array $args): int
{
if(Configuration::getConfiguration()['instance']['enabled'] === false && !isset($args['force']))
{
Log::info('net.nosial.socialbox', 'Socialbox is disabled. Use --force to initialize the instance or set `instance.enabled` to True in the configuration');
return 1;
}
print("Initializing Socialbox...\n");
foreach(DatabaseObjects::casesOrdered() as $object)
{
Log::verbose('net.nosial.socialbox', "Initializing database object {$object->value}");
try
{
Database::getConnection()->exec(file_get_contents(Resources::getDatabaseResource($object)));
}
catch (PDOException $e)
{
// Check if the error code is for "table already exists"
if ($e->getCode() === '42S01')
{
Log::warning('net.nosial.socialbox', "Database object {$object->value} already exists, skipping...");
continue;
}
else
{
Log::error('net.nosial.socialbox', "Failed to initialize database object {$object->value}: {$e->getMessage()}", $e);
return 1;
}
}
catch(\Exception $e)
{
Log::error('net.nosial.socialbox', "Failed to initialize database object {$object->value}: {$e->getMessage()}", $e);
return 1;
}
}
Log::info('net.nosial.socialbox', 'Socialbox has been initialized successfully');
return 0;
}
/**
* Returns the help message for the command.
*
* @return string The help message.
*/
public static function getHelpMessage(): string
{
return "Initialize Command - Initializes Socialbox for first-runs\n" .
"Usage: socialbox init [arguments]\n\n" .
"Arguments:\n" .
" --force - Forces the initialization process to run even the instance is disabled\n";
}
/**
* Returns a short help message for the command.
*
* @return string
*/
public static function getShortHelpMessage(): string
{
return "Initializes Socialbox for first-runs";
}
}

View file

@ -12,6 +12,9 @@ class Configuration
{
$config = new \ConfigLib\Configuration('net.nosial.socialbox');
// False by default, requires the user to enable it.
$config->setDefault('instance.enabled', false);
$config->setDefault('database.host', '127.0.0.1');
$config->setDefault('database.port', 3306);
$config->setDefault('database.username', 'root');

View file

@ -0,0 +1,15 @@
<?php
namespace Socialbox\Classes;
use InvalidArgumentException;
use Socialbox\Enums\DatabaseObjects;
class Resources
{
public static function getDatabaseResource(DatabaseObjects $object): string
{
$tables_directory = __DIR__ . DIRECTORY_SEPARATOR . 'Resources' . DIRECTORY_SEPARATOR . 'database';
return $tables_directory . DIRECTORY_SEPARATOR . $object->value;
}
}

View file

@ -0,0 +1,18 @@
create table password_authentication
(
peer_uuid varchar(36) not null comment 'The Primary unique Index for the peer UUID'
primary key,
value varchar(128) not null comment 'The hash value of the pasword',
updated timestamp default current_timestamp() not null comment 'The Timestamp for when this record was last updated',
constraint password_authentication_peer_uuid_uindex
unique (peer_uuid) comment 'The Primary unique Index for the peer UUID',
constraint password_authentication_registered_peers_uuid_fk
foreign key (peer_uuid) references registered_peers (uuid)
on update cascade on delete cascade
)
comment 'Table for housing password authentications associated with peers';
create index password_authentication_updated_index
on password_authentication (updated)
comment 'The Indefor the updated timestamp';

View file

@ -0,0 +1,20 @@
create table registered_peers
(
uuid varchar(36) default uuid() not null comment 'The Primary index for the peer uuid'
primary key,
username varchar(255) not null comment 'The Unique username associated with the peer',
flags text null comment 'Comma seprted flags associated with the peer',
registered timestamp default current_timestamp() not null comment 'The Timestamp for when the peer was registered on the network',
constraint registered_peers_pk_2
unique (username) comment 'The unique username for the peer',
constraint registered_peers_username_uindex
unique (username) comment 'The unique username for the peer',
constraint registered_peers_uuid_uindex
unique (uuid) comment 'The Primary index for the peer uuid'
)
comment 'Table for housing registered peers under this network';
create index registered_peers_registered_index
on registered_peers (registered)
comment 'The Index for the reigstered column of the peer';

View file

@ -0,0 +1,24 @@
create table sessions
(
uuid varchar(36) default uuid() not null comment 'The Unique Primary index for the session UUID'
primary key,
authenticated_peer_uuid varchar(36) null comment 'The peer the session is authenticated as, null if the session isn''t authenticated',
public_key blob not null comment 'The client''s public key provided when creating the session',
state enum ('ACTIVE', 'EXPIRED', 'CLOSED') default 'ACTIVE' not null comment 'The status of the session',
created timestamp default current_timestamp() not null comment 'The Timestamp for when the session was last created',
last_request timestamp null comment 'The Timestamp for when the last request was made using this session',
constraint sessions_uuid_uindex
unique (uuid) comment 'The Unique Primary index for the session UUID',
constraint sessions_registered_peers_uuid_fk
foreign key (authenticated_peer_uuid) references registered_peers (uuid)
on update cascade on delete cascade
);
create index sessions_authenticated_peer_index
on sessions (authenticated_peer_uuid)
comment 'The Index for the authenticated peer column';
create index sessions_created_index
on sessions (created)
comment 'The Index for the created column of the session';

View file

@ -0,0 +1,40 @@
<?php
namespace Socialbox\Enums;
use Socialbox\Classes\CliCommands\HelpCommand;
use Socialbox\Classes\CliCommands\InitializeCommand;
enum CliCommands : string
{
case INITIALIZE = 'init';
/**
* Handles the command execution, returns the exit code.
*
* @param array $args An array of arguments to be processed.
* @return int The result of the execution as an integer.
*/
public function handle(array $args): int
{
return match ($this)
{
self::INITIALIZE => InitializeCommand::execute($args),
};
}
public function getHelpMessage(): string
{
return match ($this)
{
self::INITIALIZE => InitializeCommand::getHelpMessage()
};
}
public function getShortHelpMessage(): string
{
return match ($this)
{
self::INITIALIZE => InitializeCommand::getShortHelpMessage()
};
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace Socialbox\Enums;
enum DatabaseObjects : string
{
case PASSWORD_AUTHENTICATION = 'password_authentication.sql';
case REGISTERED_PEERS = 'registered_peers.sql';
case SESSIONS = 'sessions.sql';
/**
* Returns the priority of the database object
*
* @return int The priority of the database object
*/
public function getPriority(): int
{
return match ($this)
{
self::REGISTERED_PEERS => 1,
self::PASSWORD_AUTHENTICATION, self::SESSIONS => 2,
};
}
/**
* Returns an array of cases ordered by their priority.
*
* @return array The array of cases sorted by their priority.
*/
public static function casesOrdered(): array
{
$cases = self::cases();
usort($cases, fn($a, $b) => $a->getPriority() <=> $b->getPriority());
return $cases;
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace Socialbox\Interfaces;
interface CliCommandInterface
{
/**
* Executes the given set of arguments.
*
* @param array $args An array of arguments to be processed.
* @return int The result of the execution as an integer.
*/
public static function execute(array $args): int;
/**
* Returns the help message for the command.
*
* @return string The help message for the command.
*/
public static function getHelpMessage(): string;
/**
* Returns the short help message for the command.
*
* @return string The short help message for the command.
*/
public static function getShortHelpMessage(): string;
}

View file

@ -2,6 +2,9 @@
namespace Socialbox;
use OptsLib\Parse;
use Socialbox\Enums\CliCommands;
class Program
{
/**
@ -12,7 +15,57 @@
*/
public static function main(array $args): int
{
print("Hello World from net.nosial.socialbox!" . PHP_EOL);
// Parse the arguments into a more usable array format
$args = Parse::parseArgument($args);
if(isset($args['help']))
{
if($args['help'] === true)
{
return self::displayHelp();
}
$command = CliCommands::tryFrom($args['help']);
if($command === null)
{
print(sprintf("Unknown command '%s'\n", $args['help']));
return 0;
}
print($command->getHelpMessage());
return 0;
}
if(isset($args[CliCommands::INITIALIZE->value]))
{
return CliCommands::INITIALIZE->handle($args);
}
return self::displayHelp();
}
/**
* Displays the help message for the Socialbox CLI Management Interface.
*
* This method prints out the usage instructions and a list of available commands.
*
* @return int Returns 0 upon successful display of the help message.
*/
private static function displayHelp(): int
{
print("Socialbox - CLI Management Interface\n");
print("Usage: socialbox [command] [arguments]\n\n");
print("Commands:\n");
print(" help - Displays this help message.\n");
foreach(CliCommands::cases() as $command)
{
print(sprintf(" %s - %s\n", $command->value, $command->getShortHelpMessage()));
}
print("Use 'socialbox --help=[command]' for more information about a command.\n");
return 0;
}
}