wip: 2025-01-30T09:07:10+0100 (1738224430)

This commit is contained in:
Victor Westerlund 2025-01-30 16:08:32 +01:00
parent 7313b9b0cd
commit a6018a8678
51 changed files with 699 additions and 531 deletions

View file

@ -1,17 +1,20 @@
date_time_zone = "Europe/Stockholm" [client_api]
base_url = ""
api_key = ""
verify_peer = true
[api_database] [client_time_available]
time_zone = "Europe/Stockholm"
available_to_hour = 0;
reply_average_hours = 0;
available_from_hour = 0;
[server_database]
host = "" host = ""
user = "" user = ""
pass = "" pass = ""
db = "" db = ""
[api_forgejo] [server_forgejo]
base_url = "" base_url = ""
cache_file = ""
scan_profiles = "" scan_profiles = ""
[api_client]
base_url = "https://api.vlw.one/"
api_key = ""
verify_peer = 0

19
composer.lock generated
View file

@ -11,15 +11,9 @@
"version": "dev-master", "version": "dev-master",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/VictorWesterlund/reflect-client-php.git", "url": "https://codeberg.org/reflect/client-php",
"reference": "89a8c041044c8c60cefafc4716d5d61b96c43e06" "reference": "89a8c041044c8c60cefafc4716d5d61b96c43e06"
}, },
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/VictorWesterlund/reflect-client-php/zipball/89a8c041044c8c60cefafc4716d5d61b96c43e06",
"reference": "89a8c041044c8c60cefafc4716d5d61b96c43e06",
"shasum": ""
},
"default-branch": true, "default-branch": true,
"type": "library", "type": "library",
"autoload": { "autoload": {
@ -38,10 +32,6 @@
} }
], ],
"description": "Extendable PHP interface for communicating with Reflect API over HTTP or UNIX sockets", "description": "Extendable PHP interface for communicating with Reflect API over HTTP or UNIX sockets",
"support": {
"issues": "https://github.com/VictorWesterlund/reflect-client-php/issues",
"source": "https://github.com/VictorWesterlund/reflect-client-php/tree/3.0.6"
},
"time": "2024-04-06T14:55:04+00:00" "time": "2024-04-06T14:55:04+00:00"
}, },
{ {
@ -78,7 +68,7 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://codeberg.org/vlw/php-mysql", "url": "https://codeberg.org/vlw/php-mysql",
"reference": "64c7bae3cf6124dcb64c9e8ef93425be3602e82a" "reference": "c64eb96049907da60dc9f237d26aef0e531b0015"
}, },
"default-branch": true, "default-branch": true,
"type": "library", "type": "library",
@ -98,7 +88,7 @@
} }
], ],
"description": "Abstraction library for common MySQL/MariaDB DML operations with php-mysqli", "description": "Abstraction library for common MySQL/MariaDB DML operations with php-mysqli",
"time": "2025-01-16T13:53:30+00:00" "time": "2025-01-30T09:33:10+00:00"
}, },
{ {
"name": "vlw/xenum", "name": "vlw/xenum",
@ -135,7 +125,8 @@
"stability-flags": { "stability-flags": {
"reflect/client": 20, "reflect/client": 20,
"reflect/plugin-rules": 20, "reflect/plugin-rules": 20,
"vlw/mysql": 20 "vlw/mysql": 20,
"vlw/xenum": 20
}, },
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,

View file

@ -1,21 +1,40 @@
<?php <?php
use Reflect\Call; use ReflectRules\Ruleset;
use Reflect\{Response, Path}; use Reflect\{Response, Path, Call};
const MSG_OK = "Cache file deleted"; use VLW\API\Endpoints;
const MSG_FAIL = "Cache file does not exist or can't be deleted"; use VLW\Database\Database;
use VLW\Database\Tables\About\LanguagesTable;
class DELETE_AboutLanguages { require_once Path::root("src/API/Endpoints.php");
public function __construct() {} require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Tables/About/Languages.php");
class DELETE_AboutLanguages extends Database {
protected readonly Ruleset $ruleset;
public function __construct() {
$this->ruleset = new Ruleset(strict: true);
$this->ruleset->validate_or_exit();
parent::__construct();
}
private function languages(): array {
$resp = (new Call(Endpoints::ABOUT_LANGUAGES->value))->get();
return array_column($resp->output(), LanguagesTable::ID->value);
}
// Delete languages cache file if it exists // Delete languages cache file if it exists
public function main(): Response { public function main(): Response {
// Bail out if cache is not used $this->db->for(LanguagesTable::NAME);
if (empty($_ENV["forgejo_languages"]["cache_file"])) {
return new Response(MSG_OK); foreach ($this->languages() as $language){
$this->db->delete([LanguagesTable::ID->value => $language]);
} }
return unlink($_ENV["forgejo_languages"]["cache_file"]) ? new Response(MSG_OK) : new Response(MSG_FAIL, 404); return new Response();
} }
} }

View file

@ -1,48 +1,54 @@
<?php <?php
use Reflect\Call; use vlw\MySQL\Order;
use Reflect\{Response, Path}; use Reflect\{Response, Path, Call};
use ReflectRules\{Ruleset, Rules, Type}; use ReflectRules\{Ruleset, Rules, Type};
use VLW\API\Endpoints; use VLW\API\Endpoints;
use VLW\Database\Database;
use const VLW\FORGEJO_UPDATE_CACHE_PARAM;
use VLW\Database\Tables\About\LanguagesTable;
require_once Path::root("src/Endpoints.php"); require_once Path::root("src/Consts.php");
require_once Path::root("src/API/Endpoints.php");
require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Tables/About/Languages.php");
const PARM_FORCE_RECACHE = "force_recache"; class GET_AboutLanguages extends Database {
protected readonly Ruleset $ruleset;
class GET_AboutLanguages {
protected Ruleset $ruleset;
public function __construct() { public function __construct() {
$this->ruleset = new Ruleset(strict: true); $this->ruleset = new Ruleset(strict: true);
$this->ruleset->GET([ $this->ruleset->GET([
(new Rules(PARM_FORCE_RECACHE)) (new Rules(LanguagesTable::ID->value))
->type(Type::STRING)
->min(1)
->max(parent::SIZE_VARCHAR),
(new Rules(LanguagesTable::BYTES->value))
->type(Type::NUMBER)
->min(1)
->max(parent::SIZE_UINT32),
(new Rules(FORGEJO_UPDATE_CACHE_PARAM))
->type(Type::BOOLEAN) ->type(Type::BOOLEAN)
->default(false) ->default(false)
]); ]);
$this->ruleset->validate_or_exit(); $this->ruleset->validate_or_exit();
}
private static function cache_exists(): bool { parent::__construct();
return file_exists($_ENV["forgejo_languages"]["cache_file"]);
}
private static function load_cache(): array {
return json_decode(file_get_contents($_ENV["forgejo_languages"]["cache_file"]), true);
} }
public function main(): Response { public function main(): Response {
// Delete cache file if force flag is set // Refresh the language cache if param is set
if ($_GET[PARM_FORCE_RECACHE]) { if ($_GET[FORGEJO_UPDATE_CACHE_PARAM]) {
(new Call(Endpoints::ABOUT_LANGUAGES->value))->delete(); (new Call(Endpoints::ABOUT_LANGUAGES->value))->post();
} }
return self::cache_exists() return $this->list(LanguagesTable::NAME, LanguagesTable::values(), [
// Return languages from cache LanguagesTable::BYTES->value => Order::DESC
? new Response(self::load_cache()) ]);
// Fetch and return languages (and generate cache file if enabled)
: new Response((new Call(Endpoints::ABOUT_LANGUAGES->value))->post());
} }
} }

View file

@ -1,19 +1,29 @@
<?php <?php
use Reflect\Call; use ReflectRules\Ruleset;
use Reflect\{Response, Path}; use Reflect\{Response, Path, Call};
const ERRNO_CACHE_FAILED = 0; use VLW\API\Endpoints;
use VLW\Database\Database;
use VLW\Database\Tables\About\LanguagesTable;
use const VLW\{FORGEJO_ENDPOINT_USER, FORGEJO_ENDPOINT_SEARCH};
const FORGEJO_ENDPOINT_USER = "/api/v1/users/%s"; require_once Path::root("src/Consts.php");
const FORGEJO_ENDPOINT_SEARCH = "/api/v1/repos/search?uid=%s"; require_once Path::root("src/API/Endpoints.php");
require_once Path::root("src/Database/Tables/About/Languages.php");
class POST_AboutLanguages extends Database {
protected readonly Ruleset $ruleset;
class POST_AboutLanguages {
private array $errors = [];
// Tally of all languages used in all configured repositories // Tally of all languages used in all configured repositories
private array $languages = []; private array $languages = [];
public function __construct() {} public function __construct() {
$this->ruleset = new Ruleset(strict: true);
$this->ruleset->validate_or_exit();
parent::__construct();
}
// Fetch JSON from URL // Fetch JSON from URL
private static function fetch_json(string $url): array { private static function fetch_json(string $url): array {
@ -22,20 +32,23 @@
// Fetch JSON from a Forgejo endpoint // Fetch JSON from a Forgejo endpoint
private static function fetch_endpoint(string $endpoint): array { private static function fetch_endpoint(string $endpoint): array {
$url = $_ENV["forgejo"]["base_url"] . $endpoint; $url = $_ENV["server_forgejo"]["base_url"] . $endpoint;
return self::fetch_json($url); return self::fetch_json($url);
} }
// Write $this->languages to a JSON file // Write $this->languages to a JSON file
private function cache_languages(): int|bool { private function cache_languages(): void {
$cache_filename = $_ENV["forgejo_languages"]["cache_file"]; // Delete existing cache
(new Call(Endpoints::ABOUT_LANGUAGES->value))->delete();
// Bail out if cache file is not configured $this->db->for(LanguagesTable::NAME);
if (empty($cache_filename)) {
return true; foreach ($this->languages as $language => $bytes) {
$this->db->insert([
LanguagesTable::ID->value => $language,
LanguagesTable::BYTES->value => $bytes
]);
} }
return file_put_contents($cache_filename, json_encode($this->languages));
} }
// Fetch and add languages to total from a fully-qualified Forgejo URL // Fetch and add languages to total from a fully-qualified Forgejo URL
@ -70,13 +83,10 @@
// Add languages from all public repositories for profiles in config // Add languages from all public repositories for profiles in config
private function add_repositories_from_config_profiles(): void { private function add_repositories_from_config_profiles(): void {
foreach(explode(",", $_ENV["forgejo_languages"]["scan_profiles"]) as $profile) { foreach(explode(",", $_ENV["server_forgejo"]["scan_profiles"]) as $profile) {
// Resolve user data from username // Resolve user data from username
$user = self::fetch_endpoint(sprintf(FORGEJO_ENDPOINT_USER, $profile)); $user = self::fetch_endpoint(sprintf(FORGEJO_ENDPOINT_USER, $profile));
$this->add_public_repositores($user["id"]);
if (!$this->add_public_repositores($user["id"])) {
$this->errors[] = $profile;
}
} }
} }
@ -86,11 +96,7 @@
// Sort langauges bytes tally by largest in descending order // Sort langauges bytes tally by largest in descending order
arsort($this->languages); arsort($this->languages);
// Save languages to cache $this->cache_languages();
if (!$this->cache_languages()) {
$this->errors[] = ERRNO_CACHE_FAILED;
}
return new Response($this->languages); return new Response($this->languages);
} }
} }

View file

@ -7,7 +7,7 @@
use VLW\Database\Tables\Battlestation\Config\ConfigModel; use VLW\Database\Tables\Battlestation\Config\ConfigModel;
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Battlestation/Config/Config.php"); require_once Path::root("src/Database/Tables/Battlestation/Config/Config.php");
class GET_Battlestation extends Database { class GET_Battlestation extends Database {
protected Ruleset $ruleset; protected Ruleset $ruleset;
@ -27,7 +27,7 @@
(new Rules(ConfigModel::FRIENDLY_NAME->value)) (new Rules(ConfigModel::FRIENDLY_NAME->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH) ->max(parent::SIZE_VARCHAR)
]); ]);
parent::__construct(Databases::BATTLESTATION, $this->ruleset); parent::__construct(Databases::BATTLESTATION, $this->ruleset);

View file

@ -8,8 +8,8 @@
use VLW\Database\Tables\Battlestation\Config\ChassisMbTable; use VLW\Database\Tables\Battlestation\Config\ChassisMbTable;
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Battlestation/Chassis.php"); require_once Path::root("src/Database/Tables/Battlestation/Chassis.php");
require_once Path::root("src/Database/Models/Battlestation/Config/ChassisMb.php"); require_once Path::root("src/Database/Tables/Battlestation/Config/ChassisMb.php");
class GET_BattlestationChassis extends Database { class GET_BattlestationChassis extends Database {
private const REL_MOTHERBOARDS = "motherboards"; private const REL_MOTHERBOARDS = "motherboards";
@ -31,12 +31,12 @@
(new Rules(ChassisTable::VENDOR_NAME->value)) (new Rules(ChassisTable::VENDOR_NAME->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(ChassisTable::VENDOR_MODEL->value)) (new Rules(ChassisTable::VENDOR_MODEL->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(ChassisTable::STORAGE_TWOINCHFIVE->value)) (new Rules(ChassisTable::STORAGE_TWOINCHFIVE->value))
->type(Type::NUMBER) ->type(Type::NUMBER)

View file

@ -10,8 +10,8 @@
use VLW\Database\Tables\Battlestation\Config\MbCpuCoolerModel; use VLW\Database\Tables\Battlestation\Config\MbCpuCoolerModel;
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Battlestation/Coolers.php"); require_once Path::root("src/Database/Tables/Battlestation/Coolers.php");
require_once Path::root("src/Database/Models/Battlestation/Config/MbCpuCooler.php"); require_once Path::root("src/Database/Tables/Battlestation/Config/MbCpuCooler.php");
class GET_BattlestationCoolers extends Database { class GET_BattlestationCoolers extends Database {
private const REL_MOTHERBOARDS = "motherboards"; private const REL_MOTHERBOARDS = "motherboards";
@ -48,12 +48,12 @@
(new Rules(CoolerModel::VENDOR_NAME->value)) (new Rules(CoolerModel::VENDOR_NAME->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(CoolerModel::VENDOR_MODEL->value)) (new Rules(CoolerModel::VENDOR_MODEL->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(CoolerModel::IS_RETIRED->value)) (new Rules(CoolerModel::IS_RETIRED->value))
->type(Type::BOOLEAN) ->type(Type::BOOLEAN)

View file

@ -11,8 +11,8 @@
use VLW\Database\Tables\Battlestation\Config\MbCpuCoolerModel; use VLW\Database\Tables\Battlestation\Config\MbCpuCoolerModel;
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Battlestation/Cpu.php"); require_once Path::root("src/Database/Tables/Battlestation/Cpu.php");
require_once Path::root("src/Database/Models/Battlestation/Config/MbCpuCooler.php"); require_once Path::root("src/Database/Tables/Battlestation/Config/MbCpuCooler.php");
class GET_BattlestationCpu extends Database { class GET_BattlestationCpu extends Database {
private const REL_MOTHERBOARDS = "motherboards"; private const REL_MOTHERBOARDS = "motherboards";
@ -57,12 +57,12 @@
(new Rules(CpuTable::VENDOR_NAME->value)) (new Rules(CpuTable::VENDOR_NAME->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(CpuTable::VENDOR_MODEL->value)) (new Rules(CpuTable::VENDOR_MODEL->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(CpuTable::IS_RETIRED->value)) (new Rules(CpuTable::IS_RETIRED->value))
->type(Type::BOOLEAN) ->type(Type::BOOLEAN)

View file

@ -12,8 +12,8 @@
use VLW\Database\Tables\Battlestation\Config\MbDramTable; use VLW\Database\Tables\Battlestation\Config\MbDramTable;
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Battlestation/Dram.php"); require_once Path::root("src/Database/Tables/Battlestation/Dram.php");
require_once Path::root("src/Database/Models/Battlestation/Config/MbDram.php"); require_once Path::root("src/Database/Tables/Battlestation/Config/MbDram.php");
class GET_BattlestationDram extends Database { class GET_BattlestationDram extends Database {
private const REL_MOTHERBOARDS = "motherboards"; private const REL_MOTHERBOARDS = "motherboards";
@ -55,12 +55,12 @@
(new Rules(DramTable::VENDOR_NAME->value)) (new Rules(DramTable::VENDOR_NAME->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(DramTable::VENDOR_MODEL->value)) (new Rules(DramTable::VENDOR_MODEL->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(DramTable::IS_RETIRED->value)) (new Rules(DramTable::IS_RETIRED->value))
->type(Type::BOOLEAN) ->type(Type::BOOLEAN)

View file

@ -8,8 +8,8 @@
use VLW\Database\Tables\Battlestation\Config\MbGpuTable; use VLW\Database\Tables\Battlestation\Config\MbGpuTable;
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Battlestation/Gpu.php"); require_once Path::root("src/Database/Tables/Battlestation/Gpu.php");
require_once Path::root("src/Database/Models/Battlestation/Config/MbGpu.php"); require_once Path::root("src/Database/Tables/Battlestation/Config/MbGpu.php");
class GET_BattlestationGpu extends Database { class GET_BattlestationGpu extends Database {
private const REL_MOTHERBOARDS = "motherboards"; private const REL_MOTHERBOARDS = "motherboards";
@ -35,22 +35,22 @@
(new Rules(GpuTable::VENDOR_NAME->value)) (new Rules(GpuTable::VENDOR_NAME->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(GpuTable::VENDOR_MODEL->value)) (new Rules(GpuTable::VENDOR_MODEL->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(GpuTable::VENDOR_CHIP_NAME->value)) (new Rules(GpuTable::VENDOR_CHIP_NAME->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(GpuTable::VENDOR_CHIP_MODEL->value)) (new Rules(GpuTable::VENDOR_CHIP_MODEL->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(GpuTable::IS_RETIRED->value)) (new Rules(GpuTable::IS_RETIRED->value))
->type(Type::BOOLEAN) ->type(Type::BOOLEAN)

View file

@ -18,13 +18,13 @@
}; };
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Battlestation/Mb.php"); require_once Path::root("src/Database/Tables/Battlestation/Mb.php");
require_once Path::root("src/Database/Models/Battlestation/Config/MbPsu.php"); require_once Path::root("src/Database/Tables/Battlestation/Config/MbPsu.php");
require_once Path::root("src/Database/Models/Battlestation/Config/MbGpu.php"); require_once Path::root("src/Database/Tables/Battlestation/Config/MbGpu.php");
require_once Path::root("src/Database/Models/Battlestation/Config/MbDram.php"); require_once Path::root("src/Database/Tables/Battlestation/Config/MbDram.php");
require_once Path::root("src/Database/Models/Battlestation/Config/MbStorage.php"); require_once Path::root("src/Database/Tables/Battlestation/Config/MbStorage.php");
require_once Path::root("src/Database/Models/Battlestation/Config/ChassisMb.php"); require_once Path::root("src/Database/Tables/Battlestation/Config/ChassisMb.php");
require_once Path::root("src/Database/Models/Battlestation/Config/MbCpuCooler.php"); require_once Path::root("src/Database/Tables/Battlestation/Config/MbCpuCooler.php");
class GET_BattlestationMb extends Database { class GET_BattlestationMb extends Database {
private const REL_CPU = "cpus"; private const REL_CPU = "cpus";
@ -54,30 +54,30 @@
(new Rules(MbTable::VENDOR_NAME->value)) (new Rules(MbTable::VENDOR_NAME->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(MbTable::VENDOR_MODEL->value)) (new Rules(MbTable::VENDOR_MODEL->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(MbTable::NETWORK_ETHERNET->value)) (new Rules(MbTable::NETWORK_ETHERNET->value))
->type(Type::NULL) ->type(Type::NULL)
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(MbTable::NETWORK_WLAN->value)) (new Rules(MbTable::NETWORK_WLAN->value))
->type(Type::NULL) ->type(Type::NULL)
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(MbTable::NETWORK_BLUETOOTH->value)) (new Rules(MbTable::NETWORK_BLUETOOTH->value))
->type(Type::NULL) ->type(Type::NULL)
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(MbTable::IS_RETIRED->value)) (new Rules(MbTable::IS_RETIRED->value))
->type(Type::BOOLEAN) ->type(Type::BOOLEAN)

View file

@ -11,8 +11,8 @@
use VLW\Database\Tables\Battlestation\Config\MbPsuTable; use VLW\Database\Tables\Battlestation\Config\MbPsuTable;
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Battlestation/Psu.php"); require_once Path::root("src/Database/Tables/Battlestation/Psu.php");
require_once Path::root("src/Database/Models/Battlestation/Config/MbPsu.php"); require_once Path::root("src/Database/Tables/Battlestation/Config/MbPsu.php");
class GET_BattlestationPsu extends Database { class GET_BattlestationPsu extends Database {
private const REL_MOTHERBOARDS = "motherboards"; private const REL_MOTHERBOARDS = "motherboards";
@ -34,7 +34,7 @@
(new Rules(PsuTable::POWER->value)) (new Rules(PsuTable::POWER->value))
->type(Type::NUMBER) ->type(Type::NUMBER)
->min(1) ->min(1)
->max(parent::MYSQL_INT_MAX_LENGTH), ->max(parent::SIZE_UINT8),
(new Rules(PsuTable::EIGHTYPLUS_RATING->value)) (new Rules(PsuTable::EIGHTYPLUS_RATING->value))
->type(Type::ENUM, EightyplusRatingEnum::names()), ->type(Type::ENUM, EightyplusRatingEnum::names()),
@ -42,12 +42,12 @@
(new Rules(PsuTable::VENDOR_NAME->value)) (new Rules(PsuTable::VENDOR_NAME->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(PsuTable::VENDOR_MODEL->value)) (new Rules(PsuTable::VENDOR_MODEL->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(PsuTable::IS_RETIRED->value)) (new Rules(PsuTable::IS_RETIRED->value))
->type(Type::BOOLEAN) ->type(Type::BOOLEAN)

View file

@ -13,8 +13,8 @@
use VLW\Database\Tables\Battlestation\Config\MbStorageTable; use VLW\Database\Tables\Battlestation\Config\MbStorageTable;
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Battlestation/Storage.php"); require_once Path::root("src/Database/Tables/Battlestation/Storage.php");
require_once Path::root("src/Database/Models/Battlestation/Config/MbStorage.php"); require_once Path::root("src/Database/Tables/Battlestation/Config/MbStorage.php");
class GET_BattlestationStorage extends Database { class GET_BattlestationStorage extends Database {
private const REL_MOTHERBOARDS = "motherboards"; private const REL_MOTHERBOARDS = "motherboards";
@ -39,7 +39,7 @@
(new Rules(StorageTable::DISK_SIZE->value)) (new Rules(StorageTable::DISK_SIZE->value))
->type(Type::NUMBER) ->type(Type::NUMBER)
->min(1) ->min(1)
->max(parent::MYSQL_INT_MAX_LENGTH), ->max(parent::SIZE_UINT8),
(new Rules(StorageTable::DISK_INTERFACE->value)) (new Rules(StorageTable::DISK_INTERFACE->value))
->type(Type::ENUM, StorageDiskInterfaceEnum::names()), ->type(Type::ENUM, StorageDiskInterfaceEnum::names()),
@ -50,12 +50,12 @@
(new Rules(StorageTable::VENDOR_NAME->value)) (new Rules(StorageTable::VENDOR_NAME->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(StorageTable::VENDOR_MODEL->value)) (new Rules(StorageTable::VENDOR_MODEL->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(StorageTable::IS_RETIRED->value)) (new Rules(StorageTable::IS_RETIRED->value))
->type(Type::BOOLEAN) ->type(Type::BOOLEAN)

View file

@ -7,7 +7,7 @@
use VLW\Database\Tables\Messages\MessagesTable; use VLW\Database\Tables\Messages\MessagesTable;
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Messages/Messages.php"); require_once Path::root("src/Database/Tables/Messages/Messages.php");
class POST_Messages extends Database { class POST_Messages extends Database {
protected Ruleset $ruleset; protected Ruleset $ruleset;
@ -25,23 +25,19 @@
->required() ->required()
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_TEXT_MAX_LENGTH) ->max(parent::SIZE_TEXT)
]); ]);
$ruleset->validate_or_exit(); $this->ruleset->validate_or_exit();
parent::__construct(); parent::__construct();
} }
public function main(): Response { public function main(): Response {
// Use copy of request body as entity $_POST[MessagesTable::TIMESTAMP_CREATED->value] = time();
$entity = $_POST;
$entity[MessagesTable::ID->value] = parent::gen_uuid4(); return $this->db->for(MessagesTable::NAME)->insert($_POST) === true
$entity[MessagesTable::DATE_CREATED->value] = time(); ? new Response(null, 201)
: new Response("Failed to send message", 500);
return $this->db->for(MessagesTable::NAME)->insert($entity) === true
? new Response($entity[MessagesTable::ID->value], 201)
: new Response("Failed to create message", 500);
} }
} }

View file

@ -11,7 +11,7 @@
require_once Path::root("src/Endpoints.php"); require_once Path::root("src/Endpoints.php");
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Work/Work.php"); require_once Path::root("src/Database/Tables/Work/Work.php");
class GET_Search extends Database { class GET_Search extends Database {
const GET_QUERY = "q"; const GET_QUERY = "q";
@ -26,10 +26,10 @@
->required() ->required()
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH) ->max(parent::SIZE_VARCHAR)
]); ]);
$ruleset->validate_or_exit(); $this->ruleset->validate_or_exit();
parent::__construct(); parent::__construct();
} }

View file

@ -9,7 +9,7 @@
require_once Path::root("src/Endpoints.php"); require_once Path::root("src/Endpoints.php");
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Work.php"); require_once Path::root("src/Database/Tables/Work.php");
class DELETE_Work extends Database { class DELETE_Work extends Database {
protected Ruleset $ruleset; protected Ruleset $ruleset;
@ -21,17 +21,17 @@
(new Rules(WorkTable::ID->value)) (new Rules(WorkTable::ID->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(WorkTable::TITLE->value)) (new Rules(WorkTable::TITLE->value))
->type(Type::STRING) ->type(Type::STRING)
->min(3) ->min(3)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(WorkTable::SUMMARY->value)) (new Rules(WorkTable::SUMMARY->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_TEXT_MAX_LENGTH), ->max(parent::SIZE_TEXT),
(new Rules(WorkTable::IS_LISTED->value)) (new Rules(WorkTable::IS_LISTED->value))
->type(Type::BOOLEAN), ->type(Type::BOOLEAN),
@ -39,15 +39,15 @@
(new Rules(WorkTable::DATE_MODIFIED->value)) (new Rules(WorkTable::DATE_MODIFIED->value))
->type(Type::NUMBER) ->type(Type::NUMBER)
->min(1) ->min(1)
->max(parent::MYSQL_INT_MAX_LENGTH), ->max(parent::SIZE_UINT8),
(new Rules(WorkTable::DATE_CREATED->value)) (new Rules(WorkTable::DATE_CREATED->value))
->type(Type::NUMBER) ->type(Type::NUMBER)
->min(1) ->min(1)
->max(parent::MYSQL_INT_MAX_LENGTH) ->max(parent::SIZE_UINT8)
]); ]);
$ruleset->validate_or_exit(); $this->ruleset->validate_or_exit();
parent::__construct(); parent::__construct();
} }

View file

@ -1,5 +1,6 @@
<?php <?php
use vlw\MySQL\Order;
use Reflect\{Response, Path}; use Reflect\{Response, Path};
use ReflectRules\{Ruleset, Rules, Type}; use ReflectRules\{Ruleset, Rules, Type};
@ -7,7 +8,7 @@
use VLW\Database\Tables\Work\WorkTable; use VLW\Database\Tables\Work\WorkTable;
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Work/Work.php"); require_once Path::root("src/Database/Tables/Work/Work.php");
class GET_Work extends Database { class GET_Work extends Database {
protected Ruleset $ruleset; protected Ruleset $ruleset;
@ -19,37 +20,30 @@
(new Rules(WorkTable::ID->value)) (new Rules(WorkTable::ID->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(WorkTable::TITLE->value)) (new Rules(WorkTable::TITLE->value))
->type(Type::STRING) ->type(Type::STRING)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(WorkTable::SUMMARY->value)) (new Rules(WorkTable::SUMMARY->value))
->type(Type::STRING) ->type(Type::STRING)
->max(parent::MYSQL_TEXT_MAX_LENGTH), ->max(parent::SIZE_TEXT),
(new Rules(WorkTable::IS_LISTED->value)) (new Rules(WorkTable::CREATED->value))
->type(Type::BOOLEAN) ->type(Type::STRING)
->default(true),
(new Rules(WorkTable::DATE_MODIFIED->value))
->type(Type::NUMBER)
->min(1) ->min(1)
->max(parent::MYSQL_INT_MAX_LENGTH), ->max(parent::SIZE_VARCHAR)
(new Rules(WorkTable::DATE_CREATED->value))
->type(Type::NUMBER)
->min(1)
->max(parent::MYSQL_INT_MAX_LENGTH)
]); ]);
$ruleset->validate_or_exit(); $this->ruleset->validate_or_exit();
parent::__construct(); parent::__construct();
} }
public function main(): Response { public function main(): Response {
return $this->list(WorkTable::NAME, WorkTable::values()); return $this->list(WorkTable::NAME, WorkTable::values(), [
WorkTable::CREATED->value => Order::DESC
]);
} }
} }

View file

@ -13,8 +13,8 @@
require_once Path::root("src/Endpoints.php"); require_once Path::root("src/Endpoints.php");
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Work/Work.php"); require_once Path::root("src/Database/Tables/Work/Work.php");
require_once Path::root("src/Database/Models/Work/WorkPermalinks.php"); require_once Path::root("src/Database/Tables/Work/WorkPermalinks.php");
class PATCH_Work extends Database { class PATCH_Work extends Database {
protected Ruleset $ruleset; protected Ruleset $ruleset;
@ -27,19 +27,19 @@
->required() ->required()
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH) ->max(parent::SIZE_VARCHAR)
]); ]);
$this->ruleset->POST([ $this->ruleset->POST([
(new Rules(WorkTable::TITLE->value)) (new Rules(WorkTable::TITLE->value))
->type(Type::STRING) ->type(Type::STRING)
->min(3) ->min(3)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(WorkTable::SUMMARY->value)) (new Rules(WorkTable::SUMMARY->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_TEXT_MAX_LENGTH), ->max(parent::SIZE_TEXT),
(new Rules(WorkTable::IS_LISTED->value)) (new Rules(WorkTable::IS_LISTED->value))
->type(Type::BOOLEAN), ->type(Type::BOOLEAN),
@ -47,13 +47,13 @@
(new Rules(WorkTable::DATE_MODIFIED->value)) (new Rules(WorkTable::DATE_MODIFIED->value))
->type(Type::NUMBER) ->type(Type::NUMBER)
->min(1) ->min(1)
->max(parent::MYSQL_INT_MAX_LENGTH) ->max(parent::SIZE_UINT8)
->default(time()), ->default(time()),
(new Rules(WorkTable::DATE_CREATED->value)) (new Rules(WorkTable::DATE_CREATED->value))
->type(Type::NUMBER) ->type(Type::NUMBER)
->min(1) ->min(1)
->max(parent::MYSQL_INT_MAX_LENGTH) ->max(parent::SIZE_UINT8)
]); ]);
parent::__construct(); parent::__construct();

View file

@ -13,8 +13,8 @@
require_once Path::root("src/Endpoints.php"); require_once Path::root("src/Endpoints.php");
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Work/Work.php"); require_once Path::root("src/Database/Tables/Work/Work.php");
require_once Path::root("src/Database/Models/Work/WorkPermalinks.php"); require_once Path::root("src/Database/Tables/Work/WorkPermalinks.php");
class POST_Work extends Database { class POST_Work extends Database {
protected Ruleset $ruleset; protected Ruleset $ruleset;
@ -26,13 +26,13 @@
(new Rules(WorkTable::TITLE->value)) (new Rules(WorkTable::TITLE->value))
->type(Type::STRING) ->type(Type::STRING)
->min(3) ->min(3)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH) ->max(parent::SIZE_VARCHAR)
->default(null), ->default(null),
(new Rules(WorkTable::SUMMARY->value)) (new Rules(WorkTable::SUMMARY->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_TEXT_MAX_LENGTH) ->max(parent::SIZE_TEXT)
->default(null), ->default(null),
(new Rules(WorkTable::IS_LISTED->value)) (new Rules(WorkTable::IS_LISTED->value))
@ -42,11 +42,11 @@
(new Rules(WorkTable::DATE_CREATED->value)) (new Rules(WorkTable::DATE_CREATED->value))
->type(Type::NUMBER) ->type(Type::NUMBER)
->min(1) ->min(1)
->max(parent::MYSQL_INT_MAX_LENGTH) ->max(parent::SIZE_UINT8)
->default(time()) ->default(time())
]); ]);
$ruleset->validate_or_exit(); $this->ruleset->validate_or_exit();
parent::__construct(); parent::__construct();
} }

View file

@ -8,7 +8,7 @@
use VLW\Database\Tables\Work\ActionsTable; use VLW\Database\Tables\Work\ActionsTable;
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/WorkActions.php"); require_once Path::root("src/Database/Tables/WorkActions.php");
class DELETE_WorkActions extends Database { class DELETE_WorkActions extends Database {
protected Ruleset $ruleset; protected Ruleset $ruleset;
@ -20,7 +20,7 @@
$this->ruleset->POST([ $this->ruleset->POST([
(new Rules(ActionsTable::REF_WORK_ID->value)) (new Rules(ActionsTable::REF_WORK_ID->value))
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH) ->max(parent::SIZE_VARCHAR)
]); ]);
} }

View file

@ -1,5 +1,6 @@
<?php <?php
use vlw\MySQL\Order;
use Reflect\{Response, Path}; use Reflect\{Response, Path};
use ReflectRules\{Ruleset, Rules, Type}; use ReflectRules\{Ruleset, Rules, Type};
@ -7,7 +8,7 @@
use VLW\Database\Tables\Work\ActionsTable; use VLW\Database\Tables\Work\ActionsTable;
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Work/WorkActions.php"); require_once Path::root("src/Database/Tables/Work/Actions.php");
class GET_WorkActions extends Database { class GET_WorkActions extends Database {
protected Ruleset $ruleset; protected Ruleset $ruleset;
@ -19,26 +20,17 @@
(new Rules(ActionsTable::REF_WORK_ID->value)) (new Rules(ActionsTable::REF_WORK_ID->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH) ->max(parent::SIZE_VARCHAR)
]); ]);
$ruleset->validate_or_exit(); $this->ruleset->validate_or_exit();
parent::__construct(); parent::__construct();
} }
public function main(): Response { public function main(): Response {
$response = $this->db->for(ActionsTable::NAME) return $this->list(ActionsTable::NAME, ActionsTable::values(), [
->where($_GET) ActionsTable::ORDER_IDX->value => Order::DESC
->select([
ActionsTable::REF_WORK_ID->value,
ActionsTable::DISPLAY_TEXT->value,
ActionsTable::HREF->value,
ActionsTable::CLASS_LIST->value
]); ]);
return $response->num_rows > 0
? new Response(parent::index_array_by_key($response->fetch_all(MYSQLI_ASSOC), ActionsTable::REF_WORK_ID->value))
: new Response([], 404);
} }
} }

