mirror of
https://codeberg.org/vlw/vlw.se.git
synced 2025-09-13 21:13:40 +02:00
Compare commits
38 commits
c494f5fd98
...
1c783b33ac
Author | SHA1 | Date | |
---|---|---|---|
1c783b33ac | |||
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 | |||
1ac2704124 | |||
f551d5d889 | |||
51e8215e78 | |||
7f4b54685e | |||
6dad22f226 | |||
0483e092dd | |||
ae1e992c5f | |||
43ddf1fdf6 |
203 changed files with 3697 additions and 4945 deletions
|
@ -1,7 +1,20 @@
|
||||||
[api]
|
[client_api]
|
||||||
base_url = "https://api.vlw.one/"
|
base_url = ""
|
||||||
api_key = ""
|
api_key = ""
|
||||||
verify_peer = 0
|
verify_peer = true
|
||||||
|
|
||||||
[time]
|
[client_time_available]
|
||||||
date_time_zone = "Europe/Stockholm"
|
time_zone = "Europe/Stockholm"
|
||||||
|
available_to_hour = 0;
|
||||||
|
reply_average_hours = 0;
|
||||||
|
available_from_hour = 0;
|
||||||
|
|
||||||
|
[server_database]
|
||||||
|
host = ""
|
||||||
|
user = ""
|
||||||
|
pass = ""
|
||||||
|
db = ""
|
||||||
|
|
||||||
|
[server_forgejo]
|
||||||
|
base_url = ""
|
||||||
|
scan_profiles = ""
|
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -1,8 +1,12 @@
|
||||||
assets/media/content
|
# Public assets #
|
||||||
|
#################
|
||||||
|
public/.well-known
|
||||||
|
public/assets/js/modules/npm
|
||||||
|
|
||||||
# Bootstrapping #
|
# Bootstrapping #
|
||||||
#################
|
#################
|
||||||
vendor
|
vendor
|
||||||
|
node_modules
|
||||||
.env.ini
|
.env.ini
|
||||||
|
|
||||||
# OS generated files #
|
# OS generated files #
|
||||||
|
|
101
README.md
101
README.md
|
@ -1,70 +1,67 @@
|
||||||
# vlw.se
|
# 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://github.com/victorwesterlund/vegvisir) and my [Reflect API framework](https://github.com/victorwesterlund/reflect).
|
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 [web framework](https://vegvisir.vlw.se) and its API is also built on top of my own [API framework](https://reflect.vlw.se).
|
||||||
|
|
||||||
# Installation
|
# 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 later is written in bash.
|
||||||
|
|
||||||
This website is built for PHP 8.0+ and MariaDB 14+ (for the API database).
|
**Make sure you have both of these package managers installed before proceeding:**
|
||||||
|
- [Composer](https://getcomposer.org/)
|
||||||
|
- [NPM](https://www.npmjs.com/)
|
||||||
|
|
||||||
**Confimed supported framework versions:**
|
## 1. Clone this repo
|
||||||
Vegvisir|Reflect
|
Clone/download this repo to your machine. Preferably to a non-public directory - the frameworks will handle that.
|
||||||
--|--
|
|
||||||
✅ [`2.5.0`](https://github.com/VictorWesterlund/vegvisir/releases/tag/2.5.0)|✅ [`2.7.2`](https://github.com/VictorWesterlund/reflect/releases/tag/2.7.2)
|
|
||||||
|
|
||||||
## Website (Vegvisir)
|
```
|
||||||
1. **Download this repo**
|
git clone https://codeberg.org/vlw/vlw.se --depth 1
|
||||||
|
```
|
||||||
|
|
||||||
Git clone or download this repo to any local folder
|
## 2. Install [Vegvisir](https://vegvisir.vlw.se) and [Reflect](https://reflect.vlw.se)
|
||||||
```
|
Follow the installation instructions for my web, and API framework. This site uses the default configuration for both frameworks so the only thing you need to do after you've installed both is to point the `root_path` and `endpoints` directory respectively to the directory where you cloned this repo.
|
||||||
git clone https://github.com/VictorWesterlund/vlw.se
|
|
||||||
```
|
|
||||||
2. **Download and install Vegvisir**
|
|
||||||
|
|
||||||
Follow the installation instructions for [Vegvisir](https://github.com/victorwesterlund/vegvisir) and point the `site_path` variable to the local vlw.se folder.
|
- [Vegvisir installation](https://vegvisir.vlw.se)
|
||||||
|
- [Reflect installation](https://reflect.vlw.se)
|
||||||
|
|
||||||
3. **Install dependencies**
|
*Example:*
|
||||||
|
```sh
|
||||||
|
# Vegvisir
|
||||||
|
root_path = "/var/www/vlw.se"
|
||||||
|
# Reflect
|
||||||
|
endpoints = "/var/www/vlw.se"
|
||||||
|
```
|
||||||
|
|
||||||
Install dependencies with composer.
|
## 3. Run the install script
|
||||||
```
|
Run the `install.sh` script from the root of the repo directory. [Make sure you have the required package managers installed](#installation).
|
||||||
composer install --optimize-autoloader
|
|
||||||
```
|
|
||||||
|
|
||||||
Et voila! You probably want to install the API-side too but the website itself should now be accessible from your configured Vegvisir host.
|
**Example:**
|
||||||
|
```sh
|
||||||
|
# vlw@example:$
|
||||||
|
cd /var/www/vlw.se
|
||||||
|
# vlw@example:/var/www/vlw.se$
|
||||||
|
./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
## API (Reflect)
|
## 4. Import the database templates
|
||||||
The API (and database) is where most content is stored and served from on this website.
|
There's are two SQL files in [`/src/Database/Seeds/`](https://codeberg.org/vlw/vlw.se/src/branch/master/src/Database/Seeds) that you can use to initialize the two databases required for this website.
|
||||||
|
|
||||||
1. **Download this repo**
|
- `vlw` - This database has the website data and should be added to the `db` variable under `server_database` in `/.env.ini`
|
||||||
|
- `vlw_reflect` - This is the Reflect database that has all the endpoints pre-configured. You'll have to add your own ACL rules.
|
||||||
|
|
||||||
**You can skip this if you've already downloaded the repo from step 1 in the website installation.**
|
## 5. Set environment variables
|
||||||
|
Make a copy of the `.env.example.ini` file called `.env.ini` from the root directory of the repo. There are a few parameters you can change here but the required ones are the following:
|
||||||
|
|
||||||
Otherwise... Git clone or download this repo to any local folder
|
```ini
|
||||||
```
|
[client_api]
|
||||||
git clone https://github.com/VictorWesterlund/vlw.se
|
base_url = ""
|
||||||
```
|
api_key = ""
|
||||||
|
|
||||||
2. **Download and install Reflect**
|
[server_database]
|
||||||
|
host = ""
|
||||||
|
user = ""
|
||||||
|
pass = ""
|
||||||
|
db = ""
|
||||||
|
```
|
||||||
|
|
||||||
Follow the installation instructions for [Reflect](https://github.com/victorwesterlund/vegvisir) and point the `endpoints` variable to the `/api` subdirectory in the local vlw.se folder.
|
Please refer to the comments in the ini file for more information about each field.
|
||||||
|
|
||||||
3. **Install dependencies**
|
## Done!
|
||||||
|
That should be it. Navigate to your configured Vegvisir public host!
|
||||||
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.
|
|
||||||
|
|
||||||
You also have to generate a [GitHub access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) if you wish to use the `releases` endpoint.
|
|
||||||
[Read more about this endpoint here](#)
|
|
||||||
|
|
||||||
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.
|
|
|
@ -1,14 +0,0 @@
|
||||||
[connect]
|
|
||||||
host = ""
|
|
||||||
user = ""
|
|
||||||
pass = ""
|
|
||||||
|
|
||||||
[databases]
|
|
||||||
vlw = ""
|
|
||||||
battlestation = ""
|
|
||||||
|
|
||||||
[github]
|
|
||||||
api_key = ""
|
|
||||||
# Use-Agent string sent to GitHub API
|
|
||||||
# They recommend setting it to your GitHub username or app name
|
|
||||||
user_agent = ""
|
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"require": {
|
|
||||||
"reflect/plugin-rules": "^1.5",
|
|
||||||
"victorwesterlund/xenum": "dev-master",
|
|
||||||
"victorwesterlund/libmysqldriver": "dev-master"
|
|
||||||
},
|
|
||||||
"minimum-stability": "dev"
|
|
||||||
}
|
|
135
api/composer.lock
generated
135
api/composer.lock
generated
|
@ -1,135 +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": "13ba5cc60bab24ac8ef5b1018fe4249b",
|
|
||||||
"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/libmysqldriver",
|
|
||||||
"version": "dev-master",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/VictorWesterlund/php-libmysqldriver.git",
|
|
||||||
"reference": "adc2fda90a3b8308e8a9df202d5ec418a9220ff8"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/VictorWesterlund/php-libmysqldriver/zipball/adc2fda90a3b8308e8a9df202d5ec418a9220ff8",
|
|
||||||
"reference": "adc2fda90a3b8308e8a9df202d5ec418a9220ff8",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"default-branch": true,
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"libmysqldriver\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"GPL-3.0-only"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Victor Westerlund",
|
|
||||||
"email": "victor.vesterlund@gmail.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Abstraction library for common mysqli features",
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/VictorWesterlund/php-libmysqldriver/issues",
|
|
||||||
"source": "https://github.com/VictorWesterlund/php-libmysqldriver/tree/3.6.1"
|
|
||||||
},
|
|
||||||
"time": "2024-04-29T08:17:12+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"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"packages-dev": [],
|
|
||||||
"aliases": [],
|
|
||||||
"minimum-stability": "dev",
|
|
||||||
"stability-flags": {
|
|
||||||
"victorwesterlund/xenum": 20,
|
|
||||||
"victorwesterlund/libmysqldriver": 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,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 libmysqldriver\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://github.com/VictorWesterlund/php-libmysqldriver?tab=readme-ov-file#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,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";
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
/* # Overrides */
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--primer-color-accent: 148, 255, 21;
|
|
||||||
--color-accent: rgb(var(--primer-color-accent));
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--padding);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* # Sections */
|
|
||||||
|
|
||||||
/* ## Divider */
|
|
||||||
|
|
||||||
main > 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ## Version */
|
|
||||||
|
|
||||||
section.version {
|
|
||||||
color: rgba(255, 255, 255, .2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* # 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,30 +0,0 @@
|
||||||
/* # Overrides */
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--primer-color-accent: 148, 255, 21;
|
|
||||||
--color-accent: rgb(var(--primer-color-accent));
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,290 +0,0 @@
|
||||||
/* # Overrides */
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--primer-color-accent: 148, 255, 21;
|
|
||||||
--color-accent: rgb(var(--primer-color-accent));
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
main[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,51 +0,0 @@
|
||||||
new vv.Interactions("document", {
|
|
||||||
navigateHome: () => new vv.Navigation("/").navigate(),
|
|
||||||
closeSearchbox: () => {
|
|
||||||
// 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("searchboxActive");
|
|
||||||
|
|
||||||
// Wait for the transform animation to finish
|
|
||||||
setTimeout(() => searchButtonElement.style.removeProperty("pointer-events"), transformDuration);
|
|
||||||
},
|
|
||||||
openSearchbox: () => {
|
|
||||||
document.querySelector("header").classList.add("searchboxActive");
|
|
||||||
// Select searchbox inner input element
|
|
||||||
document.querySelector("searchbox input").focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Crossfade pages on navigation
|
|
||||||
{
|
|
||||||
const mainElement = document.querySelector(vv._env.MAIN);
|
|
||||||
|
|
||||||
mainElement.addEventListener(vv.Navigation.events.LOADING, () => {
|
|
||||||
mainElement.classList.add("loading");
|
|
||||||
});
|
|
||||||
|
|
||||||
mainElement.addEventListener(vv.Navigation.events.LOADED, () => {
|
|
||||||
// Close searchbox on main page navigation
|
|
||||||
document.querySelector("header").classList.remove("searchboxActive");
|
|
||||||
|
|
||||||
// Wait 200ms for the page fade-in animation to finish
|
|
||||||
setTimeout(() => mainElement.classList.remove("loading"), 200);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 +0,0 @@
|
||||||
new vv.Interactions("battlestation-retired");
|
|
|
@ -1,72 +0,0 @@
|
||||||
new vv.Interactions("battlestation", {
|
|
||||||
toggleGroup: (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");
|
|
||||||
},
|
|
||||||
setSpecActive: (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,108 +0,0 @@
|
||||||
const EMAIL_CPY_ANIM_DUR_MSECONDS = 1000;
|
|
||||||
|
|
||||||
// Run email copied splash animation
|
|
||||||
const emailCopiedAnimation = () => {
|
|
||||||
const CONFETTI_COUNT = 40;
|
|
||||||
const CONFETTI_SCALE_PIXELS = 300;
|
|
||||||
|
|
||||||
const randomIntFromInterval = (min, max) => {
|
|
||||||
return Math.floor(Math.random() * (max - min + 1) + min)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new splash element
|
|
||||||
const splashElement = document.createElement("splash");
|
|
||||||
splashElement.innerText = "copied!";
|
|
||||||
|
|
||||||
// Set inline display to none to hide this element on pages where the splash element has no override styles defined.
|
|
||||||
splashElement.style.display = "none";
|
|
||||||
|
|
||||||
// Array of box-shadow strings as "confetti"
|
|
||||||
const confetti = [];
|
|
||||||
|
|
||||||
// Generate random confetti
|
|
||||||
for (let i = 0; i < CONFETTI_COUNT; i++) {
|
|
||||||
// Random confetti position
|
|
||||||
const x = randomIntFromInterval(CONFETTI_SCALE_PIXELS * -1, CONFETTI_SCALE_PIXELS);
|
|
||||||
const y = randomIntFromInterval(CONFETTI_SCALE_PIXELS * -1, CONFETTI_SCALE_PIXELS);
|
|
||||||
|
|
||||||
// Random confetti RGB color
|
|
||||||
const rgb = [
|
|
||||||
randomIntFromInterval(0, 255),
|
|
||||||
randomIntFromInterval(0, 255),
|
|
||||||
randomIntFromInterval(0, 255)
|
|
||||||
];
|
|
||||||
|
|
||||||
// Interpolate random values and append to outer confetti array
|
|
||||||
confetti.push(`${x}px ${y}px 0 rgb(${rgb.join(",")})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set CSS variable on splash element that in turn will be used by pseudo-element
|
|
||||||
splashElement.style.setProperty("--confetti", confetti.join(","));
|
|
||||||
|
|
||||||
// Start animation by appending the created element to the document body
|
|
||||||
document.body.appendChild(splashElement);
|
|
||||||
|
|
||||||
// Run hide animation
|
|
||||||
setTimeout(() => {
|
|
||||||
splashElement.classList.add("hide");
|
|
||||||
|
|
||||||
// Selfdestruct element when hide animation finishes
|
|
||||||
setTimeout(() => splashElement.remove(), 400);
|
|
||||||
}, EMAIL_CPY_ANIM_DUR_MSECONDS + 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
new vv.Interactions("index", {
|
|
||||||
// Copy email address to clipboard
|
|
||||||
copyEmail: async () => {
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText("victor@vlw.se");
|
|
||||||
|
|
||||||
// Run "email copied" animation!
|
|
||||||
emailCopiedAnimation();
|
|
||||||
|
|
||||||
// NOTE: I don't know, spamming the button is kinda fun
|
|
||||||
// Prevent interactions with the copy email elements while the animation is running
|
|
||||||
/*[...document.querySelectorAll("[vv-call='copyEmail']")].forEach(element => {
|
|
||||||
//element.classList.add("lock");
|
|
||||||
|
|
||||||
setTimeout(() => element.classList.remove("lock"), EMAIL_CPY_ANIM_DUR_MSECONDS);
|
|
||||||
});*/
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error.message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Open the fullscreen menu
|
|
||||||
openMenu: () => document.querySelector("menu").classList.add("active"),
|
|
||||||
// Close the fullscreen menu
|
|
||||||
closeMenu: () => document.querySelector("menu").classList.remove("active")
|
|
||||||
});
|
|
||||||
|
|
||||||
// Change site accent color on hover of menu items
|
|
||||||
if (window.matchMedia("(hover: hover)")) {
|
|
||||||
// Update root CSS variables
|
|
||||||
const updateColor = (rgb = null, hue = 0) => {
|
|
||||||
if (!rgb) {
|
|
||||||
document.documentElement.style.removeProperty("--hue-accent");
|
|
||||||
document.documentElement.style.removeProperty("--primer-color-accent");
|
|
||||||
document.documentElement.style.removeProperty("--color-accent");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.documentElement.style.setProperty("--hue-accent", `${hue}deg`);
|
|
||||||
|
|
||||||
document.documentElement.style.setProperty("--primer-color-accent", `${rgb}`);
|
|
||||||
// Compiled color variable must to be updated to receive the new RGB values
|
|
||||||
document.documentElement.style.setProperty("--color-accent", "rgb(var(--primer-color-accent)");
|
|
||||||
};
|
|
||||||
|
|
||||||
[...document.querySelectorAll("menu li")].forEach(element => {
|
|
||||||
// Change site accent color to RGB and HUE rotation defined in element dataset
|
|
||||||
element.addEventListener("mouseenter", (event) => updateColor(event.target.dataset.rgb, event.target.dataset.hue));
|
|
||||||
// Reset initial accent color and hues
|
|
||||||
element.addEventListener("mouseleave", () => updateColor());
|
|
||||||
});
|
|
||||||
|
|
||||||
// Reset color on navigation
|
|
||||||
document.querySelector(vv._env.MAIN).addEventListener(vv.Navigation.events.LOADED, () => updateColor(), { once: true });
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
new vv.Interactions("search");
|
|
|
@ -1 +0,0 @@
|
||||||
new vv.Interactions("work");
|
|
|
@ -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 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98 98"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#fff"/></svg>
|
|
Before Width: | Height: | Size: 957 B |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 6.8 KiB |
|
@ -1,7 +1,8 @@
|
||||||
{
|
{
|
||||||
"require": {
|
"require": {
|
||||||
"reflect/client": "dev-master",
|
"reflect/client": "dev-master",
|
||||||
"victorwesterlund/xenum": "dev-master"
|
"vlw/mysql": "dev-master",
|
||||||
|
"vlw/xenum": "dev-master"
|
||||||
},
|
},
|
||||||
"minimum-stability": "dev"
|
"minimum-stability": "dev"
|
||||||
}
|
}
|
||||||
|
|
69
composer.lock
generated
69
composer.lock
generated
|
@ -4,22 +4,16 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "2a8a06dc452a4eb9055d238f771dcd37",
|
"content-hash": "f74f68a452514a9d4dd011cef7648a7f",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "reflect/client",
|
"name": "reflect/client",
|
||||||
"version": "dev-master",
|
"version": "dev-master",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/VictorWesterlund/reflect-client-php.git",
|
"url": "https://codeberg.org/reflect/client-php",
|
||||||
"reference": "89a8c041044c8c60cefafc4716d5d61b96c43e06"
|
"reference": "89a8c041044c8c60cefafc4716d5d61b96c43e06"
|
||||||
},
|
},
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/VictorWesterlund/reflect-client-php/zipball/89a8c041044c8c60cefafc4716d5d61b96c43e06",
|
|
||||||
"reference": "89a8c041044c8c60cefafc4716d5d61b96c43e06",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"default-branch": true,
|
"default-branch": true,
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -38,31 +32,49 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Extendable PHP interface for communicating with Reflect API over HTTP or UNIX sockets",
|
"description": "Extendable PHP interface for communicating with Reflect API over HTTP or UNIX sockets",
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/VictorWesterlund/reflect-client-php/issues",
|
|
||||||
"source": "https://github.com/VictorWesterlund/reflect-client-php/tree/3.0.6"
|
|
||||||
},
|
|
||||||
"time": "2024-04-06T14:55:04+00:00"
|
"time": "2024-04-06T14:55:04+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "victorwesterlund/xenum",
|
"name": "vlw/mysql",
|
||||||
"version": "dev-master",
|
"version": "dev-master",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/VictorWesterlund/php-xenum.git",
|
"url": "https://codeberg.org/vlw/php-mysql",
|
||||||
"reference": "8972f06f42abd1f382807a67e937d5564bb89699"
|
"reference": "c64eb96049907da60dc9f237d26aef0e531b0015"
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/VictorWesterlund/php-xenum/zipball/8972f06f42abd1f382807a67e937d5564bb89699",
|
|
||||||
"reference": "8972f06f42abd1f382807a67e937d5564bb89699",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
},
|
||||||
"default-branch": true,
|
"default-branch": true,
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"victorwesterlund\\": "src/"
|
"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": "2025-01-30T09:33:10+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vlw/xenum",
|
||||||
|
"version": "dev-master",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://codeberg.org/vlw/php-xenum",
|
||||||
|
"reference": "1c997a5574656b88a62f5ee160ee5a6439932a2f"
|
||||||
|
},
|
||||||
|
"default-branch": true,
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"vlw\\": "src/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
@ -72,15 +84,11 @@
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Victor Westerlund",
|
"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",
|
"description": "PHP eXtended Enums. The missing quality-of-life features from PHP 8+ Enums",
|
||||||
"support": {
|
"time": "2024-12-02T10:36:32+00:00"
|
||||||
"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"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [],
|
"packages-dev": [],
|
||||||
|
@ -88,11 +96,12 @@
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"stability-flags": {
|
"stability-flags": {
|
||||||
"reflect/client": 20,
|
"reflect/client": 20,
|
||||||
"victorwesterlund/xenum": 20
|
"vlw/mysql": 20,
|
||||||
|
"vlw/xenum": 20
|
||||||
},
|
},
|
||||||
"prefer-stable": false,
|
"prefer-stable": false,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": [],
|
"platform": [],
|
||||||
"platform-dev": [],
|
"platform-dev": [],
|
||||||
"plugin-api-version": "2.0.0"
|
"plugin-api-version": "2.3.0"
|
||||||
}
|
}
|
||||||
|
|
40
endpoints/about/languages/DELETE.php
Normal file
40
endpoints/about/languages/DELETE.php
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Reflect\Rules\Ruleset;
|
||||||
|
use Reflect\{Response, Path, Call};
|
||||||
|
|
||||||
|
use VLW\API\Endpoints;
|
||||||
|
use VLW\Database\Database;
|
||||||
|
use VLW\Database\Tables\About\LanguagesTable;
|
||||||
|
|
||||||
|
require_once Path::root("src/API/Endpoints.php");
|
||||||
|
require_once Path::root("src/Database/Database.php");
|
||||||
|
require_once Path::root("src/Database/Tables/About/Languages.php");
|
||||||
|
|
||||||
|
class DELETE_AboutLanguages extends Database {
|
||||||
|
protected readonly Ruleset $ruleset;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
$this->ruleset->validate_or_exit();
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function languages(): array {
|
||||||
|
$resp = (new Call(Endpoints::ABOUT_LANGUAGES->value))->get();
|
||||||
|
|
||||||
|
return array_column($resp->output(), LanguagesTable::ID->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete languages cache file if it exists
|
||||||
|
public function main(): Response {
|
||||||
|
$this->db->for(LanguagesTable::NAME);
|
||||||
|
|
||||||
|
foreach ($this->languages() as $language){
|
||||||
|
$this->db->delete([LanguagesTable::ID->value => $language]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response();
|
||||||
|
}
|
||||||
|
}
|
43
endpoints/about/languages/GET.php
Normal file
43
endpoints/about/languages/GET.php
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use vlw\MySQL\Order;
|
||||||
|
use Reflect\{Response, Path, Call};
|
||||||
|
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||||
|
|
||||||
|
use VLW\API\Endpoints;
|
||||||
|
use VLW\Database\Database;
|
||||||
|
use VLW\Database\Tables\About\LanguagesTable;
|
||||||
|
|
||||||
|
require_once Path::root("src/API/Endpoints.php");
|
||||||
|
require_once Path::root("src/Database/Database.php");
|
||||||
|
require_once Path::root("src/Database/Tables/About/Languages.php");
|
||||||
|
|
||||||
|
class GET_AboutLanguages extends Database {
|
||||||
|
protected readonly Ruleset $ruleset;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
|
$this->ruleset->GET([
|
||||||
|
(new Rules(LanguagesTable::ID->value))
|
||||||
|
->type(Type::STRING)
|
||||||
|
->min(1)
|
||||||
|
->max(parent::SIZE_VARCHAR),
|
||||||
|
|
||||||
|
(new Rules(LanguagesTable::BYTES->value))
|
||||||
|
->type(Type::NUMBER)
|
||||||
|
->min(1)
|
||||||
|
->max(parent::SIZE_UINT32)
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->ruleset->validate_or_exit();
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main(): Response {
|
||||||
|
return $this->list(LanguagesTable::NAME, LanguagesTable::values(), [
|
||||||
|
LanguagesTable::BYTES->value => Order::DESC
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
102
endpoints/about/languages/POST.php
Normal file
102
endpoints/about/languages/POST.php
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Reflect\Rules\Ruleset;
|
||||||
|
use Reflect\{Response, Path, Call};
|
||||||
|
|
||||||
|
use VLW\API\Endpoints;
|
||||||
|
use VLW\Database\Database;
|
||||||
|
use VLW\Database\Tables\About\LanguagesTable;
|
||||||
|
use const VLW\{FORGEJO_ENDPOINT_USER, FORGEJO_ENDPOINT_SEARCH};
|
||||||
|
|
||||||
|
require_once Path::root("src/Consts.php");
|
||||||
|
require_once Path::root("src/API/Endpoints.php");
|
||||||
|
require_once Path::root("src/Database/Tables/About/Languages.php");
|
||||||
|
|
||||||
|
class POST_AboutLanguages extends Database {
|
||||||
|
protected readonly Ruleset $ruleset;
|
||||||
|
|
||||||
|
// Tally of all languages used in all configured repositories
|
||||||
|
private array $languages = [];
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
$this->ruleset->validate_or_exit();
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch JSON from URL
|
||||||
|
private static function fetch_json(string $url): array {
|
||||||
|
return json_decode(file_get_contents($url), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch JSON from a Forgejo endpoint
|
||||||
|
private static function fetch_endpoint(string $endpoint): array {
|
||||||
|
$url = $_ENV["server_forgejo"]["base_url"] . $endpoint;
|
||||||
|
return self::fetch_json($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write $this->languages to a JSON file
|
||||||
|
private function cache_languages(): void {
|
||||||
|
// Delete existing cache
|
||||||
|
(new Call(Endpoints::ABOUT_LANGUAGES->value))->delete();
|
||||||
|
|
||||||
|
$this->db->for(LanguagesTable::NAME);
|
||||||
|
|
||||||
|
foreach ($this->languages as $language => $bytes) {
|
||||||
|
$this->db->insert([
|
||||||
|
LanguagesTable::ID->value => $language,
|
||||||
|
LanguagesTable::BYTES->value => $bytes
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch and add languages to total from a fully-qualified Forgejo URL
|
||||||
|
private function add_repository_languages(string $url): void {
|
||||||
|
foreach(self::fetch_json($url) as $language => $bytes) {
|
||||||
|
// Create key for language if it doesn't exist
|
||||||
|
if (!array_key_exists($language, $this->languages)) {
|
||||||
|
$this->languages[$language] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add bytes to language in total
|
||||||
|
$this->languages[$language] += $bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tally languages from public repositories for user id
|
||||||
|
private function add_public_repositores(int $uid): bool {
|
||||||
|
$resp = self::fetch_endpoint(sprintf(FORGEJO_ENDPOINT_SEARCH, $uid));
|
||||||
|
|
||||||
|
// Bail out if request failed or if response indicated a problem
|
||||||
|
if (!$resp or $resp["ok"] === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add langauges for each public repository
|
||||||
|
foreach ($resp["data"] as $repo) {
|
||||||
|
$this->add_repository_languages($repo["languages_url"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add languages from all public repositories for profiles in config
|
||||||
|
private function add_repositories_from_config_profiles(): void {
|
||||||
|
foreach(explode(",", $_ENV["server_forgejo"]["scan_profiles"]) as $profile) {
|
||||||
|
// Resolve user data from username
|
||||||
|
$user = self::fetch_endpoint(sprintf(FORGEJO_ENDPOINT_USER, $profile));
|
||||||
|
$this->add_public_repositores($user["id"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main(): Response {
|
||||||
|
$this->add_repositories_from_config_profiles();
|
||||||
|
|
||||||
|
// Sort langauges bytes tally by largest in descending order
|
||||||
|
arsort($this->languages);
|
||||||
|
|
||||||
|
$this->cache_languages();
|
||||||
|
return new Response($this->languages);
|
||||||
|
}
|
||||||
|
}
|
38
endpoints/coffee/GET.php
Normal file
38
endpoints/coffee/GET.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use vlw\MySQL\Order;
|
||||||
|
use Reflect\{Response, Path, Call};
|
||||||
|
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||||
|
|
||||||
|
use VLW\API\Endpoints;
|
||||||
|
use VLW\Database\Database;
|
||||||
|
use VLW\Database\Tables\Coffee\CoffeeTable;
|
||||||
|
|
||||||
|
require_once Path::root("src/API/Endpoints.php");
|
||||||
|
require_once Path::root("src/Database/Database.php");
|
||||||
|
require_once Path::root("src/Database/Tables/Coffee/Coffee.php");
|
||||||
|
|
||||||
|
class GET_Coffee extends Database {
|
||||||
|
protected readonly Ruleset $ruleset;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
|
$this->ruleset->GET([
|
||||||
|
(new Rules(CoffeeTable::ID->value))
|
||||||
|
->type(Type::NUMBER)
|
||||||
|
->min(1)
|
||||||
|
->max(parent::SIZE_UINT32)
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->ruleset->validate_or_exit();
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main(): Response {
|
||||||
|
return $this->list(CoffeeTable::NAME, CoffeeTable::values(), [
|
||||||
|
CoffeeTable::ID->value => Order::DESC
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
38
endpoints/coffee/POST.php
Normal file
38
endpoints/coffee/POST.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Reflect\{Response, Path, Call};
|
||||||
|
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||||
|
|
||||||
|
use VLW\API\Endpoints;
|
||||||
|
use VLW\Database\Database;
|
||||||
|
use VLW\Database\Tables\Coffee\CoffeeTable;
|
||||||
|
|
||||||
|
require_once Path::root("src/API/Endpoints.php");
|
||||||
|
require_once Path::root("src/Database/Database.php");
|
||||||
|
require_once Path::root("src/Database/Tables/Coffee/Coffee.php");
|
||||||
|
|
||||||
|
class POST_Coffee extends Database {
|
||||||
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
|
$this->ruleset->POST([
|
||||||
|
(new Rules(CoffeeTable::ID->value))
|
||||||
|
->type(Type::NUMBER)
|
||||||
|
->min(1)
|
||||||
|
->max(parent::SIZE_UINT32)
|
||||||
|
->default(time()),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->ruleset->validate_or_exit();
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main(): Response {
|
||||||
|
return $this->db->for(CoffeeTable::NAME)->insert($_POST) === true
|
||||||
|
? new Response(null, 201)
|
||||||
|
: new Response("Database error", 500);
|
||||||
|
}
|
||||||
|
}
|
28
endpoints/coffee/stats/GET.php
Normal file
28
endpoints/coffee/stats/GET.php
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use vlw\MySQL\Order;
|
||||||
|
use Reflect\{Response, Path, Call};
|
||||||
|
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||||
|
|
||||||
|
use VLW\API\Endpoints;
|
||||||
|
use VLW\Database\Database;
|
||||||
|
use VLW\Database\Tables\Coffee\StatsTable;
|
||||||
|
|
||||||
|
require_once Path::root("src/API/Endpoints.php");
|
||||||
|
require_once Path::root("src/Database/Database.php");
|
||||||
|
require_once Path::root("src/Database/Tables/Coffee/Stats.php");
|
||||||
|
|
||||||
|
class GET_CoffeeStats extends Database {
|
||||||
|
protected readonly Ruleset $ruleset;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
$this->ruleset->validate_or_exit();
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main(): Response {
|
||||||
|
return $this->list(StatsTable::NAME, StatsTable::values());
|
||||||
|
}
|
||||||
|
}
|
35
endpoints/coffee/stats/POST.php
Normal file
35
endpoints/coffee/stats/POST.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Reflect\{Response, Path, Call};
|
||||||
|
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||||
|
|
||||||
|
use VLW\API\Endpoints;
|
||||||
|
use VLW\Database\Database;
|
||||||
|
use VLW\Database\Tables\Coffee\{CoffeeTable, StatsTable};
|
||||||
|
|
||||||
|
require_once Path::root("src/API/Endpoints.php");
|
||||||
|
require_once Path::root("src/Database/Database.php");
|
||||||
|
require_once Path::root("src/Database/Tables/Coffee/Stats.php");
|
||||||
|
require_once Path::root("src/Database/Tables/Coffee/Coffee.php");
|
||||||
|
|
||||||
|
class POST_CoffeeStats extends Database {
|
||||||
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
$this->ruleset->validate_or_exit();
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main(): Response {
|
||||||
|
$truncate = $this->db->execute_query("DELETE FROM `" . StatsTable::NAME . "`");
|
||||||
|
|
||||||
|
// Add a dummy row to run the MariaDB INSERT AFTER Trigger on the coffee database table
|
||||||
|
$insert = $this->db->for(CoffeeTable::NAME)->insert([CoffeeTable::ID->value => 0]);
|
||||||
|
// Remove the dummy row
|
||||||
|
$remove = $this->db->for(CoffeeTable::NAME)->where([CoffeeTable::ID->value => 0])->delete();
|
||||||
|
|
||||||
|
return $truncate && $insert && $remove ? new Response() : new Response("Error", 500);
|
||||||
|
}
|
||||||
|
}
|
43
endpoints/messages/POST.php
Normal file
43
endpoints/messages/POST.php
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Reflect\{Response, Path};
|
||||||
|
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||||
|
|
||||||
|
use VLW\Database\Database;
|
||||||
|
use VLW\Database\Tables\Messages\MessagesTable;
|
||||||
|
|
||||||
|
require_once Path::root("src/Database/Database.php");
|
||||||
|
require_once Path::root("src/Database/Tables/Messages/Messages.php");
|
||||||
|
|
||||||
|
class POST_Messages extends Database {
|
||||||
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
|
$this->ruleset->POST([
|
||||||
|
(new Rules(MessagesTable::EMAIL->value))
|
||||||
|
->type(Type::STRING)
|
||||||
|
->max(255)
|
||||||
|
->default(null),
|
||||||
|
|
||||||
|
(new Rules(MessagesTable::MESSAGE->value))
|
||||||
|
->required()
|
||||||
|
->type(Type::STRING)
|
||||||
|
->min(1)
|
||||||
|
->max(parent::SIZE_TEXT)
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->ruleset->validate_or_exit();
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main(): Response {
|
||||||
|
$_POST[MessagesTable::TIMESTAMP_CREATED->value] = time();
|
||||||
|
|
||||||
|
return $this->db->for(MessagesTable::NAME)->insert($_POST) === true
|
||||||
|
? new Response(null, 201)
|
||||||
|
: new Response("Failed to send message", 500);
|
||||||
|
}
|
||||||
|
}
|
35
endpoints/search/DELETE.php
Normal file
35
endpoints/search/DELETE.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use vlw\MySQL\Operators;
|
||||||
|
use Reflect\{Response, Path};
|
||||||
|
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||||
|
|
||||||
|
use VLW\Database\Database;
|
||||||
|
use VLW\Database\Tables\Search\SearchTable;
|
||||||
|
|
||||||
|
require_once Path::root("src/Database/Database.php");
|
||||||
|
require_once Path::root("src/Database/Tables/Search/Search.php");
|
||||||
|
|
||||||
|
class DELETE_Search extends Database {
|
||||||
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
|
$this->ruleset->POST([
|
||||||
|
(new Rules(SearchTable::ID->value))
|
||||||
|
->required()
|
||||||
|
->type(Type::STRING)
|
||||||
|
->min(2)
|
||||||
|
->max(parent::SIZE_VARCHAR)
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->ruleset->validate_or_exit();
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main(): Response {
|
||||||
|
return $this->db->for(SearchTable::NAME)->delete($_POST) === true ? new Response() : new Response("", 500);
|
||||||
|
}
|
||||||
|
}
|
85
endpoints/search/GET.php
Normal file
85
endpoints/search/GET.php
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use vlw\MySQL\Operators;
|
||||||
|
use Reflect\{Response, Path, Call};
|
||||||
|
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||||
|
|
||||||
|
use VLW\API\Endpoints;
|
||||||
|
use VLW\Database\Database;
|
||||||
|
use VLW\Database\Tables\Search\{SearchTable, SearchCategoryEnum};
|
||||||
|
|
||||||
|
require_once Path::root("src/API/Endpoints.php");
|
||||||
|
require_once Path::root("src/Database/Database.php");
|
||||||
|
require_once Path::root("src/Database/Tables/Search/Search.php");
|
||||||
|
|
||||||
|
class GET_Search extends Database {
|
||||||
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
|
$this->ruleset->GET([
|
||||||
|
(new Rules(SearchTable::QUERY->value))
|
||||||
|
->type(Type::STRING)
|
||||||
|
->min(2)
|
||||||
|
->max(parent::SIZE_VARCHAR)
|
||||||
|
->default(null),
|
||||||
|
|
||||||
|
(new Rules(SearchTable::ID->value))
|
||||||
|
->type(Type::STRING)
|
||||||
|
->min(1)
|
||||||
|
->max(10)
|
||||||
|
->default(null),
|
||||||
|
|
||||||
|
(new Rules(SearchTable::CATEGORY->value))
|
||||||
|
->type(Type::ENUM, SearchCategoryEnum::names())
|
||||||
|
->default(null)
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->ruleset->validate_or_exit();
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function get_query(): string {
|
||||||
|
preg_match_all("/[a-zA-Z0-9]+/", $_GET[SearchTable::QUERY->value], $matches);
|
||||||
|
|
||||||
|
return strtolower(implode("", $matches[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main(): Response {
|
||||||
|
$result = $this->db->for(SearchTable::NAME);
|
||||||
|
|
||||||
|
if ($_GET[SearchTable::ID->value]) {
|
||||||
|
$result = $result->where([SearchTable::ID->value => $_GET[SearchTable::ID->value]]);
|
||||||
|
} else if ($_GET[SearchTable::QUERY->value]) {
|
||||||
|
$query = self::get_query();
|
||||||
|
|
||||||
|
$filter = [
|
||||||
|
SearchTable::QUERY->value => [
|
||||||
|
Operators::LIKE->value => "%{$query}%"
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($_GET[SearchTable::CATEGORY->value]) {
|
||||||
|
$filter[SearchTable::CATEGORY->value] = $_GET[SearchTable::CATEGORY->value];
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $result->where($filter);
|
||||||
|
} else {
|
||||||
|
new Response([], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $result->select([
|
||||||
|
SearchTable::ID->value,
|
||||||
|
SearchTable::TITLE->value,
|
||||||
|
SearchTable::SUMMARY->value,
|
||||||
|
SearchTable::CATEGORY->value,
|
||||||
|
SearchTable::HREF->value
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $result->num_rows > 0
|
||||||
|
? new Response($result->fetch_all(MYSQLI_ASSOC))
|
||||||
|
: new Response([], 404);
|
||||||
|
}
|
||||||
|
}
|
67
endpoints/search/POST.php
Normal file
67
endpoints/search/POST.php
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use vlw\MySQL\Operators;
|
||||||
|
use Reflect\{Response, Path, Call};
|
||||||
|
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||||
|
|
||||||
|
use VLW\API\Endpoints;
|
||||||
|
use VLW\Database\Database;
|
||||||
|
use const VLW\SEARCH_QUERY_MAX_LENGTH;
|
||||||
|
use VLW\Database\Tables\Search\{SearchTable, SearchCategoryEnum};
|
||||||
|
use VLW\Database\Tables\Work\{WorkTable, ActionsTable};
|
||||||
|
|
||||||
|
require_once Path::root("src/Consts.php");
|
||||||
|
require_once Path::root("src/API/Endpoints.php");
|
||||||
|
require_once Path::root("src/Database/Database.php");
|
||||||
|
require_once Path::root("src/Database/Tables/Work/Work.php");
|
||||||
|
require_once Path::root("src/Database/Tables/Work/Actions.php");
|
||||||
|
require_once Path::root("src/Database/Tables/Search/Search.php");
|
||||||
|
|
||||||
|
class POST_Search extends Database {
|
||||||
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
$this->ruleset->validate_or_exit();
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function truncate_string(string $input): string {
|
||||||
|
return substr($input, 0, SEARCH_QUERY_MAX_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function create_query(string $input): string {
|
||||||
|
preg_match_all("/[a-zA-Z0-9]+/", $input, $matches);
|
||||||
|
|
||||||
|
return self::truncate_string(strtolower(implode("", $matches[0])));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function index_work(): void {
|
||||||
|
foreach ((new Call(Endpoints::WORK->value))->get()->output() as $result) {
|
||||||
|
$query = self::create_query(implode("", array_values($result)), 0, SEARCH_QUERY_MAX_LENGTH);
|
||||||
|
|
||||||
|
// Get actions related to current result
|
||||||
|
$actions = (new Call(Endpoints::WORK_ACTIONS->value))->params([
|
||||||
|
ActionsTable::REF_WORK_ID->value => $result[WorkTable::ID->value]
|
||||||
|
])->get()->output();
|
||||||
|
|
||||||
|
$this->db->for(SearchTable::NAME)->insert([
|
||||||
|
SearchTable::QUERY->value => $query,
|
||||||
|
SearchTable::ID->value => crc32($query),
|
||||||
|
SearchTable::TITLE->value => self::truncate_string($result[WorkTable::TITLE->value]),
|
||||||
|
SearchTable::SUMMARY->value => self::truncate_string($result[WorkTable::SUMMARY->value]),
|
||||||
|
SearchTable::CATEGORY->value => SearchCategoryEnum::WORK->name,
|
||||||
|
// Use first action as link for search result
|
||||||
|
SearchTable::HREF->value => $actions
|
||||||
|
? self::truncate_string($actions[0][ActionsTable::HREF->value])
|
||||||
|
: null
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main(): Response {
|
||||||
|
$this->index_work();
|
||||||
|
return new Response();
|
||||||
|
}
|
||||||
|
}
|
26
endpoints/update/GET.php
Normal file
26
endpoints/update/GET.php
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Reflect\{Response, Path, Call};
|
||||||
|
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||||
|
|
||||||
|
use VLW\API\Endpoints;
|
||||||
|
|
||||||
|
require_once Path::root("src/API/Endpoints.php");
|
||||||
|
|
||||||
|
class GET_Update {
|
||||||
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
$this->ruleset->validate_or_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update all runtime database endpoints
|
||||||
|
public function main(): Response {
|
||||||
|
(new Call(Endpoints::SEARCH->value))->post();
|
||||||
|
(new Call(Endpoints::COFFEE_STATS->value))->post();
|
||||||
|
(new Call(Endpoints::ABOUT_LANGUAGES->value))->post();
|
||||||
|
|
||||||
|
return new Response();
|
||||||
|
}
|
||||||
|
}
|
49
endpoints/work/GET.php
Normal file
49
endpoints/work/GET.php
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use vlw\MySQL\Order;
|
||||||
|
use Reflect\{Response, Path};
|
||||||
|
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||||
|
|
||||||
|
use VLW\Database\Database;
|
||||||
|
use VLW\Database\Tables\Work\WorkTable;
|
||||||
|
|
||||||
|
require_once Path::root("src/Database/Database.php");
|
||||||
|
require_once Path::root("src/Database/Tables/Work/Work.php");
|
||||||
|
|
||||||
|
class GET_Work extends Database {
|
||||||
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
|
$this->ruleset->GET([
|
||||||
|
(new Rules(WorkTable::ID->value))
|
||||||
|
->type(Type::STRING)
|
||||||
|
->min(1)
|
||||||
|
->max(parent::SIZE_VARCHAR),
|
||||||
|
|
||||||
|
(new Rules(WorkTable::TITLE->value))
|
||||||
|
->type(Type::STRING)
|
||||||
|
->max(parent::SIZE_VARCHAR),
|
||||||
|
|
||||||
|
(new Rules(WorkTable::SUMMARY->value))
|
||||||
|
->type(Type::STRING)
|
||||||
|
->max(parent::SIZE_TEXT),
|
||||||
|
|
||||||
|
(new Rules(WorkTable::CREATED->value))
|
||||||
|
->type(Type::STRING)
|
||||||
|
->min(1)
|
||||||
|
->max(parent::SIZE_VARCHAR)
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->ruleset->validate_or_exit();
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main(): Response {
|
||||||
|
return $this->list(WorkTable::NAME, WorkTable::values(), [
|
||||||
|
WorkTable::CREATED->value => Order::DESC
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
36
endpoints/work/actions/GET.php
Normal file
36
endpoints/work/actions/GET.php
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use vlw\MySQL\Order;
|
||||||
|
use Reflect\{Response, Path};
|
||||||
|
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||||
|
|
||||||
|
use VLW\Database\Database;
|
||||||
|
use VLW\Database\Tables\Work\ActionsTable;
|
||||||
|
|
||||||
|
require_once Path::root("src/Database/Database.php");
|
||||||
|
require_once Path::root("src/Database/Tables/Work/Actions.php");
|
||||||
|
|
||||||
|
class GET_WorkActions extends Database {
|
||||||
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
|
$this->ruleset->GET([
|
||||||
|
(new Rules(ActionsTable::REF_WORK_ID->value))
|
||||||
|
->type(Type::STRING)
|
||||||
|
->min(1)
|
||||||
|
->max(parent::SIZE_VARCHAR)
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->ruleset->validate_or_exit();
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main(): Response {
|
||||||
|
return $this->list(ActionsTable::NAME, ActionsTable::values(), [
|
||||||
|
ActionsTable::ORDER_IDX->value => Order::DESC
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
35
endpoints/work/tags/GET.php
Normal file
35
endpoints/work/tags/GET.php
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Reflect\{Response, Path};
|
||||||
|
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||||
|
|
||||||
|
use VLW\Database\Database;
|
||||||
|
use VLW\Database\Tables\Work\{TagsTable, TagsLabelEnum};
|
||||||
|
|
||||||
|
require_once Path::root("src/Database/Database.php");
|
||||||
|
require_once Path::root("src/Database/Tables/Work/Tags.php");
|
||||||
|
|
||||||
|
class GET_WorkTags extends Database {
|
||||||
|
private Ruleset $ruleset;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
|
$this->ruleset->GET([
|
||||||
|
(new Rules(TagsTable::REF_WORK_ID->value))
|
||||||
|
->min(1)
|
||||||
|
->max(parent::SIZE_VARCHAR),
|
||||||
|
|
||||||
|
(new Rules(TagsTable::LABEL->value))
|
||||||
|
->type(Type::ENUM, TagsLabelEnum::names())
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->ruleset->validate_or_exit();
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main(): Response {
|
||||||
|
return $this->list(TagsTable::NAME, TagsTable::values());
|
||||||
|
}
|
||||||
|
}
|
53
endpoints/work/timeline/GET.php
Normal file
53
endpoints/work/timeline/GET.php
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use vlw\MySQL\Order;
|
||||||
|
use Reflect\{Response, Path};
|
||||||
|
use Reflect\Rules\{Ruleset, Rules, Type};
|
||||||
|
|
||||||
|
use VLW\Database\Database;
|
||||||
|
use VLW\Database\Tables\Work\TimelineTable;
|
||||||
|
|
||||||
|
require_once Path::root("src/Database/Database.php");
|
||||||
|
require_once Path::root("src/Database/Tables/Work/Timeline.php");
|
||||||
|
|
||||||
|
class GET_WorkTimeline extends Database {
|
||||||
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
|
$this->ruleset->GET([
|
||||||
|
(new Rules(TimelineTable::REF_WORK_ID->value))
|
||||||
|
->type(Type::STRING)
|
||||||
|
->min(1)
|
||||||
|
->max(parent::SIZE_VARCHAR),
|
||||||
|
|
||||||
|
(new Rules(TimelineTable::YEAR->value))
|
||||||
|
->type(Type::NUMBER)
|
||||||
|
->min(0)
|
||||||
|
->max(parent::SIZE_UINT16),
|
||||||
|
|
||||||
|
(new Rules(TimelineTable::MONTH->value))
|
||||||
|
->type(Type::NUMBER)
|
||||||
|
->min(0)
|
||||||
|
->max(parent::SIZE_UINT8),
|
||||||
|
|
||||||
|
(new Rules(TimelineTable::DAY->value))
|
||||||
|
->type(Type::NUMBER)
|
||||||
|
->min(0)
|
||||||
|
->max(parent::SIZE_UINT8)
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->ruleset->validate_or_exit();
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main(): Response {
|
||||||
|
return $this->list(TimelineTable::NAME, TimelineTable::values(), [
|
||||||
|
TimelineTable::YEAR->value => Order::DESC,
|
||||||
|
TimelineTable::MONTH->value => Order::DESC,
|
||||||
|
TimelineTable::DAY->value => Order::DESC
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
10
install.sh
Normal file
10
install.sh
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# Install dependencies
|
||||||
|
composer install --optimize-autoloader
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# (Re)create public NPM modules folder
|
||||||
|
rm -r public/assets/js/modules/npm
|
||||||
|
mkdir public/assets/js/modules/npm
|
||||||
|
|
||||||
|
# Create link to Elevent MJS from public JS modules folder
|
||||||
|
ln -sr node_modules/elevent/src/Elevent.mjs public/assets/js/modules/npm/Elevent.mjs
|
17
package-lock.json
generated
Normal file
17
package-lock.json
generated
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"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=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
package.json
Normal file
5
package.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"elevent": "^1.0.2"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,52 +0,0 @@
|
||||||
<style><?= VV::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, currently working as IT-Lead at the biopharma start-up <a href="https://icellate.com">iCellate Medical</a> in Solna, Stockholm. I also develop and maintain <a href="https://github.com/VictorWesterlund/vegvisir">my own web framework</a> and use it to build web apps and websites - including this one.</p>
|
|
||||||
<p>The <programming/markup/command>-languages I currently use the most are (in a mostly accurate decending order): PHP, JavaScript, CSS, MySQL, Python, SQLite, Bash, and [pure] HTML.</p>
|
|
||||||
</section>
|
|
||||||
<section class="about">
|
|
||||||
<h2>This website</h2>
|
|
||||||
<p>This site and all of its components are 100% free and open source software. The website is designed and built by me from the ground up on top of my own <a href="https://github.com/victorwesterlund/vegvisir">web</a> and <a href="https://github.com/victorwesterlund/reflect">API</a> frameworks. There are <i>no cookies or trackers</i> 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://github.com/victorwesterlund/vlw.se">Checkout the website source code on GitHub</a></p>
|
|
||||||
</section>
|
|
||||||
<section class="about">
|
|
||||||
<h2>Personal</h2>
|
|
||||||
<p>With a cup of coffee ready at hand, I can at times become a real armchair detective for a <span class="interests">variety of nerdy topics I find interesting</span>, and spend hours reading as much as I can about them too. I like to skii when I'm not glued in front of a computer screen.</p>
|
|
||||||
<p>I do occationally game, and I also have a silent passion that comes out every few years for <a href="/about/battlestation" vv="about" vv-call="navigate">building gaming computers</a> - even servers recently!</p>
|
|
||||||
</section>
|
|
||||||
<section class="about">
|
|
||||||
<h2>Projects</h2>
|
|
||||||
<p>Here are the projects I'm working on right now:</p>
|
|
||||||
<p>* <a href="https://github.com/victorwesterlund/reflect">Reflect</a>: An API framework written in PHP - for PHP developers.</p>
|
|
||||||
<p>* <a href="https://github.com/victorwesterlund/vegvisir">Vegvisir</a>: A web framework written in PHP and JavaScript - for PHP and JavaScript developers.</p>
|
|
||||||
<p>See more on my <a href="work" vv="about" vv-call="navigate">works page</a>. And even more, including smaller projects on <a href="https://github.com/VictorWesterlund">my GitHub profile</a>.</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>
|
|
||||||
</section>
|
|
||||||
<hr>
|
|
||||||
<section class="version">
|
|
||||||
<p>website version: <?= VV::include("pages/about/version") ?></p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div class="interests" aria-hidden="true">
|
|
||||||
<p>practical engineering</p>
|
|
||||||
<p>music</p>
|
|
||||||
<p>astronomy</p>
|
|
||||||
<p>electronics</p>
|
|
||||||
<p>aviation</p>
|
|
||||||
<p>maritime</p>
|
|
||||||
<p>typography</p>
|
|
||||||
<p>networking</p>
|
|
||||||
<p>SSTV</p>
|
|
||||||
<p>simulations</p>
|
|
||||||
<p>ISO 8601</p>
|
|
||||||
<p>photography</p>
|
|
||||||
<p>videography</p>
|
|
||||||
</div>
|
|
||||||
<script><?= VV::js("pages/about") ?></script>
|
|
|
@ -1,36 +0,0 @@
|
||||||
<style><?= VV::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("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 Path::root("src/client/API.php");
|
|
||||||
require_once Path::root("api/src/Endpoints.php");
|
|
||||||
|
|
||||||
// Load hardware database models
|
|
||||||
require_once Path::root("api/src/databases/models/Battlestation/Mb.php");
|
|
||||||
require_once Path::root("api/src/databases/models/Battlestation/Cpu.php");
|
|
||||||
require_once Path::root("api/src/databases/models/Battlestation/Gpu.php");
|
|
||||||
require_once Path::root("api/src/databases/models/Battlestation/Psu.php");
|
|
||||||
require_once Path::root("api/src/databases/models/Battlestation/Dram.php");
|
|
||||||
require_once Path::root("api/src/databases/models/Battlestation/Storage.php");
|
|
||||||
require_once Path::root("api/src/databases/models/Battlestation/Chassis.php");
|
|
||||||
|
|
||||||
// Load hardware config database models
|
|
||||||
require_once Path::root("api/src/databases/models/Battlestation/Config/MbPsu.php");
|
|
||||||
require_once Path::root("api/src/databases/models/Battlestation/Config/MbGpu.php");
|
|
||||||
require_once Path::root("api/src/databases/models/Battlestation/Config/MbDram.php");
|
|
||||||
require_once Path::root("api/src/databases/models/Battlestation/Config/Config.php");
|
|
||||||
require_once Path::root("api/src/databases/models/Battlestation/Config/MbStorage.php");
|
|
||||||
require_once Path::root("api/src/databases/models/Battlestation/Config/ChassisMb.php");
|
|
||||||
require_once Path::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("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::media("battlestation.svg") ?>
|
|
||||||
<div class="specs">
|
|
||||||
|
|
||||||
<?php // Show motherboard details ?>
|
|
||||||
<?php if ($motherboard): ?>
|
|
||||||
<div vv="battlestation" vv-call="setSpecActive" 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 vv="battlestation" vv-call="setSpecActive" 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 vv="battlestation" vv-call="setSpecActive" 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 vv="battlestation" vv-call="setSpecActive" 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 vv="battlestation" vv-call="setSpecActive" 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 vv="battlestation" vv-call="toggleGroup" class="group">
|
|
||||||
<p>DRAM</p>
|
|
||||||
<?= VV::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 vv="battlestation" vv-call="setSpecActive" 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 vv="battlestation" vv-call="toggleGroup" class="group">
|
|
||||||
<p>Storage</p>
|
|
||||||
<?= VV::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 vv="battlestation" vv-call="setSpecActive" 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><?= VV::js("pages/about/battlestation") ?></script>
|
|
|
@ -1,19 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
A pretty naive website version fetcher that assumes the latest git tag is the
|
|
||||||
version the website is currently displaying. The intent is that any live-master
|
|
||||||
of this website should always track the master branch and pull the latest HEAD
|
|
||||||
without any exceptions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use Vegvisir\Path;
|
|
||||||
|
|
||||||
// Get tags from local git folder
|
|
||||||
$dir = scandir(Path::root(".git/refs/tags"));
|
|
||||||
|
|
||||||
// Get current version number from latest tag
|
|
||||||
$version = end($dir);
|
|
||||||
|
|
||||||
?>
|
|
||||||
<a href="https://github.com/victorwesterlund/vlw.se/releases/<?= $version ?>"><?= $version ?></a>
|
|
|
@ -1,91 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Vegvisir\Path;
|
|
||||||
|
|
||||||
use VLW\Client\API;
|
|
||||||
use VLW\API\Endpoints;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\Models\Messages\MessagesModel;
|
|
||||||
|
|
||||||
require_once Path::root("src/client/API.php");
|
|
||||||
require_once Path::root("api/src/Endpoints.php");
|
|
||||||
|
|
||||||
require_once Path::root("api/src/databases/models/Messages/Messages.php");
|
|
||||||
|
|
||||||
// Connect to VLW API
|
|
||||||
$api = new API();
|
|
||||||
|
|
||||||
?>
|
|
||||||
<style><?= VV::css("pages/contact") ?></style>
|
|
||||||
<section>
|
|
||||||
<h1>Let's chat</h1>
|
|
||||||
<p>The best way to get in touch is by email, or with the form on this page. I will try to reply as quickly as possible, probably within a few hours. The time is <i><?= (new DateTime("now", new DateTimeZone($_ENV["time"]["date_time_zone"])))->format("h:i a") ?></i> in Sweden right now.</p>
|
|
||||||
</section>
|
|
||||||
<section class="social">
|
|
||||||
<a href="mailto:victor@vlw.se"><social>
|
|
||||||
<?= VV::media("icons/email.svg") ?>
|
|
||||||
<p>e-mail</p>
|
|
||||||
</social></a>
|
|
||||||
<a href="https://mastodon.social/@vlwone"><social>
|
|
||||||
<?= VV::media("icons/mastodon.svg") ?>
|
|
||||||
<p>mastodon</p>
|
|
||||||
</social></a>
|
|
||||||
<a href="https://web.libera.chat/#vlw.se"><social>
|
|
||||||
<?= VV::media("icons/libera.svg") ?>
|
|
||||||
<p>libera.chat</p>
|
|
||||||
</social></a>
|
|
||||||
</section>
|
|
||||||
<?= VV::media("line.svg") ?>
|
|
||||||
<section class="pgp">
|
|
||||||
<?= VV::media("icons/pin.svg") ?>
|
|
||||||
<h3>encrypt your message with my OpenPGP key.</h3>
|
|
||||||
<p>my key is also listed on the <a href="https://keys.openpgp.org/search?q=victor%40vlw.se" target="_blank" rel="noopener noreferer">openPGP key server</a> for victor@vlw.se so your e-mail client can automatically retreive it if supported.</p>
|
|
||||||
<div class="buttons">
|
|
||||||
<a href="https://keys.openpgp.org/vks/v1/by-fingerprint/DCE987311CB5D2A252F58951D0AD730E1057DFC6"><button class="inline solid">download ASC</button></a>
|
|
||||||
<a href="https://emailselfdefense.fsf.org/en/" target="_blank" rel="noopener noreferer"><button class="inline">more info</button></a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<?= VV::media("line.svg") ?>
|
|
||||||
|
|
||||||
<?php // Send message on POST request ?>
|
|
||||||
<?php if ($_SERVER["REQUEST_METHOD"] === "POST"): ?>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
|
|
||||||
// Send message via API
|
|
||||||
$send = $api->call(Endpoints::MESSAGES->value)->post([
|
|
||||||
MessagesModel::EMAIL->value => $_POST[MessagesModel::EMAIL->value],
|
|
||||||
MessagesModel::MESSAGE->value => $_POST[MessagesModel::MESSAGE->value]
|
|
||||||
]);
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
<?php if ($send->ok): ?>
|
|
||||||
<section class="form-message sent">
|
|
||||||
<h3>🙏 Message sent!</h3>
|
|
||||||
</section>
|
|
||||||
<?php else: ?>
|
|
||||||
<?php // Show response body from endpoint as error if request failed ?>
|
|
||||||
<section class="form-message error">
|
|
||||||
<h3>😟 Oh no, something went wrong</h3>
|
|
||||||
<p>Response from API:</p>
|
|
||||||
<pre><?= $send->output() ?></pre>
|
|
||||||
</section>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<section class="form">
|
|
||||||
<form method="POST">
|
|
||||||
<input-group>
|
|
||||||
<label>your email (optional)</label>
|
|
||||||
<input type="email" name="<?= MessagesModel::EMAIL->value ?>" placeholder="nissehult@example.com" autocomplete="off"></input>
|
|
||||||
</input-group>
|
|
||||||
<input-group>
|
|
||||||
<label title="this field is required">your message (required)</label>
|
|
||||||
<textarea name="<?= MessagesModel::MESSAGE->value ?>" required placeholder="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed molestie dignissim mauris vel dignissim. Sed et aliquet odio, id egestas libero. Vestibulum ut dui a turpis aliquam hendrerit id et dui. Morbi eu tristique quam, sit amet dictum felis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin ac nibh a ex accumsan ullamcorper non quis eros. Nam at suscipit lacus. Nullam placerat semper sapien, vitae aliquet nisl elementum a. Duis viverra quam eros, eu vestibulum quam egestas sit amet. Duis lobortis varius malesuada. Mauris in fringilla mi. "></textarea>
|
|
||||||
</input-group>
|
|
||||||
<button class="inline solid">send</button>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<script><?= VV::js("pages/contact") ?></script>
|
|
|
@ -1,6 +0,0 @@
|
||||||
<style><?= VV::css("pages/error") ?></style>
|
|
||||||
<canvas></canvas>
|
|
||||||
<section class="error">
|
|
||||||
<h1 glitch-text><span>4</span><span>0</span><span>4</span></h1>
|
|
||||||
</section>
|
|
||||||
<script type="module"><?= VV::js("pages/error") ?></script>
|
|
|
@ -1,26 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
enum RGB: string {
|
|
||||||
case WORK = "3,255,219";
|
|
||||||
case ABOUT = "148,255,21";
|
|
||||||
case CONTACT = "255,195,255";
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
<style><?= VV::css("pages/index") ?></style>
|
|
||||||
<div class="menu">
|
|
||||||
<?= VV::media("line.svg") ?>
|
|
||||||
<menu>
|
|
||||||
<a href="/work" vv="index" vv-call="navigate"><li data-rgb="<?= RGB::WORK->value ?>" data-hue="90">work</li></a>
|
|
||||||
<a href="/about" vv="index" vv-call="navigate"><li data-rgb="<?= RGB::ABOUT->value ?>" data-hue="390">about</li></a>
|
|
||||||
<a href="/contact" vv="index" vv-call="navigate"><li data-rgb="<?= RGB::CONTACT->value ?>" data-hue="200">contact</li></a>
|
|
||||||
</menu>
|
|
||||||
<?= VV::media("line.svg") ?>
|
|
||||||
<button class="email" vv="index" vv-call="copyEmail">
|
|
||||||
<p>victor@vlw.se</p>
|
|
||||||
<p class="cta">to copy</p>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<img src="/assets/media/gazing.jpg" alt="A portrait of Victor with a pair of cartoon glasses drawn in the shape of two V's over his eyes"/>
|
|
||||||
<script><?= VV::js("pages/index") ?></script>
|
|
|
@ -1,99 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Vegvisir\Path;
|
|
||||||
|
|
||||||
use VLW\Client\API;
|
|
||||||
use VLW\API\Endpoints;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\{
|
|
||||||
WorkModel,
|
|
||||||
WorkActionsModel
|
|
||||||
};
|
|
||||||
|
|
||||||
require_once Path::root("src/client/API.php");
|
|
||||||
require_once Path::root("api/src/Endpoints.php");
|
|
||||||
|
|
||||||
require_once Path::root("api/src/databases/models/Work/Work.php");
|
|
||||||
require_once Path::root("api/src/databases/models/Work/WorkActions.php");
|
|
||||||
|
|
||||||
// Search endpoint query paramter
|
|
||||||
const SEARCH_PARAM = "q";
|
|
||||||
|
|
||||||
// Connect to VLW API
|
|
||||||
$api = new API();
|
|
||||||
|
|
||||||
// Get search results from endpoint
|
|
||||||
$response = $api->call(Endpoints::SEARCH->value)->params([SEARCH_PARAM => $_GET[SEARCH_PARAM]])->get();
|
|
||||||
|
|
||||||
?>
|
|
||||||
<style><?= VV::css("pages/search") ?></style>
|
|
||||||
<section class="search">
|
|
||||||
<form method="GET">
|
|
||||||
<search>
|
|
||||||
<input name="<? SEARCH_PARAM ?>" type="text" placeholder="search vlw.se..." value="<?= $_GET[SEARCH_PARAM] ?>"></input>
|
|
||||||
</search>
|
|
||||||
<button type="submit" class="inline solid">Search</button>
|
|
||||||
</form>
|
|
||||||
<?= VV::media("line.svg") ?>
|
|
||||||
<button class="inline">advanced search options</button>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<?php if ($response->ok): ?>
|
|
||||||
<?php // Get response body ?>
|
|
||||||
<?php $results = $response->json(); ?>
|
|
||||||
|
|
||||||
<?php // Search contains results from the work endpoint ?>
|
|
||||||
<?php if ($results[Endpoints::WORK->value]): ?>
|
|
||||||
<section class="title work">
|
|
||||||
<a href="<? Endpoints::WORK->value ?>" vv="search" vv-call="navigate"><h2>Work</h2></a>
|
|
||||||
<p><?= count($results[Endpoints::WORK->value]) ?> search result(s) from my public work</p>
|
|
||||||
</section>
|
|
||||||
<section class="results work">
|
|
||||||
|
|
||||||
<?php // List all work category search results ?>
|
|
||||||
<?php foreach ($results[Endpoints::WORK->value] as $result): ?>
|
|
||||||
<div class="result">
|
|
||||||
<h3><?= $result[WorkModel::TITLE->value] ?></h3>
|
|
||||||
<p><?= $result[WorkModel::SUMMARY->value] ?></p>
|
|
||||||
<p><?= date(API::DATE_FORMAT, $result[WorkModel::DATE_CREATED->value]) ?></p>
|
|
||||||
|
|
||||||
<?php // Get action buttons for work entity by id ?>
|
|
||||||
<?php $actions = $api->call(Endpoints::WORK_ACTIONS->value)->params([WorkActionsModel::REF_WORK_ID->value => $result[WorkModel::ID->value]])->get(); ?>
|
|
||||||
|
|
||||||
<?php // List each action button for work entity if exists ?>
|
|
||||||
<?php if ($actions->ok): ?>
|
|
||||||
<div class="actions">
|
|
||||||
<?php foreach ($actions->json() as $action): ?>
|
|
||||||
|
|
||||||
<?php // Bind VV Interactions if link is same origin, else open in new tab ?>
|
|
||||||
<?php if (!$action[WorkActionsModel::EXTERNAL->value]): ?>
|
|
||||||
<a href="<?= $action[WorkActionsModel::HREF->value] ?>" vv="search" vv-call="navigate"><button class="inline <?= $action[WorkActionsModel::CLASS_LIST->value] ?>"><?= $action[WorkActionsModel::DISPLAY_TEXT->value] ?></button></a>
|
|
||||||
<?php else: ?>
|
|
||||||
<a href="<?= $action[WorkActionsModel::HREF->value] ?>" target="_blank"><button class="inline <?= $action[WorkActionsModel::CLASS_LIST->value] ?>"><?= $action[WorkActionsModel::DISPLAY_TEXT->value] ?></button></a>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</section>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php else: ?>
|
|
||||||
|
|
||||||
<?php if (!empty($_GET[SEARCH_PARAM])): ?>
|
|
||||||
<section class="info noresults">
|
|
||||||
<img src="/assets/media/travolta.gif" alt="">
|
|
||||||
<p>No results for search term "<?= $_GET[SEARCH_PARAM] ?>"</p>
|
|
||||||
</section>
|
|
||||||
<?php else: ?>
|
|
||||||
<section class="info empty">
|
|
||||||
<?= VV::media("icons/search.svg") ?>
|
|
||||||
<p>Start typing to search</p>
|
|
||||||
</section>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<script><?= VV::js("pages/search") ?></script>
|
|
181
pages/work.php
181
pages/work.php
|
@ -1,181 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Vegvisir\Path;
|
|
||||||
|
|
||||||
use VLW\Client\API;
|
|
||||||
use VLW\API\Endpoints;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\{
|
|
||||||
WorkModel,
|
|
||||||
WorkTagsModel,
|
|
||||||
WorkActionsModel
|
|
||||||
};
|
|
||||||
|
|
||||||
require_once Path::root("src/client/API.php");
|
|
||||||
require_once Path::root("api/src/Endpoints.php");
|
|
||||||
|
|
||||||
require_once Path::root("api/src/databases/models/Work/Work.php");
|
|
||||||
require_once Path::root("api/src/databases/models/Work/WorkTags.php");
|
|
||||||
require_once Path::root("api/src/databases/models/Work/WorkActions.php");
|
|
||||||
|
|
||||||
// Connect to VLW API
|
|
||||||
$api = new API();
|
|
||||||
|
|
||||||
// Retreive rows from work endpoints
|
|
||||||
$resp_work = $api->call(Endpoints::WORK->value)->get();
|
|
||||||
|
|
||||||
// Resolve tags and actions if we got work results
|
|
||||||
if ($resp_work->ok) {
|
|
||||||
$work_tags = $api->call(Endpoints::WORK_TAGS->value)->get()->json();
|
|
||||||
$work_actions = $api->call(Endpoints::WORK_ACTIONS->value)->get()->json();
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
<style><?= VV::css("pages/work") ?></style>
|
|
||||||
|
|
||||||
<section class="git">
|
|
||||||
<?= VV::media("icons/github.svg") ?>
|
|
||||||
<p>Most of my free open-source software is available on GitHub and it's also mirrored on my server</p>
|
|
||||||
<div class="buttons">
|
|
||||||
<a href="https://github.com/victorwesterlund"><button class="inline solid">open GitHub</button></a>
|
|
||||||
<a href="https://git.vlw.se"><button class="inline">mirror</button></a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<?php if ($resp_work->ok): ?>
|
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
Order response from endpoint into a multi-dimensional array.
|
|
||||||
For example, a single item created at 14th of February 2024 would be ordered like this
|
|
||||||
[2024 => [[02 => [14 => [<row_data>]]]]]
|
|
||||||
*/
|
|
||||||
|
|
||||||
$rows = [];
|
|
||||||
// Create array of arrays ordered by decending year, month, day, items
|
|
||||||
foreach ($resp_work->json() as $row) {
|
|
||||||
// Create array for current year if it doesn't exist
|
|
||||||
if (!array_key_exists($row[WorkModel::DATE_YEAR->value], $rows)) {
|
|
||||||
$rows[$row[WorkModel::DATE_YEAR->value]] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create array for current month if it doesn't exist
|
|
||||||
if (!array_key_exists($row[WorkModel::DATE_MONTH->value], $rows[$row[WorkModel::DATE_YEAR->value]])) {
|
|
||||||
$rows[$row[WorkModel::DATE_YEAR->value]][$row[WorkModel::DATE_MONTH->value]] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create array for current day if it doesn't exist
|
|
||||||
if (!array_key_exists($row[WorkModel::DATE_DAY->value], $rows[$row[WorkModel::DATE_YEAR->value]][$row[WorkModel::DATE_MONTH->value]])) {
|
|
||||||
$rows[$row[WorkModel::DATE_YEAR->value]][$row[WorkModel::DATE_MONTH->value]][$row[WorkModel::DATE_DAY->value]] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append item to ordered array
|
|
||||||
$rows[$row[WorkModel::DATE_YEAR->value]][$row[WorkModel::DATE_MONTH->value]][$row[WorkModel::DATE_DAY->value]][] = $row;
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
<section class="timeline">
|
|
||||||
<?php // Get year int from key and array of months for current year ?>
|
|
||||||
<?php foreach($rows as $year => $months): ?>
|
|
||||||
<div class="year">
|
|
||||||
<div class="track">
|
|
||||||
<p><?= $year ?></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="months">
|
|
||||||
<?php // Get month int from key and array of days for current month ?>
|
|
||||||
<?php foreach($months as $month => $days): ?>
|
|
||||||
<div class="month">
|
|
||||||
<div class="track">
|
|
||||||
<?php // Append leading zero to month ?>
|
|
||||||
<p><?= sprintf("%02d", $month) ?></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="days">
|
|
||||||
<?php // Get day int from key and array of items for current day ?>
|
|
||||||
<?php foreach($days as $day => $items): ?>
|
|
||||||
<div class="day">
|
|
||||||
<div class="track">
|
|
||||||
<?php // Append leading zero to day ?>
|
|
||||||
<p><?= sprintf("%02d", $day) ?></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="items">
|
|
||||||
<?php foreach($items as $item): ?>
|
|
||||||
<div class="item">
|
|
||||||
|
|
||||||
<?php // Get array index ids from tags array where work entity id matches ref_work_id ?>
|
|
||||||
<?php $tag_ids = array_keys(array_column($work_tags, WorkTagsModel::REF_WORK_ID->value), $item[WorkModel::ID->value]); ?>
|
|
||||||
|
|
||||||
<?php // List tags if available ?>
|
|
||||||
<?php if($tag_ids): ?>
|
|
||||||
<div class="tags">
|
|
||||||
<?php foreach($tag_ids as $tag_id): ?>
|
|
||||||
<?php // Get tag details from tag array by index id ?>
|
|
||||||
<?php $tag = $work_tags[$tag_id]; ?>
|
|
||||||
|
|
||||||
<p class="tag <?= $tag[WorkTagsModel::NAME->value] ?>"><?= $tag[WorkTagsModel::NAME->value] ?></p>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php // Show large heading if defined ?>
|
|
||||||
<?php if (!empty($item[WorkModel::TITLE->value])): ?>
|
|
||||||
<h2><?= $item[WorkModel::TITLE->value] ?></h2>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<p><?= $item[WorkModel::SUMMARY->value] ?></p>
|
|
||||||
|
|
||||||
<?php // Get array index ids from actions array where work entity id matches ref_work_id ?>
|
|
||||||
<?php $action_ids = array_keys(array_column($work_actions, WorkTagsModel::REF_WORK_ID->value), $item[WorkModel::ID->value]); ?>
|
|
||||||
|
|
||||||
<?php // List actions if defined for item ?>
|
|
||||||
<?php if($action_ids): ?>
|
|
||||||
<div class="actions">
|
|
||||||
<?php foreach($action_ids as $action_id): ?>
|
|
||||||
<?php
|
|
||||||
// Get tag details from tag array by index id
|
|
||||||
$action = $work_actions[$action_id];
|
|
||||||
|
|
||||||
$link_attr = !$action[WorkActionsModel::EXTERNAL->value]
|
|
||||||
// Bind VV Interactions for local links
|
|
||||||
? "vv='work' vv-call='navigate'"
|
|
||||||
// Open external links in a new tab
|
|
||||||
: "target='_blank'";
|
|
||||||
|
|
||||||
$link_href = $action[WorkActionsModel::HREF->value] === null
|
|
||||||
// Navigate to work details page if no href is defined
|
|
||||||
? "/work/{$item[WorkModel::ID->value]}"
|
|
||||||
// Href is defined so use it directly
|
|
||||||
: $action[WorkActionsModel::HREF->value];
|
|
||||||
?>
|
|
||||||
|
|
||||||
<a href="<?= $link_href ?>" <?= $link_attr ?>><button class="inline <?= $action["class_list"] ?>"><?= $action["display_text"] ?></button></a>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</section>
|
|
||||||
<section class="note">
|
|
||||||
<p>This is not really the end of the list. I will add some of my notable older work at some point.</p>
|
|
||||||
</section>
|
|
||||||
<?php else: ?>
|
|
||||||
<p>Something went wrong!</p>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<script><?= VV::js("pages/work") ?></script>
|
|
135
public/about.php
Normal file
135
public/about.php
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use VLW\Database\Models\Coffee\Stats;
|
||||||
|
use VLW\Database\Models\About\Language;
|
||||||
|
use const VLW\{
|
||||||
|
FORGEJO_HREF,
|
||||||
|
FORGEJO_SI_BYTE_MULTIPLE,
|
||||||
|
DEFAULT_BUTTON_ICON
|
||||||
|
};
|
||||||
|
|
||||||
|
require_once VV::root("src/Consts.php");
|
||||||
|
require_once VV::root("src/Database/Models/Coffee/Stats.php");
|
||||||
|
require_once VV::root("src/Database/Models/About/Language.php");
|
||||||
|
|
||||||
|
$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) . " " . FORGEJO_SI_BYTE_MULTIPLE[$factor];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$coffee = new class extends Stats {
|
||||||
|
public function week_average_string(): string {
|
||||||
|
$diff = $this->week() - $this->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, 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_HREF . $language->id ?>" target="_blank"><chart-segment style="--size:<?= $languages->percent($language) ?>%;" data-lang="<?= $language->id ?>" data-bytes="<?= $language->bytes() ?>">
|
||||||
|
<span data-hover><strong><?= $languages->percent_string($language) ?> <?= $language->id ?></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_HREF . $language->id ?>"><button data-lang="<?= $language->id ?>" class="inline">
|
||||||
|
<p><?= $languages->percent_string($language) ?></p>
|
||||||
|
<p class="lang"><?= $language->id ?></p>
|
||||||
|
<p><?= $languages->bytes_si_string($language) ?></p>
|
||||||
|
<?= VV::embed(DEFAULT_BUTTON_ICON) ?>
|
||||||
|
</button></a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
|
||||||
|
</languages-list>
|
||||||
|
<stacked-bar-chart>
|
||||||
|
|
||||||
|
<?php foreach ($languages::all() as $language): ?>
|
||||||
|
<a href="<?= FORGEJO_HREF . $language->id ?>" target="_blank"><chart-segment style="--size:<?= $languages->percent($language) ?>%;" data-lang="<?= $language->id ?>" data-bytes="<?= $language->bytes() ?>">
|
||||||
|
<span data-hover><strong><?= $languages->percent_string($language) ?> <?= $language->id ?></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, 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>One thing that most people know about me is that I like coffee.. lots of coffee. In fact, I've had <?= $coffee->week() ?> cup<?= $coffee->week() === 1 ? "" : "s" ?> of coffee in the last 7 days! That's <?= $coffee->week_average_string() ?> my average of <?= $coffee->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> 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">Write me a line!</a></p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="interests" aria-hidden="true">
|
||||||
|
<p>SSTV</p>
|
||||||
|
<p>music</p>
|
||||||
|
<p>aviation</p>
|
||||||
|
<p>maritime</p>
|
||||||
|
<p>politics</p>
|
||||||
|
<p>astronomy</p>
|
||||||
|
<p>typography</p>
|
||||||
|
<p>networking</p>
|
||||||
|
<p>electronics</p>
|
||||||
|
<p>simulations</p>
|
||||||
|
<p>engineering</p>
|
||||||
|
<p>photography</p>
|
||||||
|
<p>videography</p>
|
||||||
|
<p>RFC 3339</p>
|
||||||
|
<p>digital archiving</p>
|
||||||
|
</div>
|
||||||
|
<script type="module"><?= VV::js("public/assets/js/pages/about") ?></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;
|
||||||
|
}
|
||||||
|
}
|
46
assets/css/pages/contact.css → public/assets/css/pages/contact.css
Executable file → Normal file
46
assets/css/pages/contact.css → public/assets/css/pages/contact.css
Executable file → Normal file
|
@ -3,21 +3,32 @@
|
||||||
:root {
|
:root {
|
||||||
--primer-color-accent: 255, 195, 255;
|
--primer-color-accent: 255, 195, 255;
|
||||||
--color-accent: rgb(var(--primer-color-accent));
|
--color-accent: rgb(var(--primer-color-accent));
|
||||||
|
--hue-accent: 200deg;
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
vv-shell {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--padding);
|
gap: var(--padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fingerprint {
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
/* # Sections */
|
/* # Sections */
|
||||||
|
|
||||||
main > svg {
|
vv-shell > svg {
|
||||||
margin: var(--padding) 0;
|
margin: var(--padding) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ## Modifiers */
|
||||||
|
|
||||||
|
section.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
/* ## Social */
|
/* ## Social */
|
||||||
|
|
||||||
section.social {
|
section.social {
|
||||||
|
@ -57,18 +68,22 @@ section.social social:hover {
|
||||||
fill: var(--color-accent);
|
fill: var(--color-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
section.social social.hovering p {
|
section.social social p.hovering {
|
||||||
display: initial;
|
display: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ## OpenPGP key */
|
/* ## PGP key */
|
||||||
|
|
||||||
section.pgp {
|
section.pgp {
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
border-radius: 12px;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--padding);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: rgba(var(--primer-color-accent), .15);
|
background-color: rgba(var(--primer-color-accent), .15);
|
||||||
padding: calc(var(--padding) * 1.5);
|
padding: var(--padding);
|
||||||
transform: rotate(-1.5deg);
|
transform: rotate(-1.5deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,16 +96,29 @@ section.pgp > svg {
|
||||||
}
|
}
|
||||||
|
|
||||||
section.pgp > p {
|
section.pgp > p {
|
||||||
margin-bottom: var(--padding);
|
padding: 0 var(--padding);
|
||||||
padding: var(--padding);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
section.pgp .buttons {
|
section.pgp .buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
margin-top: var(--padding);
|
||||||
gap: var(--padding);
|
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 */
|
/* ## Contact form */
|
||||||
|
|
||||||
section.form :is(input, textarea) {
|
section.form :is(input, textarea) {
|
||||||
|
@ -179,6 +207,10 @@ section.form-message.sent + section.form {
|
||||||
/* # Size queries */
|
/* # Size queries */
|
||||||
|
|
||||||
@media (min-width: 460px) {
|
@media (min-width: 460px) {
|
||||||
|
section.pgp {
|
||||||
|
padding: calc(var(--padding) * 1.5);
|
||||||
|
}
|
||||||
|
|
||||||
section.pgp .buttons {
|
section.pgp .buttons {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
2
assets/css/pages/error.css → public/assets/css/pages/error.css
Executable file → Normal file
2
assets/css/pages/error.css → public/assets/css/pages/error.css
Executable file → Normal file
|
@ -6,7 +6,7 @@ header {
|
||||||
backdrop-filter: unset;
|
backdrop-filter: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
vv-shell {
|
||||||
max-width: unset;
|
max-width: unset;
|
||||||
display: grid;
|
display: grid;
|
||||||
justify-items: center;
|
justify-items: center;
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue