From 2f0e1fdc628357d2fc86b4af3c9b38014d50f6ff Mon Sep 17 00:00:00 2001 From: ro Date: Tue, 16 Apr 2024 15:36:57 -0600 Subject: [PATCH] implemented install from scratch activated the fresh install process for setting up a brand new fipamo install --- app/Helpers/StringHelpers.php | 15 +++ .../Controllers/API/InitAPIController.php | 24 ++++ .../Controllers/API/SettingsAPIController.php | 7 + .../Controllers/Front/StartController.php | 9 +- app/Providers/FipamoServiceProvider.php | 5 + app/Services/DocService.php | 11 +- app/Services/InitService.php | 118 ++++++++++++++++ public/assets/css/dash/init.css | 38 +++--- public/assets/scripts/dash/app/Init.js | 9 ++ .../dash/app/controllers/InitController.js | 127 ++++++++++++++++++ resources/views/back/init.blade.php | 17 +++ resources/views/forms/init-fresh.blade.php | 15 +++ resources/views/forms/init-restore.blade.php | 16 +++ routes/api.php | 3 + 14 files changed, 391 insertions(+), 23 deletions(-) create mode 100644 app/Http/Controllers/API/InitAPIController.php create mode 100644 app/Services/InitService.php create mode 100644 public/assets/scripts/dash/app/Init.js create mode 100644 public/assets/scripts/dash/app/controllers/InitController.js create mode 100644 resources/views/back/init.blade.php create mode 100644 resources/views/forms/init-fresh.blade.php create mode 100644 resources/views/forms/init-restore.blade.php diff --git a/app/Helpers/StringHelpers.php b/app/Helpers/StringHelpers.php index d674b2c..b4f84ed 100644 --- a/app/Helpers/StringHelpers.php +++ b/app/Helpers/StringHelpers.php @@ -40,3 +40,18 @@ function safeString($string) ) ); } + +function randomString(int $length) +{ + $alphanum = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + $special = '*&!@%^#$'; + $alphabet = $alphanum . $special; + $random = openssl_random_pseudo_bytes($length); + $alphabet_length = strlen($alphabet); + $string = ''; + for ($i = 0; $i < $length; ++$i) { + $string .= $alphabet[ord($random[$i]) % $alphabet_length]; + } + + return $string; +} diff --git a/app/Http/Controllers/API/InitAPIController.php b/app/Http/Controllers/API/InitAPIController.php new file mode 100644 index 0000000..4cc1059 --- /dev/null +++ b/app/Http/Controllers/API/InitAPIController.php @@ -0,0 +1,24 @@ +init = $initService; + } + + //init stuff + public function setupFresh(Request $request) + { + $result = $this->init->fresh(json_decode($request->getContent())); + return response()->json($result)->header('Content-Type', 'application/json'); + } +} diff --git a/app/Http/Controllers/API/SettingsAPIController.php b/app/Http/Controllers/API/SettingsAPIController.php index 24a73d7..79e8ae7 100644 --- a/app/Http/Controllers/API/SettingsAPIController.php +++ b/app/Http/Controllers/API/SettingsAPIController.php @@ -61,4 +61,11 @@ class SettingsAPIController extends Controller return response()->download('../content/backups/' . $file, $file, ['Content-Type: application/zip']); } } + + //init stuff + public function setupFresh(Request $request) + { + $body = json_decode($request->getContent()); + dd($body); + } } diff --git a/app/Http/Controllers/Front/StartController.php b/app/Http/Controllers/Front/StartController.php index e99b954..a0c5ddf 100644 --- a/app/Http/Controllers/Front/StartController.php +++ b/app/Http/Controllers/Front/StartController.php @@ -6,12 +6,19 @@ use App\Http\Controllers\Controller; class StartController extends Controller { + protected $settings; + public function __construct() { } public function index() { - return response()->file('../public/index.html'); + //check if configs are present + if (file_exists(env('FOLKS_PATH')) && file_exists(env('SETTINGS_PATH'))) { + return response()->file('../public/index.html'); + } else { + return view('back.init', ["status" => false, "title" => "Set Up"]); + } } } diff --git a/app/Providers/FipamoServiceProvider.php b/app/Providers/FipamoServiceProvider.php index 69d3d33..4ee4b58 100644 --- a/app/Providers/FipamoServiceProvider.php +++ b/app/Providers/FipamoServiceProvider.php @@ -16,6 +16,7 @@ use App\Services\RenderService; use App\Services\SortingService; use App\Services\AssetService; use App\Services\MaintenanceService; +use App\Services\InitService; class FipamoServiceProvider extends ServiceProvider { @@ -86,6 +87,10 @@ class FipamoServiceProvider extends ServiceProvider new SettingsService(new DocService(), new ContentService()) ); }); + + $this->app->bind(InitService::class, function ($app) { + return new InitService(new DocService()); + }); } /** diff --git a/app/Services/DocService.php b/app/Services/DocService.php index 1afde60..7e15c1a 100644 --- a/app/Services/DocService.php +++ b/app/Services/DocService.php @@ -51,10 +51,15 @@ class DocService DocTools::writeSettings('../config/settings.json', $settings); } - public static function writeSettings($fileContents) + public static function writeSettings($fileContents, $location = null) { - $fileLocation = env('SETTINGS_PATH'); - $message = []; + if (is_null($location)) { + $fileLocation = env('SETTINGS_PATH'); + } else { + $fileLocation = $location; + } + + $message = []; try { if (!is_file($fileLocation)) { file_put_contents($fileLocation, json_encode($fileContents)); diff --git a/app/Services/InitService.php b/app/Services/InitService.php new file mode 100644 index 0000000..7234ed9 --- /dev/null +++ b/app/Services/InitService.php @@ -0,0 +1,118 @@ +docs = $docService; + } + + private static function validSecret($length) + { + $alphanum = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + $special = '*&!@%^#$'; + $alphabet = $alphanum . $special; + $random = openssl_random_pseudo_bytes($length); + $alphabet_length = strlen($alphabet); + $string = ''; + for ($i = 0; $i < $length; ++$i) { + $string .= $alphabet[ord($random[$i]) % $alphabet_length]; + } + //secret needs to be a valid token + if ($length == 12) { + try { + $secret = Token::create(12, $string, time() + 3600, 'localhost'); + return $string; + } catch (BuildException $e) { + //bad secret, so try agiain + return self::validSecret(12); + } + + if (Token::validate($key, $string)) { + return $string; + } else { + return self::validSecret(12); + } + } + } + + public function fresh($body) + { + //grab template files + $newFolks = json_decode( + file_get_contents('../content/config/init/folks-template.json'), + true + ); + $newSettings = json_decode( + file_get_contents('../content/config/init/settings-template.json'), + true + ); + //get form values + //$body = $request->getParsedBody(); + $handle = $body->new_member_handle; + $email = $body->new_member_email; + $pass = $body->new_member_pass; + $title = $body->new_member_title; + + $now = Carbon::now(); + //setup folks config + $hash = password_hash($pass, PASSWORD_DEFAULT); + $newFolks[0]['id'] = 0; + $newFolks[0]['handle'] = $handle; + $newFolks[0]['email'] = $email; + $newFolks[0]['password'] = $hash; + $newFolks[0]['key'] = password_hash($email, PASSWORD_DEFAULT); + $newFolks[0]['secret'] = self::validSecret(12); + $newFolks[0]['role'] = 'hnic'; + $newFolks[0]['created'] = $now->format("Y-m-d\TH:i:sP"); + $newFolks[0]['updated'] = $now->format("Y-m-d\TH:i:sP"); + //set up settings config + $newSettings['global']['title'] = $title; + + //create index file + $index = [ + 'id' => 1, + 'uuid' => createUUID(), + 'title' => 'FIRST!', + 'imageList' => '/assets/images/global/default-bg.jpg', + 'fileList' => '', + 'path' => 'content/pages/start', + 'layout' => 'index', + 'tags' => 'start, welcome', + 'author' => $handle, + 'created' => $now->format("Y-m-d\TH:i:sP"), + 'updated' => $now->format("Y-m-d\TH:i:sP"), + 'deleted' => 'false', + 'slug' => 'first', + 'menu' => 'false', + 'featured' => 'false', + 'published' => 'true', + 'content' => "# F**k Yes \n\nIf you're seeing this, you're up and running. NICE WORK!\n\nFrom here, feel free to start dropping pages to your heart's content.\n\nFor some tips about using Fipamo, check out the ![docs](https://code.playvicio.us/Are0h/Fipamo/wiki/02-Usage)\n\nAll good? Feel free to edit this page to whatever you want!\n\nYOU'RE THE CAPTAIN NOW.", + ]; + + //once all files created, write down + $this->docs->writeSettings($newSettings, '../content/config/settings.json'); + $this->docs->writeSettings($newFolks, '../content/config/folks.json'); + $this->docs->writeSettings([], '../content/config/tags.json'); + $object = (object) $index; + + $this->docs->writePages( + 'create', + 'start', + '../content/pages/start/index.md', + $this->docs::objectToMD($object) + ); + + $result = ['type' => 'blogInitGood', 'message' => 'Site Created']; + + return $result; + } +} diff --git a/public/assets/css/dash/init.css b/public/assets/css/dash/init.css index da98fcb..f6471c3 100644 --- a/public/assets/css/dash/init.css +++ b/public/assets/css/dash/init.css @@ -1,9 +1,9 @@ /* LOGIN */ section.login, -section[role="password-reset"], -section[role="restore-fresh"], -section[role="restore-backup"] { +section.password-reset, +section.restore-fresh, +section.restore-backup { margin: 15% auto; padding: 10px; width: 500px; @@ -14,7 +14,7 @@ section[role="restore-backup"] { visibility: visible; } -section[role="restore-backup"] { +section.restore-backup { display: none; visibility: hidden; color: var(--white); @@ -47,12 +47,12 @@ section.login form a { /* PASSWORD-RESET */ -section[role="password-reset"] form button { +section.password-reset form button { padding: 10px 5px; width: 82%; } -section[role="password-reset"] form input { +section.password-reset form input { width: 95%; height: 30px; padding: 5px; @@ -61,24 +61,24 @@ section[role="password-reset"] form input { /* SITE RESTORE */ -section[role="restore-fresh"] form button { +section.restore-fresh form button { padding: 10px 5px; width: 82%; } -section[role="restore-fresh"] form input { +section.restore-fresh form input { width: 95%; height: 30px; padding: 5px; margin-bottom: 10px; } -section[role="restore-backup"] form button { +section.restore-backup form button { padding: 10px 5px; width: 82%; } -section[role="restore-backup"] form input { +section.restore-backup form input { width: 95%; height: 30px; padding: 5px; @@ -89,25 +89,25 @@ section[role="restore-backup"] form input { @media only screen and (max-width: 500px) { section.login, - section[role="password-reset"], - section[role="restore-fresh"], - section[role="restore-backup"] { + section.password-reset, + section.restore-fresh, + section.restore-backup { width: 97%; } } @media only screen and (max-width: 375px) { section.login, - section[role="password-reset"], - section[role="restore-fresh"], - section[role="restore-backup"] { + section.password-reset, + section.restore-fresh, + section.restore-backup { grid-template-columns: 1fr; } section.login img, - section[role="password-reset"] img, - section[role="restore-fresh"] img, - section[role="restore-backup"] img { + section.password-reset img, + section.restore-fresh img, + section.restore-backup img { width: 50px; } } diff --git a/public/assets/scripts/dash/app/Init.js b/public/assets/scripts/dash/app/Init.js new file mode 100644 index 0000000..5e03f8d --- /dev/null +++ b/public/assets/scripts/dash/app/Init.js @@ -0,0 +1,9 @@ +import Init from './controllers/InitController.js'; + +document.addEventListener( + 'DOMContentLoaded', + function () { + new Init(); + }, + false +); diff --git a/public/assets/scripts/dash/app/controllers/InitController.js b/public/assets/scripts/dash/app/controllers/InitController.js new file mode 100644 index 0000000..898fb63 --- /dev/null +++ b/public/assets/scripts/dash/app/controllers/InitController.js @@ -0,0 +1,127 @@ +import FipamoAdminAPI from '../../libraries/FipamoAdminAPI.js'; +import Maintenance from './MaintenanceManager.js'; +import DataUitls from '../utils/DataUtils.js'; +import * as DataEvent from '../events/DataEvent.js'; +import Notfications from '../ui/Notifications.js'; +const data = new DataUitls(); +const notify = new Notfications(); + +export default class InitController { + //-------------------------- + // constructor + //-------------------------- + constructor() { + this.processing = false; + this.start(); + } + + //-------------------------- + // methods + //-------------------------- + + //TODO: Move init functions and set up to their own class + start() { + if (document.getElementById('login') || document.querySelector('.site-restore')) { + var options = document.getElementsByClassName('init-option'); + for (let index = 0; index < options.length; index++) { + options[index].addEventListener('click', e => this.handleOptions(e)); + } + if (document.getElementById('login')) { + document + .getElementById('login-btn') + .addEventListener('click', e => this.handleLogin(e)); + } else { + document + .getElementById('init-blog') + .addEventListener('click', e => this.handleSetup(e)); + document + .getElementById('blog-restore') + .addEventListener('click', e => this.handleRestore(e)); + } + } else if (document.getElementById('dash-reset')) { + document + .getElementById('get-secret-btn') + .addEventListener('click', e => this.handleReset(e)); + + document + .getElementById('reset-btn') + .addEventListener('click', e => this.handleReset(e)); + } + } + //-------------------------- + // event handlers + //-------------------------- + + handleSetup(e) { + if (this.processing) return; + let self = this; + e.stopPropagation(); + e.preventDefault(); + let setUpForm = data.formDataToJSON(document.getElementById('init-form')); + let mm = new Maintenance(); + this.processing = true; + mm.create(setUpForm) + .then(response => { + if (response.type === DataEvent.API_INIT_LAME) { + self.processing = false; + e.target.innerHTML = response.message; + } else { + self.processing = false; + e.target.innerHTML = response.message; + setTimeout(() => { + window.location = '/dashboard'; + }, 700); + } + }) + .catch(err => { + self.processing = false; + //notify.alert(err, false); + }); + } + + handleRestore(e) { + if (this.processing) return; + let self = this; + e.stopPropagation(); + e.preventDefault(); + let mm = new Maintenance(); + var form = document.getElementById('init-restore'); + this.processing = true; + mm.restore(form) + .then(response => { + if (response.type === DataEvent.REQUEST_LAME) { + self.processing = false; + e.target.innerHTML = response.message; + } else { + self.processing = false; + e.target.innerHTML = response.message; + setTimeout(() => { + window.location = '/dashboard'; + }, 1500); + } + }) + .catch(err => { + self.processing = false; + e.target.innerHTML = err; + }); + } + handleOptions(e) { + e.stopPropagation(); + e.preventDefault(); + let init = document.querySelector('.restore-fresh'); + let restore = document.querySelector('.restore-backup'); + if (e.target.id === 'init-switch-restore') { + init.style.display = 'none'; + init.style.visibility = 'hidden'; + + restore.style.display = 'grid'; + restore.style.visibility = 'visible'; + } else { + init.style.display = 'grid'; + init.style.visibility = 'visible'; + + restore.style.display = 'none'; + restore.style.visibility = 'hidden'; + } + } +} diff --git a/resources/views/back/init.blade.php b/resources/views/back/init.blade.php new file mode 100644 index 0000000..29b6826 --- /dev/null +++ b/resources/views/back/init.blade.php @@ -0,0 +1,17 @@ +@extends('frame') + +@section('title', 'The Dash | Set Up Fipamo') + + @section('main-content') +
+
+ @include('forms.init-fresh') +
+
+ @include('forms.init-restore') +
+
+ @endsection + @section('scripting') + + @endsection diff --git a/resources/views/forms/init-fresh.blade.php b/resources/views/forms/init-fresh.blade.php new file mode 100644 index 0000000..6f614f3 --- /dev/null +++ b/resources/views/forms/init-fresh.blade.php @@ -0,0 +1,15 @@ +
+ + + +
+
+ + + + + + +