View file

@ -13,8 +13,8 @@
require_once Path::root("src/Endpoints.php"); require_once Path::root("src/Endpoints.php");
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Work/Work.php"); require_once Path::root("src/Database/Tables/Work/Work.php");
require_once Path::root("src/Database/Models/Work/WorkActions.php"); require_once Path::root("src/Database/Tables/Work/WorkActions.php");
class POST_WorkActions extends Database { class POST_WorkActions extends Database {
protected Ruleset $ruleset; protected Ruleset $ruleset;
@ -26,20 +26,20 @@
(new Rules(ActionsTable::REF_WORK_ID->value)) (new Rules(ActionsTable::REF_WORK_ID->value))
->required() ->required()
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(ActionsTable::DISPLAY_TEXT->value)) (new Rules(ActionsTable::DISPLAY_TEXT->value))
->required() ->required()
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(ActionsTable::HREF->value)) (new Rules(ActionsTable::HREF->value))
->required() ->required()
->type(Type::STRING) ->type(Type::STRING)
->type(Type::NULL) ->type(Type::NULL)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(ActionsTable::CLASS_LIST->value)) (new Rules(ActionsTable::CLASS_LIST->value))
->type(Type::ARRAY) ->type(Type::ARRAY)
@ -47,7 +47,7 @@
->default([]) ->default([])
]); ]);
$ruleset->validate_or_exit(); $this->ruleset->validate_or_exit();
parent::__construct(); parent::__construct();
} }

