mirror of
https://codeberg.org/vlw/vlw.se.git
synced 2025-09-13 21:13:40 +02:00
Compare commits
37 commits
Author | SHA1 | Date | |
---|---|---|---|
e8a81b789b | |||
5e9317def5 | |||
8209ea5ecc | |||
85e8e00091 | |||
d4d73e9278 | |||
fafa8c5852 | |||
03492df615 | |||
37f2ac00c3 | |||
d5cc7fa82c | |||
17bd93dca9 | |||
6b5eee505a | |||
cd3139e778 | |||
afe450a6d2 | |||
b398354594 | |||
62ddd25f38 | |||
fef10a8ea8 | |||
a6c74f5c4f | |||
f60a27855d | |||
c5c7aaa919 | |||
f4279c0343 | |||
f76962e2ac | |||
5c7c9d2d3a | |||
041d175757 | |||
0bb7a3a8be | |||
0baa6f8d85 | |||
4b71bbd10e | |||
56cf142e0d | |||
2fa62991f9 | |||
359342c7f7 | |||
e420f33d4a | |||
e25b1b6689 | |||
3b51458dd4 | |||
ff7d4f5397 | |||
07e0046828 | |||
18e1ae5088 | |||
9e3c549e0a | |||
80c6579136 |
184 changed files with 3258 additions and 4652 deletions
|
@ -1,7 +1,15 @@
|
|||
[api]
|
||||
base_url = "https://api.vlw.one/"
|
||||
api_key = ""
|
||||
verify_peer = 0
|
||||
[mariadb]
|
||||
host = ""
|
||||
user = ""
|
||||
pass = ""
|
||||
db = ""
|
||||
|
||||
[time]
|
||||
date_time_zone = "Europe/Stockholm"
|
||||
[config_time_available]
|
||||
time_zone = "Europe/Stockholm"
|
||||
available_to_hour = 0;
|
||||
reply_average_hours = 0;
|
||||
available_from_hour = 0;
|
||||
|
||||
[service_forgejo]
|
||||
url = ""
|
||||
profiles = ""
|
24
.gitignore
vendored
24
.gitignore
vendored
|
@ -1,24 +1,2 @@
|
|||
# Public assets #
|
||||
#################
|
||||
public/robots.txt
|
||||
public/.well-known
|
||||
|
||||
assets/js/modules/npm
|
||||
|
||||
# Bootstrapping #
|
||||
#################
|
||||
vendor
|
||||
node_modules
|
||||
.env.ini
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
Icon?
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
.directory
|
||||
.env.ini
|
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
[submodule "reflect"]
|
||||
path = reflect
|
||||
url = https://codeberg.org/reflect/reflect
|
||||
[submodule "vegvisir"]
|
||||
path = vegvisir
|
||||
url = https://codeberg.org/vegvisir/vegvisir
|
88
README.md
88
README.md
|
@ -1,73 +1,27 @@
|
|||
# vlw.se
|
||||
This is the source code behind [vlw.se](https://vlw.se) which has been written from the ground up by me. This website is built on top of my [Vegvisir web framework](https://vegvisir.vlw.se) and my [Reflect API framework](https://reflect.vlw.se).
|
||||
This is the source code behind [vlw.se](https://vlw.se) which is my personal website that I have written and designed from the ground up. The website is built on top of my own [Vegvisir web framework](https://vegvisir.vlw.se) and its optional REST API is built on top of my [Reflect API framework](https://reflect.vlw.se).
|
||||
|
||||
# Installation
|
||||
If you for whatever reason want to get this website up and running for yourself this is how that is done.
|
||||
Here's how you get my website up and running on your own machine. Note, I have only tested this on Linux and the install script we will run requires Bash with `coreutils` installed.
|
||||
|
||||
## This website requires the following prerequisites
|
||||
- [PHP 8.0+](https://www.php.net/)
|
||||
- [MariaDB 14+](https://mariadb.org/)
|
||||
- [The NPM package manager](https://www.npmjs.com/)
|
||||
- [The Reflect API framework](https://reflect.vlw.se)
|
||||
- [The Vegvisir web framework](https://vegvisir.vlw.se)
|
||||
- [The composer package manager](https://getcomposer.org/)
|
||||
## Prerequisites
|
||||
- A web server
|
||||
- A MariaDB/MySQL server
|
||||
- PHP 8.4 or newer with the following extensions enabled:
|
||||
- - `php8.4-mysql`
|
||||
- - `php8.4-mbstring`
|
||||
- The composer package manager
|
||||
- Bash with `coreutils` installed (for the install script)
|
||||
|
||||
**Confimed supported framework versions:**
|
||||
Vegvisir|Reflect
|
||||
--|--
|
||||
✅ [`3.0.1`](https://codeberg.org/vegvisir/vegvisir/releases/tag/3.0.1)|✅ [`2.7.2`](https://codeberg.org/reflect/reflect/releases/tag/2.7.2)
|
||||
## 1. Clone this repo
|
||||
Clone this repository with its submodules. Preferably to a non-public directory - the frameworks will handle that.
|
||||
```
|
||||
git clone https://codeberg.org/vlw/vlw.se --recurse-submodules --depth 1
|
||||
```
|
||||
|
||||
## Website (Vegvisir)
|
||||
1. **Download this repo**
|
||||
|
||||
Git clone or download this repo to any local folder
|
||||
```
|
||||
git clone https://codeberg.org/vlw/vlw.se
|
||||
```
|
||||
2. **Download and install Vegvisir**
|
||||
|
||||
Follow the installation instructions for [Vegvisir](https://vegvisir.vlw.se/docs/installation) and point the `root_path` variable to your local vlw.se folder.
|
||||
|
||||
3. **Run the install script**
|
||||
|
||||
This bash script will install dependencies and make npm modules public.
|
||||
```
|
||||
./install.sh
|
||||
```
|
||||
|
||||
Et voila! You probably want to install the API-side too but the website itself should now be accessible from your configured Vegvisir host.
|
||||
|
||||
## API (Reflect)
|
||||
The API (and database) is where most content is stored and served from on this website.
|
||||
|
||||
1. **Download this repo**
|
||||
|
||||
**You can skip this if you've already downloaded the repo from step 1 in the website installation.**
|
||||
|
||||
Otherwise... Git clone or download this repo to any local folder
|
||||
```
|
||||
git clone https://codeberg.org/vlw/vlw.se
|
||||
```
|
||||
|
||||
2. **Download and install Reflect**
|
||||
|
||||
Follow the installation instructions for [Reflect](https://reflect.vlw.se/docs/installation) and point the `endpoints` variable to the `/api` subdirectory in the local vlw.se folder.
|
||||
|
||||
3. **Install dependencies**
|
||||
|
||||
`cd` into the api folder and install dependencies with composer.
|
||||
```
|
||||
composer install --optimize-autoloader
|
||||
```
|
||||
|
||||
4. **Create and import database**
|
||||
|
||||
[Create and] import the two databases associated with vlw.se data and the Reflect API configurations from `.sql` files on the Releases page.
|
||||
|
||||
5. **Set environment variables**
|
||||
|
||||
Make a copy of `/api/.env.example.ini` and change the `[vlwdb]` variables with your MariaDB credentials.
|
||||
|
||||
6. **Set environment variables for website**
|
||||
|
||||
It's reasonable to assume if you've installed the website from this repo that you'd also want to use the API with it. Start my making a copy of `/.env.example.ini` (root directory) and change the `[api]` variables to point to your API hostname.
|
||||
## 2. Run the install script
|
||||
Run the `install.sh` script from the root directory of this repository.
|
||||
```
|
||||
./install.sh
|
||||
```
|
||||
This script will install and configure Vegvisir, Reflect, and the website through a few propmpted steps.
|
|
@ -1,8 +0,0 @@
|
|||
[connect]
|
||||
host = ""
|
||||
user = ""
|
||||
pass = ""
|
||||
|
||||
[databases]
|
||||
vlw = ""
|
||||
battlestation = ""
|
29
api/coffee/DELETE.php
Normal file
29
api/coffee/DELETE.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
use Reflect\{Response, Path};
|
||||
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||
|
||||
use VLW\API\API;
|
||||
use VLW\Helpers\UUID;
|
||||
use VLW\Database\Models\Coffee\Coffee;
|
||||
use VLW\Database\Tables\Coffee\Coffee as CoffeeTable;
|
||||
|
||||
require_once Path::root("src/API/API.php");
|
||||
require_once Path::root("src/Database/Models/Coffee/Coffee.php");
|
||||
require_once Path::root("src/Database/Tables/Coffee/Coffee.php");
|
||||
|
||||
final class DELETE_Coffee extends API {
|
||||
public function __construct() {
|
||||
parent::__construct(new Ruleset()->GET([
|
||||
new Rules(CoffeeTable::ID->value)
|
||||
->required()
|
||||
->type(Type::STRING)
|
||||
->min(UUID::LENGTH)
|
||||
->max(UUID::LENGTH)
|
||||
]));
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
return new Response(new Coffee($_GET[CoffeeTable::ID->value])->delete());
|
||||
}
|
||||
}
|
19
api/coffee/GET.php
Normal file
19
api/coffee/GET.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
use Reflect\{Response, Path};
|
||||
|
||||
use VLW\API\API;
|
||||
use VLW\Database\Models\Coffee\Coffee;
|
||||
|
||||
require_once Path::root("src/API/API.php");
|
||||
require_once Path::root("src/Database/Models/Coffee/Coffee.php");
|
||||
|
||||
final class GET_Coffee extends API {
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
return new Response(Coffee::all());
|
||||
}
|
||||
}
|
41
api/coffee/POST.php
Normal file
41
api/coffee/POST.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
use Reflect\{Response, Path};
|
||||
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||
|
||||
use VLW\API\API;
|
||||
use VLW\Database\Models\Coffee\Coffee;
|
||||
use VLW\Database\Tables\Coffee\Coffee as CoffeeTable;
|
||||
|
||||
require_once Path::root("src/API/API.php");
|
||||
require_once Path::root("src/Database/Models/Coffee/Coffee.php");
|
||||
require_once Path::root("src/Database/Tables/Coffee/Coffee.php");
|
||||
|
||||
final class POST_Coffee extends API {
|
||||
public function __construct() {
|
||||
parent::__construct(new Ruleset(strict: true)->POST([
|
||||
new Rules(CoffeeTable::DATE_CREATED->value)
|
||||
->type(Type::STRING)
|
||||
->type(Type::NUMBER)
|
||||
->default(null)
|
||||
]));
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
$datetime = new DateTimeImmutable();
|
||||
|
||||
// Parse DateTime from POST string
|
||||
if ($_POST[CoffeeTable::DATE_CREATED->value]) {
|
||||
try {
|
||||
// Create DateTimeImmutable from Unix timestamp or datetime string
|
||||
$datetime = gettype($_POST[CoffeeTable::DATE_CREATED->value]) === "integer"
|
||||
? DateTimeImmutable::createFromTimestamp($_POST[CoffeeTable::DATE_CREATED->value])
|
||||
: new DateTimeImmutable($_POST[CoffeeTable::DATE_CREATED->value]);
|
||||
} catch (DateMalformedStringException $error) {
|
||||
return new Response($error->getMessage(), 400);
|
||||
}
|
||||
}
|
||||
|
||||
return new Response(Coffee::new($datetime));
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"require": {
|
||||
"reflect/plugin-rules": "^1.5",
|
||||
"victorwesterlund/xenum": "dev-master",
|
||||
"vlw/mysql": "dev-master"
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
125
api/composer.lock
generated
125
api/composer.lock
generated
|
@ -1,125 +0,0 @@
|
|||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "f3f2b3cb3bd789eee6af4a93f4a6e0f9",
|
||||
"packages": [
|
||||
{
|
||||
"name": "reflect/plugin-rules",
|
||||
"version": "1.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/VictorWesterlund/reflect-rules-plugin.git",
|
||||
"reference": "9c837fd1944133edfed70a63ce8b32bf67f0d94b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/VictorWesterlund/reflect-rules-plugin/zipball/9c837fd1944133edfed70a63ce8b32bf67f0d94b",
|
||||
"reference": "9c837fd1944133edfed70a63ce8b32bf67f0d94b",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ReflectRules\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"GPL-3.0-only"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Victor Westerlund",
|
||||
"email": "victor.vesterlund@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Add request search paramter and request body constraints to an API built with Reflect",
|
||||
"support": {
|
||||
"issues": "https://github.com/VictorWesterlund/reflect-rules-plugin/issues",
|
||||
"source": "https://github.com/VictorWesterlund/reflect-rules-plugin/tree/1.5.0"
|
||||
},
|
||||
"time": "2024-01-17T11:07:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "victorwesterlund/xenum",
|
||||
"version": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/VictorWesterlund/php-xenum.git",
|
||||
"reference": "8972f06f42abd1f382807a67e937d5564bb89699"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/VictorWesterlund/php-xenum/zipball/8972f06f42abd1f382807a67e937d5564bb89699",
|
||||
"reference": "8972f06f42abd1f382807a67e937d5564bb89699",
|
||||
"shasum": ""
|
||||
},
|
||||
"default-branch": true,
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"victorwesterlund\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"GPL-3.0-only"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Victor Westerlund",
|
||||
"email": "victor.vesterlund@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP eXtended Enums. The missing quality-of-life features from PHP 8+ Enums",
|
||||
"support": {
|
||||
"issues": "https://github.com/VictorWesterlund/php-xenum/issues",
|
||||
"source": "https://github.com/VictorWesterlund/php-xenum/tree/1.1.1"
|
||||
},
|
||||
"time": "2023-11-20T10:10:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "vlw/mysql",
|
||||
"version": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://codeberg.org/vlw/php-mysql",
|
||||
"reference": "619f43b3bfab9eb034dca3e54c7466055240c861"
|
||||
},
|
||||
"default-branch": true,
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"vlw\\MySQL\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"GPL-3.0-or-later"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Victor Westerlund",
|
||||
"email": "victor@vlw.se"
|
||||
}
|
||||
],
|
||||
"description": "Abstraction library for common MySQL/MariaDB DML operations with php-mysqli",
|
||||
"time": "2024-09-25T13:28:15+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": {
|
||||
"victorwesterlund/xenum": 20,
|
||||
"vlw/mysql": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.0.0"
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\Config\ConfigModel;
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Config/Config.php");
|
||||
|
||||
class GET_Battlestation extends VLWdb {
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
private array $query;
|
||||
private array $results = [];
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(ConfigModel::REF_MB_ID->value))
|
||||
->type(Type::STRING)
|
||||
->min(parent::UUID_LENGTH)
|
||||
->max(parent::UUID_LENGTH),
|
||||
|
||||
(new Rules(ConfigModel::FRIENDLY_NAME->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::BATTLESTATION, $this->ruleset);
|
||||
|
||||
// Use a copy of search parameters
|
||||
$this->query = $_GET;
|
||||
}
|
||||
|
||||
private function get_config(): array {
|
||||
return $this->results = $this->db
|
||||
->for(ConfigModel::TABLE)
|
||||
->where($this->query)
|
||||
->order([ConfigModel::DATE_BUILT->value => "DESC"])
|
||||
->select(ConfigModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Set properties as "searchable"
|
||||
parent::make_wildcard_search(ConfigModel::FRIENDLY_NAME->value, $this->query);
|
||||
|
||||
$this->get_config();
|
||||
|
||||
// Return 404 Not Found if response array is empty
|
||||
return new Response($this->results, $this->results ? 200 : 404);
|
||||
}
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\ChassisModel;
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\Config\ChassisMbModel;
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Chassis.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Config/ChassisMb.php");
|
||||
|
||||
class GET_BattlestationChassis extends VLWdb {
|
||||
private const REL_MOTHERBOARDS = "motherboards";
|
||||
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
private array $query;
|
||||
private array $results = [];
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(ChassisModel::ID->value))
|
||||
->type(Type::STRING)
|
||||
->min(parent::UUID_LENGTH)
|
||||
->max(parent::UUID_LENGTH),
|
||||
|
||||
(new Rules(ChassisModel::VENDOR_NAME->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(ChassisModel::VENDOR_MODEL->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(ChassisModel::STORAGE_TWOINCHFIVE->value))
|
||||
->type(Type::NUMBER)
|
||||
->type(Type::NULL)
|
||||
->min(0)
|
||||
->max(parent::MYSQL_TINYINT_MAX_LENGTH),
|
||||
|
||||
(new Rules(ChassisModel::STORAGE_THREEINCHFIVE->value))
|
||||
->type(Type::NUMBER)
|
||||
->type(Type::NULL)
|
||||
->min(0)
|
||||
->max(parent::MYSQL_TINYINT_MAX_LENGTH),
|
||||
|
||||
(new Rules(ChassisModel::IS_RETIRED->value))
|
||||
->type(Type::BOOLEAN)
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::BATTLESTATION, $this->ruleset);
|
||||
|
||||
// Use a copy of search parameters
|
||||
$this->query = $_GET;
|
||||
}
|
||||
|
||||
private function get_motherboards(): void {
|
||||
foreach ($this->results as &$result) {
|
||||
// Get motherboard id from relationship by chassis id
|
||||
$result[self::REL_MOTHERBOARDS] = $this->db
|
||||
->for(ChassisMbModel::TABLE)
|
||||
->where([ChassisMbModel::REF_CHASSIS_ID->value => $result[ChassisModel::ID->value]])
|
||||
->select(ChassisMbModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_chassis(): array {
|
||||
return $this->results = $this->db
|
||||
->for(ChassisModel::TABLE)
|
||||
->where($this->query)
|
||||
->order([ChassisModel::DATE_AQUIRED->value => "DESC"])
|
||||
->select(ChassisModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Set properties as "searchable"
|
||||
parent::make_wildcard_search(ChassisModel::VENDOR_NAME->value, $this->query);
|
||||
parent::make_wildcard_search(ChassisModel::VENDOR_MODEL->value, $this->query);
|
||||
|
||||
// Get hardware
|
||||
$this->get_chassis();
|
||||
|
||||
// Resolve hardware relationships
|
||||
$this->get_motherboards();
|
||||
|
||||
// Return 404 Not Found if response array is empty
|
||||
return new Response($this->results, $this->results ? 200 : 404);
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\{
|
||||
CoolerModel
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\Config\MbCpuCoolerModel;
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Coolers.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Config/MbCpuCooler.php");
|
||||
|
||||
class GET_BattlestationCoolers extends VLWdb {
|
||||
private const REL_MOTHERBOARDS = "motherboards";
|
||||
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
private array $query;
|
||||
private array $results = [];
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(CoolerModel::ID->value))
|
||||
->type(Type::STRING)
|
||||
->min(parent::UUID_LENGTH)
|
||||
->max(parent::UUID_LENGTH),
|
||||
|
||||
(new Rules(CoolerModel::TYPE_LIQUID->value))
|
||||
->type(Type::BOOLEAN),
|
||||
|
||||
(new Rules(CoolerModel::SIZE_FAN->value))
|
||||
->type(Type::NULL)
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_INTR_MAX_LENGTH),
|
||||
|
||||
(new Rules(CoolerModel::SIZE_RADIATOR->value))
|
||||
->type(Type::NULL)
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_INTR_MAX_LENGTH),
|
||||
|
||||
(new Rules(CoolerModel::VENDOR_NAME->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(CoolerModel::VENDOR_MODEL->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(CoolerModel::IS_RETIRED->value))
|
||||
->type(Type::BOOLEAN)
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::BATTLESTATION, $this->ruleset);
|
||||
|
||||
// Use a copy of search parameters
|
||||
$this->query = $_GET;
|
||||
}
|
||||
|
||||
private function get_motherboards(): void {
|
||||
foreach ($this->results as &$result) {
|
||||
// Get motherboard id from relationship by chassis id
|
||||
$result[self::REL_MOTHERBOARDS] = $this->db
|
||||
->for(MbCoolerModel::TABLE)
|
||||
->where([MbCoolerModel::REF_COOLER_ID->value => $result[CoolerModel::ID->value]])
|
||||
->select(MbCoolerModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_coolers(): array {
|
||||
return $this->results = $this->db
|
||||
->for(CoolerModel::TABLE)
|
||||
->where($this->query)
|
||||
->order([CoolerModel::DATE_AQUIRED->value => "DESC"])
|
||||
->select(CoolerModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Set properties as "searchable"
|
||||
parent::make_wildcard_search(CoolerModel::VENDOR_NAME->value, $this->query);
|
||||
parent::make_wildcard_search(CoolerModel::VENDOR_MODEL->value, $this->query);
|
||||
|
||||
// Get hardware
|
||||
$this->get_coolers();
|
||||
|
||||
// Resolve hardware relationships
|
||||
$this->get_motherboards();
|
||||
|
||||
// Return 404 Not Found if response array is empty
|
||||
return new Response($this->results, $this->results ? 200 : 404);
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\{
|
||||
CpuModel,
|
||||
ClassEnum
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\Config\MbCpuCoolerModel;
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Cpu.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Config/MbCpuCooler.php");
|
||||
|
||||
class GET_BattlestationCpu extends VLWdb {
|
||||
private const REL_MOTHERBOARDS = "motherboards";
|
||||
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
private array $query;
|
||||
private array $results = [];
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(CpuModel::ID->value))
|
||||
->type(Type::STRING)
|
||||
->min(parent::UUID_LENGTH)
|
||||
->max(parent::UUID_LENGTH),
|
||||
|
||||
(new Rules(CpuModel::CLOCK_BASE->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1),
|
||||
|
||||
(new Rules(CpuModel::CLOCK_TURBO->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1),
|
||||
|
||||
(new Rules(CpuModel::CORE_COUNT_PERFORMANCE->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_TINYINT_MAX_LENGTH),
|
||||
|
||||
(new Rules(CpuModel::CORE_COUNT_EFFICIENCY->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_TINYINT_MAX_LENGTH),
|
||||
|
||||
(new Rules(CpuModel::CORE_THREADS->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_TINYINT_MAX_LENGTH),
|
||||
|
||||
(new Rules(CpuModel::VENDOR_NAME->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(CpuModel::VENDOR_MODEL->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(CpuModel::IS_RETIRED->value))
|
||||
->type(Type::BOOLEAN)
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::BATTLESTATION, $this->ruleset);
|
||||
|
||||
// Use a copy of search parameters
|
||||
$this->query = $_GET;
|
||||
}
|
||||
|
||||
private function get_motherboards(): void {
|
||||
foreach ($this->results as &$result) {
|
||||
// Get motherboard id from relationship by chassis id
|
||||
$result[self::REL_MOTHERBOARDS] = $this->db
|
||||
->for(MbCpuCoolerModel::TABLE)
|
||||
->where([MbCpuCoolerModel::REF_CPU_ID->value => $result[CpuModel::ID->value]])
|
||||
->select(MbCpuCoolerModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_cpu(): array {
|
||||
return $this->results = $this->db
|
||||
->for(CpuModel::TABLE)
|
||||
->where($this->query)
|
||||
->order([CpuModel::DATE_AQUIRED->value => "DESC"])
|
||||
->select(CpuModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Set properties as "searchable"
|
||||
parent::make_wildcard_search(CpuModel::VENDOR_NAME->value, $this->query);
|
||||
parent::make_wildcard_search(CpuModel::VENDOR_MODEL->value, $this->query);
|
||||
|
||||
// Get hardware
|
||||
$this->get_cpu();
|
||||
|
||||
// Resolve hardware relationships
|
||||
$this->get_motherboards();
|
||||
|
||||
// Return 404 Not Found if response array is empty
|
||||
return new Response($this->results, $this->results ? 200 : 404);
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\{
|
||||
DramModel,
|
||||
DramFormfactorEnum,
|
||||
DramTechnologyEnum
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\Config\MbDramModel;
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Dram.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Config/MbDram.php");
|
||||
|
||||
class GET_BattlestationDram extends VLWdb {
|
||||
private const REL_MOTHERBOARDS = "motherboards";
|
||||
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
private array $query;
|
||||
private array $results = [];
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(DramModel::ID->value))
|
||||
->type(Type::STRING)
|
||||
->min(parent::UUID_LENGTH)
|
||||
->max(parent::UUID_LENGTH),
|
||||
|
||||
(new Rules(DramModel::CAPACITY->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1),
|
||||
|
||||
(new Rules(DramModel::SPEED->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1),
|
||||
|
||||
(new Rules(DramModel::FORMFACTOR->value))
|
||||
->type(Type::ENUM, DramFormfactorEnum::names()),
|
||||
|
||||
(new Rules(DramModel::TECHNOLOGY->value))
|
||||
->type(Type::ENUM, DramTechnologyEnum::names()),
|
||||
|
||||
(new Rules(DramModel::ECC->value))
|
||||
->type(Type::BOOLEAN),
|
||||
|
||||
(new Rules(DramModel::BUFFERED->value))
|
||||
->type(Type::BOOLEAN),
|
||||
|
||||
(new Rules(DramModel::VENDOR_NAME->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(DramModel::VENDOR_MODEL->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(DramModel::IS_RETIRED->value))
|
||||
->type(Type::BOOLEAN)
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::BATTLESTATION, $this->ruleset);
|
||||
|
||||
// Use a copy of search parameters
|
||||
$this->query = $_GET;
|
||||
}
|
||||
|
||||
private function get_motherboards(): void {
|
||||
foreach ($this->results as &$result) {
|
||||
// Get motherboard id from relationship by chassis id
|
||||
$result[self::REL_MOTHERBOARDS] = $this->db
|
||||
->for(MbDramModel::TABLE)
|
||||
->where([MbDramModel::REF_DRAM_ID->value => $result[DramModel::ID->value]])
|
||||
->select(MbDramModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_dram(): array {
|
||||
return $this->results = $this->db
|
||||
->for(DramModel::TABLE)
|
||||
->where($this->query)
|
||||
->order([DramModel::DATE_AQUIRED->value => "DESC"])
|
||||
->select(DramModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Set properties as "searchable"
|
||||
parent::make_wildcard_search(DramModel::VENDOR_NAME->value, $this->query);
|
||||
parent::make_wildcard_search(DramModel::VENDOR_MODEL->value, $this->query);
|
||||
|
||||
// Get hardware
|
||||
$this->get_dram();
|
||||
|
||||
// Resolve hardware relationships
|
||||
$this->get_motherboards();
|
||||
|
||||
// Return 404 Not Found if response array is empty
|
||||
return new Response($this->results, $this->results ? 200 : 404);
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\GpuModel;
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\Config\MbGpuModel;
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Gpu.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Config/MbGpu.php");
|
||||
|
||||
class GET_BattlestationGpu extends VLWdb {
|
||||
private const REL_MOTHERBOARDS = "motherboards";
|
||||
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
private array $query;
|
||||
private array $results = [];
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(GpuModel::ID->value))
|
||||
->type(Type::STRING)
|
||||
->min(parent::UUID_LENGTH)
|
||||
->max(parent::UUID_LENGTH),
|
||||
|
||||
(new Rules(GpuModel::MEMORY->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1),
|
||||
|
||||
(new Rules(GpuModel::VENDOR_NAME->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(GpuModel::VENDOR_MODEL->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(GpuModel::VENDOR_CHIP_NAME->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(GpuModel::VENDOR_CHIP_MODEL->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(GpuModel::IS_RETIRED->value))
|
||||
->type(Type::BOOLEAN)
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::BATTLESTATION, $this->ruleset);
|
||||
|
||||
// Use a copy of search parameters
|
||||
$this->query = $_GET;
|
||||
}
|
||||
|
||||
private function get_motherboards(): void {
|
||||
foreach ($this->results as &$result) {
|
||||
// Get motherboard id from relationship by chassis id
|
||||
$result[self::REL_MOTHERBOARDS] = $this->db
|
||||
->for(MbGpuModel::TABLE)
|
||||
->where([MbGpuModel::REF_GPU_ID->value => $result[GpuModel::ID->value]])
|
||||
->select(MbGpuModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_gpu(): array {
|
||||
return $this->results = $this->db
|
||||
->for(GpuModel::TABLE)
|
||||
->where($this->query)
|
||||
->order([GpuModel::DATE_AQUIRED->value => "DESC"])
|
||||
->select(GpuModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Set properties as "searchable"
|
||||
parent::make_wildcard_search(GpuModel::VENDOR_NAME->value, $this->query);
|
||||
parent::make_wildcard_search(GpuModel::VENDOR_MODEL->value, $this->query);
|
||||
parent::make_wildcard_search(GpuModel::VENDOR_CHIP_NAME->value, $this->query);
|
||||
parent::make_wildcard_search(GpuModel::VENDOR_CHIP_MODEL->value, $this->query);
|
||||
|
||||
// Get hardware
|
||||
$this->get_gpu();
|
||||
|
||||
// Resolve hardware relationships
|
||||
$this->get_motherboards();
|
||||
|
||||
// Return 404 Not Found if response array is empty
|
||||
return new Response($this->results, $this->results ? 200 : 404);
|
||||
}
|
||||
}
|
|
@ -1,194 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\{
|
||||
MbModel,
|
||||
MbFormfactorEnum
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\Config\{
|
||||
MbGpuModel,
|
||||
MbPsuModel,
|
||||
MbDramModel,
|
||||
MbStorageModel,
|
||||
ChassisMbModel,
|
||||
MbCpuCoolerModel
|
||||
};
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Mb.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Config/MbPsu.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Config/MbGpu.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Config/MbDram.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Config/MbStorage.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Config/ChassisMb.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Config/MbCpuCooler.php");
|
||||
|
||||
class GET_BattlestationMb extends VLWdb {
|
||||
private const REL_CPU = "cpus";
|
||||
private const REL_PSU = "psus";
|
||||
private const REL_GPU = "gpus";
|
||||
private const REL_DRAM = "dram";
|
||||
private const REL_STORAGE = "storage";
|
||||
private const REL_CHASSIS = "chassis";
|
||||
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
private array $query;
|
||||
private array $results = [];
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(MbModel::ID->value))
|
||||
->type(Type::STRING)
|
||||
->min(parent::UUID_LENGTH)
|
||||
->max(parent::UUID_LENGTH),
|
||||
|
||||
(new Rules(MbModel::FORMFACTOR->value))
|
||||
->type(Type::ENUM, MbFormfactorEnum::names()),
|
||||
|
||||
(new Rules(MbModel::VENDOR_NAME->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(MbModel::VENDOR_MODEL->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(MbModel::NETWORK_ETHERNET->value))
|
||||
->type(Type::NULL)
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(MbModel::NETWORK_WLAN->value))
|
||||
->type(Type::NULL)
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(MbModel::NETWORK_BLUETOOTH->value))
|
||||
->type(Type::NULL)
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(MbModel::IS_RETIRED->value))
|
||||
->type(Type::BOOLEAN)
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::BATTLESTATION, $this->ruleset);
|
||||
|
||||
// Use a copy of search parameters
|
||||
$this->query = $_GET;
|
||||
}
|
||||
|
||||
private function get_chassis(): void {
|
||||
foreach ($this->results as &$result) {
|
||||
// Get motherboard id from relationship by chassis id
|
||||
$result[self::REL_CHASSIS] = $this->db
|
||||
->for(ChassisMbModel::TABLE)
|
||||
->where([ChassisMbModel::REF_MB_ID->value => $result[MbModel::ID->value]])
|
||||
->select(ChassisMbModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_psu(): void {
|
||||
foreach ($this->results as &$result) {
|
||||
// Get motherboard id from relationship by chassis id
|
||||
$result[self::REL_PSU] = $this->db
|
||||
->for(MbPsuModel::TABLE)
|
||||
->where([MbPsuModel::REF_MB_ID->value => $result[MbModel::ID->value]])
|
||||
->select(MbPsuModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_cpu(): void {
|
||||
foreach ($this->results as &$result) {
|
||||
// Get motherboard id from relationship by chassis id
|
||||
$result[self::REL_CPU] = $this->db
|
||||
->for(MbCpuCoolerModel::TABLE)
|
||||
->where([MbCpuCoolerModel::REF_MB_ID->value => $result[MbModel::ID->value]])
|
||||
->select(MbCpuCoolerModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_gpu(): void {
|
||||
foreach ($this->results as &$result) {
|
||||
// Get motherboard id from relationship by chassis id
|
||||
$result[self::REL_GPU] = $this->db
|
||||
->for(MbGpuModel::TABLE)
|
||||
->where([MbGpuModel::REF_MB_ID->value => $result[MbModel::ID->value]])
|
||||
->select(MbGpuModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_dram(): void {
|
||||
foreach ($this->results as &$result) {
|
||||
// Get motherboard id from relationship by chassis id
|
||||
$result[self::REL_DRAM] = $this->db
|
||||
->for(MbDramModel::TABLE)
|
||||
->where([MbDramModel::REF_MB_ID->value => $result[MbModel::ID->value]])
|
||||
->select(MbDramModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_storage(): void {
|
||||
foreach ($this->results as &$result) {
|
||||
// Get motherboard id from relationship by chassis id
|
||||
$result[self::REL_STORAGE] = $this->db
|
||||
->for(MbStorageModel::TABLE)
|
||||
->where([MbStorageModel::REF_MB_ID->value => $result[MbModel::ID->value]])
|
||||
->select(MbStorageModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
|
||||
private function get_motherboards(): array {
|
||||
return $this->results = $this->db
|
||||
->for(MbModel::TABLE)
|
||||
->where($this->query)
|
||||
->order([MbModel::DATE_AQUIRED->value => "DESC"])
|
||||
->select(MbModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Set properties as "searchable"
|
||||
parent::make_wildcard_search(MbModel::VENDOR_NAME->value, $this->query);
|
||||
parent::make_wildcard_search(MbModel::VENDOR_MODEL->value, $this->query);
|
||||
|
||||
// Get hardware
|
||||
$this->get_motherboards();
|
||||
|
||||
// Resolve hardware relationships
|
||||
$this->get_chassis();
|
||||
$this->get_cpu();
|
||||
$this->get_psu();
|
||||
$this->get_gpu();
|
||||
$this->get_dram();
|
||||
$this->get_storage();
|
||||
|
||||
// Return 404 Not Found if response array is empty
|
||||
return new Response($this->results, $this->results ? 200 : 404);
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\{
|
||||
PsuModel,
|
||||
EightyplusRatingEnum
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\Config\MbPsuModel;
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Psu.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Config/MbPsu.php");
|
||||
|
||||
class GET_BattlestationPsu extends VLWdb {
|
||||
private const REL_MOTHERBOARDS = "motherboards";
|
||||
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
private array $query;
|
||||
private array $results = [];
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(PsuModel::ID->value))
|
||||
->type(Type::STRING)
|
||||
->min(parent::UUID_LENGTH)
|
||||
->max(parent::UUID_LENGTH),
|
||||
|
||||
(new Rules(PsuModel::POWER->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_INT_MAX_LENGTH),
|
||||
|
||||
(new Rules(PsuModel::EIGHTYPLUS_RATING->value))
|
||||
->type(Type::ENUM, EightyplusRatingEnum::names()),
|
||||
|
||||
(new Rules(PsuModel::VENDOR_NAME->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(PsuModel::VENDOR_MODEL->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(PsuModel::IS_RETIRED->value))
|
||||
->type(Type::BOOLEAN)
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::BATTLESTATION, $this->ruleset);
|
||||
|
||||
// Use a copy of search parameters
|
||||
$this->query = $_GET;
|
||||
}
|
||||
|
||||
private function get_motherboards(): void {
|
||||
foreach ($this->results as &$result) {
|
||||
// Get motherboard id from relationship by chassis id
|
||||
$result[self::REL_MOTHERBOARDS] = $this->db
|
||||
->for(MbPsuModel::TABLE)
|
||||
->where([MbPsuModel::REF_PSU_ID->value => $result[PsuModel::ID->value]])
|
||||
->select(MbPsuModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_psu(): array {
|
||||
return $this->results = $this->db
|
||||
->for(PsuModel::TABLE)
|
||||
->where($this->query)
|
||||
->order([PsuModel::DATE_AQUIRED->value => "DESC"])
|
||||
->select(PsuModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Set properties as "searchable"
|
||||
parent::make_wildcard_search(PsuModel::VENDOR_NAME->value, $this->query);
|
||||
parent::make_wildcard_search(PsuModel::VENDOR_MODEL->value, $this->query);
|
||||
|
||||
// Get hardware
|
||||
$this->get_psu();
|
||||
|
||||
// Resolve hardware relationships
|
||||
$this->get_motherboards();
|
||||
|
||||
// Return 404 Not Found if response array is empty
|
||||
return new Response($this->results, $this->results ? 200 : 404);
|
||||
}
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\{
|
||||
StorageModel,
|
||||
StorageDiskTypeEnum,
|
||||
StorageDiskInterfaceEnum,
|
||||
StorageDiskFormfactorEnum
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\Config\MbStorageModel;
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Storage.php");
|
||||
require_once Path::root("src/databases/models/Battlestation/Config/MbStorage.php");
|
||||
|
||||
class GET_BattlestationStorage extends VLWdb {
|
||||
private const REL_MOTHERBOARDS = "motherboards";
|
||||
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
private array $query;
|
||||
private array $results = [];
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(StorageModel::ID->value))
|
||||
->type(Type::STRING)
|
||||
->min(parent::UUID_LENGTH)
|
||||
->max(parent::UUID_LENGTH),
|
||||
|
||||
(new Rules(StorageModel::DISK_TYPE->value))
|
||||
->type(Type::ENUM, StorageDiskTypeEnum::names()),
|
||||
|
||||
(new Rules(StorageModel::DISK_SIZE->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_INT_MAX_LENGTH),
|
||||
|
||||
(new Rules(StorageModel::DISK_INTERFACE->value))
|
||||
->type(Type::ENUM, StorageDiskInterfaceEnum::names()),
|
||||
|
||||
(new Rules(StorageModel::DISK_FORMFACTOR->value))
|
||||
->type(Type::ENUM, StorageDiskFormfactorEnum::names()),
|
||||
|
||||
(new Rules(StorageModel::VENDOR_NAME->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(StorageModel::VENDOR_MODEL->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(StorageModel::IS_RETIRED->value))
|
||||
->type(Type::BOOLEAN)
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::BATTLESTATION, $this->ruleset);
|
||||
|
||||
// Use a copy of search parameters
|
||||
$this->query = $_GET;
|
||||
}
|
||||
|
||||
private function get_motherboards(): void {
|
||||
foreach ($this->results as &$result) {
|
||||
// Get motherboard id from relationship by chassis id
|
||||
$result[self::REL_MOTHERBOARDS] = $this->db
|
||||
->for(MbStorageModel::TABLE)
|
||||
->where([MbStorageModel::REF_STORAGE_ID->value => $result[StorageModel::ID->value]])
|
||||
->select(MbStorageModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_storage(): array {
|
||||
return $this->results = $this->db
|
||||
->for(StorageModel::TABLE)
|
||||
->where($this->query)
|
||||
->order([StorageModel::DATE_AQUIRED->value => "DESC"])
|
||||
->select(StorageModel::values())
|
||||
->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Set properties as "searchable"
|
||||
parent::make_wildcard_search(StorageModel::VENDOR_NAME->value, $this->query);
|
||||
parent::make_wildcard_search(StorageModel::VENDOR_MODEL->value, $this->query);
|
||||
|
||||
// Get hardware
|
||||
$this->get_storage();
|
||||
|
||||
// Resolve hardware relationships
|
||||
$this->get_motherboards();
|
||||
|
||||
// Return 404 Not Found if response array is empty
|
||||
return new Response($this->results, $this->results ? 200 : 404);
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Messages\MessagesModel;
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Messages/Messages.php");
|
||||
|
||||
class POST_Messages extends VLWdb {
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->POST([
|
||||
(new Rules(MessagesModel::EMAIL->value))
|
||||
->type(Type::STRING)
|
||||
->max(255)
|
||||
->default(null),
|
||||
|
||||
(new Rules(MessagesModel::MESSAGE->value))
|
||||
->required()
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_TEXT_MAX_LENGTH)
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::VLW, $this->ruleset);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Use copy of request body as entity
|
||||
$entity = $_POST;
|
||||
|
||||
$entity[MessagesModel::ID->value] = parent::gen_uuid4();
|
||||
$entity[MessagesModel::DATE_CREATED->value] = time();
|
||||
|
||||
return $this->db->for(MessagesModel::TABLE)->insert($entity) === true
|
||||
? new Response($entity[MessagesModel::ID->value], 201)
|
||||
: new Response("Failed to create message", 500);
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Call;
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Endpoints;
|
||||
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Work\WorkModel;
|
||||
|
||||
require_once Path::root("src/Endpoints.php");
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Work/Work.php");
|
||||
|
||||
class GET_Search extends VLWdb {
|
||||
const GET_QUERY = "q";
|
||||
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(self::GET_QUERY))
|
||||
->required()
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::VLW, $this->ruleset);
|
||||
}
|
||||
|
||||
private function search_work(): Response {
|
||||
return (new Call(Endpoints::WORK->value))->params([
|
||||
WorkModel::TITLE->value => $_GET[self::GET_QUERY],
|
||||
WorkModel::SUMMARY->value => $_GET[self::GET_QUERY]
|
||||
])->get();
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
$results = [
|
||||
Endpoints::WORK->value => $this->search_work()->output()
|
||||
];
|
||||
|
||||
// Calculate the total number of results from all searched endpoints
|
||||
$num_results = array_sum(array_map(fn(array $result): int => count($result), array_values($results)));
|
||||
|
||||
// Return 404 if no search results
|
||||
return new Response($results, $num_results > 0 ? 200 : 404);
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use const VLW\API\RESP_DELETE_OK;
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Work\WorkModel;
|
||||
|
||||
require_once Path::root("src/Endpoints.php");
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Work.php");
|
||||
|
||||
class DELETE_Work extends VLWdb {
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->POST([
|
||||
(new Rules(WorkModel::ID->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkModel::TITLE->value))
|
||||
->type(Type::STRING)
|
||||
->min(3)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkModel::SUMMARY->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_TEXT_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkModel::IS_LISTABLE->value))
|
||||
->type(Type::BOOLEAN),
|
||||
|
||||
(new Rules(WorkModel::IS_READABLE->value))
|
||||
->type(Type::BOOLEAN),
|
||||
|
||||
(new Rules(WorkModel::DATE_MODIFIED->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_INT_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkModel::DATE_CREATED->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_INT_MAX_LENGTH)
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::VLW, $this->ruleset);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
return $this->db->for(FieldsEnumsModel::TABLE)->delete($_POST) === true
|
||||
? new Response(RESP_DELETE_OK)
|
||||
: new Response("Failed to delete work entity", 500);
|
||||
}
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Work\WorkModel;
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Work/Work.php");
|
||||
|
||||
class GET_Work extends VLWdb {
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(WorkModel::ID->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkModel::TITLE->value))
|
||||
->type(Type::STRING)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkModel::SUMMARY->value))
|
||||
->type(Type::STRING)
|
||||
->max(parent::MYSQL_TEXT_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkModel::IS_LISTABLE->value))
|
||||
->type(Type::BOOLEAN)
|
||||
->default(true),
|
||||
|
||||
(new Rules(WorkModel::IS_READABLE->value))
|
||||
->type(Type::BOOLEAN)
|
||||
->default(true),
|
||||
|
||||
(new Rules(WorkModel::DATE_MODIFIED->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_INT_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkModel::DATE_CREATED->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_INT_MAX_LENGTH)
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::VLW, $this->ruleset);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Use copy of search paramters as filters
|
||||
$filters = $_GET;
|
||||
|
||||
// Do a wildcard search on the title column if provided
|
||||
if (array_key_exists(WorkModel::TITLE->value, $_GET)) {
|
||||
$filters[WorkModel::TITLE->value] = [
|
||||
"LIKE" => "%{$_GET[WorkModel::TITLE->value]}%"
|
||||
];
|
||||
}
|
||||
|
||||
// Do a wildcard search on the summary column if provided
|
||||
if (array_key_exists(WorkModel::SUMMARY->value, $_GET)) {
|
||||
$filters[WorkModel::SUMMARY->value] = [
|
||||
"LIKE" => "%{$_GET[WorkModel::SUMMARY->value]}%"
|
||||
];
|
||||
}
|
||||
|
||||
$response = $this->db->for(WorkModel::TABLE)
|
||||
->where($filters)
|
||||
->select([
|
||||
WorkModel::ID->value,
|
||||
WorkModel::TITLE->value,
|
||||
WorkModel::SUMMARY->value,
|
||||
WorkModel::IS_LISTABLE->value,
|
||||
WorkModel::IS_READABLE->value,
|
||||
WorkModel::DATE_YEAR->value,
|
||||
WorkModel::DATE_MONTH->value,
|
||||
WorkModel::DATE_DAY->value,
|
||||
WorkModel::DATE_MODIFIED->value,
|
||||
WorkModel::DATE_CREATED->value
|
||||
]);
|
||||
|
||||
return $response->num_rows > 0
|
||||
? new Response($response->fetch_all(MYSQLI_ASSOC))
|
||||
: new Response([], 404);
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Call;
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Endpoints;
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Work\{
|
||||
WorkModel,
|
||||
WorkPermalinksModel
|
||||
};
|
||||
|
||||
require_once Path::root("src/Endpoints.php");
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Work/Work.php");
|
||||
require_once Path::root("src/databases/models/Work/WorkPermalinks.php");
|
||||
|
||||
class PATCH_Work extends VLWdb {
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(WorkModel::ID->value))
|
||||
->required()
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
||||
]);
|
||||
|
||||
$this->ruleset->POST([
|
||||
(new Rules(WorkModel::TITLE->value))
|
||||
->type(Type::STRING)
|
||||
->min(3)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkModel::SUMMARY->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_TEXT_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkModel::IS_LISTABLE->value))
|
||||
->type(Type::BOOLEAN),
|
||||
|
||||
(new Rules(WorkModel::IS_READABLE->value))
|
||||
->type(Type::BOOLEAN),
|
||||
|
||||
(new Rules(WorkModel::DATE_MODIFIED->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_INT_MAX_LENGTH)
|
||||
->default(time()),
|
||||
|
||||
(new Rules(WorkModel::DATE_CREATED->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_INT_MAX_LENGTH)
|
||||
]);
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
// Generate a slug URL from string
|
||||
private static function gen_slug(string $input): string {
|
||||
return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $input)));
|
||||
}
|
||||
|
||||
// Compute and return modeled year, month, and day from Unix timestamp in request body
|
||||
private static function gen_date_created(): array {
|
||||
return [
|
||||
WorkModel::DATE_YEAR->value => date("Y", $_POST[WorkModel::DATE_CREATED->value]),
|
||||
WorkModel::DATE_MONTH ->value => date("n", $_POST[WorkModel::DATE_CREATED->value]),
|
||||
WorkModel::DATE_DAY->value => date("j", $_POST[WorkModel::DATE_CREATED->value])
|
||||
];
|
||||
}
|
||||
|
||||
private function get_entity_by_id(string $id): Response {
|
||||
return (new Call(Endpoints::WORK->value))->params([
|
||||
WorkModel::ID->value => $id
|
||||
])->get();
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Use copy of request body as entity
|
||||
$entity = $_POST;
|
||||
|
||||
// Generate a new slug id from title if changed
|
||||
if ($_POST[WorkModel::TITLE->value]) {
|
||||
$slug = $_POST[WorkModel::TITLE->value];
|
||||
|
||||
// Bail out if the slug generated from the new tite already exist
|
||||
if ($this->get_entity_by_id($slug)) {
|
||||
return new Response("An entity with this title already exist", 409);
|
||||
}
|
||||
|
||||
// Add the new slug to update entity
|
||||
$entity[WorkModel::ID] = $slug;
|
||||
}
|
||||
|
||||
// Generate new work date fields from timestamp
|
||||
if ($_POST[WorkModel::DATE_CREATED->value]) {
|
||||
array_merge($entity, self::gen_date_created());
|
||||
}
|
||||
|
||||
// Update entity by existing id
|
||||
return $this->db->for(WorkModel::TABLE)->where([WorkModel::ID->value => $_GET[WorkModel::ID->value]])->update($entity) === true
|
||||
? new Response($_GET[WorkModel::ID->value])
|
||||
: new Response("Failed to update entity", 500);
|
||||
}
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Call;
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Endpoints;
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Work\{
|
||||
WorkModel,
|
||||
WorkPermalinksModel
|
||||
};
|
||||
|
||||
require_once Path::root("src/Endpoints.php");
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Work/Work.php");
|
||||
require_once Path::root("src/databases/models/Work/WorkPermalinks.php");
|
||||
|
||||
class POST_Work extends VLWdb {
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->POST([
|
||||
(new Rules(WorkModel::TITLE->value))
|
||||
->type(Type::STRING)
|
||||
->min(3)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
||||
->default(null),
|
||||
|
||||
(new Rules(WorkModel::SUMMARY->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_TEXT_MAX_LENGTH)
|
||||
->default(null),
|
||||
|
||||
(new Rules(WorkModel::IS_LISTABLE->value))
|
||||
->type(Type::BOOLEAN)
|
||||
->default(false),
|
||||
|
||||
(new Rules(WorkModel::IS_READABLE->value))
|
||||
->type(Type::BOOLEAN)
|
||||
->default(false),
|
||||
|
||||
(new Rules(WorkModel::DATE_CREATED->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_INT_MAX_LENGTH)
|
||||
->default(time())
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::VLW, $this->ruleset);
|
||||
}
|
||||
|
||||
// Generate a slug URL from string
|
||||
private static function gen_slug(string $input): string {
|
||||
return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $input)));
|
||||
}
|
||||
|
||||
// Compute and return modeled year, month, and day from a Unix timestamp
|
||||
private static function gen_date_created(): array {
|
||||
// Use provided timestamp in request
|
||||
$date_created = $_POST[WorkModel::DATE_CREATED->value];
|
||||
|
||||
return [
|
||||
WorkModel::DATE_YEAR->value => date("Y", $date_created),
|
||||
WorkModel::DATE_MONTH ->value => date("n", $date_created),
|
||||
WorkModel::DATE_DAY->value => date("j", $date_created)
|
||||
];
|
||||
}
|
||||
|
||||
private function get_entity_by_id(string $id): Response {
|
||||
return (new Call(Endpoints::WORK->value))->params([
|
||||
WorkModel::ID->value => $id
|
||||
])->get();
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Use copy of request body as entity
|
||||
$entity = $_POST;
|
||||
|
||||
// Generate URL slug from title text or UUID if undefined
|
||||
$entity[WorkModel::ID->value] = $_POST[WorkModel::TITLE->value]
|
||||
? self::gen_slug($_POST[WorkModel::TITLE->value])
|
||||
: parent::gen_uuid4();
|
||||
|
||||
// Bail out here if a work entry with id had been created already
|
||||
if ($this->get_entity_by_id($entity[WorkModel::ID->value])->ok) {
|
||||
return new Response("An entity with id '{$slug}' already exist", 409);
|
||||
}
|
||||
|
||||
// Generate the necessary date fields
|
||||
array_merge($entity, self::gen_date_created());
|
||||
|
||||
// Let's try to insert the new entity
|
||||
if (!$this->db->for(WorkModel::TABLE)->insert($entity)) {
|
||||
return new Response("Failed to insert work entry", 500);
|
||||
}
|
||||
|
||||
// Generate permalink for new entity
|
||||
return (new Call(Endpoints::WORK_PERMALINKS->value))->post([
|
||||
WorkPermalinksModel::ID => $entity[WorkModel::ID->value],
|
||||
WorkPermalinksModel::REF_WORK_ID => $entity[WorkModel::ID->value],
|
||||
WorkPermalinksModel::DATE_CREATED => time()
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use const VLW\API\RESP_DELETE_OK;
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Work\WorkActionsModel;
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/WorkActions.php");
|
||||
|
||||
class DELETE_WorkActions extends VLWdb {
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->POST([
|
||||
(new Rules(WorkActionsModel::REF_WORK_ID->value))
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
||||
]);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
return $this->db->for(WorkActionsModel::TABLE)->delete($_POST) === true
|
||||
? new Response(RESP_DELETE_OK)
|
||||
: new Response("Failed to delete action for work entity", 500);
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Work\WorkActionsModel;
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Work/WorkActions.php");
|
||||
|
||||
class GET_WorkActions extends VLWdb {
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(WorkActionsModel::REF_WORK_ID->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::VLW, $this->ruleset);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
$response = $this->db->for(WorkActionsModel::TABLE)
|
||||
->where($_GET)
|
||||
->select([
|
||||
WorkActionsModel::REF_WORK_ID->value,
|
||||
WorkActionsModel::DISPLAY_TEXT->value,
|
||||
WorkActionsModel::HREF->value,
|
||||
WorkActionsModel::CLASS_LIST->value,
|
||||
WorkActionsModel::EXTERNAL->value
|
||||
]);
|
||||
|
||||
return $response->num_rows > 0
|
||||
? new Response($response->fetch_all(MYSQLI_ASSOC))
|
||||
: new Response([], 404);
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Call;
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Endpoints;
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Work\{
|
||||
WorkModel,
|
||||
WorkActionsModel
|
||||
};
|
||||
|
||||
require_once Path::root("src/Endpoints.php");
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Work/Work.php");
|
||||
require_once Path::root("src/databases/models/Work/WorkActions.php");
|
||||
|
||||
class POST_WorkActions extends VLWdb {
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->POST([
|
||||
(new Rules(WorkActionsModel::REF_WORK_ID->value))
|
||||
->required()
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkActionsModel::DISPLAY_TEXT->value))
|
||||
->required()
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkActionsModel::HREF->value))
|
||||
->required()
|
||||
->type(Type::STRING)
|
||||
->type(Type::NULL)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkActionsModel::CLASS_LIST->value))
|
||||
->type(Type::ARRAY)
|
||||
->min(1)
|
||||
->default([]),
|
||||
|
||||
(new Rules(WorkActionsModel::EXTERNAL->value))
|
||||
->type(Type::BOOLEAN)
|
||||
->default(false)
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::VLW, $this->ruleset);
|
||||
}
|
||||
|
||||
private static function get_entity(): Response {
|
||||
return (new Call(Endpoints::WORK->value))->params([
|
||||
WorkModel::ID->value => $_POST[WorkActionsModel::REF_WORK_ID->value]
|
||||
])->get();
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Bail out if work entity could not be fetched
|
||||
$entity = self::get_entity();
|
||||
if (!$entity->ok) {
|
||||
return $entity;
|
||||
}
|
||||
|
||||
return $this->db->for(WorkActionsModel::TABLE)->insert($_POST) === true
|
||||
? new Response($_POST[WorkActionsModel::REF_WORK_ID->value], 201)
|
||||
: new Response("Failed to add action to work entity", 500);
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Work\WorkPermalinksModel;
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Work/WorkPermalinks.php");
|
||||
|
||||
class GET_WorkPermalinks extends VLWdb {
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(WorkPermalinksModel::ID->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkPermalinksModel::REF_WORK_ID->value))
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::VLW, $this->ruleset);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
$response = $this->db->for(WorkPermalinksModel::TABLE)
|
||||
->where($_GET)
|
||||
->select([
|
||||
WorkPermalinksModel::ID->value,
|
||||
WorkPermalinksModel::REF_WORK_ID->value,
|
||||
WorkPermalinksModel::DATE_CREATED->value
|
||||
]);
|
||||
|
||||
return $response->num_rows > 0
|
||||
? new Response($response->fetch_all(MYSQLI_ASSOC))
|
||||
: new Response([], 404);
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Call;
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Work\WorkPermalinksModel;
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Work/WorkPermalinks.php");
|
||||
|
||||
class POST_WorkPermalinks extends VLWdb {
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->POST([
|
||||
(new Rules(WorkPermalinksModel::ID->value))
|
||||
->required()
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkPermalinksModel::REF_WORK_ID->value))
|
||||
->required()
|
||||
->type(Type::STRING)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkPermalinksModel::DATE_CREATED->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::MYSQL_INT_MAX_LENGTH)
|
||||
->default(time())
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::VLW, $this->ruleset);
|
||||
}
|
||||
|
||||
private static function get_entity(): Response {
|
||||
return (new Call(Endpoints::WORK->value))->params([
|
||||
WorkModel::ID->value => $_POST[WorkTagsModel::REF_WORK_ID->value]
|
||||
])->get();
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Bail out if work entity could not be fetched
|
||||
$entity = self::get_entity();
|
||||
if (!$entity->ok) {
|
||||
return $entity;
|
||||
}
|
||||
|
||||
return $this->db->for(WorkPermalinksModel::TABLE)->insert($_POST) === true
|
||||
? new Response($_POST[WorkPermalinksModel::ID->value], 201)
|
||||
: new Response("Failed to add permalink to work entity", 500);
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use const VLW\API\RESP_DELETE_OK;
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Work\WorkTagsModel;
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Work/WorkTags.php");
|
||||
|
||||
class DELETE_WorkTags extends VLWdb {
|
||||
private Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(WorkTagsModel::REF_WORK_ID->value))
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkTagsModel::NAME->value))
|
||||
->type(Type::ENUM, WorkTagsNameEnum::names())
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::VLW, $this->ruleset);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
return $this->db->for(WorkTagsModel::TABLE)->delete($_POST) === true
|
||||
? new Response(RESP_DELETE_OK)
|
||||
: new Response("Failed to delete value from document", 500);
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Work\{
|
||||
WorkTagsModel,
|
||||
WorkTagsNameEnum
|
||||
};
|
||||
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Work/WorkTags.php");
|
||||
|
||||
class GET_WorkTags extends VLWdb {
|
||||
private Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(WorkTagsModel::REF_WORK_ID->value))
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkTagsModel::NAME->value))
|
||||
->type(Type::ENUM, WorkTagsNameEnum::names())
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::VLW, $this->ruleset);
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
$response = $this->db->for(WorkTagsModel::TABLE)
|
||||
->where($_GET)
|
||||
->select([
|
||||
WorkTagsModel::REF_WORK_ID->value,
|
||||
WorkTagsModel::NAME->value
|
||||
]);
|
||||
|
||||
return $response->num_rows > 0
|
||||
? new Response($response->fetch_all(MYSQLI_ASSOC))
|
||||
: new Response([], 404);
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Reflect\Call;
|
||||
use Reflect\Path;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Type;
|
||||
use ReflectRules\Rules;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use VLW\API\Endpoints;
|
||||
use VLW\API\Databases\VLWdb\{
|
||||
VLWdb,
|
||||
Databases
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Work\{
|
||||
WorkModel,
|
||||
WorkTagsModel,
|
||||
WorkTagsNameEnum
|
||||
};
|
||||
|
||||
require_once Path::root("src/Endpoints.php");
|
||||
require_once Path::root("src/databases/VLWdb.php");
|
||||
require_once Path::root("src/databases/models/Work/Work.php");
|
||||
require_once Path::root("src/databases/models/Work/WorkTags.php");
|
||||
|
||||
class POST_WorkTags extends VLWdb {
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->POST([
|
||||
(new Rules(WorkTagsModel::REF_WORK_ID->value))
|
||||
->required()
|
||||
->min(1)
|
||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||
|
||||
(new Rules(WorkTagsModel::NAME->value))
|
||||
->required()
|
||||
->type(Type::ENUM, WorkTagsNameEnum::names())
|
||||
]);
|
||||
|
||||
parent::__construct(Databases::VLW, $this->ruleset);
|
||||
}
|
||||
|
||||
private static function get_entity(): Response {
|
||||
return (new Call(Endpoints::WORK->value))->params([
|
||||
WorkModel::ID->value => $_POST[WorkTagsModel::REF_WORK_ID->value]
|
||||
])->get();
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Bail out if work entity could not be fetched
|
||||
$entity = self::get_entity();
|
||||
if (!$entity->ok) {
|
||||
return $entity;
|
||||
}
|
||||
|
||||
return $this->db->for(WorkTagsModel::TABLE)->insert($_POST) === true
|
||||
? new Response($_POST[WorkTagsModel::REF_WORK_ID->value], 201)
|
||||
: new Response("Failed to add tag to work entity", 500);
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
# Install dependencies
|
||||
composer install --optimize-autoloader
|
19
api/languages/GET.php
Normal file
19
api/languages/GET.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
use Reflect\{Response, Path};
|
||||
|
||||
use VLW\API\API;
|
||||
use VLW\Database\Models\Languages\Language;
|
||||
|
||||
require_once Path::root("src/API/API.php");
|
||||
require_once Path::root("src/Database/Models/Languages/Language.php");
|
||||
|
||||
final class GET_Languages extends API {
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
return new Response(Language::all());
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API;
|
||||
|
||||
// Default string to return when a DELETE request is successful
|
||||
const RESP_DELETE_OK = "OK";
|
||||
|
||||
// Enum of all available VLW endpoints grouped by category
|
||||
enum Endpoints: string {
|
||||
case SEARCH = "/search";
|
||||
|
||||
case MESSAGES = "/messages";
|
||||
|
||||
case WORK = "/work";
|
||||
case WORK_TAGS = "/work/tags";
|
||||
case WORK_ACTIONS = "/work/actions";
|
||||
|
||||
case BATTLESTATION = "/battlestation";
|
||||
case BATTLESTATION_MB = "/battlestation/mb";
|
||||
case BATTLESTATION_CPU = "/battlestation/cpu";
|
||||
case BATTLESTATION_GPU = "/battlestation/gpu";
|
||||
case BATTLESTATION_PSU = "/battlestation/psu";
|
||||
case BATTLESTATION_DRAM = "/battlestation/dram";
|
||||
case BATTLESTATION_STORAGE = "/battlestation/storage";
|
||||
case BATTLESTATION_COOLERS = "/battlestation/coolers";
|
||||
case BATTLESTATION_CHASSIS = "/battlestation/chassis";
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb;
|
||||
|
||||
use Reflect\Path;
|
||||
use Reflect\Request;
|
||||
use Reflect\Response;
|
||||
use ReflectRules\Ruleset;
|
||||
|
||||
use vlw\MySQL\MySQL;
|
||||
|
||||
enum Databases: string {
|
||||
case VLW = "vlw";
|
||||
case BATTLESTATION = "battlestation";
|
||||
}
|
||||
|
||||
class VLWdb {
|
||||
const UUID_LENGTH = 36;
|
||||
|
||||
const MYSQL_INT_MAX_LENGTH = 2147483647;
|
||||
const MYSQL_TEXT_MAX_LENGTH = 65538;
|
||||
const MYSQL_VARCHAR_MAX_LENGTH = 255;
|
||||
const MYSQL_TINYINT_MAX_LENGTH = 255;
|
||||
|
||||
protected readonly MySQL $db;
|
||||
|
||||
public function __construct(Databases $database, Ruleset $ruleset) {
|
||||
// Validate provided Ruleset before attempting to connect to the database
|
||||
self::eval_ruleset_or_exit($ruleset);
|
||||
|
||||
// Create new MariaDB connection
|
||||
$this->db = new MySQL(
|
||||
$_ENV["connect"]["host"],
|
||||
$_ENV["connect"]["user"],
|
||||
$_ENV["connect"]["pass"],
|
||||
$_ENV["databases"][$database->value],
|
||||
);
|
||||
}
|
||||
|
||||
// Generate and return UUID4 string
|
||||
public static function gen_uuid4(): string {
|
||||
return sprintf("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
|
||||
// 32 bits for "time_low"
|
||||
mt_rand(0, 0xffff), mt_rand(0, 0xffff),
|
||||
|
||||
// 16 bits for "time_mid"
|
||||
mt_rand(0, 0xffff),
|
||||
|
||||
// 16 bits for "time_hi_and_version",
|
||||
// four most significant bits holds version number 4
|
||||
mt_rand(0, 0x0fff) | 0x4000,
|
||||
|
||||
// 16 bits, 8 bits for "clk_seq_hi_res",
|
||||
// 8 bits for "clk_seq_low",
|
||||
// two most significant bits holds zero and one for variant DCE1.1
|
||||
mt_rand(0, 0x3fff) | 0x8000,
|
||||
|
||||
// 48 bits for "node"
|
||||
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
|
||||
);
|
||||
}
|
||||
|
||||
// Mutate the value by array key $property_name into a libmysqldriver\MySQL custom operator
|
||||
// https://codeberg.org/vlw/php-mysql#define-custom-operators
|
||||
public static function make_wildcard_search(string $property_name, array &$filters): array {
|
||||
// Bail out if property name is not set in filters array or if its value is null
|
||||
if (!array_key_exists($property_name, $filters) || $filters[$property_name] === null) {
|
||||
return $filters;
|
||||
}
|
||||
|
||||
// Mutate filter value into a custom operator array
|
||||
$filters[$property_name] = [
|
||||
"LIKE" => "%{$filters[$property_name]}%"
|
||||
];
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
// Bail out if provided ReflectRules\Ruleset is invalid
|
||||
private static function eval_ruleset_or_exit(Ruleset $ruleset): ?Response {
|
||||
return !$ruleset->is_valid() ? new Response($ruleset->get_errors(), 422) : null;
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Battlestation;
|
||||
|
||||
use victorwesterlund\xEnum;
|
||||
|
||||
enum ChassisModel: string {
|
||||
use xEnum;
|
||||
|
||||
const TABLE = "chassis";
|
||||
|
||||
case ID = "id";
|
||||
case VENDOR_NAME = "vendor_name";
|
||||
case VENDOR_MODEL = "vendor_model";
|
||||
case STORAGE_TWOINCHFIVE = "storage_2i5hi";
|
||||
case STORAGE_THREEINCHFIVE = "storage_3i5hi";
|
||||
case DATE_AQUIRED = "date_aquired";
|
||||
case IS_RETIRED = "is_retired";
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Battlestation\Config;
|
||||
|
||||
use victorwesterlund\xEnum;
|
||||
|
||||
enum ChassisMbModel: string {
|
||||
use xEnum;
|
||||
|
||||
const TABLE = "config_chassis_mb";
|
||||
|
||||
case REF_CHASSIS_ID = "ref_chassis_id";
|
||||
case REF_MB_ID = "ref_mb_id";
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Battlestation\Config;
|
||||
|
||||
use victorwesterlund\xEnum;
|
||||
|
||||
enum ConfigModel: string {
|
||||
use xEnum;
|
||||
|
||||
const TABLE = "config";
|
||||
|
||||
case REF_MB_ID = "ref_mb_id";
|
||||
case FRIENDLY_NAME = "friendly_name";
|
||||
case DATE_BUILT = "date_built";
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Battlestation\Config;
|
||||
|
||||
use victorwesterlund\xEnum;
|
||||
|
||||
enum SocketTypeEnum {
|
||||
use xEnum;
|
||||
|
||||
case SLOTTED;
|
||||
case INTEGRATED;
|
||||
}
|
||||
|
||||
enum MbCpuCoolerModel: string {
|
||||
use xEnum;
|
||||
|
||||
const TABLE = "config_mb_cpu_cooler";
|
||||
|
||||
case REF_MB_ID = "ref_mb_id";
|
||||
case REF_CPU_ID = "ref_cpu_id";
|
||||
case REF_COOLER_ID = "ref_cooler_id";
|
||||
case SOCKET = "socket";
|
||||
case SOCKET_TYPE = "socket_type";
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Battlestation\Config;
|
||||
|
||||
use victorwesterlund\xEnum;
|
||||
|
||||
enum SocketTypeModel {
|
||||
use xEnum;
|
||||
|
||||
case SLOTTED;
|
||||
case INTEGRATED;
|
||||
}
|
||||
|
||||
enum MbDramModel: string {
|
||||
use xEnum;
|
||||
|
||||
const TABLE = "config_mb_dram";
|
||||
|
||||
case REF_MB_ID = "ref_mb_id";
|
||||
case REF_DRAM_ID = "ref_dram_id";
|
||||
case SOCKET = "socket";
|
||||
case SOCKET_TYPE = "socket_type";
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Battlestation\Config;
|
||||
|
||||
use victorwesterlund\xEnum;
|
||||
|
||||
enum MbGpuModel: string {
|
||||
use xEnum;
|
||||
|
||||
const TABLE = "config_mb_gpu";
|
||||
|
||||
case REF_MB_ID = "ref_mb_id";
|
||||
case REF_GPU_ID = "ref_gpu_id";
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Battlestation\Config;
|
||||
|
||||
use victorwesterlund\xEnum;
|
||||
|
||||
enum MbPsuModel: string {
|
||||
use xEnum;
|
||||
|
||||
const TABLE = "config_mb_psu";
|
||||
|
||||
case REF_MB_ID = "ref_mb_id";
|
||||
case REF_PSU_ID = "ref_psu_id";
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Battlestation\Config;
|
||||
|
||||
use victorwesterlund\xEnum;
|
||||
|
||||
enum MbStorageSlotFormfactorEnum: string {
|
||||
use xEnum;
|
||||
|
||||
case TWODOTFIVE = "2.5";
|
||||
case THREEDOTFIVE = "3.5";
|
||||
case MDOTTWO = "M.2";
|
||||
case EXTERNAL = "EXTERNAL";
|
||||
}
|
||||
|
||||
enum MbStorageModel: string {
|
||||
use xEnum;
|
||||
|
||||
const TABLE = "config_mb_storage";
|
||||
|
||||
case REF_MB_ID = "ref_mb_id";
|
||||
case REF_STORAGE_ID = "ref_storage_id";
|
||||
case INTERFACE = "interface";
|
||||
case SLOT_FORMFACTOR = "slot_formfactor";
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Battlestation;
|
||||
|
||||
enum CoolersModel: string {
|
||||
const TABLE = "coolers";
|
||||
|
||||
case ID = "id";
|
||||
case TYPE_LIQUID = "type_liquid";
|
||||
case SIZE_FAN = "size_fan";
|
||||
case SIZE_RADIATOR = "size_radiator";
|
||||
case VENDOR_NAME = "vendor_name";
|
||||
case VENDOR_MODEL = "vendor_model";
|
||||
case DATE_AQUIRED = "date_aquired";
|
||||
case IS_RETIRED = "is_retired";
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Battlestation;
|
||||
|
||||
use victorwesterlund\xEnum;
|
||||
|
||||
enum ClassEnum {
|
||||
use xEnum;
|
||||
|
||||
case DESKTOP;
|
||||
case LAPTOP;
|
||||
case SERVER;
|
||||
}
|
||||
|
||||
enum CpuModel: string {
|
||||
use xEnum;
|
||||
|
||||
const TABLE = "cpu";
|
||||
|
||||
case ID = "id";
|
||||
case CPU_CLASS = "class";
|
||||
case CLOCK_BASE = "clock_base";
|
||||
case CLOCK_TURBO = "clock_turbo";
|
||||
case CORE_COUNT_PERFORMANCE = "core_count_performance";
|
||||
case CORE_COUNT_EFFICIENCY = "core_count_efficiency";
|
||||
case CORE_THREADS = "core_threads";
|
||||
case VENDOR_NAME = "vendor_name";
|
||||
case VENDOR_MODEL = "vendor_model";
|
||||
case DATE_AQUIRED = "date_aquired";
|
||||
case IS_RETIRED = "is_retired";
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Battlestation;
|
||||
|
||||
use victorwesterlund\xEnum;
|
||||
|
||||
enum DramFormfactorEnum {
|
||||
use xEnum;
|
||||
|
||||
case DIMM;
|
||||
case SODIMM;
|
||||
}
|
||||
|
||||
enum DramTechnologyEnum {
|
||||
use xEnum;
|
||||
|
||||
case DDR4;
|
||||
case DDR5;
|
||||
}
|
||||
|
||||
enum DramModel: string {
|
||||
use xEnum;
|
||||
|
||||
const TABLE = "dram";
|
||||
|
||||
case ID = "id";
|
||||
case CAPACITY = "capacity";
|
||||
case SPEED = "speed";
|
||||
case FORMFACTOR = "formfactor";
|
||||
case TECHNOLOGY = "technology";
|
||||
case ECC = "ecc";
|
||||
case BUFFERED = "buffered";
|
||||
case VENDOR_NAME = "vendor_name";
|
||||
case VENDOR_MODEL = "vendor_model";
|
||||
case DATE_AQUIRED = "date_aquired";
|
||||
case IS_RETIRED = "is_retired";
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Battlestation;
|
||||
|
||||
use victorwesterlund\xEnum;
|
||||
|
||||
enum GpuModel: string {
|
||||
use xEnum;
|
||||
|
||||
const TABLE = "gpu";
|
||||
|
||||
case ID = "id";
|
||||
case MEMORY = "memory";
|
||||
case VENDOR_NAME = "vendor_name";
|
||||
case VENDOR_MODEL = "vendor_model";
|
||||
case VENDOR_CHIP_NAME = "vendor_chip_name";
|
||||
case VENDOR_CHIP_MODEL = "vendor_chip_model";
|
||||
case DATE_AQUIRED = "date_aquired";
|
||||
case IS_RETIRED = "is_retired";
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Battlestation;
|
||||
|
||||
use victorwesterlund\xEnum;
|
||||
|
||||
enum MbFormfactorEnum {
|
||||
use xEnum;
|
||||
|
||||
case ATX;
|
||||
case MTX;
|
||||
case ITX;
|
||||
case LAPTOP;
|
||||
}
|
||||
|
||||
enum MbModel: string {
|
||||
use xEnum;
|
||||
|
||||
const TABLE = "mb";
|
||||
|
||||
case ID = "id";
|
||||
case FORMFACTOR = "formfactor";
|
||||
case VENDOR_NAME = "vendor_name";
|
||||
case VENDOR_MODEL = "vendor_model";
|
||||
case NETWORK_ETHERNET = "network_ethernet";
|
||||
case NETWORK_WLAN = "network_wlan";
|
||||
case NETWORK_BLUETOOTH = "network_bluetooth";
|
||||
case DATE_AQUIRED = "date_aquired";
|
||||
case IS_RETIRED = "is_retired";
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Battlestation;
|
||||
|
||||
use victorwesterlund\xEnum;
|
||||
|
||||
enum EightyplusRatingEnum {
|
||||
use xEnum;
|
||||
|
||||
case BASE;
|
||||
case BRONZE;
|
||||
case SILVER;
|
||||
case GOLD;
|
||||
case PLATINUM;
|
||||
case TITANIUM;
|
||||
}
|
||||
|
||||
enum PsuModel: string {
|
||||
use xEnum;
|
||||
|
||||
const TABLE = "psu";
|
||||
|
||||
case ID = "id";
|
||||
case POWER = "power";
|
||||
case VENDOR_NAME = "vendor_name";
|
||||
case VENDOR_MODEL = "vendor_model";
|
||||
case TYPE_MODULAR = "type_modular";
|
||||
case EIGHTYPLUS_RATING = "80plus_rating";
|
||||
case DATE_AQUIRED = "date_aquired";
|
||||
case IS_RETIRED = "is_retired";
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Battlestation;
|
||||
|
||||
use victorwesterlund\xEnum;
|
||||
|
||||
enum StorageDiskTypeEnum {
|
||||
use xEnum;
|
||||
|
||||
case SSD;
|
||||
case HDD;
|
||||
}
|
||||
|
||||
enum StorageDiskInterfaceEnum {
|
||||
use xEnum;
|
||||
|
||||
case SATA;
|
||||
case NVME;
|
||||
case USB;
|
||||
}
|
||||
|
||||
enum StorageDiskFormfactorEnum{
|
||||
use xEnum;
|
||||
|
||||
case TWODOTFIVE;
|
||||
case THREEDOTFIVE;
|
||||
case MDOTTWO;
|
||||
}
|
||||
|
||||
enum StorageModel: string {
|
||||
use xEnum;
|
||||
|
||||
const TABLE = "storage";
|
||||
|
||||
case ID = "id";
|
||||
case DISK_TYPE = "disk_type";
|
||||
case DISK_SIZE = "disk_size";
|
||||
case DISK_SECTORS = "disk_sectors";
|
||||
case DISK_INTERFACE = "disk_interface";
|
||||
case DISK_FORMFACTOR = "disk_formfactor";
|
||||
case VENDOR_NAME = "vendor_name";
|
||||
case VENDOR_MODEL = "vendor_model";
|
||||
case DATE_AQUIRED = "date_aquired";
|
||||
case IS_RETIRED = "is_retired";
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Messages;
|
||||
|
||||
enum MessagesModel: string {
|
||||
const TABLE = "messages";
|
||||
|
||||
case ID = "id";
|
||||
case EMAIL = "email";
|
||||
case MESSAGE = "message";
|
||||
case IS_READ = "is_read";
|
||||
case IS_SPAM = "is_spam";
|
||||
case IS_SAVED = "is_saved";
|
||||
case DATE_CREATED = "date_created";
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Work;
|
||||
|
||||
enum WorkModel: string {
|
||||
const TABLE = "work";
|
||||
|
||||
case ID = "id";
|
||||
case TITLE = "title";
|
||||
case SUMMARY = "summary";
|
||||
case COVER_SRCSET = "cover_srcset";
|
||||
case IS_LISTABLE = "is_listable";
|
||||
case IS_READABLE = "is_readable";
|
||||
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";
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Work;
|
||||
|
||||
enum WorkActionsModel: string {
|
||||
const TABLE = "work_actions";
|
||||
|
||||
case REF_WORK_ID = "ref_work_id";
|
||||
case DISPLAY_TEXT = "display_text";
|
||||
case HREF = "href";
|
||||
case CLASS_LIST = "class_list";
|
||||
case EXTERNAL = "external";
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Work;
|
||||
|
||||
enum WorkMediaModel: string {
|
||||
const TABLE = "work_media";
|
||||
|
||||
case ANCHOR = "anchor";
|
||||
case MEDIA = "media";
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Work;
|
||||
|
||||
enum WorkPermalinksModel: string {
|
||||
const TABLE = "work_permalinks";
|
||||
|
||||
case SLUG = "slug";
|
||||
case ANCHOR = "anchor";
|
||||
case DATE_TIMESTAMP_CREATED = "date_timestamp_created";
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\API\Databases\VLWdb\Models\Work;
|
||||
|
||||
use victorwesterlund\xEnum;
|
||||
|
||||
enum WorkTagsNameEnum {
|
||||
use xEnum;
|
||||
|
||||
case VLW;
|
||||
case RELEASE;
|
||||
case WEBSITE;
|
||||
}
|
||||
|
||||
enum WorkTagsModel: string {
|
||||
const TABLE = "work_tags";
|
||||
|
||||
case REF_WORK_ID = "ref_work_id";
|
||||
case NAME = "name";
|
||||
}
|
71
api/update/GET.php
Normal file
71
api/update/GET.php
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
use \vlw\xEnum;
|
||||
use Reflect\{Response, Path};
|
||||
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||
|
||||
use VLW\API\API;
|
||||
use VLW\Helpers\{
|
||||
Forgejo,
|
||||
GenerateSearch,
|
||||
GenerateTimeline
|
||||
};
|
||||
|
||||
require_once Path::root("src/API/API.php");
|
||||
require_once Path::root("src/Helpers/Forgejo.php");
|
||||
require_once Path::root("src/Helpers/GenerateSearch.php");
|
||||
require_once Path::root("src/Helpers/GenerateTimeline.php");
|
||||
|
||||
enum ServiceEnum: string {
|
||||
use xEnum;
|
||||
|
||||
case ALL = "all";
|
||||
case SEARCH = "search";
|
||||
case FORGEJO = "forgejo";
|
||||
case TIMELINE = "timeline";
|
||||
}
|
||||
|
||||
final class GET_Update extends API {
|
||||
private const KEY_SERVICE = "service";
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(new Ruleset()->GET([
|
||||
new Rules(self::KEY_SERVICE)
|
||||
->type(Type::ENUM, ServiceEnum::values())
|
||||
->default(ServiceEnum::ALL->value)
|
||||
]));
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
switch ($_GET[self::KEY_SERVICE]) {
|
||||
case ServiceEnum::FORGEJO->value:
|
||||
return new Response($this->update_forgejo());
|
||||
|
||||
case ServiceEnum::SEARCH->value:
|
||||
return new Response($this->update_search());
|
||||
|
||||
case ServiceEnum::TIMELINE->value:
|
||||
return new Response($this->update_timeline());
|
||||
|
||||
case ServiceEnum::ALL->value:
|
||||
default:
|
||||
return new Response(
|
||||
$this->update_timeline() &&
|
||||
$this->update_search() &&
|
||||
$this->update_forgejo()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function update_timeline(): bool {
|
||||
return new GenerateTimeline()->generate();
|
||||
}
|
||||
|
||||
private function update_search(): bool {
|
||||
return new GenerateSearch()->generate();
|
||||
}
|
||||
|
||||
private function update_forgejo(): bool {
|
||||
return new Forgejo()->update();
|
||||
}
|
||||
}
|
28
api/work/GET.php
Normal file
28
api/work/GET.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
use Reflect\{Response, Path};
|
||||
|
||||
use VLW\API\API;
|
||||
use VLW\Database\Models\Work\{Work, Tag};
|
||||
|
||||
require_once Path::root("src/API/API.php");
|
||||
require_once Path::root("src/Database/Models/Work/Tag.php");
|
||||
require_once Path::root("src/Database/Models/Work/Work.php");
|
||||
|
||||
final class GET_Work extends API {
|
||||
private static function entity(Work $work): object {
|
||||
return (object) [
|
||||
"tags" => array_map(fn(Tag $tag): string => $tag->label->name, Tag::from($work)),
|
||||
"actions" => [],
|
||||
"details" => $work
|
||||
];
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
return new Response(array_map(fn(Work $work): object => self::entity($work), Work::all()));
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
/* # Overrides */
|
||||
|
||||
:root {
|
||||
--primer-color-accent: 148, 255, 21;
|
||||
--color-accent: rgb(var(--primer-color-accent));
|
||||
}
|
||||
|
||||
vv-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--padding);
|
||||
}
|
||||
|
||||
/* # Sections */
|
||||
|
||||
/* ## Divider */
|
||||
|
||||
vv-shell > hr {
|
||||
border-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
/* ## About */
|
||||
|
||||
section.about {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--padding) / 2);
|
||||
}
|
||||
|
||||
section.about p:first-of-type:first-letter {
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
margin-right: .1rem;
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
section.about span.interests {
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
color: var(--color-accent);
|
||||
animation: interests-hue 5s infinite linear;
|
||||
}
|
||||
|
||||
section.about p i:not(:hover) {
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
/* # Interests */
|
||||
|
||||
div.interests {
|
||||
--text-shadow-blur: 30px;
|
||||
|
||||
transition: 300ms opacity;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-weight: bold;
|
||||
pointer-events: none;
|
||||
font-size: clamp(16px, 15vw, 50px);
|
||||
color: var(--color-accent);
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
z-index: 200;
|
||||
}
|
||||
|
||||
div.interests.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
div.interests p {
|
||||
transition: 500ms transform cubic-bezier(.34,0,0,.99);
|
||||
position: absolute;
|
||||
text-shadow:
|
||||
0 0 var(--text-shadow-blur) black,
|
||||
0 0 var(--text-shadow-blur) black,
|
||||
0 0 var(--text-shadow-blur) black,
|
||||
0 0 var(--text-shadow-blur) black,
|
||||
0 0 var(--text-shadow-blur) black;
|
||||
}
|
||||
|
||||
@keyframes interests-hue {
|
||||
to {
|
||||
-webkit-filter: hue-rotate(360deg);
|
||||
filter: hue-rotate(360deg);
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/* # Overrides */
|
||||
|
||||
:root {
|
||||
--primer-color-accent: 148, 255, 21;
|
||||
--color-accent: rgb(var(--primer-color-accent));
|
||||
}
|
||||
|
||||
vv-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--padding);
|
||||
}
|
||||
|
||||
/* # Content */
|
||||
|
||||
/* ## Title */
|
||||
|
||||
section.title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
/* ## Actions */
|
||||
|
||||
section.actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--padding);
|
||||
}
|
||||
|
||||
/* # Size quries */
|
||||
|
||||
@media (max-width: 800px) {
|
||||
section.actions {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
section.actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
|
@ -1,290 +0,0 @@
|
|||
/* # Overrides */
|
||||
|
||||
:root {
|
||||
--primer-color-accent: 148, 255, 21;
|
||||
--color-accent: rgb(var(--primer-color-accent));
|
||||
}
|
||||
|
||||
vv-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--padding);
|
||||
}
|
||||
|
||||
/* # Content */
|
||||
|
||||
/* ## Title */
|
||||
|
||||
section.title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
padding: calc(var(--padding) * 1.5);
|
||||
background-color: rgba(var(--primer-color-accent), .1);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
section.title > div {
|
||||
margin-top: calc(var(--padding) / 2);
|
||||
display: flex;
|
||||
gap: var(--padding);
|
||||
}
|
||||
|
||||
/* ## Heading */
|
||||
|
||||
section.heading h1::before,
|
||||
section.heading h1::after {
|
||||
opacity: .4;
|
||||
}
|
||||
|
||||
section.heading h1::before {
|
||||
content: "“";
|
||||
}
|
||||
|
||||
section.heading h1::after {
|
||||
content: "”";
|
||||
}
|
||||
|
||||
/* ## Config */
|
||||
|
||||
section.config {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 300px 1fr;
|
||||
gap: calc(var(--padding) * 2);
|
||||
}
|
||||
|
||||
section.config:nth-child(4n+2) {
|
||||
grid-template-columns: 1fr 300px;
|
||||
}
|
||||
|
||||
section.config:nth-child(4n+2) > svg {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
/* ### PC */
|
||||
|
||||
section.config > svg {
|
||||
position: sticky;
|
||||
top: calc(var(--running-size) + var(--padding));
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
section.config > svg :is(rect, path) {
|
||||
transition: 300ms;
|
||||
stroke: white;
|
||||
}
|
||||
|
||||
section.config > svg.active :is(rect, path),
|
||||
section.config > svg:hover :is(rect, path) {
|
||||
opacity: .4;
|
||||
}
|
||||
|
||||
section.config > svg g.active rect,
|
||||
section.config > svg g.active path,
|
||||
section.config > svg g:not(.group):hover rect,
|
||||
section.config > svg g:not(.group):hover path {
|
||||
opacity: 1;
|
||||
stroke: var(--color-accent);
|
||||
}
|
||||
|
||||
section.config > svg g.active rect,
|
||||
section.config > svg g:not(.group):hover rect {
|
||||
filter: drop-shadow(0 0 10px rgba(var(--primer-color-accent), .4));
|
||||
}
|
||||
|
||||
/* #### Case */
|
||||
|
||||
section.config g.case:not(:hover, .active) :is(rect, path) {
|
||||
opacity: .2;
|
||||
}
|
||||
|
||||
section.config > svg g.active path,
|
||||
section.config > svg g:not(.group):hover path {
|
||||
fill: var(--color-accent);
|
||||
}
|
||||
|
||||
/* #### Motherboard */
|
||||
|
||||
section.config > svg .mb .chips {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* #### Active states */
|
||||
|
||||
section.config > svg g:not(.group) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
section.config[data-dram="1"] > svg g.drams g.dram:nth-child(1),
|
||||
section.config[data-dram="2"] > svg g.drams g.dram:nth-child(3n+1),
|
||||
section.config[data-dram="3"] > svg g.drams g.dram:nth-child(-n+3),
|
||||
section.config[data-dram="4"] > svg g.drams g.dram,
|
||||
|
||||
section.config[data-drives-mdottwo="1"] > svg g.mdottwo g.drive:nth-child(1),
|
||||
section.config[data-drives-mdottwo="2"] > svg g.mdottwo g.drive:nth-child(-n+2),
|
||||
section.config[data-drives-mdottwo="3"] > svg g.mdottwo g.drive:nth-child(-n+3),
|
||||
|
||||
section.config[data-drives-twodotfive="1"] > svg g.twodotfive g.drive:nth-child(1),
|
||||
section.config[data-drives-twodotfive="2"] > svg g.twodotfive g.drive:nth-child(-n+2),
|
||||
section.config[data-drives-twodotfive="3"] > svg g.twodotfive g.drive:nth-child(-n+3),
|
||||
|
||||
section.config[data-drives-threedotfive="1"] > svg g.threedotfive g.drive:nth-child(1),
|
||||
section.config[data-drives-threedotfive="2"] > svg g.threedotfive g.drive:nth-child(-n+2),
|
||||
section.config[data-drives-threedotfive="3"] > svg g.threedotfive g.drive:nth-child(-n+3),
|
||||
|
||||
section.config[data-mb="1"] > svg g.mb,
|
||||
section.config[data-psu="1"] > svg g.psu,
|
||||
section.config[data-gpu="1"] > svg g.gpu,
|
||||
section.config[data-cpu="1"] > svg g.cpu,
|
||||
section.config[data-case="1"] > svg g.case {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
/* ## Specs */
|
||||
|
||||
section.config .specs {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--padding) / 2);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
section.config .specs :is(.spec, .group) {
|
||||
--border-width: 4px;
|
||||
|
||||
transition: 300ms background-color, 300ms border-color, 500ms box-shadow;
|
||||
padding: calc(var(--padding) - var(--border-width));
|
||||
border: solid var(--border-width) transparent;
|
||||
background-color: rgba(255, 255, 255, .03);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
section.config .specs :is(.spec, .group) * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ### Active state */
|
||||
|
||||
section.config .specs.active {
|
||||
background-color: rgba(255, 255, 255, .03);
|
||||
}
|
||||
|
||||
section.config .specs.active :is(.group, .spec:not(.active)) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ### Spec */
|
||||
|
||||
section.config .specs .spec {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
section.config .specs .spec:hover {
|
||||
border-color: rgba(255, 255, 255, .05);
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
box-shadow: 0 0 30px 10px rgba(255, 255, 255, .05);
|
||||
}
|
||||
|
||||
section.config .specs .spec.active {
|
||||
border-color: var(--color-accent);
|
||||
background-color: rgba(var(--primer-color-accent), .1);
|
||||
box-shadow: 0 0 30px 10px rgba(var(--primer-color-accent), .05);
|
||||
cursor: initial;
|
||||
}
|
||||
|
||||
section.config .specs.active .spec.active {
|
||||
position: sticky;
|
||||
top: calc(var(--running-size) + var(--padding));
|
||||
}
|
||||
|
||||
section.config .specs .spec h3 {
|
||||
color: rgba(255, 255, 255, .3);
|
||||
}
|
||||
|
||||
section.config .specs .spec span {
|
||||
color: white;
|
||||
}
|
||||
|
||||
section.config .specs .spec > div {
|
||||
display: none;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: calc(var(--padding) / 2);
|
||||
margin-top: var(--padding);
|
||||
}
|
||||
|
||||
section.config .specs .spec.active > div {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
section.config .specs .spec > div label {
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
section.config .specs .spec > svg {
|
||||
display: none;
|
||||
height: calc(var(--padding) / 2);
|
||||
margin: 0 auto;
|
||||
margin-top: calc(var(--padding) / 2);
|
||||
fill: var(--color-accent);
|
||||
}
|
||||
|
||||
/* ### Group */
|
||||
|
||||
section.config .specs .group {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
section.config .specs .group.active {
|
||||
background-color: rgba(255, 255, 255, .2);
|
||||
}
|
||||
|
||||
section.config .specs .group:hover {
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
section.config .specs .group.active:hover {
|
||||
background-color: rgba(255, 255, 255, .3);
|
||||
}
|
||||
|
||||
section.config .specs .group > svg {
|
||||
transition: 300ms transform;
|
||||
fill: var(--color-accent);
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
section.config .specs .group.active > svg {
|
||||
transform: rotateX(180deg);
|
||||
}
|
||||
|
||||
/* #### Collection */
|
||||
|
||||
section.config .specs .collection {
|
||||
display: none;
|
||||
}
|
||||
|
||||
section.config .specs .group.active + .collection {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
/* # Size quries */
|
||||
|
||||
@media (max-width: 700px) {
|
||||
section.title > div {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
section.config,
|
||||
section.config:nth-child(4n+2) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
section.config > svg {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/* # Overrides */
|
||||
|
||||
[vv-page="/search"]:not(body) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--padding);
|
||||
}
|
||||
|
||||
/* # Sections */
|
||||
|
||||
/* ## Search */
|
||||
|
||||
section.search {
|
||||
width: 100%;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--padding);
|
||||
background-color: rgba(255, 255, 255, .05);
|
||||
padding: calc(var(--padding) * 1.5);
|
||||
margin-bottom: calc(var(--padding) * 2);
|
||||
}
|
||||
|
||||
vv-shell[vv-page="/search"] > section.search {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
section.search form {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
section.search search {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
section.search input {
|
||||
width: 100%;
|
||||
border: none;
|
||||
color: black;
|
||||
outline: none;
|
||||
padding: var(--padding);
|
||||
background-color: var(--color-accent);
|
||||
}
|
||||
|
||||
section.search button[type="submit"] {
|
||||
width: 100%;
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
section.search > svg {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* # Search results */
|
||||
|
||||
section.results .result {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--padding) / 2);
|
||||
}
|
||||
|
||||
/* ## Titles */
|
||||
|
||||
section.title a h2 {
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
section.title a h2::before {
|
||||
content: "// ";
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* ## Work */
|
||||
|
||||
section.results.work {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: calc(var(--padding) / 2);
|
||||
}
|
||||
|
||||
section.results.work .result {
|
||||
padding: var(--padding);
|
||||
background-color: rgba(255, 255, 255, .03);
|
||||
border-radius: 6px;
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
import { Elevent } from "/assets/js/modules/npm/Elevent.mjs";
|
||||
|
||||
new Elevent("click", document.querySelectorAll(".group"), (event) => {
|
||||
// Collapse self if already active and current target
|
||||
if (event.target.classList.contains("active")) {
|
||||
return event.target.classList.remove("active");
|
||||
}
|
||||
|
||||
// Collapse all and open current target
|
||||
[...event.target.closest(".specs").querySelectorAll(".group")].forEach(element => element.classList.remove("active"));
|
||||
event.target.classList.add("active");
|
||||
});
|
||||
|
||||
new Elevent("click", document.querySelectorAll(".spec"), (event) => {
|
||||
event.target.classList.add("active");
|
||||
event.target.addEventListener("mouseleave", () => event.target.classList.remove("active"));
|
||||
});
|
||||
|
||||
// Bind hover listeners for components in the SVGs
|
||||
[...document.querySelectorAll("section.config g:not(.group)")].forEach(element => {
|
||||
element.addEventListener("mouseenter", () => {
|
||||
// Find an element in the most adjacent speclist and highlighit it
|
||||
const target = element.closest("section.config").querySelector(`.spec[data-target="${element.dataset.target}"]`);
|
||||
// Get closest specs wrapper element
|
||||
const specsElement = target.closest(".specs");
|
||||
// Spec item is part of a collection, we need to expand the group if that is the case
|
||||
const collectionElement = target.closest(".collection") ?? null;
|
||||
// Don't close the group after hove ends
|
||||
let closeGroupOnLeave = false;
|
||||
|
||||
// Set fixed height on .specs wrapper to prevent glitchy page jumping when scrolled
|
||||
specsElement.style.setProperty("height", `${specsElement.offsetHeight}px`);
|
||||
target.classList.add("active");
|
||||
specsElement.classList.add("active");
|
||||
|
||||
if (collectionElement) {
|
||||
// Close the group on leave if the group wasn't active before hovering
|
||||
closeGroupOnLeave = !collectionElement.previousElementSibling.classList.contains("active");
|
||||
|
||||
collectionElement.previousElementSibling.classList.add("active");
|
||||
}
|
||||
|
||||
// Bind hover leave listener
|
||||
element.addEventListener("mouseleave", () => {
|
||||
// Reset to initial states
|
||||
target.classList.remove("active");
|
||||
specsElement.classList.remove("active");
|
||||
specsElement.style.removeProperty("height");
|
||||
|
||||
// Group was closed prior to hover, let's close it on hover leave
|
||||
if (closeGroupOnLeave) {
|
||||
collectionElement.previousElementSibling.classList.remove("active");
|
||||
}
|
||||
}, { once: true });
|
||||
});
|
||||
});
|
||||
|
||||
// Bind event listeners for components in the spec lists
|
||||
[...document.querySelectorAll("section.config .spec:not(.group)")].forEach(element => {
|
||||
element.addEventListener("mouseenter", () => {
|
||||
const svgTarget = element.closest("section.config").querySelector(`svg`);
|
||||
const target = svgTarget.querySelector(`svg g[data-target="${element.dataset.target}"]`);
|
||||
|
||||
svgTarget.classList.add("active");
|
||||
target.classList.add("active");
|
||||
|
||||
element.addEventListener("mouseleave", () => {
|
||||
svgTarget.classList.remove("active");
|
||||
target.classList.remove("active");
|
||||
}, { once: true });
|
||||
});
|
||||
});
|
|
@ -1,50 +0,0 @@
|
|||
import { Elevent } from "/assets/js/modules/npm/Elevent.mjs";
|
||||
|
||||
const CLASSNAME_SEARCHBOX_ACTIVE = "searchboxActive";
|
||||
|
||||
// Handle search box open/close buttons
|
||||
{
|
||||
// Open search box
|
||||
new Elevent("click", document.querySelector(".searchbox-open"), () => {
|
||||
document.querySelector("header").classList.add(CLASSNAME_SEARCHBOX_ACTIVE);
|
||||
// Select searchbox inner input element
|
||||
document.querySelector("searchbox input").focus();
|
||||
});
|
||||
|
||||
// Close searchbox
|
||||
new Elevent("click", document.querySelector(".searchbox-close"), () => {
|
||||
// Disable search button interaction while animation is running
|
||||
// This is required to prevent conflicts with the :hover "peak" transformation
|
||||
const searchButtonElement = document.querySelector("header button.search");
|
||||
const transformDuration = parseInt(window.getComputedStyle(searchButtonElement).getPropertyValue("--transform-duration"));
|
||||
searchButtonElement.style.setProperty("pointer-events", "none");
|
||||
|
||||
document.querySelector("header").classList.remove(CLASSNAME_SEARCHBOX_ACTIVE);
|
||||
|
||||
// Wait for the transform animation to finish
|
||||
setTimeout(() => searchButtonElement.style.removeProperty("pointer-events"), transformDuration);
|
||||
});
|
||||
}
|
||||
|
||||
// Root shell navigation event handlers
|
||||
{
|
||||
// On all top shell navigations
|
||||
new Elevent(vv.Navigation.EVENTS.STARTED, vv.Navigation.rootShellElement, () => {
|
||||
// Close searchbox on top shell navigations
|
||||
document.querySelector("header").classList.remove(CLASSNAME_SEARCHBOX_ACTIVE);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle search logic
|
||||
{
|
||||
const searchResultsElement = document.querySelector("search-results");
|
||||
|
||||
document.querySelector("header input[type='search']").addEventListener("input", (event) => {
|
||||
// Debounce user input
|
||||
clearTimeout(event.target._throttle);
|
||||
event.target._throttle = setTimeout(() => {
|
||||
// Navigate search-results element on user input
|
||||
new vv.Navigation(`/search?q=${event.target.value}`).navigate(searchResultsElement);
|
||||
}, 100);
|
||||
});
|
||||
}
|
|
@ -1,173 +0,0 @@
|
|||
<svg viewBox="0 0 236 288" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask fill="#fff" id="a"><path d="M19 269a1 1 0 0 1-1-1v-11a1 1 0 0 1 1-1v13Z"></path></mask>
|
||||
<mask fill="#fff" id="b"><path d="M87 241a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1v-7Z"></path></mask>
|
||||
<mask fill="#fff" id="c"><path d="M87 251a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1v-7Z"></path></mask>
|
||||
<mask fill="#fff" id="d"><path d="M179 272.051c0-.58.471-1.051 1.051-1.051h8.898c.58 0 1.051.471 1.051 1.051h-11Z"></path></mask>
|
||||
<mask fill="#fff" id="e"><path d="M191 272.051c0-.58.471-1.051 1.051-1.051h2.898c.58 0 1.051.471 1.051 1.051h-5Z"></path></mask>
|
||||
<mask fill="#fff" id="f"><path d="M179 257.333c0-.58.471-1.051 1.051-1.051h8.898c.58 0 1.051.471 1.051 1.051h-11Z"></path></mask>
|
||||
<mask fill="#fff" id="g"><path d="M191 257.333c0-.58.471-1.051 1.051-1.051h2.898c.58 0 1.051.471 1.051 1.051h-5Z"></path></mask>
|
||||
<mask fill="#fff" id="h"><path d="M179 242.615c0-.58.471-1.051 1.051-1.051h8.898c.58 0 1.051.471 1.051 1.051h-11Z"></path></mask>
|
||||
<mask fill="#fff" id="i"><path d="M191 242.615c0-.58.471-1.051 1.051-1.051h2.898c.58 0 1.051.471 1.051 1.051h-5Z"></path></mask>
|
||||
<mask fill="#fff" id="j"><path d="M133 257.333c0-.58.471-1.051 1.051-1.051h8.898c.58 0 1.051.471 1.051 1.051h-11Z"></path></mask>
|
||||
<mask fill="#fff" id="k"><path d="M145 257.333c0-.58.471-1.051 1.051-1.051h2.898c.58 0 1.051.471 1.051 1.051h-5Z"></path></mask>
|
||||
<mask fill="#fff" id="l"><path d="M133 242.615c0-.58.471-1.051 1.051-1.051h8.898c.58 0 1.051.471 1.051 1.051h-11Z"></path></mask>
|
||||
<mask fill="#fff" id="m"><path d="M145 242.615c0-.58.471-1.051 1.051-1.051h2.898c.58 0 1.051.471 1.051 1.051h-5Z"></path></mask>
|
||||
<mask fill="#fff" id="n"><path d="M133 272.051c0-.58.471-1.051 1.051-1.051h8.898c.58 0 1.051.471 1.051 1.051h-11Z"></path></mask>
|
||||
<mask fill="#fff" id="o"><path d="M145 272.051c0-.58.471-1.051 1.051-1.051h2.898c.58 0 1.051.471 1.051 1.051h-5Z"></path></mask>
|
||||
<mask fill="#fff" id="p"><path d="M186 40a1 1 0 0 1 1-1h9a1 1 0 0 1 1 1h-11Z"></path></mask>
|
||||
<mask fill="#fff" id="q"><path d="M198 40a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1h-5Z"></path></mask>
|
||||
<mask fill="#fff" id="r"><path d="M186 51a1 1 0 0 1 1-1h9a1 1 0 0 1 1 1h-11Z"></path></mask>
|
||||
<mask fill="#fff" id="s"><path d="M198 51a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1h-5Z"></path></mask>
|
||||
<mask fill="#fff" id="t"><path d="M186 62a1 1 0 0 1 1-1h9a1 1 0 0 1 1 1h-11Z"></path></mask>
|
||||
<mask fill="#fff" id="u"><path d="M198 62a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1h-5Z"></path></mask>
|
||||
<mask fill="#fff" id="v"><path d="M186 73a1 1 0 0 1 1-1h9a1 1 0 0 1 1 1h-11Z"></path></mask>
|
||||
<mask fill="#fff" id="w"><path d="M198 73a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1h-5Z"></path></mask>
|
||||
<mask fill="#fff" id="x"><path d="M129.535 123.781h-1.082a.453.453 0 0 1 0-.906h1.082v.906Z"></path></mask>
|
||||
<mask fill="#fff" id="y"><path d="M129.535 115.625h-1.082a.453.453 0 0 1 0-.906h1.082v.906Z"></path></mask>
|
||||
<mask fill="#fff" id="z"><path d="M129.535 121.969H129a1 1 0 0 1-1-1v-3.438a1 1 0 0 1 1-1h.535v5.438Z"></path></mask>
|
||||
<mask fill="#fff" id="A"><path d="M92.535 123.781h-1.082a.453.453 0 0 1 0-.906h1.082v.906Z"></path></mask>
|
||||
<mask fill="#fff" id="B"><path d="M92.535 115.625h-1.082a.453.453 0 0 1 0-.906h1.082v.906Z"></path></mask>
|
||||
<mask fill="#fff" id="C"><path d="M92.535 121.969H92a1 1 0 0 1-1-1v-3.438a1 1 0 0 1 1-1h.535v5.438Z"></path></mask>
|
||||
<mask fill="#fff" id="D"><path d="M68 143v-2a1 1 0 0 1 1-1h29a1 1 0 0 1 1 1v2H68Z"></path></mask>
|
||||
<mask fill="#fff" id="E"><path d="M101 143v-2a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1v2h-3Z"></path></mask>
|
||||
<mask fill="#fff" id="F"><path d="M64 143v-2a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1v2h-3Z"></path></mask>
|
||||
<mask fill="#fff" id="G"><path d="M55.535 123.781h-1.082a.453.453 0 0 1 0-.906h1.082v.906Z"></path></mask>
|
||||
<mask fill="#fff" id="H"><path d="M55.535 115.625h-1.082a.453.453 0 0 1 0-.906h1.082v.906Z"></path></mask>
|
||||
<mask fill="#fff" id="I"><path d="M55.535 121.969H55a1 1 0 0 1-1-1v-3.438a1 1 0 0 1 1-1h.535v5.438Z"></path></mask>
|
||||
<mask fill="#fff" id="J"><path d="M76 38h1a1 1 0 0 1 1 1v33a1 1 0 0 1-1 1h-1V38Z"></path></mask>
|
||||
<mask fill="#fff" id="K"><path d="M76 76h1a1 1 0 0 1 1 1v22a1 1 0 0 1-1 1h-1V76Z"></path></mask>
|
||||
<mask fill="#fff" id="L"><path d="M60 38h1a1 1 0 0 1 1 1v33a1 1 0 0 1-1 1h-1V38Z"></path></mask>
|
||||
<mask fill="#fff" id="M"><path d="M60 76h1a1 1 0 0 1 1 1v22a1 1 0 0 1-1 1h-1V76Z"></path></mask>
|
||||
<mask fill="#fff" id="N"><path d="M137 100h-1a1 1 0 0 1-1-1V66a1 1 0 0 1 1-1h1v35Z"></path></mask>
|
||||
<mask fill="#fff" id="O"><path d="M137 62h-1a1 1 0 0 1-1-1V39a1 1 0 0 1 1-1h1v24Z"></path></mask>
|
||||
<mask fill="#fff" id="P"><path d="M155 100h-1a1 1 0 0 1-1-1V66a1 1 0 0 1 1-1h1v35Z"></path></mask>
|
||||
<mask fill="#fff" id="Q"><path d="M155 62h-1a1 1 0 0 1-1-1V39a1 1 0 0 1 1-1h1v24Z"></path></mask>
|
||||
<g data-target="case" data-index="1" class="case">
|
||||
<rect height="278" rx="3" stroke="#fff" stroke-width="6" width="230" x="3" y="7"></rect>
|
||||
<path d="M20.639 216.906a2 2 0 0 1 1.674-.906h192.14a2 2 0 0 1 1.756 1.043l2.18 4c.726 1.333-.239 2.957-1.756 2.957H19.697c-1.589 0-2.543-1.764-1.674-3.094l2.616-4ZM5.451.793A2 2 0 0 1 7.046 0h222.808a2 2 0 0 1 1.692.934l2.521 4c.84 1.331-.118 3.066-1.692 3.066H4.021C2.369 8 1.429 6.11 2.425 4.793l3.026-4Z" fill="#fff"></path>
|
||||
</g>
|
||||
<g data-target="psu" class="psu">
|
||||
<path d="M20 268v-11h-4v11h4Zm-3-12v13h4v-13h-4Zm3 1a1 1 0 0 1-1 1v-4a3 3 0 0 0-3 3h4Zm-1 10a1 1 0 0 1 1 1h-4a3 3 0 0 0 3 3v-4Z" fill="#fff" mask="url(#a)"></path>
|
||||
<path d="M86 242v5h4v-5h-4Zm3 6v-7h-4v7h4Zm-3-1a1 1 0 0 1 1-1v4a3 3 0 0 0 3-3h-4Zm1-4a1 1 0 0 1-1-1h4a3 3 0 0 0-3-3v4Z" fill="#fff" mask="url(#b)"></path>
|
||||
<path d="M86 252v5h4v-5h-4Zm3 6v-7h-4v7h4Zm-3-1a1 1 0 0 1 1-1v4a3 3 0 0 0 3-3h-4Zm1-4a1 1 0 0 1-1-1h4a3 3 0 0 0-3-3v4Z" fill="#fff" mask="url(#c)"></path>
|
||||
<rect height="38" rx="3.5" stroke="#fff" stroke-width="3" width="65" x="20.5" y="234.5"></rect>
|
||||
</g>
|
||||
<g class="group drives threedotfive">
|
||||
<g data-target="drive" class="drive">
|
||||
<rect height="9.564" rx="2" stroke="#fff" stroke-width="2" width="41" x="176" y="263.436"></rect>
|
||||
<path d="M180.051 273h8.898v-4h-8.898v4Zm9.949-2.949h-11v4h11v-4ZM188.949 273a.95.95 0 0 1-.949-.949h4a3.051 3.051 0 0 0-3.051-3.051v4Zm-8.898-4a3.051 3.051 0 0 0-3.051 3.051h4a.95.95 0 0 1-.949.949v-4Z" fill="#fff" mask="url(#d)"></path>
|
||||
<path d="M192.051 273h2.898v-4h-2.898v4Zm3.949-2.949h-5v4h5v-4ZM194.949 273a.95.95 0 0 1-.949-.949h4a3.051 3.051 0 0 0-3.051-3.051v4Zm-2.898-4a3.051 3.051 0 0 0-3.051 3.051h4a.95.95 0 0 1-.949.949v-4Z" fill="#fff" mask="url(#e)"></path>
|
||||
</g>
|
||||
<g data-target="drive" class="drive">
|
||||
<rect height="9.564" rx="2" stroke="#fff" stroke-width="2" width="41" x="176" y="248.718"></rect>
|
||||
<path d="M192.051 258.282h2.898v-4h-2.898v4Zm3.949-2.949h-5v4h5v-4Zm-1.051 2.949a.95.95 0 0 1-.949-.949h4a3.051 3.051 0 0 0-3.051-3.051v4Zm-2.898-4a3.051 3.051 0 0 0-3.051 3.051h4a.95.95 0 0 1-.949.949v-4Z" fill="#fff" mask="url(#g)"></path>
|
||||
<path d="M180.051 258.282h8.898v-4h-8.898v4Zm9.949-2.949h-11v4h11v-4Zm-1.051 2.949a.95.95 0 0 1-.949-.949h4a3.051 3.051 0 0 0-3.051-3.051v4Zm-8.898-4a3.051 3.051 0 0 0-3.051 3.051h4a.95.95 0 0 1-.949.949v-4Z" fill="#fff" mask="url(#f)"></path>
|
||||
</g>
|
||||
<g data-target="drive" class="drive">
|
||||
<rect height="9.564" rx="2" stroke="#fff" stroke-width="2" width="41" x="176" y="234"></rect>
|
||||
<path d="M192.051 243.564h2.898v-4h-2.898v4Zm3.949-2.949h-5v4h5v-4Zm-1.051 2.949a.95.95 0 0 1-.949-.949h4a3.051 3.051 0 0 0-3.051-3.051v4Zm-2.898-4a3.051 3.051 0 0 0-3.051 3.051h4a.95.95 0 0 1-.949.949v-4Z" fill="#fff" mask="url(#i)"></path>
|
||||
<path d="M180.051 243.564h8.898v-4h-8.898v4Zm9.949-2.949h-11v4h11v-4Zm-1.051 2.949a.95.95 0 0 1-.949-.949h4a3.051 3.051 0 0 0-3.051-3.051v4Zm-8.898-4a3.051 3.051 0 0 0-3.051 3.051h4a.95.95 0 0 1-.949.949v-4Z" fill="#fff" mask="url(#h)"></path>
|
||||
</g>
|
||||
<g data-target="drive" class="drive">
|
||||
<rect height="9.564" rx="2" stroke="#fff" stroke-width="2" width="41" x="130" y="248.718"></rect>
|
||||
<path d="M134.051 258.282h8.898v-4h-8.898v4Zm9.949-2.949h-11v4h11v-4Zm-1.051 2.949a.95.95 0 0 1-.949-.949h4a3.051 3.051 0 0 0-3.051-3.051v4Zm-8.898-4a3.051 3.051 0 0 0-3.051 3.051h4a.95.95 0 0 1-.949.949v-4Z" fill="#fff" mask="url(#j)"></path>
|
||||
<path d="M146.051 258.282h2.898v-4h-2.898v4Zm3.949-2.949h-5v4h5v-4Zm-1.051 2.949a.95.95 0 0 1-.949-.949h4a3.051 3.051 0 0 0-3.051-3.051v4Zm-2.898-4a3.051 3.051 0 0 0-3.051 3.051h4a.95.95 0 0 1-.949.949v-4Z" fill="#fff" mask="url(#k)"></path>
|
||||
</g>
|
||||
<g data-target="drive" class="drive">
|
||||
<rect height="9.564" rx="2" stroke="#fff" stroke-width="2" width="41" x="130" y="234"></rect>
|
||||
<path d="M134.051 243.564h8.898v-4h-8.898v4Zm9.949-2.949h-11v4h11v-4Zm-1.051 2.949a.95.95 0 0 1-.949-.949h4a3.051 3.051 0 0 0-3.051-3.051v4Zm-8.898-4a3.051 3.051 0 0 0-3.051 3.051h4a.95.95 0 0 1-.949.949v-4Z" fill="#fff" mask="url(#l)"></path>
|
||||
<path d="M146.051 243.564h2.898v-4h-2.898v4Zm3.949-2.949h-5v4h5v-4Zm-1.051 2.949a.95.95 0 0 1-.949-.949h4a3.051 3.051 0 0 0-3.051-3.051v4Zm-2.898-4a3.051 3.051 0 0 0-3.051 3.051h4a.95.95 0 0 1-.949.949v-4Z" fill="#fff" mask="url(#m)"></path>
|
||||
</g>
|
||||
<g data-target="drive" class="drive">
|
||||
<rect height="9.564" rx="2" stroke="#fff" stroke-width="2" width="41" x="130" y="263.436"></rect>
|
||||
<path d="M134.051 273h8.898v-4h-8.898v4Zm9.949-2.949h-11v4h11v-4ZM142.949 273a.95.95 0 0 1-.949-.949h4a3.051 3.051 0 0 0-3.051-3.051v4Zm-8.898-4a3.051 3.051 0 0 0-3.051 3.051h4a.95.95 0 0 1-.949.949v-4Z" fill="#fff" mask="url(#n)"></path>
|
||||
<path d="M146.051 273h2.898v-4h-2.898v4Zm3.949-2.949h-5v4h5v-4ZM148.949 273a.95.95 0 0 1-.949-.949h4a3.051 3.051 0 0 0-3.051-3.051v4Zm-2.898-4a3.051 3.051 0 0 0-3.051 3.051h4a.95.95 0 0 1-.949.949v-4Z" fill="#fff" mask="url(#o)"></path>
|
||||
</g>
|
||||
</g>
|
||||
<g class="group drives twodotfive">
|
||||
<g data-target="drive" class="drive">
|
||||
<rect height="5" rx="2" stroke="#fff" stroke-width="2" width="41" x="183" y="36"></rect>
|
||||
<path d="M187 41h9v-4h-9v4Zm10-3h-11v4h11v-4Zm-1 3a1 1 0 0 1-1-1h4a3 3 0 0 0-3-3v4Zm-9-4a3 3 0 0 0-3 3h4a1 1 0 0 1-1 1v-4Z" fill="#fff" mask="url(#p)"></path>
|
||||
<path d="M199 41h3v-4h-3v4Zm4-3h-5v4h5v-4Zm-1 3a1 1 0 0 1-1-1h4a3 3 0 0 0-3-3v4Zm-3-4a3 3 0 0 0-3 3h4a1 1 0 0 1-1 1v-4Z" fill="#fff" mask="url(#q)"></path>
|
||||
</g>
|
||||
<g data-target="drive" class="drive">
|
||||
<rect height="5" rx="2" stroke="#fff" stroke-width="2" width="41" x="183" y="47"></rect>
|
||||
<path d="M187 52h9v-4h-9v4Zm10-3h-11v4h11v-4Zm-1 3a1 1 0 0 1-1-1h4a3 3 0 0 0-3-3v4Zm-9-4a3 3 0 0 0-3 3h4a1 1 0 0 1-1 1v-4Z" fill="#fff" mask="url(#r)"></path>
|
||||
<path d="M199 52h3v-4h-3v4Zm4-3h-5v4h5v-4Zm-1 3a1 1 0 0 1-1-1h4a3 3 0 0 0-3-3v4Zm-3-4a3 3 0 0 0-3 3h4a1 1 0 0 1-1 1v-4Z" fill="#fff" mask="url(#s)"></path>
|
||||
</g>
|
||||
<g data-target="drive" class="drive">
|
||||
<rect height="5" rx="2" stroke="#fff" stroke-width="2" width="41" x="183" y="58"></rect>
|
||||
<path d="M187 63h9v-4h-9v4Zm10-3h-11v4h11v-4Zm-1 3a1 1 0 0 1-1-1h4a3 3 0 0 0-3-3v4Zm-9-4a3 3 0 0 0-3 3h4a1 1 0 0 1-1 1v-4Z" fill="#fff" mask="url(#t)"></path>
|
||||
<path d="M199 63h3v-4h-3v4Zm4-3h-5v4h5v-4Zm-1 3a1 1 0 0 1-1-1h4a3 3 0 0 0-3-3v4Zm-3-4a3 3 0 0 0-3 3h4a1 1 0 0 1-1 1v-4Z" fill="#fff" mask="url(#u)"></path>
|
||||
</g>
|
||||
<g data-target="drive" class="drive">
|
||||
<rect height="5" rx="2" stroke="#fff" stroke-width="2" width="41" x="183" y="69"></rect>
|
||||
<path d="M187 74h9v-4h-9v4Zm10-3h-11v4h11v-4Zm-1 3a1 1 0 0 1-1-1h4a3 3 0 0 0-3-3v4Zm-9-4a3 3 0 0 0-3 3h4a1 1 0 0 1-1 1v-4Z" fill="#fff" mask="url(#v)"></path>
|
||||
<path d="M199 74h3v-4h-3v4Zm4-3h-5v4h5v-4Zm-1 3a1 1 0 0 1-1-1h4a3 3 0 0 0-3-3v4Zm-3-4a3 3 0 0 0-3 3h4a1 1 0 0 1-1 1v-4Z" fill="#fff" mask="url(#w)"></path>
|
||||
</g>
|
||||
</g>
|
||||
<g data-target="mb" class="mb">
|
||||
<rect height="176" rx="3.5" stroke="#fff" stroke-width="3" width="152" x="22.5" y="25.5"></rect>
|
||||
<path d="m31.19 123.309-.01.007-7.32 5.782c-1.968 1.554-4.86.153-4.86-2.354V42a3 3 0 0 1 3-3h15a3 3 0 0 1 3 3v73.048c0 .931-.432 1.81-1.17 2.377l-7.64 5.884Z" fill="#fff"></path>
|
||||
<path d="m31.19 123.309-.01.007-7.32 5.782c-1.968 1.554-4.86.153-4.86-2.354V42a3 3 0 0 1 3-3h15a3 3 0 0 1 3 3v73.048c0 .931-.432 1.81-1.17 2.377l-7.64 5.884Z" stroke="#fff" stroke-width="2"></path>
|
||||
<g class="chips">
|
||||
<rect fill="#fff" height="13" rx="2" width="13" x="23" y="44"></rect>
|
||||
<rect fill="#fff" height="13" rx="2" width="13" x="23" y="61"></rect>
|
||||
<rect fill="#fff" height="13" rx="2" width="13" x="23" y="78"></rect>
|
||||
</g>
|
||||
</g>
|
||||
<g class="group drives mdottwo">
|
||||
<g data-target="drive" class="drive">
|
||||
<path d="M129.535 123.781v2h2v-2h-2Zm0-.906h2v-2h-2v2Zm0-1.094h-1.082v4h1.082v-4Zm-1.082 3.094h1.082v-4h-1.082v4Zm-.918-2v.906h4v-.906h-4Zm2.465.453c0 .854-.693 1.547-1.547 1.547v-4a2.453 2.453 0 0 0-2.453 2.453h4Zm-1.547-1.547c.854 0 1.547.693 1.547 1.547h-4a2.453 2.453 0 0 0 2.453 2.453v-4Z" fill="#fff" mask="url(#x)"></path>
|
||||
<path d="M129.535 115.625v2h2v-2h-2Zm0-.906h2v-2h-2v2Zm0-1.094h-1.082v4h1.082v-4Zm-1.082 3.094h1.082v-4h-1.082v4Zm-.918-2v.906h4v-.906h-4Zm2.465.453c0 .854-.693 1.547-1.547 1.547v-4a2.453 2.453 0 0 0-2.453 2.453h4Zm-1.547-1.547c.854 0 1.547.693 1.547 1.547h-4a2.453 2.453 0 0 0 2.453 2.453v-4Z" fill="#fff" mask="url(#y)"></path>
|
||||
<path d="M129.535 121.969v2h2v-2h-2Zm0-5.438h2v-2h-2v2Zm0 3.438H129v4h.535v-4Zm.465 1v-3.438h-4v3.438h4Zm-1-2.438h.535v-4H129v4Zm-1.465-2v5.438h4v-5.438h-4Zm2.465 1a1 1 0 0 1-1 1v-4a3 3 0 0 0-3 3h4Zm-1 2.438a1 1 0 0 1 1 1h-4a3 3 0 0 0 3 3v-4Z" fill="#fff" mask="url(#z)"></path>
|
||||
<rect height="12.5" rx="2" stroke="#fff" stroke-width="2" width="29.465" x="130.535" y="113"></rect>
|
||||
</g>
|
||||
<g data-target="drive" class="drive">
|
||||
<path d="M92.535 123.781v2h2v-2h-2Zm0-.906h2v-2h-2v2Zm0-1.094h-1.082v4h1.082v-4Zm-1.082 3.094h1.082v-4h-1.082v4Zm-.918-2v.906h4v-.906h-4Zm2.465.453c0 .854-.693 1.547-1.547 1.547v-4A2.453 2.453 0 0 0 89 123.328h4Zm-1.547-1.547c.854 0 1.547.693 1.547 1.547h-4a2.453 2.453 0 0 0 2.453 2.453v-4Z" fill="#fff" mask="url(#A)"></path>
|
||||
<path d="M92.535 115.625v2h2v-2h-2Zm0-.906h2v-2h-2v2Zm0-1.094h-1.082v4h1.082v-4Zm-1.082 3.094h1.082v-4h-1.082v4Zm-.918-2v.906h4v-.906h-4Zm2.465.453c0 .854-.693 1.547-1.547 1.547v-4A2.453 2.453 0 0 0 89 115.172h4Zm-1.547-1.547c.854 0 1.547.693 1.547 1.547h-4a2.453 2.453 0 0 0 2.453 2.453v-4Z" fill="#fff" mask="url(#B)"></path>
|
||||
<path d="M92.535 121.969v2h2v-2h-2Zm0-5.438h2v-2h-2v2Zm0 3.438H92v4h.535v-4Zm.465 1v-3.438h-4v3.438h4Zm-1-2.438h.535v-4H92v4Zm-1.465-2v5.438h4v-5.438h-4Zm2.465 1a1 1 0 0 1-1 1v-4a3 3 0 0 0-3 3h4Zm-1 2.438a1 1 0 0 1 1 1h-4a3 3 0 0 0 3 3v-4Z" fill="#fff" mask="url(#C)"></path>
|
||||
<rect height="12.5" rx="2" stroke="#fff" stroke-width="2" width="29.465" x="93.535" y="113"></rect>
|
||||
</g>
|
||||
<g data-target="drive" class="drive">
|
||||
<path d="M55.535 123.781v2h2v-2h-2Zm0-.906h2v-2h-2v2Zm0-1.094h-1.082v4h1.082v-4Zm-1.082 3.094h1.082v-4h-1.082v4Zm-.918-2v.906h4v-.906h-4Zm2.465.453c0 .854-.693 1.547-1.547 1.547v-4A2.453 2.453 0 0 0 52 123.328h4Zm-1.547-1.547c.854 0 1.547.693 1.547 1.547h-4a2.453 2.453 0 0 0 2.453 2.453v-4Z" fill="#fff" mask="url(#G)"></path>
|
||||
<path d="M55.535 115.625v2h2v-2h-2Zm0-.906h2v-2h-2v2Zm0-1.094h-1.082v4h1.082v-4Zm-1.082 3.094h1.082v-4h-1.082v4Zm-.918-2v.906h4v-.906h-4Zm2.465.453c0 .854-.693 1.547-1.547 1.547v-4A2.453 2.453 0 0 0 52 115.172h4Zm-1.547-1.547c.854 0 1.547.693 1.547 1.547h-4a2.453 2.453 0 0 0 2.453 2.453v-4Z" fill="#fff" mask="url(#H)"></path>
|
||||
<path d="M55.535 121.969v2h2v-2h-2Zm0-5.438h2v-2h-2v2Zm0 3.438H55v4h.535v-4Zm.465 1v-3.438h-4v3.438h4Zm-1-2.438h.535v-4H55v4Zm-1.465-2v5.438h4v-5.438h-4Zm2.465 1a1 1 0 0 1-1 1v-4a3 3 0 0 0-3 3h4Zm-1 2.438a1 1 0 0 1 1 1h-4a3 3 0 0 0 3 3v-4Z" fill="#fff" mask="url(#I)"></path>
|
||||
<rect height="12.5" rx="2" stroke="#fff" stroke-width="2" width="29.465" x="56.535" y="113"></rect>
|
||||
</g>
|
||||
</g>
|
||||
<g data-target="gpu" class="gpu">
|
||||
<path d="M59 148h86" stroke="#fff" stroke-linecap="round"></path>
|
||||
<path d="M59.5 136a1.5 1.5 0 0 0-3 0h3Zm0 8v-8h-3v8h3Z" fill="#fff"></path>
|
||||
<path d="M68 143v-2a1 1 0 0 1 1-1h29a1 1 0 0 1 1 1v2H68Z" mask="url(#D)" stroke="#fff" stroke-width="3"></path>
|
||||
<path d="M101 143v-2a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1v2h-3Z" mask="url(#E)" stroke="#fff" stroke-width="3"></path>
|
||||
<path d="M64 143v-2a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1v2h-3Z" mask="url(#F)" stroke="#fff" stroke-width="3"></path>
|
||||
<path d="M55 147a3 3 0 0 1 3-3h88a3 3 0 0 1 3 3v.525c0 .237-.028.473-.083.703l-2.764 11.474a3.001 3.001 0 0 1-2.917 2.298H58a3 3 0 0 1-3-3v-12Z" stroke="#fff" stroke-width="2"></path>
|
||||
</g>
|
||||
<g class="group drams">
|
||||
<g data-target="dram" class="dram">
|
||||
<path d="M60 38v-2h-2v2h2Zm0 35h-2v2h2v-2Zm0-33h1v-4h-1v4Zm0-1v33h4V39h-4Zm1 32h-1v4h1v-4Zm1 2V38h-4v35h4Zm-2-1a1 1 0 0 1 1-1v4a3 3 0 0 0 3-3h-4Zm1-32a1 1 0 0 1-1-1h4a3 3 0 0 0-3-3v4Z" fill="#fff" mask="url(#L)"></path>
|
||||
<path d="M60 76v-2h-2v2h2Zm0 24h-2v2h2v-2Zm0-22h1v-4h-1v4Zm0-1v22h4V77h-4Zm1 21h-1v4h1v-4Zm1 2V76h-4v24h4Zm-2-1a1 1 0 0 1 1-1v4a3 3 0 0 0 3-3h-4Zm1-21a1 1 0 0 1-1-1h4a3 3 0 0 0-3-3v4Z" fill="#fff" mask="url(#M)"></path>
|
||||
<rect height="66" rx="2" stroke="#fff" stroke-width="2" width="4" x="55" y="36"></rect>
|
||||
</g>
|
||||
<g data-target="dram" class="dram">
|
||||
<path d="M76 38v-2h-2v2h2Zm0 35h-2v2h2v-2Zm0-33h1v-4h-1v4Zm0-1v33h4V39h-4Zm1 32h-1v4h1v-4Zm1 2V38h-4v35h4Zm-2-1a1 1 0 0 1 1-1v4a3 3 0 0 0 3-3h-4Zm1-32a1 1 0 0 1-1-1h4a3 3 0 0 0-3-3v4Z" fill="#fff" mask="url(#J)"></path>
|
||||
<path d="M76 76v-2h-2v2h2Zm0 24h-2v2h2v-2Zm0-22h1v-4h-1v4Zm0-1v22h4V77h-4Zm1 21h-1v4h1v-4Zm1 2V76h-4v24h4Zm-2-1a1 1 0 0 1 1-1v4a3 3 0 0 0 3-3h-4Zm1-21a1 1 0 0 1-1-1h4a3 3 0 0 0-3-3v4Z" fill="#fff" mask="url(#K)"></path>
|
||||
<rect height="66" rx="2" stroke="#fff" stroke-width="2" width="4" x="71" y="36"></rect>
|
||||
</g>
|
||||
<g data-target="dram" class="dram">
|
||||
<path d="M137 100v2h2v-2h-2Zm0-35h2v-2h-2v2Zm0 33h-1v4h1v-4Zm0 1V66h-4v33h4Zm-1-32h1v-4h-1v4Zm-1-2v35h4V65h-4Zm2 1a1 1 0 0 1-1 1v-4a3 3 0 0 0-3 3h4Zm-1 32a1 1 0 0 1 1 1h-4a3 3 0 0 0 3 3v-4Z" fill="#fff" mask="url(#N)"></path>
|
||||
<path d="M137 62v2h2v-2h-2Zm0-24h2v-2h-2v2Zm0 22h-1v4h1v-4Zm0 1V39h-4v22h4Zm-1-21h1v-4h-1v4Zm-1-2v24h4V38h-4Zm2 1a1 1 0 0 1-1 1v-4a3 3 0 0 0-3 3h4Zm-1 21a1 1 0 0 1 1 1h-4a3 3 0 0 0 3 3v-4Z" fill="#fff" mask="url(#O)"></path>
|
||||
<rect height="66" rx="2" stroke="#fff" stroke-width="2" transform="rotate(180 142 102)" width="4" x="142" y="102"></rect>
|
||||
</g>
|
||||
<g data-target="dram" class="dram">
|
||||
<path d="M155 100v2h2v-2h-2Zm0-35h2v-2h-2v2Zm0 33h-1v4h1v-4Zm0 1V66h-4v33h4Zm-1-32h1v-4h-1v4Zm-1-2v35h4V65h-4Zm2 1a1 1 0 0 1-1 1v-4a3 3 0 0 0-3 3h4Zm-1 32a1 1 0 0 1 1 1h-4a3 3 0 0 0 3 3v-4Z" fill="#fff" mask="url(#P)"></path>
|
||||
<path d="M155 62v2h2v-2h-2Zm0-24h2v-2h-2v2Zm0 22h-1v4h1v-4Zm0 1V39h-4v22h4Zm-1-21h1v-4h-1v4Zm-1-2v24h4V38h-4Zm2 1a1 1 0 0 1-1 1v-4a3 3 0 0 0-3 3h4Zm-1 21a1 1 0 0 1 1 1h-4a3 3 0 0 0 3 3v-4Z" fill="#fff" mask="url(#Q)"></path>
|
||||
<rect height="66" rx="2" stroke="#fff" stroke-width="2" transform="rotate(180 160 102)" width="4" x="160" y="102"></rect>
|
||||
</g>
|
||||
</g>
|
||||
<g data-target="cpu" class="cpu">
|
||||
<path d="M98 56a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1h-3ZM113 56a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1h-3ZM103 56a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1h-3ZM108 56a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1h-3ZM116 82a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1h3ZM101 82a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1h3ZM111 82a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1h3ZM106 82a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1h3ZM120 60a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1v-3ZM120 75a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1v-3ZM120 65a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1v-3ZM120 70a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1v-3ZM94 78a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1v3ZM94 63a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1v3ZM94 73a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1v3ZM94 68a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1v3Z" fill="#fff"></path>
|
||||
<rect height="24" rx="3" stroke="#fff" stroke-width="2" width="24" x="95" y="57"></rect>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 19 KiB |
|
@ -1 +0,0 @@
|
|||
<svg viewBox="0 0 78.743 46.968" xmlns="http://www.w3.org/2000/svg"><path d="m530.436 290.342 6.658 8.935c4.438 5.956 8.512 11.037 12.222 15.242 3.71 4.205 7.691 8.797 11.945 13.776a590.023 590.023 0 0 1 11.95 14.425c3.713 4.638 9.56 11.441 17.54 20.41 7.98 8.97 15.892 17.872 23.736 26.707a2267.256 2267.256 0 0 0 21.633 24.05c6.578 7.197 11.225 8.056 13.942 2.576 2.717-5.48 8.45-12.8 17.199-21.96 8.748-9.162 15.904-16.447 21.468-21.856a2287.547 2287.547 0 0 1 17.272-16.611 674.569 674.569 0 0 0 16.506-16.229c5.054-5.154 9.753-9.968 14.098-14.443 4.344-4.475 9.494-10.016 15.448-16.624 5.954-6.607 10.747-11.748 14.38-15.422 3.631-3.675 6.18-6.009 7.645-7.003a16.094 16.094 0 0 1 4.757-2.202 16.095 16.095 0 0 1 5.212-.568c1.768.096 3.474.47 5.12 1.126a16.094 16.094 0 0 1 4.492 2.701 16.094 16.094 0 0 1 3.394 3.995 16.094 16.094 0 0 1 1.941 4.87c.381 1.729.476 3.473.285 5.234a16.094 16.094 0 0 1-1.402 5.051 16.095 16.095 0 0 1-2.94 4.34c-15.004 15.842-39.417-8.906-22.252-23.487a16.094 16.094 0 0 1 4.491-2.703 16.094 16.094 0 0 1 5.12-1.127 16.095 16.095 0 0 1 5.211.567 16.095 16.095 0 0 1 4.758 2.2 16.094 16.094 0 0 1 3.805 3.606 16.094 16.094 0 0 1 2.456 4.631c.565 1.678.848 3.403.848 5.173 0 1.771-.283 3.495-.849 5.173a16.095 16.095 0 0 1-2.455 4.632l-1.606 2.112-5.37 4.582c-3.583 3.055-8.51 7.767-14.786 14.135a11933.445 11933.445 0 0 0-15.839 16.1c-4.284 4.364-8.943 9.26-13.975 14.688a75638.076 75638.076 0 0 1-15.826 17.063 2642.057 2642.057 0 0 0-15.89 17.273c-5.073 5.567-11.507 12.866-19.3 21.897-7.794 9.03-13.63 15.948-17.51 20.753a496.057 496.057 0 0 1-12.49 14.836c-4.447 5.085-9.976 7.77-16.588 8.052-6.612.283-12.583-2.325-17.913-7.822-5.33-5.498-11.117-11.861-17.361-19.091-6.245-7.23-11.274-13.11-15.087-17.64a385.872 385.872 0 0 0-11.496-13.06 905.91 905.91 0 0 1-12.117-13.426c-4.228-4.774-10.087-11.613-17.577-20.518-7.49-8.905-13.37-15.797-17.635-20.676-4.266-4.88-8.437-9.663-12.511-14.35-4.075-4.687-8.453-9.915-13.135-15.684-4.681-5.77-7.355-9.208-8.02-10.317a15.914 15.914 0 0 1-1.567-3.517 15.937 15.937 0 0 1 .724-11.305 15.914 15.914 0 0 1 2.003-3.288 15.914 15.914 0 0 1 2.733-2.713 15.913 15.913 0 0 1 3.302-1.98 15.914 15.914 0 0 1 3.68-1.132 15.914 15.914 0 0 1 3.845-.219c1.29.083 2.552.319 3.785.708 1.233.39 2.401.92 3.505 1.593a15.914 15.914 0 0 1 3.023 2.386l1.366 1.375z" transform="matrix(.26458 0 0 .26458 -132.758 -75.015)"/></svg>
|
Before Width: | Height: | Size: 2.3 KiB |
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"require": {
|
||||
"reflect/client": "dev-master",
|
||||
"victorwesterlund/xenum": "dev-master"
|
||||
"vlw/mysql": "dev-master",
|
||||
"vlw/xenum": "dev-master"
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
|
|
56
composer.lock
generated
56
composer.lock
generated
|
@ -4,65 +4,49 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "2a8a06dc452a4eb9055d238f771dcd37",
|
||||
"content-hash": "a7ce20d192550ef2d037220b593b5eb9",
|
||||
"packages": [
|
||||
{
|
||||
"name": "reflect/client",
|
||||
"name": "vlw/mysql",
|
||||
"version": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/VictorWesterlund/reflect-client-php.git",
|
||||
"reference": "89a8c041044c8c60cefafc4716d5d61b96c43e06"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/VictorWesterlund/reflect-client-php/zipball/89a8c041044c8c60cefafc4716d5d61b96c43e06",
|
||||
"reference": "89a8c041044c8c60cefafc4716d5d61b96c43e06",
|
||||
"shasum": ""
|
||||
"url": "https://codeberg.org/vlw/php-mysql",
|
||||
"reference": "0e367f797fa9348408881ed758976f21e8c667e4"
|
||||
},
|
||||
"default-branch": true,
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Reflect\\": "src/Reflect/"
|
||||
"vlw\\MySQL\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"GPL-2.0-only"
|
||||
"GPL-3.0-or-later"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Victor Westerlund",
|
||||
"email": "victor.vesterlund@gmail.com"
|
||||
"email": "victor@vlw.se"
|
||||
}
|
||||
],
|
||||
"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"
|
||||
"description": "Abstraction library for common MySQL/MariaDB DML operations with php-mysqli",
|
||||
"time": "2025-07-29T07:46:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "victorwesterlund/xenum",
|
||||
"name": "vlw/xenum",
|
||||
"version": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/VictorWesterlund/php-xenum.git",
|
||||
"reference": "8972f06f42abd1f382807a67e937d5564bb89699"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/VictorWesterlund/php-xenum/zipball/8972f06f42abd1f382807a67e937d5564bb89699",
|
||||
"reference": "8972f06f42abd1f382807a67e937d5564bb89699",
|
||||
"shasum": ""
|
||||
"url": "https://codeberg.org/vlw/php-xenum",
|
||||
"reference": "ba3f43a9e2787bf938cfbfcb85ea87e5062df294"
|
||||
},
|
||||
"default-branch": true,
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"victorwesterlund\\": "src/"
|
||||
"vlw\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
|
@ -72,27 +56,23 @@
|
|||
"authors": [
|
||||
{
|
||||
"name": "Victor Westerlund",
|
||||
"email": "victor.vesterlund@gmail.com"
|
||||
"email": "victor@vlw.se"
|
||||
}
|
||||
],
|
||||
"description": "PHP eXtended Enums. The missing quality-of-life features from PHP 8+ Enums",
|
||||
"support": {
|
||||
"issues": "https://github.com/VictorWesterlund/php-xenum/issues",
|
||||
"source": "https://github.com/VictorWesterlund/php-xenum/tree/1.1.1"
|
||||
},
|
||||
"time": "2023-11-20T10:10:39+00:00"
|
||||
"time": "2025-05-10T11:28:03+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": {
|
||||
"reflect/client": 20,
|
||||
"victorwesterlund/xenum": 20
|
||||
"vlw/mysql": 20,
|
||||
"vlw/xenum": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.0.0"
|
||||
"plugin-api-version": "2.3.0"
|
||||
}
|
||||
|
|
257
install.sh
257
install.sh
|
@ -1,10 +1,251 @@
|
|||
# Install dependencies
|
||||
composer install --optimize-autoloader
|
||||
npm install
|
||||
#!/bin/bash
|
||||
|
||||
# (Re)create public NPM modules folder
|
||||
rm -r assets/js/modules/npm
|
||||
mkdir assets/js/modules/npm
|
||||
# Define constants
|
||||
DB_VLW="vlw"
|
||||
DB_API="vlw_api"
|
||||
|
||||
# Create link to Elevent MJS from public JS modules folder
|
||||
ln -sr node_modules/elevent/src/Elevent.mjs assets/js/modules/npm/Elevent.mjs
|
||||
# Initialize variables
|
||||
cwd=""
|
||||
database_app_host=""
|
||||
database_app_user=""
|
||||
database_seed_host=""
|
||||
database_seed_user=""
|
||||
database_app_password=""
|
||||
database_seed_password=""
|
||||
|
||||
echo_err() {
|
||||
echo "!! -> $1"
|
||||
}
|
||||
|
||||
# Make sure we have all the system packages we need to proceed with the installation
|
||||
check_sys_depend() {
|
||||
local valid=true
|
||||
local packages=("git" "composer")
|
||||
|
||||
for package in "${packages[@]}" ; do
|
||||
if ! dpkg -l | grep -qw "ii ${package}" ; then
|
||||
echo_err "Package '${package}' is not installed."
|
||||
valid=false
|
||||
fi
|
||||
done
|
||||
|
||||
# Bail out if any required package is missing
|
||||
if [ "$valid" = false ] ; then
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
install_vegvisir() {
|
||||
echo
|
||||
echo "Installing Vegvisir into '$cwd/vegvisir'"
|
||||
|
||||
curl -fsSL https://codeberg.org/vegvisir/install/raw/branch/master/install.sh | bash -s -- --install=n --overwrite=y --example=n --dir="$cwd"
|
||||
}
|
||||
|
||||
install_reflect() {
|
||||
echo
|
||||
echo "Installing Reflect into '$cwd/reflect'"
|
||||
|
||||
curl -fsSL https://codeberg.org/reflect/install/raw/branch/master/install.sh | bash -s -- --install=n --overwrite=y --seed=n --dir="$cwd" --endpoints="api" --host="$database_app_host" --user="$database_app_user" --password="$database_app_password" --db="$DB_API"
|
||||
}
|
||||
|
||||
install_vlw() {
|
||||
composer install --classmap-authoritative
|
||||
}
|
||||
|
||||
seed_databases() {
|
||||
echo
|
||||
echo "-- Database seeding --"
|
||||
echo "We're now going to seed the databases '$DB_VLW' and '$DB_API' with default data"
|
||||
echo "- Make sure that both databases exist and are empty"
|
||||
echo "- Your credentials for this user '$(whoami)' might be different from your app credentials (php-mysql)"
|
||||
echo
|
||||
|
||||
# Database seed hostname
|
||||
echo "Enter the full hostname of your MySQL/MariaDB server to use in this script for seeding."
|
||||
read -p "Press enter to use the same host as the app credentials [$database_app_host]: " database_seed_host </dev/tty
|
||||
|
||||
# Use the same database host as the app configuration
|
||||
if [[ "$database_seed_host" == "" ]] ; then
|
||||
database_seed_host=$database_app_host
|
||||
fi
|
||||
|
||||
# Database seed user
|
||||
echo
|
||||
echo "Enter the username for your MySQL/MariaDB server to use in this script for seeding."
|
||||
read -p "Press enter to use the same host as the app credentials [$database_app_user]: " database_seed_user </dev/tty
|
||||
|
||||
# Use the same database user as the app configuration
|
||||
if [[ "$database_seed_user" == "" ]] ; then
|
||||
database_seed_user=$database_app_user
|
||||
fi
|
||||
|
||||
# Database seed password
|
||||
echo
|
||||
echo "Enter the password for your MySQL/MariaDB server to use in this script for seeding."
|
||||
echo "Enter 'null' to disable password authentication"
|
||||
read -p "Press enter to use the same password as the app credentials [<database_password>]: " database_seed_password </dev/tty
|
||||
|
||||
# Use the same database password as the app configuration
|
||||
if [[ "$database_seed_password" == "" ]] ; then
|
||||
database_seed_password=$database_app_password
|
||||
fi
|
||||
|
||||
# Seed the main database
|
||||
mysql -h "$database_seed_host" -u "$database_seed_user" --password="$database_seed_password" $DB_VLW < src/Database/Seeds/vlw.sql
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo_err "Installation aborted: MySQL error"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Seed the API database
|
||||
mysql -h "$database_seed_host" -u "$database_seed_user" --password="$database_seed_password" $DB_API < src/Database/Seeds/api.sql
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo_err "Installation aborted: MySQL error"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
configure_vlw() {
|
||||
local config_password=""
|
||||
|
||||
local config_available=""
|
||||
local config_available_to=""
|
||||
local config_available_from=""
|
||||
local config_available_average=""
|
||||
local config_available_timezone=""
|
||||
|
||||
local config_forgejo=""
|
||||
local config_forgejo_url=""
|
||||
local config_forgejo_profiles=""
|
||||
|
||||
# A configuration file already exists
|
||||
if [[ -f ".env.ini" ]] ; then
|
||||
echo
|
||||
echo "A configuration file already exists at: ${cwd}/.env.ini"
|
||||
read -p "Do you want to overwrite this file? (y/n): " overwrite </dev/tty
|
||||
|
||||
# Remove existing configuration file or abort
|
||||
if [[ "$overwrite" == "y" || "$overwrite" == "Y" ]] ; then
|
||||
echo "Removing existing configuration and proceeding with the installation in ${cwd}..."
|
||||
rm .env.ini
|
||||
else
|
||||
echo_err "Installation aborted: Configuration file already exists"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "-- Database configuration --"
|
||||
read -p "Enter the full hostname of your MySQL/MariaDB server (php-mysql): " database_app_host </dev/tty
|
||||
read -p "Enter the app username for your MySQL/MariaDB server (php-mysql): " database_app_user </dev/tty
|
||||
read -p "Enter the app password for your MySQL/MariaDB server (php-mysql): " config_password </dev/tty
|
||||
|
||||
# Coerce empty input as null keyword for later configurations
|
||||
if [[ "$config_password" == "" ]] ; then
|
||||
database_app_password="null"
|
||||
fi
|
||||
|
||||
echo
|
||||
read -p "(Optional) Would you like to configure time available settings? (y/n): " config_available </dev/tty
|
||||
|
||||
# Check the user's response
|
||||
if [[ "$config_available" == "n" || "$config_available" == "N" ]]; then
|
||||
config_available_to=0
|
||||
config_available_from=0
|
||||
config_available_average=0
|
||||
config_available_timezone="Europe/Stockholm"
|
||||
fi
|
||||
|
||||
if ! [[ -n "$config_available_timezone" ]]; then
|
||||
read -p "Enter your timezone in IANA Time Zone Database Format ('Europe/Stockholm' for example): " config_available_timezone </dev/tty
|
||||
fi
|
||||
|
||||
if ! [[ -n "$config_available_from" ]]; then
|
||||
read -p "Enter time available from hour (24-hour format): " config_available_from </dev/tty
|
||||
fi
|
||||
|
||||
if ! [[ -n "$config_available_to" ]]; then
|
||||
read -p "Enter time available to hour (24-hour format): " config_available_to </dev/tty
|
||||
fi
|
||||
|
||||
if ! [[ -n "$config_available_average" ]]; then
|
||||
read -p "Enter average reply time in hours: " config_available_average </dev/tty
|
||||
fi
|
||||
|
||||
echo
|
||||
read -p "(Optional) Would you like to configure Forgejo language updates? (y/n): " config_forgejo </dev/tty
|
||||
|
||||
# Check the user's response
|
||||
if [[ "$config_forgejo" == "n" || "$config_forgejo" == "N" ]]; then
|
||||
config_forgejo_url="https://git.vlw.se"
|
||||
config_forgejo_profiles="vlw,vegvisir,reflect"
|
||||
fi
|
||||
|
||||
if ! [[ -n "$config_forgejo_url" ]]; then
|
||||
read -p "Enter a hostname to a Forgejo instance ('https://git.vlw.se' for example): " config_forgejo_url </dev/tty
|
||||
fi
|
||||
|
||||
if ! [[ -n "$config_forgejo_profiles" ]]; then
|
||||
read -p "Enter a comma seperated list of Forgejo profiles to scan ('vlw,vegvisir,reflect' for example): " config_forgejo_profiles </dev/tty
|
||||
fi
|
||||
|
||||
local config=(
|
||||
"; This config file was generated automatically by ./install.sh"
|
||||
"; Refer to '.env.example.ini' for more information"
|
||||
"[mariadb]"
|
||||
"host = '$database_app_host'"
|
||||
"user = '$database_app_user'"
|
||||
"pass = '$config_password'"
|
||||
"db = '$DB_VLW'"
|
||||
"[config_time_available]"
|
||||
"time_zone = '$config_available_timezone'"
|
||||
"available_to_hour = '$config_available_to'"
|
||||
"reply_average_hours = '$config_available_average'"
|
||||
"available_from_hour = '$config_available_from'"
|
||||
"[service_forgejo]"
|
||||
"url = '$config_forgejo_url'"
|
||||
"profiles = '$config_forgejo_profiles'"
|
||||
)
|
||||
|
||||
for line in "${config[@]}" ; do
|
||||
echo "${line}" >> .env.ini
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
# Get the current working directory
|
||||
cwd=$(pwd)
|
||||
|
||||
check_sys_depend
|
||||
|
||||
configure_vlw
|
||||
seed_databases
|
||||
|
||||
install_vlw
|
||||
install_vegvisir
|
||||
install_reflect
|
||||
|
||||
echo "-- Success --"
|
||||
echo "vlw.se has been installed! :)"
|
||||
echo "- Point all traffic to your web server to '${cwd}/vegvisir/public/index.php'"
|
||||
echo "- Point all traffic to your REST API server to '${cwd}/reflect/public/index.php'"
|
||||
echo "-------------"
|
||||
echo
|
||||
}
|
||||
|
||||
# Prompt the user for confirmation
|
||||
echo
|
||||
echo "-- Installing vlw.se --"
|
||||
echo "You are currently in: $(pwd)"
|
||||
read -p "Do you want to proceed with the installation in this directory? (y/n): " choice </dev/tty
|
||||
|
||||
# Check the user's response
|
||||
if [[ "$choice" == "y" || "$choice" == "Y" ]] ; then
|
||||
echo "Proceeding with the installation in $(pwd)..."
|
||||
main
|
||||
else
|
||||
echo "Installation aborted."
|
||||
fi
|
||||
|
|
17
package-lock.json
generated
17
package-lock.json
generated
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"name": "vlw.se",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"elevent": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/elevent": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/elevent/-/elevent-1.0.2.tgz",
|
||||
"integrity": "sha512-ks5LBUBTg4Bpfmj99OcFAzuDGzBRDEZhTyxmq/Y3RbsdBQ4JCaIUYB0M15OBvBWgIn1BnCo4WCSmw0/YbCJliw=="
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"elevent": "^1.0.2"
|
||||
}
|
||||
}
|
127
public/about.php
127
public/about.php
|
@ -1,43 +1,123 @@
|
|||
<style><?= VV::css("assets/css/pages/about") ?></style>
|
||||
<?php
|
||||
|
||||
use VLW\Database\Models\Coffee\Coffee;
|
||||
use VLW\Database\Models\Languages\Language;
|
||||
|
||||
require_once VV::root("src/Database/Models/Coffee/Coffee.php");
|
||||
require_once VV::root("src/Database/Models/Languages/Language.php");
|
||||
|
||||
const FORGEJO = "https://git.vlw.se/explore/repos?language=";
|
||||
const SI_BYTE_MULTIPLE = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
|
||||
$languages = new class extends Language {
|
||||
private readonly int $total_bytes;
|
||||
|
||||
public function __construct() {
|
||||
$this->total_bytes = array_sum(array_map(fn(Language $language): int => $language->bytes, parent::all()));
|
||||
}
|
||||
|
||||
public function percent(Language $language, int $mode = PHP_ROUND_HALF_UP): int {
|
||||
return round(($language->bytes / $this->total_bytes) * 100, 0, $mode);
|
||||
}
|
||||
|
||||
public function percent_string(Language $language): string {
|
||||
return ($this->percent($language) > 1 ? $this->percent($language) : "<1") . "%";
|
||||
}
|
||||
|
||||
public function bytes_si_string(Language $language): string {
|
||||
// Calculate factor for unit
|
||||
$factor = floor((strlen($language->bytes) - 1) / 3);
|
||||
// Divide by radix 10
|
||||
$format = $language->bytes / pow(1000, $factor);
|
||||
|
||||
return round($format) . " " . SI_BYTE_MULTIPLE[$factor];
|
||||
}
|
||||
};
|
||||
|
||||
$coffee = new class extends Coffee {
|
||||
public readonly int $count_week;
|
||||
public readonly int $count_week_average;
|
||||
|
||||
public function __construct() {
|
||||
$this->count_week = parent::count_week();
|
||||
$this->count_week_average = parent::count_week_average();
|
||||
}
|
||||
|
||||
public function week_average_string(): string {
|
||||
$diff = $this->count_week - $this->count_week_average;
|
||||
|
||||
return match (true) {
|
||||
$diff < 0 => "less than",
|
||||
$diff === 0 => "the same as",
|
||||
$diff > 0 => "more than"
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
?>
|
||||
<style><?= VV::css("public/assets/css/pages/about") ?></style>
|
||||
<section class="intro">
|
||||
<h2 aria-hidden="true">Hi, I'm</h2>
|
||||
<h1>Victor Westerlund</h1>
|
||||
</section>
|
||||
<hr aria-hidden="true">
|
||||
<section class="about">
|
||||
<p>I​'m a full-stack web developer from Sweden.</p>
|
||||
<p>The <programming/markup/command/query/whatever>-languages I currently use the most are (in a mostly accurate decending order): PHP, JavaScript, CSS, MySQL, TypeScript, Python, SQLite, Bash, and HTML.</p>
|
||||
<p>I​'m a full-stack web developer from Sweden, and welcome to my little personal corner of the Internet!</p>
|
||||
<p>My coding happens almost exclusivly in <a href="https://github.com/coder/code-server">code-server</a>, which is a fork of VSCode that runs entirely in the browser. I keep my development environment tucked away in a lightweight Debian VA that I can tote around to whatever host machine I happen to work on. I also keep an ephemeral Debian Live ISO ready which boots into a VM RAM disk where I can mess around without fear or breaking things or try new software.</p>
|
||||
<p>I used to list the <programming/markup/command/whatever>-languages here that I use the most and order them by guesstimating how much I use each one. But then I thought it would be better to just show you instead using this chart that automatically pulls the total bytes for each language from my <a href="https://git.vlw.se/explore/repos">public repos on Forgejo</a>.</p>
|
||||
</section>
|
||||
<section class="languages">
|
||||
<stacked-bar-chart>
|
||||
|
||||
<?php foreach ($languages::all() as $language): ?>
|
||||
<a href="<?= FORGEJO . $language->name ?>" target="_blank"><chart-segment style="--size:<?= $languages->percent($language) ?>%;" data-lang="<?= $language->name ?>" data-bytes="<?= $language->bytes ?>">
|
||||
<span data-hover><strong><?= $languages->percent_string($language) ?> <?= $language->name ?></strong><br>(<?= $language->bytes ?> bytes)</span>
|
||||
</chart-segment></a>
|
||||
<?php endforeach; ?>
|
||||
|
||||
</stacked-bar-chart>
|
||||
<languages-list>
|
||||
|
||||
<?php foreach ($languages::all() as $language): ?>
|
||||
<a href="<?= FORGEJO . $language->name ?>"><button data-lang="<?= $language->name ?>" class="inline">
|
||||
<p><?= $languages->percent_string($language) ?></p>
|
||||
<p class="lang"><?= $language->name ?></p>
|
||||
<p><?= $languages->bytes_si_string($language) ?></p>
|
||||
<?= VV::embed("public/assets/media/icons/chevron.svg") ?>
|
||||
</button></a>
|
||||
<?php endforeach; ?>
|
||||
|
||||
</languages-list>
|
||||
<stacked-bar-chart>
|
||||
|
||||
<?php foreach ($languages::all() as $language): ?>
|
||||
<a href="<?= FORGEJO . $language->name ?>" target="_blank"><chart-segment style="--size:<?= $languages->percent($language) ?>%;" data-lang="<?= $language->name ?>" data-bytes="<?= $language->bytes ?>">
|
||||
<span data-hover><strong><?= $languages->percent_string($language) ?> <?= $language->name ?></strong><br>(<?= $language->bytes ?> bytes)</span>
|
||||
</chart-segment></a>
|
||||
<?php endforeach; ?>
|
||||
|
||||
</stacked-bar-chart>
|
||||
|
||||
</section>
|
||||
<section class="about">
|
||||
<h2>This website</h2>
|
||||
<p>This site and all of its components are <a href="https://codeberg.org/vlw/vlw.se">100% free and open source software</a>. The website is designed and built by me from the ground up using my <a href="https://vegvisir.vlw.se">web</a> and <a href="https://reflect.vlw.se">API</a> frameworks as the foundation. You will find no cookies or trackers here. The only information I have about you is your public IP-address and which resources on this site your browser requests. None of this data is used for any kind of analytics.</p>
|
||||
<p><a href="https://srv.vlw.se"><i>See detailed information about all servers/services on this domain</i></a></p>
|
||||
<p>This site and all of its components, including texts and graphics have been created by me and are all <a href="https://codeberg.org/vlw/vlw.se">100% free and open source</a>. Feel free to use anything you see on this website in your own projects as long as it's under the same GNU GPLv3-or-later license. The website is designed by me on top of my own <a href="https://vegvisir.vlw.se">web framework</a> and <a href="https://reflect.vlw.se">API framework</a>.</p>
|
||||
<p>You won't find any cookies or trackers on this site! The only information I have about you are in the standard NGINX access and error logs, which get overwritten automatically after some time.</p>
|
||||
</section>
|
||||
<section class="about">
|
||||
<h2>Personal</h2>
|
||||
<p>Coffee, of course.. and..</p>
|
||||
<p>At times, I become a true, amateur, armchair detective for a <span class="interests">variety of your typical-nerdy topics that I find interesting</span>. And will spend a disproportionate to real-world-personal-use amount of time reading about that stuff too.</p>
|
||||
<p>Another silent passion of mine that comes out every few years is <a href="/about/battlestation" vv="about" vv-call="navigate">building computers</a> and fiddling with weird networking stuff.</p>
|
||||
<p>And then of course I don't mind some occational gaming, and watching movies and TV-series.</p>
|
||||
</section>
|
||||
<section class="about">
|
||||
<h2>Projects</h2>
|
||||
<p>Here are some projects I'm working on right now:</p>
|
||||
<p>* <a href="https://vegvisir.vlw.se">Vegvisir</a>: A web navigation framework for PHP.</p>
|
||||
<p>* <a href="https://reflect.vlw.se">Reflect</a>: A REST API framework for PHP developers.</p>
|
||||
<p>There is more stuff on my <a href="work" vv="about" vv-call="navigate">works page</a> and even more stuff on <a href="https://codeberg.org/vlw">my Codeberg profile</a>.</p>
|
||||
<p><a href="https://git.vlw.se/vlw"><i>and even EVEN more stuff on my Forgejo</i></a></p>
|
||||
<p>One thing that most people know about me is that I like coffee.. lots of coffee. In fact, I've had <?= $coffee->count_week ?> cup<?= $coffee->count_week === 1 ? "" : "s" ?> of coffee in the last 7 days! That's <?= $coffee->week_average_string() ?> my average of <?= $coffee->count_week_average ?> per week, impressive! Even though you just read that.. I don't consider myself <i>too much</i> of a coffee snob! As long as it's dark roast and warm, I'm probably happy to have it.</p>
|
||||
<p>At times, I become a true, amateur, armchair detective for a <span class="interests">variety of your typical-nerdy topics that I find interesting</span> and you can bet I spend way more time reading about those things than I will ever have use for in life.</p>
|
||||
<p>Another silent passion of mine that comes out every few years is building computers and fiddling with networking stuff.</p>
|
||||
</section>
|
||||
<hr>
|
||||
<section class="about">
|
||||
<h3>GitHub</h3>
|
||||
<p>I have <a href="https://giveupgithub.com" target="_blank" rel="noopener noreferer">given up GitHub</a> for their increasing number of injustices againts its users, last betrayal being GitHub's for-profit "Copilot" product which was illegaly trained on copylefted software on its platform.</p>
|
||||
<p>I signed up and started using GitHub before I became aware of how opressive to to its users and deceptive their business model is. I wasn't aware of the situation.</p>
|
||||
<p>While I am a bit skeptical to do this in case history repeats itself; [most of] <a href="https://codeberg.org/vlw">my work is now on Codeberg</a> instead. Unfortunately some things like old pull-requests, issues, and branch archives can not be migrated completely.</p>
|
||||
<p>I have <a href="https://giveupgithub.com" target="_blank" rel="noopener noreferer">given up GitHub</a> and moved most of my free software to <a href="https://codeberg.org/vlw">Codeberg</a>. You can still find my <a href="https://github.com/VictorWesterlund">GitHub profile here</a> but I don't use it for source control of my projects anymore.</p>
|
||||
</section>
|
||||
<hr>
|
||||
<section>
|
||||
<p>Let's work on something together or just have a chat? <a href="contact" vv="about" vv-call="navigate">Write me a line!</a></p>
|
||||
<p>Let's work on something together or just have a chat? <a href="contact">Write me a line!</a></p>
|
||||
</section>
|
||||
|
||||
<div class="interests" aria-hidden="true">
|
||||
|
@ -54,6 +134,7 @@
|
|||
<p>engineering</p>
|
||||
<p>photography</p>
|
||||
<p>videography</p>
|
||||
<p>ISO 8601</p>
|
||||
<p>RFC 3339</p>
|
||||
<p>digital archiving</p>
|
||||
</div>
|
||||
<script type="module"><?= VV::js("assets/js/pages/about") ?></script>
|
||||
<script><?= VV::js("public/assets/js/pages/about") ?></script>
|
|
@ -1,36 +0,0 @@
|
|||
<style><?= VV::css("assets/css/pages/about/battlestation-retired") ?></style>
|
||||
<section class="title">
|
||||
<h1>Retired components</h1>
|
||||
<p>I'd be happy to send you any component that you find here for "free". The only thing I ask in return is that you pay for shipping.</p>
|
||||
<p>This page is still a work-in-progress. You can use my API to get a list of retired components by hardware category for now.</p>
|
||||
</section>
|
||||
<section class="actions">
|
||||
<a href="<?= $_ENV["api"]["base_url"] ?>battlestation/chassis?is_retired=true" target="_blank">
|
||||
<button class="inline">Cases (API)</button>
|
||||
</a>
|
||||
<a href="<?= $_ENV["api"]["base_url"] ?>battlestation/cpu?is_retired=true" target="_blank">
|
||||
<button class="inline">CPUs (API)</button>
|
||||
</a>
|
||||
<a href="<?= $_ENV["api"]["base_url"] ?>battlestation/gpu?is_retired=true" target="_blank">
|
||||
<button class="inline">GPUs (API)</button>
|
||||
</a>
|
||||
<a href="<?= $_ENV["api"]["base_url"] ?>battlestation/mb?is_retired=true" target="_blank">
|
||||
<button class="inline">Motherboards (API)</button>
|
||||
</a>
|
||||
<a href="<?= $_ENV["api"]["base_url"] ?>battlestation/psu?is_retired=true" target="_blank">
|
||||
<button class="inline">PSUs (API)</button>
|
||||
</a>
|
||||
<a href="<?= $_ENV["api"]["base_url"] ?>battlestation/storage?is_retired=true" target="_blank">
|
||||
<button class="inline">Storage (API)</button>
|
||||
</a>
|
||||
</section>
|
||||
<section class="title">
|
||||
<h2>Found something you like?</h2>
|
||||
<p>Please note; I can't guarantee the thing you want will work as expected, or work at all! But I will test the compontent for you if I still have means at hand to do so.</p>
|
||||
</section>
|
||||
<section class="actions">
|
||||
<a href="/contact" vv="battlestation-retired" vv-call="navigate">
|
||||
<button class="inline solid">Contact me</button>
|
||||
</a>
|
||||
</section>
|
||||
<script><?= VV::js("assets/js/pages/about/battlestation-retired") ?></script>
|
|
@ -1,496 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Vegvisir\Path;
|
||||
|
||||
use VLW\Client\API;
|
||||
use VLW\API\Endpoints;
|
||||
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\{
|
||||
MbModel,
|
||||
CpuModel,
|
||||
GpuModel,
|
||||
PsuModel,
|
||||
DramModel,
|
||||
StorageModel,
|
||||
ChassisModel
|
||||
};
|
||||
use VLW\API\Databases\VLWdb\Models\Battlestation\Config\{
|
||||
MbPsuModel,
|
||||
MbGpuModel,
|
||||
MbDramModel,
|
||||
ConfigModel,
|
||||
MbStorageModel,
|
||||
ChassisMbModel,
|
||||
MbCpuCoolerModel,
|
||||
MbStorageSlotFormfactorEnum
|
||||
};
|
||||
|
||||
require_once VV::root("src/client/API.php");
|
||||
require_once VV::root("api/src/Endpoints.php");
|
||||
|
||||
// Load hardware database models
|
||||
require_once VV::root("api/src/databases/models/Battlestation/Mb.php");
|
||||
require_once VV::root("api/src/databases/models/Battlestation/Cpu.php");
|
||||
require_once VV::root("api/src/databases/models/Battlestation/Gpu.php");
|
||||
require_once VV::root("api/src/databases/models/Battlestation/Psu.php");
|
||||
require_once VV::root("api/src/databases/models/Battlestation/Dram.php");
|
||||
require_once VV::root("api/src/databases/models/Battlestation/Storage.php");
|
||||
require_once VV::root("api/src/databases/models/Battlestation/Chassis.php");
|
||||
|
||||
// Load hardware config database models
|
||||
require_once VV::root("api/src/databases/models/Battlestation/Config/MbPsu.php");
|
||||
require_once VV::root("api/src/databases/models/Battlestation/Config/MbGpu.php");
|
||||
require_once VV::root("api/src/databases/models/Battlestation/Config/MbDram.php");
|
||||
require_once VV::root("api/src/databases/models/Battlestation/Config/Config.php");
|
||||
require_once VV::root("api/src/databases/models/Battlestation/Config/MbStorage.php");
|
||||
require_once VV::root("api/src/databases/models/Battlestation/Config/ChassisMb.php");
|
||||
require_once VV::root("api/src/databases/models/Battlestation/Config/MbCpuCooler.php");
|
||||
|
||||
const GIGA = 0x3B9ACA00;
|
||||
const MEGA = 0xF4240;
|
||||
|
||||
// Connect to VLW API
|
||||
$api = new API();
|
||||
|
||||
$config = $api->call(Endpoints::BATTLESTATION->value)->get();
|
||||
|
||||
?>
|
||||
<style><?= VV::css("assets/css/pages/about/battlestation") ?></style>
|
||||
<?php if ($config->ok): ?>
|
||||
<section class="title">
|
||||
<h1>Battle­stations</h1>
|
||||
<p>I'd be happy to send you, dear reader, any component that you find here for "free" that hasn't been retired yet. The only thing I ask in return is that you pay for shipping.</p>
|
||||
<p>I can't guarantee the thing you want will work as expected, or work at all! But I will test the compontent for you if I still have means at hand to do so.</p>
|
||||
<div>
|
||||
<a href="/about/battlestation-retired" vv="battlestation" vv-call="navigate"><button class="inline solid">Retired components</button></a>
|
||||
<a href="/contact" vv="battlestation" vv-call="navigate"><button class="inline">Contact me</button></a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php foreach ($config->json() as $config): ?>
|
||||
|
||||
<?php
|
||||
|
||||
// Get motherboard details by ref_mb_id from config
|
||||
$motherboard = $api->call(Endpoints::BATTLESTATION_MB->value)->params([
|
||||
MbModel::ID->value => $config[ChassisMbModel::REF_MB_ID->value]
|
||||
])->get()->json()[0];
|
||||
|
||||
?>
|
||||
|
||||
<section class="heading">
|
||||
<h1><?= $config[ConfigModel::FRIENDLY_NAME->value] ?? "Lucious" ?></h1>
|
||||
<p>This rig was built: <?= date(API::DATE_FORMAT, $config[ConfigModel::DATE_BUILT->value]) ?></p>
|
||||
</section>
|
||||
<section class="config"
|
||||
data-mb="1"
|
||||
data-cpu="<?= count($motherboard["cpus"]) ?>"
|
||||
data-psu="<?= count($motherboard["psus"]) ?>"
|
||||
data-gpu="<?= count($motherboard["gpus"]) ?>"
|
||||
data-dram="<?= count($motherboard["dram"]) ?>"
|
||||
data-case="<?= count($motherboard["chassis"]) ?>"
|
||||
data-drives-mdottwo="<?= count(array_keys(array_column($motherboard["storage"], MbStorageModel::SLOT_FORMFACTOR->value), MbStorageSlotFormfactorEnum::MDOTTWO->value)) ?>"
|
||||
data-drives-twodotfive="<?= count(array_keys(array_column($motherboard["storage"], MbStorageModel::SLOT_FORMFACTOR->value), MbStorageSlotFormfactorEnum::TWODOTFIVE->value)) ?>"
|
||||
data-drives-threedotfive="<?= count(array_keys(array_column($motherboard["storage"], MbStorageModel::SLOT_FORMFACTOR->value), MbStorageSlotFormfactorEnum::THREEDOTFIVE->value)) ?>"
|
||||
>
|
||||
<?= VV::embed("assets/media/battlestation.svg") ?>
|
||||
<div class="specs">
|
||||
|
||||
<?php // Show motherboard details ?>
|
||||
<?php if ($motherboard): ?>
|
||||
<div data-target="mb" class="spec">
|
||||
<p>Motherboard</p>
|
||||
<h3><?= $motherboard[MbModel::VENDOR_NAME->value] ?> <span><?= $motherboard[MbModel::VENDOR_MODEL->value] ?></span></h3>
|
||||
<div>
|
||||
<div>
|
||||
<label>Formfactor</label>
|
||||
<p><?= $motherboard[MbModel::FORMFACTOR->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Brand name</label>
|
||||
<p><?= $motherboard[MbModel::VENDOR_NAME->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Brand model</label>
|
||||
<p><?= $motherboard[MbModel::VENDOR_MODEL->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>LAN</label>
|
||||
<p><?= $motherboard[MbModel::NETWORK_ETHERNET->value] ?? "No LAN" ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>WLAN</label>
|
||||
<p><?= $motherboard[MbModel::NETWORK_WLAN->value] ?? "No WLAN" ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Bluetooth</label>
|
||||
<p><?= $motherboard[MbModel::NETWORK_BLUETOOTH->value] ?? "No Bluetooth" ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Aquired</label>
|
||||
<p><?= date(API::DATE_FORMAT, $motherboard[MbModel::DATE_AQUIRED->value]) ?></p>
|
||||
</div>
|
||||
|
||||
<?php if ($motherboard[MbModel::IS_RETIRED->value]): ?>
|
||||
<div>
|
||||
<label>Retired</label>
|
||||
<p>Yes</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php // List all cases (lol) ?>
|
||||
<?php foreach ($motherboard["chassis"] as $mb_chassis): ?>
|
||||
|
||||
<?php // Get case details from endpoint by id ?>
|
||||
<?php $case = $api->call(Endpoints::BATTLESTATION_CHASSIS->value)->params([
|
||||
ChassisModel::ID->value => $mb_chassis[ChassisMbModel::REF_CHASSIS_ID->value]
|
||||
])->get()->json()[0]; ?>
|
||||
|
||||
<div data-target="case" class="spec">
|
||||
<p>Case</p>
|
||||
<h3><?= $case[ChassisModel::VENDOR_NAME->value] ?> <span><?= $case[ChassisModel::VENDOR_MODEL->value] ?></span></h3>
|
||||
<div>
|
||||
<div>
|
||||
<label>Brand name</label>
|
||||
<p><?= $case[ChassisModel::VENDOR_NAME->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Brand model</label>
|
||||
<p><?= $case[ChassisModel::VENDOR_MODEL->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Nº 2.5" slots</label>
|
||||
<p><?= $case[ChassisModel::STORAGE_TWOINCHFIVE->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Nº 3.5" slots</label>
|
||||
<p><?= $case[ChassisModel::STORAGE_THREEINCHFIVE->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Aquired</label>
|
||||
<p><?= date(API::DATE_FORMAT, $case[ChassisModel::DATE_AQUIRED->value]) ?></p>
|
||||
</div>
|
||||
|
||||
<?php if ($case[ChassisModel::IS_RETIRED->value]): ?>
|
||||
<div>
|
||||
<label>Retired</label>
|
||||
<p>Yes</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php // List all CPUs ?>
|
||||
<?php foreach ($motherboard["cpus"] as $mb_cpu): ?>
|
||||
|
||||
<?php // Get case details from endpoint by id ?>
|
||||
<?php $cpu = $api->call(Endpoints::BATTLESTATION_CPU->value)->params([
|
||||
CpuModel::ID->value => $mb_cpu[MbCpuCoolerModel::REF_CPU_ID->value]
|
||||
])->get()->json()[0]; ?>
|
||||
|
||||
<div data-target="cpu" class="spec">
|
||||
<p>CPU</p>
|
||||
<h3><?= $cpu[CpuModel::VENDOR_NAME->value] ?> <span><?= $cpu[CpuModel::VENDOR_MODEL->value] ?></span></h3>
|
||||
<div>
|
||||
<div>
|
||||
<label>Brand name</label>
|
||||
<p><?= $cpu[CpuModel::VENDOR_NAME->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Brand model</label>
|
||||
<p><?= $cpu[CpuModel::VENDOR_MODEL->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Class</label>
|
||||
<p><?= $cpu[CpuModel::CPU_CLASS->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Base Clockspeed</label>
|
||||
<p><?= $cpu[CpuModel::CLOCK_BASE->value] / GIGA ?>GHz</p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Turbo Clockspeed</label>
|
||||
<p><?= $cpu[CpuModel::CLOCK_TURBO->value] / GIGA ?>GHz</p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Nº cores (P/E)</label>
|
||||
<p><?= $cpu[CpuModel::CORE_COUNT_PERFORMANCE->value] + $cpu[CpuModel::CORE_COUNT_EFFICIENCY->value] ?> (<?= $cpu[CpuModel::CORE_COUNT_PERFORMANCE->value] ?>/<?= $cpu[CpuModel::CORE_COUNT_EFFICIENCY->value] ?>)</p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Nº total threads</label>
|
||||
<p><?= $cpu[CpuModel::CORE_THREADS->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Aquired</label>
|
||||
<p><?= date(API::DATE_FORMAT, $cpu[CpuModel::DATE_AQUIRED->value]) ?></p>
|
||||
</div>
|
||||
|
||||
<?php if ($cpu[CpuModel::IS_RETIRED->value]): ?>
|
||||
<div>
|
||||
<label>Retired</label>
|
||||
<p>Yes</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<label>In motherboard slot number</label>
|
||||
<p><?= $mb_cpu[MbCpuCoolerModel::SOCKET->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Motherboard slot type</label>
|
||||
<p><?= $mb_cpu[MbCpuCoolerModel::SOCKET_TYPE->value] ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php // List all GPUs ?>
|
||||
<?php foreach ($motherboard["gpus"] as $mb_gpu): ?>
|
||||
|
||||
<?php // Get case details from endpoint by id ?>
|
||||
<?php $gpu = $api->call(Endpoints::BATTLESTATION_GPU->value)->params([
|
||||
GpuModel::ID->value => $mb_gpu[MbGpuModel::REF_GPU_ID->value]
|
||||
])->get()->json()[0]; ?>
|
||||
|
||||
<div data-target="gpu" class="spec">
|
||||
<p>GPU</p>
|
||||
<h3><?= $gpu[GpuModel::VENDOR_NAME->value] ?> <span><?= $gpu[GpuModel::VENDOR_CHIP_MODEL->value] ?></span></h3>
|
||||
<div>
|
||||
<div>
|
||||
<label>Chip brand name</label>
|
||||
<p><?= $gpu[GpuModel::VENDOR_CHIP_NAME->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Chip brand model</label>
|
||||
<p><?= $gpu[GpuModel::VENDOR_CHIP_MODEL->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>VRAM</label>
|
||||
<p><?= $gpu[GpuModel::MEMORY->value] / GIGA ?>GB</p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Brand name</label>
|
||||
<p><?= $gpu[GpuModel::VENDOR_NAME->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Brand model</label>
|
||||
<p><?= $gpu[GpuModel::VENDOR_MODEL->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Aquired</label>
|
||||
<p><?= date(API::DATE_FORMAT, $gpu[GpuModel::DATE_AQUIRED->value]) ?></p>
|
||||
</div>
|
||||
|
||||
<?php if ($gpu[GpuModel::IS_RETIRED->value]): ?>
|
||||
<div>
|
||||
<label>Retired</label>
|
||||
<p>Yes</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php // List all PSUs ?>
|
||||
<?php foreach ($motherboard["psus"] as $mb_psu): ?>
|
||||
|
||||
<?php // Get case details from endpoint by id ?>
|
||||
<?php $psu = $api->call(Endpoints::BATTLESTATION_PSU->value)->params([
|
||||
PsuModel::ID->value => $mb_psu[MbPsuModel::REF_PSU_ID->value]
|
||||
])->get()->json()[0]; ?>
|
||||
|
||||
<div data-target="psu" class="spec">
|
||||
<p>PSU</p>
|
||||
<h3><?= $psu[PsuModel::VENDOR_NAME->value] ?> <span><?= $psu[PsuModel::VENDOR_MODEL->value] ?></span> <span><?= $psu[PsuModel::POWER->value] ?>W</span></h3>
|
||||
<div>
|
||||
<div>
|
||||
<label>Power</label>
|
||||
<p><?= $psu[PsuModel::POWER->value] ?>W</p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Brand name</label>
|
||||
<p><?= $psu[PsuModel::VENDOR_NAME->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Brand model</label>
|
||||
<p><?= $psu[PsuModel::VENDOR_MODEL->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Is modular?</label>
|
||||
<p><?= $psu[PsuModel::TYPE_MODULAR->value] === "TRUE" ? "Yes" : "No" ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>80+ Rating</label>
|
||||
<p><?= $psu[PsuModel::EIGHTYPLUS_RATING->value] ?? "None" ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Aquired</label>
|
||||
<p><?= date(API::DATE_FORMAT, $psu[PsuModel::DATE_AQUIRED->value]) ?></p>
|
||||
</div>
|
||||
|
||||
<?php if ($psu[PsuModel::IS_RETIRED->value]): ?>
|
||||
<div>
|
||||
<label>Retired</label>
|
||||
<p>Yes</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<div class="group">
|
||||
<p>DRAM</p>
|
||||
<?= VV::embed("assets/media/icons/chevron.svg") ?>
|
||||
</div>
|
||||
|
||||
<div class="collection">
|
||||
<?php // List all DRAM ?>
|
||||
<?php foreach ($motherboard["dram"] as $mb_dram): ?>
|
||||
|
||||
<?php // Get case details from endpoint by id ?>
|
||||
<?php $dram = $api->call(Endpoints::BATTLESTATION_DRAM->value)->params([
|
||||
DramModel::ID->value => $mb_dram[MbDramModel::REF_DRAM_ID->value]
|
||||
])->get()->json()[0]; ?>
|
||||
|
||||
<div data-target="dram" class="spec">
|
||||
<p>DRAM - <?= $dram[DramModel::TECHNOLOGY->value] ?></p>
|
||||
<h3><?= $dram[DramModel::VENDOR_NAME->value] ?>
|
||||
<span><?= $dram[DramModel::CAPACITY->value] / GIGA ?>GB</span>
|
||||
<span><?= $dram[DramModel::SPEED->value] / MEGA ?>MHz</span>
|
||||
</h3>
|
||||
<div>
|
||||
<div>
|
||||
<label>Capacity</label>
|
||||
<p><?= $dram[DramModel::CAPACITY->value] / GIGA ?>GB</p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Speed</label>
|
||||
<p><?= $dram[DramModel::SPEED->value] / MEGA ?>MHz</p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Brand name</label>
|
||||
<p><?= $dram[DramModel::VENDOR_NAME->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Brand model</label>
|
||||
<p><?= $dram[DramModel::VENDOR_MODEL->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Formfactor</label>
|
||||
<p><?= $dram[DramModel::FORMFACTOR->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Technology</label>
|
||||
<p><?= $dram[DramModel::TECHNOLOGY->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Is ECC?</label>
|
||||
<p><?= $dram[DramModel::ECC->value] === "TRUE" ? "Yes" : "No" ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Is buffered?</label>
|
||||
<p><?= $dram[DramModel::BUFFERED->value] === "TRUE" ? "Yes" : "No" ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Aquired</label>
|
||||
<p><?= date(API::DATE_FORMAT, $dram[DramModel::DATE_AQUIRED->value]) ?></p>
|
||||
</div>
|
||||
|
||||
<?php if ($dram[DramModel::IS_RETIRED->value]): ?>
|
||||
<div>
|
||||
<label>Retired</label>
|
||||
<p>Yes</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<label>In motherboard slot number</label>
|
||||
<p><?= $mb_dram[MbDramModel::SOCKET->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Motherboard slot type</label>
|
||||
<p><?= $mb_dram[MbDramModel::SOCKET_TYPE->value] ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="group">
|
||||
<p>Storage</p>
|
||||
<?= VV::embed("assets/media/icons/chevron.svg") ?>
|
||||
</div>
|
||||
|
||||
<div class="collection">
|
||||
<?php // List all storage ?>
|
||||
<?php foreach ($motherboard["storage"] as $mb_storage): ?>
|
||||
|
||||
<?php // Get case details from endpoint by id ?>
|
||||
<?php $storage = $api->call(Endpoints::BATTLESTATION_STORAGE->value)->params([
|
||||
StorageModel::ID->value => $mb_storage[MbStorageModel::REF_STORAGE_ID->value]
|
||||
])->get()->json()[0]; ?>
|
||||
|
||||
<div data-target="drive" class="spec">
|
||||
<p><?= $storage[StorageModel::DISK_FORMFACTOR->value] ?> <?= $storage[StorageModel::DISK_TYPE->value] ?></p>
|
||||
<h3>
|
||||
<?= $storage[StorageModel::VENDOR_NAME->value] ?>
|
||||
<span><?= floor($storage[StorageModel::DISK_SIZE->value] / GIGA) ?>GB</span>
|
||||
</h3>
|
||||
<div>
|
||||
<div>
|
||||
<label>Type</label>
|
||||
<p><?= $storage[StorageModel::DISK_TYPE->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Capacity</label>
|
||||
<p><?= floor($storage[StorageModel::DISK_SIZE->value] / GIGA) ?>GB</p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Interface</label>
|
||||
<p><?= $storage[StorageModel::DISK_INTERFACE->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Formfactor</label>
|
||||
<p><?= $storage[StorageModel::DISK_FORMFACTOR->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Brand name</label>
|
||||
<p><?= $storage[StorageModel::VENDOR_NAME->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Brand model</label>
|
||||
<p><?= $storage[StorageModel::VENDOR_MODEL->value] ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label>Aquired</label>
|
||||
<p><?= date(API::DATE_FORMAT, $storage[StorageModel::DATE_AQUIRED->value]) ?></p>
|
||||
</div>
|
||||
|
||||
<?php if ($storage[StorageModel::IS_RETIRED->value]): ?>
|
||||
<div>
|
||||
<label>Retired</label>
|
||||
<p>Yes</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<label>Attatched via interface</label>
|
||||
<p><?= $mb_storage[MbStorageModel::INTERFACE->value] ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
<script type="module"><?= VV::js("assets/js/pages/about/battlestation") ?></script>
|
0
assets/css/fonts.css → public/assets/css/fonts.css
Executable file → Normal file
0
assets/css/fonts.css → public/assets/css/fonts.css
Executable file → Normal file
286
public/assets/css/pages/about.css
Normal file
286
public/assets/css/pages/about.css
Normal file
|
@ -0,0 +1,286 @@
|
|||
/* # Overrides */
|
||||
|
||||
:root {
|
||||
--primer-color-accent: 148, 255, 21;
|
||||
--color-accent: rgb(var(--primer-color-accent));
|
||||
--hue-accent: 390deg;
|
||||
|
||||
--primer-color-go: 0, 173, 216;
|
||||
--primer-color-php: 79, 93, 149;
|
||||
--primer-color-css: 86, 61, 124;
|
||||
--primer-color-html: 227, 76, 38;
|
||||
--primer-color-shell: 137, 224, 81;
|
||||
--primer-color-python: 53, 114, 165;
|
||||
--primer-color-typescript: 49, 120, 198;
|
||||
--primer-color-javascript: 241, 224, 90;
|
||||
|
||||
--color-go: rgb(var(--primer-color-go));
|
||||
--color-php: rgb(var(--primer-color-php));
|
||||
--color-css: rgb(var(--primer-color-css));
|
||||
--color-html: rgb(var(--primer-color-html));
|
||||
--color-shell: rgb(var(--primer-color-shell));
|
||||
--color-python: rgb(var(--primer-color-python));
|
||||
--color-typescript: rgb(var(--primer-color-typescript));
|
||||
--color-javascript: rgb(var(--primer-color-javascript));
|
||||
}
|
||||
|
||||
vv-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--padding);
|
||||
}
|
||||
|
||||
/* # Sections */
|
||||
|
||||
/* ## Divider */
|
||||
|
||||
vv-shell > hr {
|
||||
border-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
/* ## About */
|
||||
|
||||
section.about {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--padding) / 2);
|
||||
}
|
||||
|
||||
section.about p:first-of-type:first-letter {
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
margin-right: .1rem;
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
section.about span.interests {
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
color: var(--color-accent);
|
||||
animation: interests-hue 5s infinite linear;
|
||||
}
|
||||
|
||||
/* ## Languages */
|
||||
|
||||
section.languages {
|
||||
margin: calc(var(--padding) / 1.5) 0;
|
||||
}
|
||||
|
||||
section.languages stacked-bar-chart {
|
||||
gap: 3px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
border-radius: 100px;
|
||||
height: var(--padding);
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
section.languages stacked-bar-chart:last-of-type {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
section.languages stacked-bar-chart:hover chart-segment {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
section.languages stacked-bar-chart chart-segment {
|
||||
--border-corner-radius: 100px;
|
||||
|
||||
transition: 150ms opacity;
|
||||
width: var(--size, 0%);
|
||||
min-width: 3%;
|
||||
height: 100%;
|
||||
color: white;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
section.languages stacked-bar-chart a:nth-child(odd) chart-segment {
|
||||
background-color: rgba(255, 255, 255, .3);
|
||||
}
|
||||
|
||||
/* ### Round corners */
|
||||
|
||||
section.languages stacked-bar-chart a:first-child chart-segment {
|
||||
border-top-right-radius: var(--padding);
|
||||
border-bottom-right-radius: var(--padding);
|
||||
border-top-left-radius: var(--border-corner-radius);
|
||||
border-bottom-left-radius: var(--border-corner-radius);
|
||||
}
|
||||
|
||||
section.languages stacked-bar-chart a:last-child chart-segment {
|
||||
border-top-left-radius: var(--padding);
|
||||
border-bottom-left-radius: var(--padding);
|
||||
border-top-right-radius: var(--border-corner-radius);
|
||||
border-bottom-right-radius: var(--border-corner-radius);
|
||||
}
|
||||
|
||||
section.languages stacked-bar-chart:last-of-type a:first-child chart-segment {
|
||||
border-top-left-radius: var(--padding);
|
||||
border-bottom-left-radius: var(--padding);
|
||||
border-top-right-radius: var(--border-corner-radius);
|
||||
border-bottom-right-radius: var(--border-corner-radius);
|
||||
}
|
||||
|
||||
section.languages stacked-bar-chart:last-of-type a:last-child chart-segment {
|
||||
border-top-right-radius: var(--padding);
|
||||
border-bottom-right-radius: var(--padding);
|
||||
border-top-left-radius: var(--border-corner-radius);
|
||||
border-bottom-left-radius: var(--border-corner-radius);
|
||||
}
|
||||
|
||||
/* ### Texts */
|
||||
|
||||
section.languages stacked-bar-chart chart-segment p {
|
||||
text-align: center;
|
||||
color: inherit;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0 3px;
|
||||
}
|
||||
|
||||
section.languages stacked-bar-chart chart-segment[style="--size:0%;"] p span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
section.languages stacked-bar-chart chart-segment[style="--size:0%;"] p::before {
|
||||
content: "<1%";
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
section.languages stacked-bar-chart chart-segment [data-hover] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ### Colors */
|
||||
|
||||
section.languages stacked-bar-chart a chart-segment[data-lang="Go"] { background-color: var(--color-go); }
|
||||
section.languages stacked-bar-chart a chart-segment[data-lang="PHP"] { background-color: var(--color-php); }
|
||||
section.languages stacked-bar-chart a chart-segment[data-lang="CSS"] { background-color: var(--color-css); }
|
||||
section.languages stacked-bar-chart a chart-segment[data-lang="HTML"] { background-color: var(--color-html); }
|
||||
section.languages stacked-bar-chart a chart-segment[data-lang="Python"] { background-color: var(--color-python); }
|
||||
section.languages stacked-bar-chart a chart-segment[data-lang="TypeScript"] { background-color: var(--color-typescript); }
|
||||
section.languages stacked-bar-chart a chart-segment[data-lang="Shell"] { background-color: var(--color-shell); color: black; }
|
||||
section.languages stacked-bar-chart a chart-segment[data-lang="JavaScript"] { background-color: var(--color-javascript); color: black; }
|
||||
|
||||
/* ### Legend */
|
||||
|
||||
section.languages languages-list {
|
||||
gap: calc(var(--padding) / 2);
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
margin: var(--padding) 0;
|
||||
}
|
||||
|
||||
section.languages languages-list language-item {
|
||||
gap: 10px;
|
||||
display: flex;
|
||||
border-radius: 8px;
|
||||
align-items: center;
|
||||
fill: var(--color-php);
|
||||
padding: calc(var(--padding) / 1.5);
|
||||
border: solid 1px rgba(255, 255, 255, .1);
|
||||
background: linear-gradient(139deg, rgba(0, 0, 0, 0) 0%, rgba(79, 93, 144, .2) 100%);
|
||||
}
|
||||
|
||||
section.languages languages-list language-item svg {
|
||||
width: 2em;
|
||||
margin-left: auto;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
section.languages button p.lang {
|
||||
font-size: 1.3em;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
/* # Interests */
|
||||
|
||||
div.interests {
|
||||
--text-shadow-blur: 30px;
|
||||
|
||||
transition: 300ms opacity;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-weight: bold;
|
||||
pointer-events: none;
|
||||
font-size: clamp(16px, 15vw, 50px);
|
||||
color: var(--color-accent);
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
z-index: 200;
|
||||
}
|
||||
|
||||
div.interests.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
div.interests p {
|
||||
transition: 500ms transform cubic-bezier(.34,0,0,.99);
|
||||
position: absolute;
|
||||
text-shadow:
|
||||
0 0 var(--text-shadow-blur) black,
|
||||
0 0 var(--text-shadow-blur) black,
|
||||
0 0 var(--text-shadow-blur) black,
|
||||
0 0 var(--text-shadow-blur) black,
|
||||
0 0 var(--text-shadow-blur) black;
|
||||
}
|
||||
|
||||
@keyframes interests-hue {
|
||||
to {
|
||||
-webkit-filter: hue-rotate(360deg);
|
||||
filter: hue-rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Feature queries */
|
||||
|
||||
@media (hover: hover) {
|
||||
section.languages stacked-bar-chart chart-segment:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
section.languages stacked-bar-chart chart-segment [data-hover] {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
transform: translate(0, 0);
|
||||
background-color: inherit;
|
||||
padding: 5px 10px;
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
border-radius: 6px;
|
||||
z-index: 2000;
|
||||
-webkit-backdrop-filter: brightness(.2) blur(20px);
|
||||
backdrop-filter: brightness(.2) blur(20px);
|
||||
}
|
||||
|
||||
section.languages stacked-bar-chart chart-segment [data-hover].hovering {
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
|
||||
/* Size queries */
|
||||
|
||||
@media (max-width: 900px) {
|
||||
section.languages languages-list {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 650px) {
|
||||
section.languages languages-list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
27
assets/css/pages/contact.css → public/assets/css/pages/contact.css
Executable file → Normal file
27
assets/css/pages/contact.css → public/assets/css/pages/contact.css
Executable file → Normal file
|
@ -3,6 +3,7 @@
|
|||
:root {
|
||||
--primer-color-accent: 255, 195, 255;
|
||||
--color-accent: rgb(var(--primer-color-accent));
|
||||
--hue-accent: 200deg;
|
||||
}
|
||||
|
||||
vv-shell {
|
||||
|
@ -28,10 +29,6 @@ section.center {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
section.fade {
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
/* ## Social */
|
||||
|
||||
section.social {
|
||||
|
@ -71,7 +68,7 @@ section.social social:hover {
|
|||
fill: var(--color-accent);
|
||||
}
|
||||
|
||||
section.social social.hovering p {
|
||||
section.social social p.hovering {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
|
@ -81,11 +78,12 @@ section.pgp {
|
|||
max-width: 800px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
border-radius: 12px;
|
||||
flex-direction: column;
|
||||
gap: var(--padding);
|
||||
text-align: center;
|
||||
background-color: rgba(var(--primer-color-accent), .15);
|
||||
padding: calc(var(--padding) * 1.5);
|
||||
padding: var(--padding);
|
||||
transform: rotate(-1.5deg);
|
||||
}
|
||||
|
||||
|
@ -108,6 +106,19 @@ section.pgp .buttons {
|
|||
gap: var(--padding);
|
||||
}
|
||||
|
||||
/* ## Blockquote */
|
||||
|
||||
cite {
|
||||
text-decoration: underline;
|
||||
text-decoration-style: dotted;
|
||||
text-decoration-color: rgba(var(--primer-color-accent), .5);
|
||||
text-decoration-thickness: .1em;
|
||||
|
||||
&:hover {
|
||||
text-decoration-color: var(--color-accent);
|
||||
}
|
||||
}
|
||||
|
||||
/* ## Contact form */
|
||||
|
||||
section.form :is(input, textarea) {
|
||||
|
@ -196,6 +207,10 @@ section.form-message.sent + section.form {
|
|||
/* # Size queries */
|
||||
|
||||
@media (min-width: 460px) {
|
||||
section.pgp {
|
||||
padding: calc(var(--padding) * 1.5);
|
||||
}
|
||||
|
||||
section.pgp .buttons {
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
0
assets/css/pages/error.css → public/assets/css/pages/error.css
Executable file → Normal file
0
assets/css/pages/error.css → public/assets/css/pages/error.css
Executable file → Normal file
7
assets/css/pages/index.css → public/assets/css/pages/index.css
Executable file → Normal file
7
assets/css/pages/index.css → public/assets/css/pages/index.css
Executable file → Normal file
|
@ -38,7 +38,7 @@ vv-shell img {
|
|||
padding: unset;
|
||||
text-align: right;
|
||||
font-size: clamp(20px, 8vh, 60px);
|
||||
font-weight: 900;
|
||||
font-weight: bold;
|
||||
line-height: clamp(20px, 8vh, 60px);
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
@ -49,6 +49,11 @@ vv-shell img {
|
|||
|
||||
.menu svg {
|
||||
width: 100%;
|
||||
animation: dash 1500ms linear infinite;
|
||||
}
|
||||
|
||||
@keyframes dash {
|
||||
to { stroke-dashoffset: 32; }
|
||||
}
|
||||
|
||||
/* ### Copy email button */
|
84
public/assets/css/pages/search.css
Normal file
84
public/assets/css/pages/search.css
Normal file
|
@ -0,0 +1,84 @@
|
|||
vv-shell[vv-page="/search"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--padding) / 2);
|
||||
}
|
||||
|
||||
/* # Search */
|
||||
|
||||
section.search {
|
||||
display: flex;
|
||||
gap: var(--padding);
|
||||
border-radius: 6px;
|
||||
padding: var(--padding);
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
section.search form {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
section.search input {
|
||||
flex: 1 1 auto;
|
||||
border-radius: 6px;
|
||||
padding: 0 var(--padding);
|
||||
border: solid 2px rgba(255, 255, 255, .1);
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
section.search input:focus {
|
||||
outline: none;
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
section.search select {
|
||||
padding: 5px;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
section.search select :is(option, optgroup) {
|
||||
color: black;
|
||||
}
|
||||
|
||||
/* # Center */
|
||||
|
||||
section.center {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
fill: var(--color-accent);
|
||||
gap: calc(var(--padding) / 2);
|
||||
}
|
||||
|
||||
section.center svg {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
/* # Result */
|
||||
|
||||
section.result {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
section.result button {
|
||||
flex: 1 1 auto;
|
||||
text-align: left;
|
||||
padding: var(--padding);
|
||||
}
|
||||
|
||||
/* # Stats */
|
||||
|
||||
section.stats {
|
||||
min-height: calc(var(--padding) * 2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: calc(var(--padding) / 2);
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
vv-shell[vv-page="/search"] section.stats button {
|
||||
display: none;
|
||||
}
|
16
public/assets/css/pages/work/archive.css
Normal file
16
public/assets/css/pages/work/archive.css
Normal file
|
@ -0,0 +1,16 @@
|
|||
/* # Overrides */
|
||||
|
||||
:root {
|
||||
--primer-color-accent: 3, 255, 219;
|
||||
--color-accent: rgb(var(--primer-color-accent));
|
||||
--hue-accent: 90deg;
|
||||
}
|
||||
|
||||
vv-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--padding);
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
overflow-x: initial;
|
||||
}
|
167
public/assets/css/pages/work/index.css
Normal file
167
public/assets/css/pages/work/index.css
Normal file
|
@ -0,0 +1,167 @@
|
|||
/* # Overrides */
|
||||
|
||||
:root {
|
||||
--primer-color-accent: 3, 255, 219;
|
||||
--color-accent: rgb(var(--primer-color-accent));
|
||||
--hue-accent: 90deg;
|
||||
|
||||
--primer-color-reflect: 220, 26, 0;
|
||||
--primer-color-vegvisir: 0, 128, 255;
|
||||
}
|
||||
|
||||
vv-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--padding);
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
overflow-x: initial;
|
||||
}
|
||||
|
||||
/* # Sections */
|
||||
|
||||
/* ## Hero */
|
||||
|
||||
section.hero {
|
||||
--color-accent: rgb(255, 255, 255);
|
||||
|
||||
display: grid;
|
||||
gap: var(--padding);
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
|
||||
section.hero .item {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
section.hero .wrapper {
|
||||
gap: var(--padding);
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: baseline;
|
||||
flex-direction: column;
|
||||
padding: calc(var(--padding) * 1.5);
|
||||
}
|
||||
|
||||
section.hero .item .title {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
gap: var(--padding);
|
||||
grid-template-columns: 40px 1fr;
|
||||
}
|
||||
|
||||
section.hero .item .title svg {
|
||||
height: 3em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
section.hero .actions {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
/* ### Vegivisr */
|
||||
|
||||
section.hero .item.vegvisir {
|
||||
--primer-color-accent: var(--primer-color-vegvisir);
|
||||
--color-accent: rgb(var(--primer-color-vegvisir));
|
||||
|
||||
color: var(--color-vegvisir);
|
||||
background-color: rgba(var(--primer-color-vegvisir), .1);
|
||||
}
|
||||
|
||||
/* ### Reflect */
|
||||
|
||||
section.hero .item.reflect {
|
||||
--primer-color-accent: var(--primer-color-reflect);
|
||||
--color-accent: rgb(var(--primer-color-reflect));
|
||||
|
||||
color: var(--color-reflect);
|
||||
background-color: rgba(var(--primer-color-reflect), .2);
|
||||
}
|
||||
|
||||
/* ## Heading */
|
||||
|
||||
section.heading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
section.heading svg {
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
/* ## Featured */
|
||||
|
||||
section.featured {
|
||||
display: grid;
|
||||
gap: var(--padding);
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
|
||||
section.featured featured-item {
|
||||
display: flex;
|
||||
fill: white;
|
||||
color: white;
|
||||
border-radius: 8px;
|
||||
align-items: baseline;
|
||||
flex-direction: column;
|
||||
padding: var(--padding);
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
section.featured featured-item .title {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: calc(var(--padding) / 2);
|
||||
}
|
||||
|
||||
section.featured featured-item .title svg {
|
||||
height: 2em;
|
||||
fill: var(--color-accent);
|
||||
}
|
||||
|
||||
section.featured featured-item img {
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
section.featured featured-item .actions {
|
||||
gap: var(--padding);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: var(--padding);
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
/* # Size queries */
|
||||
|
||||
@media (min-width: 400px) {
|
||||
section.featured featured-item .actions {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
section.hero {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
section.featured {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
section.featured featured-item .actions button.collapse p {
|
||||
display: none;
|
||||
}
|
||||
}
|
50
assets/css/pages/work.css → public/assets/css/pages/work/timeline.css
Executable file → Normal file
50
assets/css/pages/work.css → public/assets/css/pages/work/timeline.css
Executable file → Normal file
|
@ -3,6 +3,7 @@
|
|||
:root {
|
||||
--primer-color-accent: 3, 255, 219;
|
||||
--color-accent: rgb(var(--primer-color-accent));
|
||||
--hue-accent: 90deg;
|
||||
}
|
||||
|
||||
vv-shell {
|
||||
|
@ -27,7 +28,7 @@ section.git {
|
|||
border-radius: 6px;
|
||||
}
|
||||
|
||||
section.git svg {
|
||||
section.git > svg {
|
||||
fill: white;
|
||||
width: 60px;
|
||||
}
|
||||
|
@ -120,13 +121,10 @@ section.timeline .items .item img {
|
|||
}
|
||||
|
||||
section.timeline .items .item .actions {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
/* ## Note */
|
||||
|
||||
section.note {
|
||||
text-align: center;
|
||||
gap: var(--padding);
|
||||
}
|
||||
|
||||
/* # Size queries */
|
||||
|
@ -137,23 +135,6 @@ section.note {
|
|||
}
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
section.git {
|
||||
display: grid;
|
||||
grid-template-columns: 70px 1fr 400px;
|
||||
align-items: center;
|
||||
gap: calc(var(--padding) * 1.5);
|
||||
}
|
||||
|
||||
section.git svg {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
section.git .buttons {
|
||||
justify-content: end;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
section.timeline {
|
||||
padding: unset;
|
||||
|
@ -188,7 +169,28 @@ section.note {
|
|||
border-top-color: rgba(var(--primer-color-accent), .2);
|
||||
}
|
||||
|
||||
section.timeline .items .item .actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
section.timeline .year:first-of-type .month:first-of-type .day:first-of-type .items .item:first-of-type {
|
||||
margin-top: var(--padding);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
section.git {
|
||||
display: grid;
|
||||
grid-template-columns: 70px 1fr 400px;
|
||||
align-items: center;
|
||||
gap: calc(var(--padding) * 1.5);
|
||||
}
|
||||
|
||||
section.git svg {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
section.git .buttons {
|
||||
justify-content: end;
|
||||
}
|
||||
}
|
16
public/assets/css/pages/work/wip.css
Normal file
16
public/assets/css/pages/work/wip.css
Normal file
|
@ -0,0 +1,16 @@
|
|||
/* # Overrides */
|
||||
|
||||
:root {
|
||||
--primer-color-accent: 3, 255, 219;
|
||||
--color-accent: rgb(var(--primer-color-accent));
|
||||
--hue-accent: 90deg;
|
||||
}
|
||||
|
||||
vv-shell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--padding);
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
overflow-x: initial;
|
||||
}
|
|
@ -11,7 +11,8 @@
|
|||
/* # Cornerstones */
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
fill: inherit;
|
||||
box-sizing: border-box;
|
||||
font-family: "Roboto Mono", sans-serif;
|
||||
color: inherit;
|
||||
|
@ -88,6 +89,32 @@ h3 {
|
|||
font-size: 25px;
|
||||
}
|
||||
|
||||
/* ## Page transition */
|
||||
|
||||
[vv-loading] * {
|
||||
transition: 200ms opacity;
|
||||
}
|
||||
|
||||
[vv-loading="true"] * {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
[vv-loading="true"]::after {
|
||||
content: "";
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 45px;
|
||||
height: 49px;
|
||||
background-size: contain;
|
||||
image-rendering: pixelated;
|
||||
transform: translate(-50%, -50%);
|
||||
background-image: url("/assets/media/spinner.gif");
|
||||
-webkit-filter: hue-rotate(var(--hue-accent));
|
||||
filter: hue-rotate(var(--hue-accent));
|
||||
}
|
||||
|
||||
/* ## Buttons */
|
||||
|
||||
button {
|
||||
|
@ -101,35 +128,44 @@ button {
|
|||
/* ### Inline */
|
||||
|
||||
button.inline {
|
||||
padding: calc(var(--padding) / 2) var(--padding);
|
||||
color: white;
|
||||
border: solid 2px white;
|
||||
border-radius: 6px;
|
||||
gap: 10px;
|
||||
display: flex;
|
||||
border-radius: 7px;
|
||||
align-items: center;
|
||||
fill: var(--color-accent);
|
||||
padding: calc(var(--padding) / 1.5);
|
||||
background: linear-gradient(139deg, rgba(0, 0, 0, 0) 0%, rgba(var(--primer-color-accent), .1) 100%);
|
||||
}
|
||||
|
||||
button.inline:not(.solid) {
|
||||
box-shadow:
|
||||
0 0 0 2px rgba(var(--primer-color-accent), .1),
|
||||
10px 7px 40px 3px rgba(var(--primer-color-accent), .06)
|
||||
;
|
||||
}
|
||||
|
||||
button.inline svg {
|
||||
flex: none;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
button.inline svg:last-child {
|
||||
width: 1.5em;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
button.inline svg.chevron:last-child {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
button.inline.solid {
|
||||
fill: black;
|
||||
color: black;
|
||||
border: solid 2px rgba(var(--primer-color-accent), 1);
|
||||
border-color: var(--color-accent);
|
||||
background-color: var(--color-accent);
|
||||
}
|
||||
|
||||
a > button::after {
|
||||
content: " ➜";
|
||||
}
|
||||
|
||||
/* ### Text links */
|
||||
|
||||
a[target="_blank"] > button::after,
|
||||
:is(h1, h2, h3, p, li) > a[target="_blank"]::after {
|
||||
content: " ↑";
|
||||
color: var(--color-accent);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
a > button.solid:not(:hover)::after {
|
||||
color: black;
|
||||
}
|
||||
|
||||
/* ## Header */
|
||||
|
||||
header {
|
||||
|
@ -250,6 +286,10 @@ header searchbox input {
|
|||
border: none;
|
||||
}
|
||||
|
||||
header searchbox input::placeholder {
|
||||
color: rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
/* #### Active */
|
||||
|
||||
header.searchboxActive > * {
|
||||
|
@ -274,6 +314,9 @@ vv-shell {
|
|||
search-results {
|
||||
transition: 500ms opacity, 300ms transform;
|
||||
position: fixed;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--padding);
|
||||
top: var(--running-size);
|
||||
right: 0;
|
||||
width: 100%;
|
||||
|
@ -285,12 +328,7 @@ search-results {
|
|||
transform: scale(.99);
|
||||
transform-origin: 100% 0;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
search-results:not([vv-page]) {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
header.searchboxActive ~ search-results {
|
||||
|
@ -299,6 +337,10 @@ header.searchboxActive ~ search-results {
|
|||
transform: scale(1);
|
||||
}
|
||||
|
||||
search-results section.search {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ### "Start typing" prompt */
|
||||
|
||||
search-results .info {
|
||||
|
@ -306,11 +348,11 @@ search-results .info {
|
|||
align-items: center;
|
||||
flex-direction: column;
|
||||
margin: auto;
|
||||
gap: 3svh;
|
||||
gap: var(--padding);
|
||||
}
|
||||
|
||||
search-results .info :is(svg, img) {
|
||||
width: 128px;
|
||||
width: 60px;
|
||||
fill: var(--color-accent);
|
||||
}
|
||||
|
||||
|
@ -326,7 +368,8 @@ search-results .info :is(svg, img) {
|
|||
/* # Components */
|
||||
|
||||
button.inline {
|
||||
transition: 200ms background-color, 200ms border-color, 200ms color;
|
||||
transition-duration: 300ms;
|
||||
transition-property: background-color, border-color, box-shadow, color, fill;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
|
@ -334,8 +377,19 @@ search-results .info :is(svg, img) {
|
|||
background-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
button.solid:hover {
|
||||
button.inline:hover {
|
||||
fill: var(--color-accent);
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
button.inline:not(.solid):hover {
|
||||
box-shadow:
|
||||
0 0 0 2px rgba(var(--primer-color-accent), 1),
|
||||
10px 7px 30px 3px rgba(var(--primer-color-accent), .07)
|
||||
;
|
||||
}
|
||||
|
||||
button.solid:hover {
|
||||
border-color: rgba(var(--primer-color-accent), .2);
|
||||
background-color: rgba(var(--primer-color-accent), .2);
|
||||
box-shadow: 0 -10px 20px 10px rgba(var(--primer-color-accent), .05);
|
32
public/assets/js/modules/Hoverpop.mjs
Normal file
32
public/assets/js/modules/Hoverpop.mjs
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { Elevent } from "/assets/js/modules/npm/Elevent.mjs";
|
||||
|
||||
export const TARGET_SELECTOR = "[data-hover]";
|
||||
|
||||
export class Hoverpop {
|
||||
/**
|
||||
* Bind hover targets on provided Elevent-compatible element(s)
|
||||
* @param {HTMLElement|HTMLElements|string} elements
|
||||
*/
|
||||
constructor(elements) {
|
||||
// Bind hover targets on element(s)
|
||||
new Elevent("mouseenter", elements, (event) => {
|
||||
const element = event.target.querySelector(TARGET_SELECTOR);
|
||||
|
||||
// Bail out if target element contains no hover target element
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
element.classList.add("hovering");
|
||||
event.target.addEventListener("mousemove", (event) => {
|
||||
const x = event.layerX - (element.clientWidth / 2);
|
||||
const y = event.layerY + element.clientHeight;
|
||||
|
||||
element.style.setProperty("transform", `translate(${x}px, ${y}px)`);
|
||||
});
|
||||
});
|
||||
|
||||
// Bind hover leave targets on element(s)
|
||||
new Elevent("mouseleave", elements, () => elements.forEach(element => element.querySelector(TARGET_SELECTOR)?.classList.remove("hovering")));
|
||||
}
|
||||
}
|
0
assets/js/modules/glitch/Generator.mjs → public/assets/js/modules/glitch/Generator.mjs
Executable file → Normal file
0
assets/js/modules/glitch/Generator.mjs → public/assets/js/modules/glitch/Generator.mjs
Executable file → Normal file
0
assets/js/modules/glitch/Glitch.mjs → public/assets/js/modules/glitch/Glitch.mjs
Executable file → Normal file
0
assets/js/modules/glitch/Glitch.mjs → public/assets/js/modules/glitch/Glitch.mjs
Executable file → Normal file
0
assets/js/modules/glitch/GlitchWorker.js → public/assets/js/modules/glitch/GlitchWorker.js
Executable file → Normal file
0
assets/js/modules/glitch/GlitchWorker.js → public/assets/js/modules/glitch/GlitchWorker.js
Executable file → Normal file
25
assets/js/pages/about.js → public/assets/js/pages/about.js
Executable file → Normal file
25
assets/js/pages/about.js → public/assets/js/pages/about.js
Executable file → Normal file
|
@ -56,12 +56,23 @@ const implodeInterests = () => {
|
|||
// Bind mouse or touch events depending on pointer type of device
|
||||
const canHover = window.matchMedia("(pointer: fine)").matches;
|
||||
|
||||
interestsElement.addEventListener(canHover ? "mouseenter" : "touchstart", () => {
|
||||
// Get absolute position of the trigger element
|
||||
const size = interestsElement.getBoundingClientRect();
|
||||
|
||||
explodeInterests(size.x, size.y);
|
||||
});
|
||||
|
||||
// Explode interests when mouse hovers or touch hold starts
|
||||
interestsElement.addEventListener(canHover ? "mouseenter" : "touchstart", (event) => explodeInterests(event.x, event.y));
|
||||
// Implode interests when mouse leaves or touch hold ends
|
||||
interestsElement.addEventListener(canHover ? "mouseleave" : "touchend", () => implodeInterests());
|
||||
}
|
||||
|
||||
// Language bar chart hover tooltip
|
||||
document.querySelectorAll("stacked-bar-chart chart-segment").forEach(element => {
|
||||
const tooltipElement = element.querySelector("[data-hover]");
|
||||
|
||||
element.addEventListener("mouseenter", () => tooltipElement.classList.add("hovering"));
|
||||
element.addEventListener("mouseleave", () => tooltipElement.classList.remove("hovering"));
|
||||
|
||||
element.addEventListener("mousemove", (event) => {
|
||||
const x = event.layerX - (tooltipElement.clientWidth / 2);
|
||||
const y = event.layerY + (tooltipElement.clientHeight - 30);
|
||||
|
||||
tooltipElement.style.setProperty("transform", `translate(${x}px, ${y}px)`);
|
||||
});
|
||||
});
|
29
assets/js/pages/contact.js → public/assets/js/pages/contact.js
Executable file → Normal file
29
assets/js/pages/contact.js → public/assets/js/pages/contact.js
Executable file → Normal file
|
@ -60,27 +60,16 @@ class ContactForm {
|
|||
form ? (new ContactForm(form)) : ContactForm.removeSavedMessage();
|
||||
}
|
||||
|
||||
// Social links hover
|
||||
{
|
||||
const socialElementHover = (target) => {
|
||||
const element = target.querySelector("p");
|
||||
document.querySelectorAll("social").forEach(element => {
|
||||
const tooltipElement = element.querySelector("[data-hover]");
|
||||
|
||||
target.classList.add("hovering");
|
||||
target.addEventListener("mousemove", (event) => {
|
||||
const x = event.layerX - (element.clientWidth / 2);
|
||||
const y = event.layerY + element.clientHeight;
|
||||
element.addEventListener("mouseenter", () => tooltipElement.classList.add("hovering"));
|
||||
element.addEventListener("mouseleave", () => tooltipElement.classList.remove("hovering"));
|
||||
|
||||
element.style.setProperty("transform", `translate(${x}px, ${y}px)`);
|
||||
});
|
||||
};
|
||||
element.addEventListener("mousemove", (event) => {
|
||||
const x = event.layerX - (tooltipElement.clientWidth / 2);
|
||||
const y = event.layerY + tooltipElement.clientHeight;
|
||||
|
||||
const elements = [...document.querySelectorAll("social")];
|
||||
|
||||
elements.forEach(element => {
|
||||
element.addEventListener("mouseenter", () => socialElementHover(element));
|
||||
|
||||
element.addEventListener("mouseleave", () => {
|
||||
elements.forEach(element => element.classList.remove("hovering"));
|
||||
});
|
||||
tooltipElement.style.setProperty("transform", `translate(${x}px, ${y}px)`);
|
||||
});
|
||||
}
|
||||
});
|
0
assets/js/pages/error.js → public/assets/js/pages/error.js
Executable file → Normal file
0
assets/js/pages/error.js → public/assets/js/pages/error.js
Executable file → Normal file
6
assets/js/pages/index.js → public/assets/js/pages/index.js
Executable file → Normal file
6
assets/js/pages/index.js → public/assets/js/pages/index.js
Executable file → Normal file
|
@ -1,5 +1,3 @@
|
|||
import { Elevent } from "/assets/js/modules/npm/Elevent.mjs";
|
||||
|
||||
// Click to copy email button
|
||||
{
|
||||
const EMAIL_CPY_ANIM_DUR_MSECONDS = 1000;
|
||||
|
@ -55,7 +53,7 @@ import { Elevent } from "/assets/js/modules/npm/Elevent.mjs";
|
|||
}, EMAIL_CPY_ANIM_DUR_MSECONDS + 100);
|
||||
}
|
||||
|
||||
new Elevent("click", document.querySelector(".email"), async () => {
|
||||
document.querySelector(".email").addEventListener("click", async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText("victor@vlw.se");
|
||||
|
||||
|
@ -103,6 +101,6 @@ import { Elevent } from "/assets/js/modules/npm/Elevent.mjs";
|
|||
});
|
||||
|
||||
// Reset color on navigation
|
||||
vv.Navigation.rootShellElement.addEventListener(vv.Navigation.EVENTS.STARTED, () => updateColor(), { once: true });
|
||||
VV.shell.addEventListener(VV.EVENT.START, () => updateColor(), { once: true });
|
||||
}
|
||||
}
|
0
assets/js/pages/search.js → public/assets/js/pages/search.js
Executable file → Normal file
0
assets/js/pages/search.js → public/assets/js/pages/search.js
Executable file → Normal file
4
public/assets/js/pages/work/archive.js
Normal file
4
public/assets/js/pages/work/archive.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
// Redirect to work page if no href is defined
|
||||
if (!new URLSearchParams(window.location.search).has("href")) {
|
||||
new VV().navigate("/work");
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue