From 5f96ecd96f28593f22a169a0706601313a237cb6 Mon Sep 17 00:00:00 2001 From: Graphictoria Date: Sat, 16 Apr 2022 23:36:42 -0400 Subject: [PATCH] indev maintenance page, indev /asset impl --- .../Http/Controllers/ContentController.php | 16 ++++ .../Controllers/MaintenanceController.php | 83 ++++++++-------- web/app/Http/Kernel.php | 1 + .../PreventRequestsDuringMaintenance.php | 2 +- web/app/Models/AssetGenre.php | 11 +++ web/config/database.php | 20 ++++ ..._04_15_000406_create_asset_types_table.php | 9 ++ .../2022_04_15_000413_create_assets_table.php | 18 ++++ ..._15_000429_create_asset_versions_table.php | 9 ++ ...04_17_030538_create_asset_genres_table.php | 33 +++++++ web/public/mix-manifest.json | 1 + web/resources/js/pages/Maintenance.js | 95 +++++++++++++++++++ web/resources/js/util/HTTP.js | 16 ++++ web/resources/views/maintenance.blade.php | 25 +++++ web/routes/apis.php | 6 -- web/routes/web.php | 6 +- web/webpack.mix.js | 1 + 17 files changed, 304 insertions(+), 48 deletions(-) create mode 100644 web/app/Http/Controllers/ContentController.php create mode 100644 web/app/Models/AssetGenre.php create mode 100644 web/database/migrations/2022_04_17_030538_create_asset_genres_table.php create mode 100644 web/resources/js/pages/Maintenance.js create mode 100644 web/resources/js/util/HTTP.js create mode 100644 web/resources/views/maintenance.blade.php diff --git a/web/app/Http/Controllers/ContentController.php b/web/app/Http/Controllers/ContentController.php new file mode 100644 index 0000000..7d83d5d --- /dev/null +++ b/web/app/Http/Controllers/ContentController.php @@ -0,0 +1,16 @@ +input('id')); + } +} diff --git a/web/app/Http/Controllers/MaintenanceController.php b/web/app/Http/Controllers/MaintenanceController.php index fd2929e..fceb5bf 100644 --- a/web/app/Http/Controllers/MaintenanceController.php +++ b/web/app/Http/Controllers/MaintenanceController.php @@ -10,6 +10,10 @@ use App\Models\WebsiteConfiguration; class MaintenanceController extends Controller { + public function showPage() { + return view('maintenance'); + } + /** * Handles the maintenance bypass request. * @@ -20,50 +24,49 @@ class MaintenanceController extends Controller $password = $request->input('password'); $buttons = $request->input('buttons'); - if($password && $buttons) - { - $mtconf = json_decode(WebsiteConfiguration::whereName('MaintenancePassword')->first()->value); - - if($password == $mtconf->password) - { - $btns = array_slice($buttons, -count($mtconf->combination)); - $data = json_decode(file_get_contents(storage_path('framework/down')), true); - - if(isset($data['secret']) && $btns === $mtconf->combination) - { - $trustedHosts = explode(',', env('TRUSTED_HOSTS')); - $origin = parse_url($request->headers->get('origin'), PHP_URL_HOST); - $passCheck = false; - - foreach($trustedHosts as &$host) - { - if(str_ends_with($origin, $host)) - $passCheck = true; - } - - $expiresAt = Carbon::now()->addHours(24); - $bypassCookie = new Cookie('gt_constraint', base64_encode(json_encode([ - 'expires_at' => $expiresAt->getTimestamp(), - 'mac' => hash_hmac('SHA256', $expiresAt->getTimestamp(), $data['secret']), - ])), $expiresAt); - - if($passCheck) - $bypassCookie = $bypassCookie->withDomain('.' . $origin); - - return response('') - ->withCookie($bypassCookie); - } - } - - return response('') - ->setStatusCode(403); - } - else - { + if(!$password || !$buttons) { return response('{"errors":[{"code":400,"message":"BadRequest"}]}') ->setStatusCode(400) ->header('Cache-Control', 'private') ->header('Content-Type', 'application/json; charset=utf-8'); } + + /* */ + + $mtconf = json_decode(WebsiteConfiguration::whereName('MaintenancePassword')->first()->value); + + if(file_exists(storage_path('framework/down')) && $password == $mtconf->password) + { + $btns = array_slice($buttons, -count($mtconf->combination)); + $data = json_decode(file_get_contents(storage_path('framework/down')), true); + + if(isset($data['secret']) && $btns === $mtconf->combination) + { + $trustedHosts = explode(',', env('TRUSTED_HOSTS')); + $origin = parse_url($request->headers->get('origin'), PHP_URL_HOST); + $passCheck = false; + + foreach($trustedHosts as &$host) + { + if(str_ends_with($origin, $host)) + $passCheck = true; + } + + $expiresAt = Carbon::now()->addHours(24); + $bypassCookie = new Cookie('gt_constraint', base64_encode(json_encode([ + 'expires_at' => $expiresAt->getTimestamp(), + 'mac' => hash_hmac('SHA256', $expiresAt->getTimestamp(), $data['secret']), + ])), $expiresAt); + + if($passCheck) + $bypassCookie = $bypassCookie->withDomain('.' . $origin); + + return response('') + ->withCookie($bypassCookie); + } + } + + return response('') + ->setStatusCode(403); } } diff --git a/web/app/Http/Kernel.php b/web/app/Http/Kernel.php index b26462e..3c37488 100644 --- a/web/app/Http/Kernel.php +++ b/web/app/Http/Kernel.php @@ -37,6 +37,7 @@ class Kernel extends HttpKernel \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, + \App\Http\Middleware\PreventRequestsDuringMaintenance::class, ], 'admin' => [ diff --git a/web/app/Http/Middleware/PreventRequestsDuringMaintenance.php b/web/app/Http/Middleware/PreventRequestsDuringMaintenance.php index 444821c..1b7d639 100644 --- a/web/app/Http/Middleware/PreventRequestsDuringMaintenance.php +++ b/web/app/Http/Middleware/PreventRequestsDuringMaintenance.php @@ -21,7 +21,7 @@ class PreventRequestsDuringMaintenance * * @var array */ - protected $except = ['banners/data', 'maintenance/bypass']; + protected $except = ['maintenance', 'maintenance/bypass']; /** * Create a new middleware instance. diff --git a/web/app/Models/AssetGenre.php b/web/app/Models/AssetGenre.php new file mode 100644 index 0000000..7910df3 --- /dev/null +++ b/web/app/Models/AssetGenre.php @@ -0,0 +1,11 @@ + env('MYSQL_ATTR_SSL_CA'), ]) : [], ], + + 'mysql-asset' => [ + 'driver' => 'mysql', + 'url' => env('DATABASE_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_ASSET_DATABASE', 'gtoriadev_primary'), + 'username' => env('DB_USERNAME', 'gtoriadev_primary'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], 'pgsql' => [ 'driver' => 'pgsql', diff --git a/web/database/migrations/2022_04_15_000406_create_asset_types_table.php b/web/database/migrations/2022_04_15_000406_create_asset_types_table.php index 466f7eb..188c56c 100644 --- a/web/database/migrations/2022_04_15_000406_create_asset_types_table.php +++ b/web/database/migrations/2022_04_15_000406_create_asset_types_table.php @@ -6,6 +6,13 @@ use Illuminate\Support\Facades\Schema; class CreateAssetTypesTable extends Migration { + /** + * The database connection that should be used by the migration. + * + * @var string + */ + protected $connection = 'mysql-asset'; + /** * Run the migrations. * @@ -15,6 +22,8 @@ class CreateAssetTypesTable extends Migration { Schema::create('asset_types', function (Blueprint $table) { $table->id(); + $table->longText('name'); + $table->boolean('binaryFormat')->default(false); $table->timestamps(); }); } diff --git a/web/database/migrations/2022_04_15_000413_create_assets_table.php b/web/database/migrations/2022_04_15_000413_create_assets_table.php index ab361cb..ba87aa3 100644 --- a/web/database/migrations/2022_04_15_000413_create_assets_table.php +++ b/web/database/migrations/2022_04_15_000413_create_assets_table.php @@ -6,6 +6,13 @@ use Illuminate\Support\Facades\Schema; class CreateAssetsTable extends Migration { + /** + * The database connection that should be used by the migration. + * + * @var string + */ + protected $connection = 'mysql-asset'; + /** * Run the migrations. * @@ -13,8 +20,19 @@ class CreateAssetsTable extends Migration */ public function up() { + // TODO: relational genres? + // god i fucking hate foreign keys Schema::create('assets', function (Blueprint $table) { $table->id(); + $table->unsignedBigInteger('creatorId'); + $table->longText('title'); + $table->longText('description'); + $table->json('settings'); + $table->boolean('commentsEnabled')->default(true); + $table->boolean('purchasable')->default(false); + $table->unsignedBigInteger('assetVersionId'); + $table->unsignedBigInteger('assetTypeId'); + $table->unsignedBigInteger('assetGenreId'); $table->timestamps(); }); } diff --git a/web/database/migrations/2022_04_15_000429_create_asset_versions_table.php b/web/database/migrations/2022_04_15_000429_create_asset_versions_table.php index 1b53497..1f1b8f1 100644 --- a/web/database/migrations/2022_04_15_000429_create_asset_versions_table.php +++ b/web/database/migrations/2022_04_15_000429_create_asset_versions_table.php @@ -6,6 +6,13 @@ use Illuminate\Support\Facades\Schema; class CreateAssetVersionsTable extends Migration { + /** + * The database connection that should be used by the migration. + * + * @var string + */ + protected $connection = 'mysql-asset'; + /** * Run the migrations. * @@ -15,6 +22,8 @@ class CreateAssetVersionsTable extends Migration { Schema::create('asset_versions', function (Blueprint $table) { $table->id(); + $table->unsignedBigInteger('assetId'); + $table->longText('cdnHash'); $table->timestamps(); }); } diff --git a/web/database/migrations/2022_04_17_030538_create_asset_genres_table.php b/web/database/migrations/2022_04_17_030538_create_asset_genres_table.php new file mode 100644 index 0000000..6d8b672 --- /dev/null +++ b/web/database/migrations/2022_04_17_030538_create_asset_genres_table.php @@ -0,0 +1,33 @@ +id(); + $table->longText('name'); + $table->longText('iconUrl'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('asset_genres'); + } +} diff --git a/web/public/mix-manifest.json b/web/public/mix-manifest.json index 9525ade..e716ec6 100644 --- a/web/public/mix-manifest.json +++ b/web/public/mix-manifest.json @@ -1,4 +1,5 @@ { "/js/app.js": "/js/app.js", + "/js/pages/maintenance.js": "/js/pages/maintenance.js", "/css/graphictoria.css": "/css/graphictoria.css" } diff --git a/web/resources/js/pages/Maintenance.js b/web/resources/js/pages/Maintenance.js new file mode 100644 index 0000000..09f505d --- /dev/null +++ b/web/resources/js/pages/Maintenance.js @@ -0,0 +1,95 @@ +// © XlXi 2021 +// Graphictoria 5 + +import axios from 'axios'; + +import $ from 'jquery'; + +import * as ReactDOM from 'react-dom'; +import React, { useRef, Suspense } from 'react'; + +import { buildGenericApiUrl } from '../util/HTTP.js'; + +import { Canvas, useFrame } from '@react-three/fiber'; +import { Instances, Instance, PerspectiveCamera, useGLTF } from '@react-three/drei'; + +const randomVector = (r) => [r / 2 - Math.random() * r, r / 2 - Math.random() * r, r / 2 - Math.random() * r]; +const randomEuler = () => [Math.random() * Math.PI, Math.random() * Math.PI, Math.random() * Math.PI]; +const randomData = Array.from({ length: 2000 }, (r = 200) => ({ random: Math.random(), position: randomVector(r), rotation: randomEuler() })); + +function Scene() { + const { nodes, materials } = useGLTF('/models/graphictoriapart.glb'); + + return ( + <> + + + + { + randomData.map((props, i) => ( + + )) + } + + + + ); +} + +function Box({ random, ...props }){ + const ref = useRef() + useFrame((state) => { + const t = state.clock.getElapsedTime() + random * 10000 + ref.current.rotation.set(Math.cos(t / 4) / 2, Math.sin(t / 4) / 2, Math.cos(t / 1.5) / 2) + ref.current.position.y = Math.sin(t / 1.5) / 2 + }); + return ( + + + + ) +} + +function Camera({ ...props }){ + const ref = useRef() + useFrame((state) => { + const t = state.clock.getElapsedTime() / 30 + ref.current.position.x = 10 * Math.cos(t); + ref.current.position.y = 4 * Math.sin(t); + ref.current.position.z = 10 * Math.sin(t); + ref.current.lookAt(0, 0, 0); + }); + return ( + + ) +} + +let ButtonHistory = [] + +function attemptBypass() { + axios.post(buildGenericApiUrl('apis', 'v1/maintenance/bypass'), { + 'password': $('#gt_mt_buttons > input').val(), + 'buttons': ButtonHistory.slice(-40) + }) + .then((response) => { + window.location.reload(); + }); +} + +$(document).ready(function() { + ReactDOM.render( + ( + + + + ), + document.getElementsByClassName('gtoria-maintenance-background')[0] + ); + + $('#gt_mt_buttons').on('click', 'button', function() { + let ButtonId = parseInt(this.getAttribute('name').substr(8)); //gt_mtbtnX + + ButtonHistory.push(ButtonId); + attemptBypass(); + }); +}); \ No newline at end of file diff --git a/web/resources/js/util/HTTP.js b/web/resources/js/util/HTTP.js new file mode 100644 index 0000000..45dbe1c --- /dev/null +++ b/web/resources/js/util/HTTP.js @@ -0,0 +1,16 @@ +// © XlXi 2021 +// Graphictoria 5 + +const urlObject = new URL(document.location.href); + +export function getCurrentDomain() { + return urlObject.hostname.replace(/^[^.]+\./g, ''); +}; + +export function getProtocol() { + return urlObject.protocol; +}; + +export function buildGenericApiUrl(subdomain, path) { + return `${getProtocol()}//${subdomain}.${getCurrentDomain()}/${path}`; +}; \ No newline at end of file diff --git a/web/resources/views/maintenance.blade.php b/web/resources/views/maintenance.blade.php new file mode 100644 index 0000000..297af5b --- /dev/null +++ b/web/resources/views/maintenance.blade.php @@ -0,0 +1,25 @@ +@php +$noFooter = true; + +$buttons = str_split('Graphictoria') +@endphp + +@extends('layouts.app') + +@section('page-specific') + +@endsection + +@section('content') +
+
+

Graphictoria is currently under maintenance.

+

Our cyborg team of highly trained code-monkes are working to make Graphictoria better. We'll be back soon!

+
+ + @foreach($buttons as $index => $button) + + @endforeach +
+
+@endsection diff --git a/web/routes/apis.php b/web/routes/apis.php index 4533a11..6fd03a7 100644 --- a/web/routes/apis.php +++ b/web/routes/apis.php @@ -2,12 +2,6 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; -use App\Http\Controllers\AuthController; -use App\Http\Controllers\BannerController; -use App\Http\Controllers\GamesController; -use App\Http\Controllers\UserController; -use App\Http\Controllers\Auth\RegisterController; -use App\Http\Controllers\Controller; // remove this and clean up the code lol use App\Models\User; /* diff --git a/web/routes/web.php b/web/routes/web.php index 678c23b..c032581 100644 --- a/web/routes/web.php +++ b/web/routes/web.php @@ -17,4 +17,8 @@ use Illuminate\Support\Facades\Route; Route::view('/', 'home'); // misc -Route::view('/javascript', 'javascript'); \ No newline at end of file +Route::any('/maintenance', 'MaintenanceController@showPage'); +Route::view('/javascript', 'javascript'); + +// client +Route::get('/asset', 'ContentController@fetchAsset'); diff --git a/web/webpack.mix.js b/web/webpack.mix.js index e99935e..aec3072 100644 --- a/web/webpack.mix.js +++ b/web/webpack.mix.js @@ -14,6 +14,7 @@ require('laravel-mix-banner'); */ mix.js('resources/js/app.js', 'public/js') + .js('resources/js/pages/maintenance.js', 'public/js/pages') .react() .sass('resources/sass/graphictoria.scss', 'public/css') .banner({