View file

@ -7,7 +7,7 @@
use VLW\Database\Tables\Work\PermalinksTable; use VLW\Database\Tables\Work\PermalinksTable;
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Work/WorkPermalinks.php"); require_once Path::root("src/Database/Tables/Work/WorkPermalinks.php");
class GET_WorkPermalinks extends Database { class GET_WorkPermalinks extends Database {
protected Ruleset $ruleset; protected Ruleset $ruleset;
@ -19,15 +19,15 @@
(new Rules(PermalinksTable::ID->value)) (new Rules(PermalinksTable::ID->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(PermalinksTable::REF_WORK_ID->value)) (new Rules(PermalinksTable::REF_WORK_ID->value))
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH) ->max(parent::SIZE_VARCHAR)
]); ]);
$ruleset->validate_or_exit(); $this->ruleset->validate_or_exit();
parent::__construct(); parent::__construct();
} }

View file

@ -8,7 +8,7 @@
use VLW\Database\Tables\Work\PermalinksTable; use VLW\Database\Tables\Work\PermalinksTable;
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Work/WorkPermalinks.php"); require_once Path::root("src/Database/Tables/Work/WorkPermalinks.php");
class POST_WorkPermalinks extends Database { class POST_WorkPermalinks extends Database {
protected Ruleset $ruleset; protected Ruleset $ruleset;
@ -21,22 +21,22 @@
->required() ->required()
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(PermalinksTable::REF_WORK_ID->value)) (new Rules(PermalinksTable::REF_WORK_ID->value))
->required() ->required()
->type(Type::STRING) ->type(Type::STRING)
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(PermalinksTable::DATE_CREATED->value)) (new Rules(PermalinksTable::DATE_CREATED->value))
->type(Type::NUMBER) ->type(Type::NUMBER)
->min(1) ->min(1)
->max(parent::MYSQL_INT_MAX_LENGTH) ->max(parent::SIZE_UINT8)
->default(time()) ->default(time())
]); ]);
$ruleset->validate_or_exit(); $this->ruleset->validate_or_exit();
parent::__construct(); parent::__construct();
} }