+ +
diff --git a/resources/views/forms/init-restore.blade.php b/resources/views/forms/init-restore.blade.php new file mode 100644 index 0000000..555bd14 --- /dev/null +++ b/resources/views/forms/init-restore.blade.php @@ -0,0 +1,16 @@ +
+ + + +
+
+ +
+ + +
+

+ +

+ +
diff --git a/routes/api.php b/routes/api.php index eb52653..9ddc914 100644 --- a/routes/api.php +++ b/routes/api.php @@ -5,6 +5,7 @@ use App\Http\Controllers\API\AuthAPIController; use App\Http\Controllers\API\PageAPIController; use App\Http\Controllers\API\FileUploadAPIController; use App\Http\Controllers\API\SettingsAPIController; +use App\Http\Controllers\API\InitAPIController; /* |-------------------------------------------------------------------------- @@ -30,3 +31,5 @@ Route::put("/v1/settings/sync", [SettingsAPIController::class, 'sync']); Route::put("/v1/settings/nav-sync", [SettingsAPIController::class, 'navSync']); Route::put("/v1/backup/create", [SettingsAPIController::class, 'createBackup']); Route::get("/v1/backup/download", [SettingsAPIController::class, 'downloadBackup']); +//init +Route::post("/v1/init", [InitAPIController::class, 'setupFresh']);