View file

@ -8,7 +8,7 @@
use VLW\Database\Tables\Work\TagsTable; use VLW\Database\Tables\Work\TagsTable;
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Work/WorkTags.php"); require_once Path::root("src/Database/Tables/Work/WorkTags.php");
class DELETE_WorkTags extends Database { class DELETE_WorkTags extends Database {
private Ruleset $ruleset; private Ruleset $ruleset;
@ -19,13 +19,13 @@
$this->ruleset->GET([ $this->ruleset->GET([
(new Rules(TagsTable::REF_WORK_ID->value)) (new Rules(TagsTable::REF_WORK_ID->value))
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(TagsTable::NAME->value)) (new Rules(TagsTable::NAME->value))
->type(Type::ENUM, TagsNameEnum::names()) ->type(Type::ENUM, TagsNameEnum::names())
]); ]);
$ruleset->validate_or_exit(); $this->ruleset->validate_or_exit();
parent::__construct(); parent::__construct();
} }

View file

@ -4,13 +4,10 @@
use ReflectRules\{Ruleset, Rules, Type}; use ReflectRules\{Ruleset, Rules, Type};
use VLW\Database\Database; use VLW\Database\Database;
use VLW\Database\Tables\Work\{ use VLW\Database\Tables\Work\{TagsTable, TagsLabelEnum};
TagsTable,
TagsNameEnum
};
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Work/WorkTags.php"); require_once Path::root("src/Database/Tables/Work/Tags.php");
class GET_WorkTags extends Database { class GET_WorkTags extends Database {
private Ruleset $ruleset; private Ruleset $ruleset;
@ -21,27 +18,18 @@
$this->ruleset->GET([ $this->ruleset->GET([
(new Rules(TagsTable::REF_WORK_ID->value)) (new Rules(TagsTable::REF_WORK_ID->value))
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(TagsTable::NAME->value)) (new Rules(TagsTable::LABEL->value))
->type(Type::ENUM, TagsNameEnum::names()) ->type(Type::ENUM, TagsLabelEnum::names())
]); ]);
$ruleset->validate_or_exit(); $this->ruleset->validate_or_exit();
parent::__construct(); parent::__construct();
} }
public function main(): Response { public function main(): Response {
$response = $this->db->for(TagsTable::NAME) return $this->list(TagsTable::NAME, TagsTable::values());
->where($_GET)
->select([
TagsTable::REF_WORK_ID->value,
TagsTable::NAME->value
]);
return $response->num_rows > 0
? new Response(parent::index_array_by_key($response->fetch_all(MYSQLI_ASSOC), TagsTable::REF_WORK_ID->value))
: new Response([], 404);
} }
} }

View file

@ -14,8 +14,8 @@
require_once Path::root("src/Endpoints.php"); require_once Path::root("src/Endpoints.php");
require_once Path::root("src/Database/Database.php"); require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Models/Work/Work.php"); require_once Path::root("src/Database/Tables/Work/Work.php");
require_once Path::root("src/Database/Models/Work/WorkTags.php"); require_once Path::root("src/Database/Tables/Work/WorkTags.php");
class POST_WorkTags extends Database { class POST_WorkTags extends Database {
protected Ruleset $ruleset; protected Ruleset $ruleset;
@ -27,14 +27,14 @@
(new Rules(TagsTable::REF_WORK_ID->value)) (new Rules(TagsTable::REF_WORK_ID->value))
->required() ->required()
->min(1) ->min(1)
->max(parent::MYSQL_VARCHAR_MAX_LENGTH), ->max(parent::SIZE_VARCHAR),
(new Rules(TagsTable::NAME->value)) (new Rules(TagsTable::NAME->value))
->required() ->required()
->type(Type::ENUM, TagsNameEnum::names()) ->type(Type::ENUM, TagsNameEnum::names())
]); ]);
$ruleset->validate_or_exit(); $this->ruleset->validate_or_exit();
parent::__construct(); parent::__construct();
} }

View file

@ -0,0 +1,53 @@
<?php
use vlw\MySQL\Order;
use Reflect\{Response, Path};
use ReflectRules\{Ruleset, Rules, Type};
use VLW\Database\Database;
use VLW\Database\Tables\Work\TimelineTable;
require_once Path::root("src/Database/Database.php");
require_once Path::root("src/Database/Tables/Work/Timeline.php");
class GET_WorkTimeline extends Database {
protected Ruleset $ruleset;
public function __construct() {
$this->ruleset = new Ruleset(strict: true);
$this->ruleset->GET([
(new Rules(TimelineTable::REF_WORK_ID->value))
->type(Type::STRING)
->min(1)
->max(parent::SIZE_VARCHAR),
(new Rules(TimelineTable::YEAR->value))
->type(Type::NUMBER)
->min(0)
->max(parent::SIZE_UINT16),
(new Rules(TimelineTable::MONTH->value))
->type(Type::NUMBER)
->min(0)
->max(parent::SIZE_UINT8),
(new Rules(TimelineTable::DAY->value))
->type(Type::NUMBER)
->min(0)
->max(parent::SIZE_UINT8)
]);
$this->ruleset->validate_or_exit();
parent::__construct();
}
public function main(): Response {
return $this->list(TimelineTable::NAME, TimelineTable::values(), [
TimelineTable::YEAR->value => Order::DESC,
TimelineTable::MONTH->value => Order::DESC,
TimelineTable::DAY->value => Order::DESC
]);
}
}

View file

@ -1,64 +1,39 @@
<?php <?php
use Vegvisir\Path; use VLW\Database\Models\About\Language;
use Reflect\Response; use const VLW\{
FORGEJO_HREF,
FORGEJO_SI_BYTE_MULTIPLE,
DEFAULT_BUTTON_ICON
};
use VLW\Client\API; require_once VV::root("src/Consts.php");
use VLW\API\Endpoints; require_once VV::root("src/Database/Models/About/Language.php");
require_once VV::root("src/client/API.php"); $languages = new class extends Language {
require_once VV::root("api/src/Endpoints.php"); private readonly int $total_bytes;
const BYTE_UNITS = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const FORGEJO_HREF = "https://git.vlw.se/explore/repos?q=&sort=recentupdate&language=";
$langs = new class extends API {
public readonly int $total_bytes;
private readonly Response $resp;
private readonly array $languages;
public function __construct() { public function __construct() {
parent::__construct(); $this->total_bytes = array_sum(array_map(fn(Language $language): int => $language->bytes(), parent::all()));
// Fetch languages from endpoint
$this->resp = $this->call(Endpoints::ABOUT_LANGUAGES->value)->get();
// We got a response from endpoint
if ($this->resp->ok) {
$this->languages = $this->resp->json();
$this->total_bytes = array_sum($this->languages);
}
} }
// Return all languages as (string) language => (int) language_bytes public function percent(Language $language, int $mode = PHP_ROUND_HALF_UP): int {
public function all(): array { return round(($language->bytes() / $this->total_bytes) * 100, 0, $mode);
return $this->languages;
} }
// Return percent of total for all languages public function percent_string(Language $language): string {
public function get_percent(string $lang, int $mode = PHP_ROUND_HALF_UP): int { return ($this->percent($language) > 1 ? $this->percent($language) : "<1") . "%";
return round(($this->languages[$lang] / $this->total_bytes) * 100, 0, $mode);
} }
// Return language bytes as percent of whole public function bytes_si_string(Language $language): string {
public function get_percent_str(string $lang): string {
$percent = $this->get_percent($lang, PHP_ROUND_HALF_DOWN);
return ($percent > 1 ? $percent : "<1") . "%";
}
// Return languages bytes as a multiple-byte decimal unit
public function get_bytes_str(string $lang): string {
$bytes = $this->languages[$lang];
// Calculate factor for unit // Calculate factor for unit
$factor = floor((strlen($bytes) - 1) / 3); $factor = floor((strlen($language->bytes()) - 1) / 3);
// Divide by radix 10 // Divide by radix 10
$format = $bytes / pow(1000, $factor); $format = $language->bytes() / pow(1000, $factor);
return round($format) . " " . BYTE_UNITS[$factor]; return round($format) . " " . FORGEJO_SI_BYTE_MULTIPLE[$factor];
}
} }
};
?> ?>
<style><?= VV::css("public/assets/css/pages/about") ?></style> <style><?= VV::css("public/assets/css/pages/about") ?></style>
@ -74,32 +49,30 @@
<section class="languages"> <section class="languages">
<stacked-bar-chart> <stacked-bar-chart>
<?php foreach ($langs->all() as $lang => $bytes): ?> <?php foreach ($languages::all() as $language): ?>
<a href="<?= FORGEJO_HREF . $lang ?>" target="_blank"><chart-segment style="--size:<?= $langs->get_percent($lang) ?>%;" data-lang="<?= $lang ?>" data-bytes="<?= $bytes ?>"> <a href="<?= FORGEJO_HREF . $language->id ?>" target="_blank"><chart-segment style="--size:<?= $languages->percent($language) ?>%;" data-lang="<?= $language->id ?>" data-bytes="<?= $language->bytes() ?>">
<span data-hover><strong><?= $langs->get_percent_str($lang) ?> <?= $lang ?></strong><br>(<?= $bytes ?> bytes)</span> <span data-hover><strong><?= $languages->percent_string($language) ?> <?= $language->id ?></strong><br>(<?= $language->bytes() ?> bytes)</span>
<!--<p><span><?= $lang ?></span></p>-->
</chart-segment></a> </chart-segment></a>
<?php endforeach; ?> <?php endforeach; ?>
</stacked-bar-chart> </stacked-bar-chart>
<languages-list> <languages-list>
<?php foreach ($langs->all() as $lang => $bytes): ?> <?php foreach ($languages::all() as $language): ?>
<a href="<?= FORGEJO_HREF . $lang ?>"><button data-lang="<?= $lang ?>" class="inline"> <a href="<?= FORGEJO_HREF . $language->id ?>"><button data-lang="<?= $language->id ?>" class="inline">
<p><?= $langs->get_percent_str($lang) ?></p> <p><?= $languages->percent_string($language) ?></p>
<p class="lang"><?= $lang ?></p> <p class="lang"><?= $language->id ?></p>
<p><?= $langs->get_bytes_str($lang) ?></p> <p><?= $languages->bytes_si_string($language) ?></p>
<?= VV::embed("public/assets/media/icons/chevron.svg") ?> <?= VV::embed(DEFAULT_BUTTON_ICON) ?>
</button></a> </button></a>
<?php endforeach; ?> <?php endforeach; ?>
</languages-list> </languages-list>
<stacked-bar-chart> <stacked-bar-chart>
<?php foreach ($langs->all() as $lang => $bytes): ?> <?php foreach ($languages::all() as $language): ?>
<a href="<?= FORGEJO_HREF . $lang ?>" target="_blank"><chart-segment style="--size:<?= $langs->get_percent($lang) ?>%;" data-lang="<?= $lang ?>" data-bytes="<?= $bytes ?>"> <a href="<?= FORGEJO_HREF . $language->id ?>" target="_blank"><chart-segment style="--size:<?= $languages->percent($language) ?>%;" data-lang="<?= $language->id ?>" data-bytes="<?= $language->bytes() ?>">
<span data-hover><strong><?= $langs->get_percent_str($lang) ?> <?= $lang ?></strong><br>(<?= $bytes ?> bytes)</span> <span data-hover><strong><?= $languages->percent_string($language) ?> <?= $language->id ?></strong><br>(<?= $language->bytes() ?> bytes)</span>
<!--<p><span><?= $lang ?></span></p>-->
</chart-segment></a> </chart-segment></a>
<?php endforeach; ?> <?php endforeach; ?>

View file

@ -29,22 +29,22 @@
require_once VV::root("api/src/Endpoints.php"); require_once VV::root("api/src/Endpoints.php");
// Load hardware database models // Load hardware database models
require_once VV::root("api/src/Database/Models/Battlestation/Mb.php"); require_once VV::root("api/src/Database/Tables/Battlestation/Mb.php");
require_once VV::root("api/src/Database/Models/Battlestation/Cpu.php"); require_once VV::root("api/src/Database/Tables/Battlestation/Cpu.php");
require_once VV::root("api/src/Database/Models/Battlestation/Gpu.php"); require_once VV::root("api/src/Database/Tables/Battlestation/Gpu.php");
require_once VV::root("api/src/Database/Models/Battlestation/Psu.php"); require_once VV::root("api/src/Database/Tables/Battlestation/Psu.php");
require_once VV::root("api/src/Database/Models/Battlestation/Dram.php"); require_once VV::root("api/src/Database/Tables/Battlestation/Dram.php");
require_once VV::root("api/src/Database/Models/Battlestation/Storage.php"); require_once VV::root("api/src/Database/Tables/Battlestation/Storage.php");
require_once VV::root("api/src/Database/Models/Battlestation/Chassis.php"); require_once VV::root("api/src/Database/Tables/Battlestation/Chassis.php");
// Load hardware config database models // Load hardware config database models
require_once VV::root("api/src/Database/Models/Battlestation/Config/MbPsu.php"); require_once VV::root("api/src/Database/Tables/Battlestation/Config/MbPsu.php");
require_once VV::root("api/src/Database/Models/Battlestation/Config/MbGpu.php"); require_once VV::root("api/src/Database/Tables/Battlestation/Config/MbGpu.php");
require_once VV::root("api/src/Database/Models/Battlestation/Config/MbDram.php"); require_once VV::root("api/src/Database/Tables/Battlestation/Config/MbDram.php");
require_once VV::root("api/src/Database/Models/Battlestation/Config/Config.php"); require_once VV::root("api/src/Database/Tables/Battlestation/Config/Config.php");
require_once VV::root("api/src/Database/Models/Battlestation/Config/MbStorage.php"); require_once VV::root("api/src/Database/Tables/Battlestation/Config/MbStorage.php");
require_once VV::root("api/src/Database/Models/Battlestation/Config/ChassisMb.php"); require_once VV::root("api/src/Database/Tables/Battlestation/Config/ChassisMb.php");
require_once VV::root("api/src/Database/Models/Battlestation/Config/MbCpuCooler.php"); require_once VV::root("api/src/Database/Tables/Battlestation/Config/MbCpuCooler.php");
const GIGA = 0x3B9ACA00; const GIGA = 0x3B9ACA00;
const MEGA = 0xF4240; const MEGA = 0xF4240;

View file

@ -2,58 +2,43 @@
use Vegvisir\Path; use Vegvisir\Path;
use VLW\Client\API; use VLW\API\{Client, Endpoints};
use VLW\API\Endpoints;
use VLW\Database\Tables\Messages\MessagesTable; use VLW\Database\Tables\Messages\MessagesTable;
require_once VV::root("src/client/API.php"); require_once VV::root("src/API/API.php");
require_once VV::root("api/src/Endpoints.php"); require_once VV::root("src/API/Endpoints.php");
require_once VV::root("src/Database/Tables/Messages/Messages.php");
require_once VV::root("api/src/Database/Models/Messages/Messages.php");
// Connect to VLW API
$api = new API();
class Date extends DateTimeImmutable {
private const AVAILABLE_TO_HOUR = 16;
private const AVAILABLE_FROM_HOUR = 10;
private const REPLY_TIME_HOURS_AVAILABLE = 2;
$date = new class extends DateTimeImmutable {
public function __construct() { public function __construct() {
// Set DateTime for configured timezone // Set DateTime for configured timezone
parent::__construct("now", new DateTimeZone($_ENV["time"]["date_time_zone"])); parent::__construct("now", new DateTimeZone($_ENV["client_time_available"]["time_zone"]));
} }
// Return current hour in 24-hour format // Return current hour in 24-hour format
private function get_hour(): int { private function hour(): int {
return (int) $this->format("G"); return (int) $this->format("G");
} }
// Returns true if current time is between available from and to hours // Returns true if current time is between available from and to hours
public function is_available(): bool { public function is_available(): bool {
return $this->get_hour() >= self::AVAILABLE_FROM_HOUR && $this->get_hour() < self::AVAILABLE_TO_HOUR; return $this->hour() >= $_ENV["client_time_available"]["available_from_hour"] && $this->hour() < $_ENV["client_time_available"]["available_to_hour"];
} }
public function get_estimated_reply_hours(): int { public function get_estimated_reply_hours(): int {
// I'm available! Return the estimated reply time for that // I'm available! Return the estimated reply time for that
if ($this->is_available()) { if ($this->is_available()) {
return self::REPLY_TIME_HOURS_AVAILABLE; return $_ENV["client_time_available"]["reply_average_hours"];
} }
$hour = $this->get_hour(); return $this->hour() < $_ENV["client_time_available"]["available_from_hour"]
return $hour < self::AVAILABLE_FROM_HOUR
// Return hours past midnight until I become available (clamped to estimated reply hours) // Return hours past midnight until I become available (clamped to estimated reply hours)
? max(self::AVAILABLE_FROM_HOUR - $hour, self::REPLY_TIME_HOURS_AVAILABLE) ? max($_ENV["client_time_available"]["available_from_hour"] - $this->hour(), $_ENV["client_time_available"]["reply_average_hours"])
// Return hours before midnight until I become available (clamped to estimated reply hours) // Return hours before midnight until I become available (clamped to estimated reply hours)
: max(self::AVAILABLE_FROM_HOUR + (24 - $hour), self::REPLY_TIME_HOURS_AVAILABLE); : max($_ENV["client_time_available"]["available_from_hour"] + (24 - $this->hour()), $_ENV["client_time_available"]["reply_average_hours"]);
} }
} }
$date = new Date();
?> ?>
<style><?= VV::css("public/assets/css/pages/contact") ?></style> <style><?= VV::css("public/assets/css/pages/contact") ?></style>
<section> <section>
@ -103,7 +88,7 @@
<?php <?php
// Send message via API // Send message via API
$send = $api->call(Endpoints::MESSAGES->value)->post([ $send = (new Client())->call(Endpoints::MESSAGES->value)->post([
MessagesTable::EMAIL->value => $_POST[MessagesTable::EMAIL->value], MessagesTable::EMAIL->value => $_POST[MessagesTable::EMAIL->value],
MessagesTable::MESSAGE->value => $_POST[MessagesTable::MESSAGE->value] MessagesTable::MESSAGE->value => $_POST[MessagesTable::MESSAGE->value]
]); ]);

View file

@ -13,8 +13,8 @@
require_once VV::root("src/client/API.php"); require_once VV::root("src/client/API.php");
require_once VV::root("api/src/Endpoints.php"); require_once VV::root("api/src/Endpoints.php");
require_once VV::root("api/src/Database/Models/Work/Work.php"); require_once VV::root("api/src/Database/Tables/Work/Work.php");
require_once VV::root("api/src/Database/Models/Work/WorkActions.php"); require_once VV::root("api/src/Database/Tables/Work/WorkActions.php");
// Search endpoint query paramter // Search endpoint query paramter
const SEARCH_PARAM = "q"; const SEARCH_PARAM = "q";

View file

@ -1,43 +1,10 @@
<?php <?php
use Reflect\Response; use VLW\Database\Models\Work\Work;
use const VLW\{TIMELINE_PREVIEW_LIMIT_PARAM, TIMELINE_PREVIEW_LIMIT_COUNT};
use VLW\Client\API; require_once VV::root("src/Consts.php");
use VLW\API\Endpoints; require_once VV::root("src/Database/Models/Work/Work.php");
use VLW\Database\Tables\Work\WorkTable;
require_once VV::root("src/client/API.php");
require_once VV::root("api/src/Endpoints.php");
require_once VV::root("api/src/Database/Models/Work/Work.php");
// Number of items from the timeline to display on this page
const TIMELINE_PREVIEW_LIMIT = 5;
$work = new class extends API {
const ERROR_MSG = "Something went wrong";
private readonly Response $resp;
public function __construct() {
parent::__construct();
// Get work items from endpoint
$this->resp = $this->call(Endpoints::WORK->value)->params([
WorkTable::IS_LISTED->value => true
])->get();
}
private function get_item(string $key): array {
$idx = array_search($key, array_column($this->resp->json(), WorkTable::ID->value));
return $this->resp->json()[$idx];
}
public function get_summary(string $key): string {
return $this->resp->ok ? $this->get_item($key)[WorkTable::SUMMARY->value] : self::ERROR_MSG;
}
}
?> ?>
<style><?= VV::css("public/assets/css/pages/work") ?></style> <style><?= VV::css("public/assets/css/pages/work") ?></style>
@ -48,7 +15,7 @@
<?= VV::embed("public/assets/media/icons/vegvisir.svg") ?> <?= VV::embed("public/assets/media/icons/vegvisir.svg") ?>
<h1>vegvisir</h1> <h1>vegvisir</h1>
</div> </div>
<p><?= $work->get_summary("vlw/vegvisir") ?></p> <p><?= (new Work("vlw/vegvisir"))->summary() ?></p>
<div class="actions"> <div class="actions">
<a href="https://vegvisir.vlw.se"><button class="inline solid"> <a href="https://vegvisir.vlw.se"><button class="inline solid">
<p>read more</p> <p>read more</p>
@ -63,7 +30,7 @@
<?= VV::embed("public/assets/media/icons/reflect.svg") ?> <?= VV::embed("public/assets/media/icons/reflect.svg") ?>
<h1>reflect</h1> <h1>reflect</h1>
</div> </div>
<p><?= $work->get_summary("vlw/reflect") ?></p> <p><?= (new Work("vlw/reflect"))->summary() ?></p>
<div class="actions"> <div class="actions">
<a href="https://reflect.vlw.se"><button class="inline solid"> <a href="https://reflect.vlw.se"><button class="inline solid">
<p>read more</p> <p>read more</p>
@ -105,7 +72,7 @@
<?= VV::embed("public/assets/media/icons/repo.svg") ?> <?= VV::embed("public/assets/media/icons/repo.svg") ?>
</div> </div>
<h3>vlw/php-mysql</h3> <h3>vlw/php-mysql</h3>
<p><?= $work->get_summary("vlw/php-mysql") ?></p> <p><?= (new Work("vlw/php-mysql"))->summary() ?></p>
<div class="actions"> <div class="actions">
<a href="https://codeberg.org/vlw/php-mysql"><button class="inline"> <a href="https://codeberg.org/vlw/php-mysql"><button class="inline">
<?= VV::embed("public/assets/media/icons/codeberg.svg") ?> <?= VV::embed("public/assets/media/icons/codeberg.svg") ?>
@ -119,7 +86,7 @@
<?= VV::embed("public/assets/media/icons/star.svg") ?> <?= VV::embed("public/assets/media/icons/star.svg") ?>
</div> </div>
<h3>Website for iCellate Medical</h3> <h3>Website for iCellate Medical</h3>
<p><?= $work->get_summary("icellate/website") ?></p> <p><?= (new Work("icellate/website"))->summary() ?></p>
<div class="actions"> <div class="actions">
<a href="/work/icellate/website"><button class="inline"> <a href="/work/icellate/website"><button class="inline">
<p>read more</p> <p>read more</p>
@ -132,7 +99,7 @@
<?= VV::embed("public/assets/media/icons/star.svg") ?> <?= VV::embed("public/assets/media/icons/star.svg") ?>
</div> </div>
<h3>Modernizing GeneMate by iCellate</h3> <h3>Modernizing GeneMate by iCellate</h3>
<p><?= $work->get_summary("icellate/genemate") ?></p> <p><?= (new Work("icellate/genemate"))->summary() ?></p>
<div class="actions"> <div class="actions">
<a href="/work/icellate/genemate"><button class="inline"> <a href="/work/icellate/genemate"><button class="inline">
<p>read more</p> <p>read more</p>
@ -145,7 +112,7 @@
<?= VV::embed("public/assets/media/icons/star.svg") ?> <?= VV::embed("public/assets/media/icons/star.svg") ?>
</div> </div>
<h3>Custom pages for Deltaco AB</h3> <h3>Custom pages for Deltaco AB</h3>
<p><?= $work->get_summary("deltaco/asyncapp") ?></p> <p><?= (new Work("deltaco/asyncapp"))->summary() ?></p>
<div class="actions"> <div class="actions">
<a href="/work/deltaco/asyncapp"><button class="inline"> <a href="/work/deltaco/asyncapp"><button class="inline">
<p>read more</p> <p>read more</p>
@ -157,7 +124,7 @@
<section class="heading"> <section class="heading">
<h1>latest projects</h1> <h1>latest projects</h1>
</section> </section>
<?= VV::include("public/work/timeline?limit=" . TIMELINE_PREVIEW_LIMIT) ?> <?= VV::include("public/work/timeline?" . http_build_query([TIMELINE_PREVIEW_LIMIT_PARAM => TIMELINE_PREVIEW_LIMIT_COUNT])) ?>
<section class="heading"> <section class="heading">
<a href="/work/timeline"><button class="inline solid"> <a href="/work/timeline"><button class="inline solid">
<p>view full timeline</p> <p>view full timeline</p>

View file

@ -1,97 +1,51 @@
<?php <?php
use Vegvisir\Path; use VLW\Database\Models\Work\Timeline;
use Reflect\Response; use const VLW\{
ICONS_DIR,
use VLW\Client\API; DEFAULT_BUTTON_ICON,
use VLW\API\Endpoints; TIMELINE_PREVIEW_LIMIT_PARAM
use VLW\Database\Tables\Work\{
WorkTable,
TagsTable,
ActionsTable
}; };
require_once VV::root("src/client/API.php"); require_once VV::root("src/Consts.php");
require_once VV::root("api/src/Endpoints.php"); require_once VV::root("src/Database/Models/Work/Timeline.php");
require_once VV::root("api/src/Database/Models/Work/Work.php"); $timeline = new class extends Timeline {
require_once VV::root("api/src/Database/Models/Work/WorkTags.php"); public function __construct() {}
require_once VV::root("api/src/Database/Models/Work/WorkActions.php");
$work = new class extends API { public static function ordered(): array {
private const API_PARAM_LIMIT = "limit"; // Get timeline list limit from search param if set
$limit = array_key_exists(TIMELINE_PREVIEW_LIMIT_PARAM, $_GET) ? (int) $_GET[TIMELINE_PREVIEW_LIMIT_PARAM] : null;
private readonly Response $resp;
private readonly Response $tags;
private readonly Response $actions;
public function __construct() {
parent::__construct();
$this->resp = $this->call(Endpoints::WORK->value)->params([
WorkTable::IS_LISTED->value => true,
self::API_PARAM_LIMIT => $_GET[self::API_PARAM_LIMIT] ?? null
])->get();
// Fetch metadata for work items if we got an ok from work endpoint
if ($this->resp->ok) {
$this->tags = $this->call(Endpoints::WORK_TAGS->value)->get();
$this->actions = $this->call(Endpoints::WORK_ACTIONS->value)->get();
}
}
/*
Order response from endpoint into a multi-dimensional array.
For example, a single item created at 14th of February 2024 would be ordered like this
[2024 => [[02 => [14 => [<row_data>]]]]]
*/
public function get_timeline(): array {
if (!$this->resp->ok) {
return [];
}
$timeline = []; $timeline = [];
// Create array of arrays ordered by decending year, month, day, items foreach (parent::all() as $idx => $item) {
foreach ($this->resp->json() as $row) { // Use year as the first dimension
// Create array for current year if it doesn't exist if (!array_key_exists($item->year(), $timeline)) {
if (!array_key_exists($row[WorkTable::DATE_YEAR->value], $timeline)) { $timeline[$item->year()] = [];
$timeline[$row[WorkTable::DATE_YEAR->value]] = [];
} }
// Create array for current month if it doesn't exist // And month as the second dimension
if (!array_key_exists($row[WorkTable::DATE_MONTH->value], $timeline[$row[WorkTable::DATE_YEAR->value]])) { if (!array_key_exists($item->month(), $timeline[$item->year()])) {
$timeline[$row[WorkTable::DATE_YEAR->value]][$row[WorkTable::DATE_MONTH->value]] = []; $timeline[$item->year()][$item->month()] = [];
} }
// Create array for current day if it doesn't exist // Lastly, day as the third dimension
if (!array_key_exists($row[WorkTable::DATE_DAY->value], $timeline[$row[WorkTable::DATE_YEAR->value]][$row[WorkTable::DATE_MONTH->value]])) { if (!array_key_exists($item->day(), $timeline[$item->year()][$item->month()])) {
$timeline[$row[WorkTable::DATE_YEAR->value]][$row[WorkTable::DATE_MONTH->value]][$row[WorkTable::DATE_DAY->value]] = []; $timeline[$item->year()][$item->month()][$item->day()] = [];
} }
// Append item to ordered array // Append Work instance on Timeline object to the output array by year->month->day
$timeline[$row[WorkTable::DATE_YEAR->value]][$row[WorkTable::DATE_MONTH->value]][$row[WorkTable::DATE_DAY->value]][] = $row; $timeline[$item->year()][$item->month()][$item->day()][] = $item->work();
// Bail out here if we've reached the theshold for items to display
if ($limit && $idx === $limit) {
return $timeline;
}
} }
return $timeline; return $timeline;
} }
public function get_tags(string $key): array {
if (!$this->resp->ok) {
return [];
}
return in_array($key, $this->tags->json()) ? $this->tags->json()[$key] : [];
}
public function get_actions(string $key): array {
if (!$this->resp->ok) {
return [];
}
return array_key_exists($key, $this->actions->json()) ? $this->actions->json()[$key] : [];
}
} }
?> ?>
@ -113,7 +67,7 @@
<section class="timeline"> <section class="timeline">
<?php // Get year int from key and array of months for current year ?> <?php // Get year int from key and array of months for current year ?>
<?php foreach ($work->get_timeline() as $year => $months): ?> <?php foreach ($timeline::ordered() as $year => $months): ?>
<div class="year"> <div class="year">
<div class="track"> <div class="track">
<p><?= $year ?></p> <p><?= $year ?></p>
@ -138,43 +92,51 @@
</div> </div>
<div class="items"> <div class="items">
<?php foreach ($items as $item): ?> <?php foreach ($items as $work): ?>
<div class="item"> <div class="item">
<?php // List tags if available ?> <?php // List tags if available ?>
<?php if ($work->get_tags($item[WorkTable::ID->value])): ?> <?php if ($work->tags()): ?>
<div class="tags"> <div class="tags">
<?php foreach ($work->get_tags($item[WorkTable::ID->value]) as $tag): ?>
<p class="tag <?= $tag[TagsTable::NAME->value] ?>"><?= $tag[TagsTable::NAME->value] ?></p> <?php foreach ($work->tags() as $tag): ?>
<p class="tag <?= $tag->label()->name ?>"><?= $tag->label()->name ?></p>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<?php endif; ?> <?php endif; ?>
<?php // Show large heading if defined ?> <?php if ($work->title()): ?>
<?php if (!empty($item[WorkTable::TITLE->value])): ?> <h2><?= $work->title() ?></h2>
<h2><?= $item[WorkTable::TITLE->value] ?></h2>
<?php endif; ?> <?php endif; ?>
<p><?= $item[WorkTable::SUMMARY->value] ?></p> <p><?= $work->summary() ?></p>
<div class="actions"> <div class="actions">
<?php if ($work->get_actions($item[WorkTable::ID->value])): ?>
<?php // Display each action button ?> <?php if ($work->actions()): ?>
<?php foreach ($work->get_actions($item[WorkTable::ID->value]) as $action): ?> <?php foreach ($work->actions() as $action): ?>
<a href="<?= $action[ActionsTable::HREF->value] ?>"><button class="inline <?= $action[ActionsTable::CLASS_LIST->value] ?>"> <a href="<?= $action->href() ?? "/work/{$work->id}" ?>"><button class="inline <?= implode(" ", $action->classes()) ?>">
<p><?= $action[ActionsTable::DISPLAY_TEXT->value] ?></p> <?php if ($action->icon_prepended()): ?>
<?= VV::embed("public/assets/media/icons/chevron.svg") ?> <?= VV::embed(ICONS_DIR . $action->icon_prepended()) ?>
<?php endif; ?>
<p><?= $action->display_text() ?></p>
<?php if ($action->icon_appended()): ?>
<?= VV::embed(ICONS_DIR . $action->icon_appended()) ?>
<?php else: ?>
<?= VV::embed(DEFAULT_BUTTON_ICON) ?>
<?php endif; ?>
</button></a> </button></a>
<?php endforeach; ?> <?php endforeach; ?>
<?php // Display a link to namespaced page on vlw.se if no action is defined ?>
<?php else: ?> <?php else: ?>
<a href="<?= $item[WorkTable::ID->value] ?>"><button class="inline"> <a href="<?= "/work/{$work->id}" ?>"><button class="inline">
<p>read more</p> <p>read more</p>
<?= VV::embed("public/assets/media/icons/chevron.svg") ?> <?= VV::embed(DEFAULT_BUTTON_ICON) ?>
</button></a> </button></a>
<?php endif; ?> <?php endif; ?>
</div> </div>
</div> </div>

View file

@ -10,9 +10,9 @@
public function __construct() { public function __construct() {
parent::__construct( parent::__construct(
$_ENV["api"]["base_url"], $_ENV["client_api"]["base_url"],
$_ENV["api"]["api_key"], $_ENV["client_api"]["api_key"],
$_ENV["api"]["verify_peer"] $_ENV["client_api"]["verify_peer"]
); );
} }
} }

View file

@ -13,6 +13,7 @@
case MESSAGES = "/messages"; case MESSAGES = "/messages";
case WORK_TAGS = "/work/tags"; case WORK_TAGS = "/work/tags";
case WORK_ACTIONS = "/work/actions"; case WORK_ACTIONS = "/work/actions";
case WORK_TIMELINE = "/work/timeline";
case BATTLESTATION = "/battlestation"; case BATTLESTATION = "/battlestation";
case ABOUT_LANGUAGES = "/about/languages"; case ABOUT_LANGUAGES = "/about/languages";
case BATTLESTATION_MB = "/battlestation/mb"; case BATTLESTATION_MB = "/battlestation/mb";

28
src/Consts.php Normal file
View file

@ -0,0 +1,28 @@
<?php
namespace VLW;
/**
* # Media
* Constants related to media files
*/
const MEDIA_DIR = "/public/assets/media/";
const ICONS_DIR = MEDIA_DIR . "icons/";
const DEFAULT_BUTTON_ICON = ICONS_DIR . "chevron.svg";
/**
* # Timeline
* Constants related to the work timeline
*/
const TIMELINE_PREVIEW_LIMIT_PARAM = "limit";
const TIMELINE_PREVIEW_LIMIT_COUNT = 5;
/**
* # Forgejo
* Constants related to the fetching and caching of real-time prog. language use on Forgejo
*/
const FORGEJO_HREF = "https://git.vlw.se/explore/repos?q=&sort=recentupdate&language=";
const FORGEJO_ENDPOINT_USER = "/api/v1/users/%s";
const FORGEJO_ENDPOINT_SEARCH = "/api/v1/repos/search?uid=%s";
const FORGEJO_SI_BYTE_MULTIPLE = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const FORGEJO_UPDATE_CACHE_PARAM = "update";

View file

@ -7,21 +7,29 @@
use vlw\MySQL\MySQL; use vlw\MySQL\MySQL;
class Database { class Database {
public const SIZE_UUID = 36;
public const SIZE_TEXT = 65535;
public const SIZE_UINT8 = 2 ** 8 -1;
public const SIZE_UINT16 = 2 ** 16 -1;
public const SIZE_UINT32 = 2 ** 32 -1;
public const SIZE_UINT64 = 2 ** 64 -1;
public const SIZE_VARCHAR = 255;
protected readonly MySQL $db; protected readonly MySQL $db;
public function __construct() { public function __construct() {
// Create new MariaDB connection // Create new MariaDB connection
$this->db = new MySQL( $this->db = new MySQL(
$_ENV["api_database"]["host"], $_ENV["server_database"]["host"],
$_ENV["api_database"]["user"], $_ENV["server_database"]["user"],
$_ENV["api_database"]["pass"], $_ENV["server_database"]["pass"],
$_ENV["api_database"]["db"], $_ENV["server_database"]["db"],
); );
} }
// Return all rows from a table using $_GET paramters as the WHERE clause as a Reflect\Response // Return all rows from a table using $_GET paramters as the WHERE clause as a Reflect\Response
public function list(string $table, array $columns, array $order = null): Response { public function list(string $table, array $columns, array $order = null): Response {
$resp = $this->for($table)->where($_GET); $resp = $this->db->for($table)->where(array_intersect_key($_GET, array_flip($columns)));
// Optionally order rows by columns // Optionally order rows by columns
if ($order) { if ($order) {

View file

@ -0,0 +1,30 @@
<?php
namespace VLW\Database\Models\About;
use \VV;
use VLW\API\Endpoints;
use VLW\Database\Models\Model;
use VLW\Database\Tables\About\LanguagesTable;
require_once VV::root("src/Consts.php");
require_once VV::root("src/API/Endpoints.php");
require_once VV::root("src/Database/Models/Model.php");
require_once VV::root("src/Database/Tables/About/Languages.php");
class Language extends Model {
public function __construct(public readonly string $id) {
parent::__construct(Endpoints::ABOUT_LANGUAGES, [
LanguagesTable::ID->value => $this->id
]);
}
public static function all(array $params = []): array {
return array_map(fn(array $item): Language => new Language($item[LanguagesTable::ID->value]), parent::list(Endpoints::ABOUT_LANGUAGES, $params));
}
public function bytes(): int {
return $this->get(LanguagesTable::BYTES->value);
}
}

View file

@ -10,13 +10,17 @@
require_once VV::root("src/API/Endpoints.php"); require_once VV::root("src/API/Endpoints.php");
abstract class Model { abstract class Model {
private readonly array $row; abstract public static function all(array $params = []): array;
private array $row;
public function __construct( public function __construct(
public readonly Endpoints $endpoint, public readonly ?Endpoints $endpoint = null,
private readonly array $params = [] private readonly array $params = []
) { ) {
$this->row = self::first(self::list($endpoint, $params)); if ($this->endpoint) {
$this->assign(self::first(self::list($endpoint, $params)));
}
} }
public static function first(array $array): array { public static function first(array $array): array {
@ -24,11 +28,16 @@
} }
public static function list(Endpoints $endpoint, array $params = []): array { public static function list(Endpoints $endpoint, array $params = []): array {
$resp = (new API())->call($endpoint->value)->params($params)->get(); $resp = (new Client())->call($endpoint->value)->params($params)->get();
return $resp->ok ? $resp->json() : []; return $resp->ok ? $resp->json() : [];
} }
public function assign(array $row): self {
$this->row = $row;
return $this;
}
public function get(string $key): mixed { public function get(string $key): mixed {
return $this->data[$key] ?? null; return $this->row[$key] ?? null;
} }
} }

View file

@ -0,0 +1,51 @@
<?php
namespace VLW\Database\Models\Work;
use \VV;
use VLW\API\Endpoints;
use VLW\Database\Models\Model;
use VLW\Database\Models\Work\Work;
use VLW\Database\Tables\Work\ActionsTable;
require_once VV::root("src/API/Endpoints.php");
require_once VV::root("src/Database/Models/Model.php");
require_once VV::root("src/Database/Models/Work/Work.php");
require_once VV::root("src/Database/Tables/Work/Actions.php");
class Action extends Model {
public function __construct() {
parent::__construct();
}
public static function all(array $params = []): array {
return array_map(fn(array $item): Action => (new Action())->assign($item), parent::list(Endpoints::WORK_ACTIONS, $params));
}
public static function from(Work $work): array {
return self::all([
ActionsTable::REF_WORK_ID->value => $work->id
]);
}
public function icon_prepended(): ?string {
return $this->get(ActionsTable::ICON_PREPENDED->value);
}
public function icon_appended(): ?string {
return $this->get(ActionsTable::ICON_APPENDED->value);
}
public function display_text(): string {
return $this->get(ActionsTable::DISPLAY_TEXT->value);
}
public function href(): ?string {
return $this->get(ActionsTable::HREF->value);
}
public function classes(): array {
return $this->get(ActionsTable::CLASS_LIST->value) ? explode(",", $this->get(ActionsTable::CLASS_LIST->value)) : [];
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace VLW\Database\Models\Work;
use \VV;
use VLW\API\Endpoints;
use VLW\Database\Models\Model;
use VLW\Database\Models\Work\Work;
use VLW\Database\Tables\Work\{TagsTable, TagsLabelEnum};
require_once VV::root("src/API/Endpoints.php");
require_once VV::root("src/Database/Models/Model.php");
require_once VV::root("src/Database/Models/Work/Work.php");
require_once VV::root("src/Database/Tables/Work/Tags.php");
class Tag extends Model {
public function __construct() {
parent::__construct();
}
public static function all(array $params = []): array {
return array_map(fn(array $item): Tag => (new Tag())->assign($item), parent::list(Endpoints::WORK_TAGS, $params));
}
public static function from(Work $work): array {
return self::all([
TagsTable::REF_WORK_ID->value => $work->id
]);
}
public function label(): TagsLabelEnum {
return TagsLabelEnum::from($this->get(TagsTable::LABEL->value));
}
}

View file

@ -1,26 +1,28 @@
<?php <?php
namespace VLW\Database\Models; namespace VLW\Database\Models\Work;
use \VV; use \VV;
use VLW\Endpoints; use VLW\API\Endpoints;
use VLW\Database\Models\Model; use VLW\Database\Models\Model;
use VLW\Database\Models\Work\Work; use VLW\Database\Models\Work\Work;
use VLW\Database\Tables\Work\TimelineTable;
require_once VV::root("src/Endpoints.php"); require_once VV::root("src/API/Endpoints.php");
require_once VV::root("src/Database/Models/Model.php"); require_once VV::root("src/Database/Models/Model.php");
require_once VV::root("src/Database/Models/Work/Work.php"); require_once VV::root("src/Database/Models/Work/Work.php");
require_once VV::root("src/Database/Tables/Work/Timeline.php");
class Timeline extends Model { class Timeline extends Model {
public function __construct(public readonly string $id) { public function __construct(public readonly string $id) {
parent::__construct(Endpoints::TIMELINE, [ parent::__construct(Endpoints::WORK_TIMELINE, [
TimelineTable::ID->value => $this->id TimelineTable::REF_WORK_ID->value => $this->id
]); ]);
} }
public static function list(): array { public static function all(array $params = []): array {
return array_map(fn(array $item): Timeline => new Timeline($item[TimelineTable::ID->value]), parent::list(Endpoints::TIMELINE)); return array_map(fn(array $item): Timeline => new Timeline($item[TimelineTable::REF_WORK_ID->value]), parent::list(Endpoints::WORK_TIMELINE, $params));
} }
public function work(): Work { public function work(): Work {

View file

@ -2,4 +2,47 @@
namespace VLW\Database\Models\Work; namespace VLW\Database\Models\Work;
class Work use \VV;
use VLW\API\Endpoints;
use VLW\Database\Models\Model;
use VLW\Database\Tables\Work\WorkTable;
use VLW\Database\Models\Work\{Tag, Action};
require_once VV::root("src/API/Endpoints.php");
require_once VV::root("src/Database/Models/Model.php");
require_once VV::root("src/Database/Models/Work/Tag.php");
require_once VV::root("src/Database/Tables/Work/Work.php");
require_once VV::root("src/Database/Models/Work/Action.php");
class Work extends Model {
public function __construct(public readonly string $id) {
parent::__construct(Endpoints::WORK, [
WorkTable::ID->value => $this->id
]);
}
public static function all(array $params = []): array {
return array_map(fn(array $item): Work => new Work($item[WorkTable::ID->value]), parent::list(Endpoints::WORK, $params));
}
public function title(): ?string {
return $this->get(WorkTable::TITLE->value);
}
public function summary(): string {
return $this->get(WorkTable::SUMMARY->value);
}
public function created(): DateTimeImmutable {
return new DateTimeImmutable($this->get(WorkTable::CREATED->value));
}
public function tags(): array {
return Tag::from($this);
}
public function actions(): array {
return Action::from($this);
}
}

View file

@ -0,0 +1,14 @@
<?php
namespace VLW\Database\Tables\About;
use vlw\xEnum;
enum LanguagesTable: string {
use xEnum;
const NAME = "about_languages";
case ID = "id";
case BYTES = "bytes";
}

View file

@ -5,11 +5,7 @@
enum MessagesTable: string { enum MessagesTable: string {
const NAME = "messages"; const NAME = "messages";
case ID = "id";
case EMAIL = "email"; case EMAIL = "email";
case MESSAGE = "message"; case MESSAGE = "message";
case IS_READ = "is_read"; case TIMESTAMP_CREATED = "timestamp_created";
case IS_SPAM = "is_spam";
case IS_SAVED = "is_saved";
case DATE_CREATED = "date_created";
} }

View file

@ -2,12 +2,16 @@
namespace VLW\Database\Tables\Work; namespace VLW\Database\Tables\Work;
use vlw\xEnum;
enum ActionsTable: string { enum ActionsTable: string {
use xEnum;
const NAME = "work_actions"; const NAME = "work_actions";
case REF_WORK_ID = "ref_work_id"; case REF_WORK_ID = "ref_work_id";
case ICON_PREFIX = "icon_prefix"; case ICON_PREPENDED = "icon_prepended";
case ICON_SUFFIX = "icon_suffix"; case ICON_APPENDED = "icon_appended";
case ORDER_IDX = "order_idx"; case ORDER_IDX = "order_idx";
case DISPLAY_TEXT = "display_text"; case DISPLAY_TEXT = "display_text";
case HREF = "href"; case HREF = "href";

View file

@ -4,18 +4,20 @@
use vlw\xEnum; use vlw\xEnum;
enum TagsNameEnum { enum TagsLabelEnum: string {
use xEnum; use xEnum;
case VLW; case VLW = "VLW";
case RELEASE; case RELEASE = "RELEASE";
case WEBSITE; case WEBSITE = "WEBSITE";
case REPO; case REPO = "REPO";
} }
enum TagsTable: string { enum TagsTable: string {
use xEnum;
const NAME = "work_tags"; const NAME = "work_tags";
case REF_WORK_ID = "ref_work_id"; case REF_WORK_ID = "ref_work_id";
case NAME = "name"; case LABEL = "label";
} }

View file

@ -0,0 +1,16 @@
<?php
namespace VLW\Database\Tables\Work;
use vlw\xEnum;
enum TimelineTable: string {
use xEnum;
const NAME = "work_timeline";
case REF_WORK_ID = "ref_work_id";
case YEAR = "year";
case MONTH = "month";
case DAY = "day";
}

View file

@ -12,11 +12,5 @@
case ID = "id"; case ID = "id";
case TITLE = "title"; case TITLE = "title";
case SUMMARY = "summary"; case SUMMARY = "summary";
case COVER_SRCSET = "cover_srcset"; case CREATED = "created";
case IS_LISTED = "is_listed";
case DATE_YEAR = "date_year";
case DATE_MONTH = "date_month";
case DATE_DAY = "date_day";
case DATE_MODIFIED = "date_modified";
case DATE_CREATED = "date_created";
} }