first
This commit is contained in:
commit
1d23fba328
|
@ -0,0 +1,38 @@
|
|||
<h1 align="center">UNIT3D Community Edition Installer</h1>
|
||||
|
||||
<p align="center">
|
||||
🎉<b>A Big Thanks To All Our <a href="https://github.com/HDInnovations/UNIT3D-Community-Edition/graphs/contributors">Contributors</a> and <a href="https://github.com/sponsors/HDVinnie">Sponsors</a></b>🎉
|
||||
</p>
|
||||
|
||||
<p align="center"><b>NOTE: This only works for a fresh server with nothing on it but a new OS install!</b></p>
|
||||
|
||||
## This Repository
|
||||
Installer for the [UNIT3D-Community-Edition](https://github.com/HDInnovations/UNIT3D-Community-Edition).
|
||||
|
||||
**Officially Supported OS's**
|
||||
- Ubuntu 22.04 LTS (Jammy Jellyfish)
|
||||
- Ubuntu 20.04 LTS (Focal Fossa)
|
||||
|
||||
**Unstable WIP**
|
||||
- Ubuntu 24.04 LTS (Noble Numbat)
|
||||
|
||||
|
||||
**We offer install and tuning services for a small price if not comfortable installing and tuninng server yourself. Otherwise if want to install yurself run commannd below.**
|
||||
|
||||
**To install run the following:** (and follow the instructions. must be a fresh deicated server with nothing on it besides supported OS. Also must have a proper valid domain pointing to your server IP via A RECORD and CNAME for www)
|
||||
```
|
||||
sudo apt -y install git
|
||||
git clone https://github.com/HDInnovations/UNIT3D-Installer.git installer
|
||||
cd installer
|
||||
sudo ./install.sh
|
||||
```
|
||||
|
||||
**NOTE: If you are running UNIT3D-Community-Edition on a non HTTPS instance you MUST change the following configs.**
|
||||
```
|
||||
.env <-- SESSION_SECURE_COOKIE must be set to false
|
||||
config/secure-headers.php <-- HTTP Strict Transport Security must be set to false
|
||||
config/secure-headers.php <-- Content Security Policy must be disabled
|
||||
```
|
||||
|
||||
### Suggestions and/or Bug Reporting
|
||||
We encourage the use of [GitHub Issues](https://github.com/HDInnovations/UNIT3D-INSTALLER/issues/new)!
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
date_default_timezone_set('America/New_York');
|
||||
|
||||
// include the composer autoloader
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
// include our custom helpers
|
||||
require_once __DIR__ . '/src/Helpers/helpers.php';
|
||||
|
||||
$app = new Application();
|
||||
|
||||
// register our commands
|
||||
$app->addCommands([
|
||||
new App\Commands\InstallCommand(),
|
||||
]);
|
||||
|
||||
// run the app
|
||||
try {
|
||||
$app->run();
|
||||
} catch (Exception $e) {
|
||||
die($e->getMessage());
|
||||
}
|
||||
?>
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"chmod": "0755",
|
||||
"directories": [
|
||||
"src"
|
||||
],
|
||||
"files": [
|
||||
""
|
||||
],
|
||||
"finder": [
|
||||
{
|
||||
"name": "*.php",
|
||||
"exclude": [
|
||||
],
|
||||
"in": "vendor"
|
||||
}
|
||||
],
|
||||
"git-version": "package_version",
|
||||
"main": "artisan",
|
||||
"output": "unit3d.phar",
|
||||
"stub": true
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "unit3d/installer",
|
||||
"description": "Installer for UNIT3D",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": ">=8.3",
|
||||
"ext-dom": "*",
|
||||
"symfony/console": "^4.4.49",
|
||||
"symfony/process": "^4.4.44",
|
||||
"symfony/yaml": "^4.4.45",
|
||||
"symfony/var-dumper": "^4.4.47"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "src/"
|
||||
}
|
||||
},
|
||||
"license": "MIT",
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.6.15"
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source tools/colors.sh
|
||||
|
||||
# Detect OS
|
||||
case $(head -n1 /etc/issue | cut -f 1 -d ' ') in
|
||||
Ubuntu) type="ubuntu" ;;
|
||||
*) type='' ;;
|
||||
esac
|
||||
|
||||
# Unable to detect OS Properly
|
||||
# Note: OVH and other providers remove the contents of /etc/issue in their OS templates
|
||||
# so we need to ask the user manually to tell us what the OS is as a Fallback
|
||||
# Ref: https://github.com/ServNX/UNIT3D-INSTALLER/issues/8
|
||||
if [ "$type" = '' ]; then
|
||||
echo -e "\n$Red We was unable to automatically determine your OS! $Color_Off"
|
||||
echo -e "\n$Purple This can happen if you are using an OS template from a provider like OVH amongst others. $Color_Off\n"
|
||||
|
||||
PS3='Please select the # for your OS: '
|
||||
options=("Ubuntu 24.04" "Ubuntu 22.04" "Ubuntu 20.04" "Quit")
|
||||
select opt in "${options[@]}"
|
||||
do
|
||||
case $opt in
|
||||
"Ubuntu 24.04")
|
||||
echo 'Ubuntu 24.04 LTS \n \l' > /etc/issue
|
||||
type='ubuntu'
|
||||
break
|
||||
;;
|
||||
"Ubuntu 22.04")
|
||||
echo 'Ubuntu 22.04 LTS \n \l' > /etc/issue
|
||||
type='ubuntu'
|
||||
break
|
||||
;;
|
||||
"Ubuntu 20.04")
|
||||
echo 'Ubuntu 20.04 LTS \n \l' > /etc/issue
|
||||
type='ubuntu'
|
||||
break
|
||||
;;
|
||||
"Quit")
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo -e "$Red Invalid Option $REPLY $Color_Off"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
if [ -e $type.sh ]; then
|
||||
bash ./$type.sh
|
||||
fi
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="tests/bootstrap.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false">
|
||||
<testsuites>
|
||||
<testsuite name="Application Test Suite">
|
||||
<directory suffix="Test.php">./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">./src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
abstract class BaseTestCase extends TestCase
|
||||
{
|
||||
protected $input;
|
||||
protected $output;
|
||||
protected $io;
|
||||
protected $salt;
|
||||
protected $config;
|
||||
|
||||
public function __construct($name = null, array $data = [], $dataName = '')
|
||||
{
|
||||
parent::__construct($name, $data, $dataName);
|
||||
|
||||
$this->input = new ArgvInput();
|
||||
$this->output = new ConsoleOutput();
|
||||
|
||||
$this->io = new SymfonyStyle($this->input, $this->output);
|
||||
}
|
||||
|
||||
protected function getOsConfig($path)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected function setOsConfig($path, $value)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
<?php
|
||||
|
||||
namespace App\Classes;
|
||||
|
||||
use RecursiveArrayIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
|
||||
class Config
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $values = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->build();
|
||||
}
|
||||
|
||||
public function get($path, $default = null)
|
||||
{
|
||||
$array = $this->values;
|
||||
|
||||
if (!empty($path)) {
|
||||
$keys = explode('.', $path);
|
||||
foreach ($keys as $key) {
|
||||
if (isset($array[$key])) {
|
||||
$array = $array[$key];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
public function set($path, $value)
|
||||
{
|
||||
if (!empty($path)) {
|
||||
$at = &$this->values;
|
||||
$keys = explode('.', $path);
|
||||
|
||||
while (count($keys) > 0) {
|
||||
if (count($keys) === 1) {
|
||||
if (is_array($at)) {
|
||||
$at[array_shift($keys)] = $value;
|
||||
} else {
|
||||
throw new \RuntimeException("Can not set value at this path ($path) because is not array.");
|
||||
}
|
||||
} else {
|
||||
$key = array_shift($keys);
|
||||
|
||||
if (!isset($at[$key])) {
|
||||
$at[$key] = array();
|
||||
}
|
||||
|
||||
$at = &$at[$key];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->values = $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function app($path, $value = null)
|
||||
{
|
||||
$path = "app.$path";
|
||||
return $value ? $this->set($path, $value) : $this->get($path);
|
||||
}
|
||||
|
||||
public function os($path, $value = null)
|
||||
{
|
||||
$path = 'os.' . distname() . '.' . $path;
|
||||
return $value ? $this->set($path, $value) : $this->get($path);
|
||||
}
|
||||
|
||||
public function add($path, array $values)
|
||||
{
|
||||
$get = (array)$this->get($path);
|
||||
$this->set($path, $this->arrayMergeRecursiveDistinct($get, $values));
|
||||
}
|
||||
|
||||
public function have($path)
|
||||
{
|
||||
$keys = explode('.', $path);
|
||||
$array = $this->values;
|
||||
foreach ($keys as $key) {
|
||||
if (isset($array[$key])) {
|
||||
$array = $array[$key];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setValues($values)
|
||||
{
|
||||
$this->values = $values;
|
||||
}
|
||||
|
||||
public function getValues()
|
||||
{
|
||||
return $this->values;
|
||||
}
|
||||
|
||||
public function search($searchKey, $array = null)
|
||||
{
|
||||
if ($array == null) {
|
||||
$array = $this->values;
|
||||
}
|
||||
|
||||
//create a recursive iterator to loop over the array recursively
|
||||
$iter = new RecursiveIteratorIterator(
|
||||
new RecursiveArrayIterator($array),
|
||||
RecursiveIteratorIterator::SELF_FIRST);
|
||||
|
||||
//loop over the iterator
|
||||
foreach ($iter as $key => $value) {
|
||||
//if the key matches our search
|
||||
if ($key === $searchKey) {
|
||||
//add the current key
|
||||
$keys = array($key);
|
||||
//loop up the recursive chain
|
||||
for ($i = $iter->getDepth() - 1; $i >= 0; $i--) {
|
||||
//add each parent key
|
||||
array_unshift($keys, $iter->getSubIterator($i)->key());
|
||||
}
|
||||
//return our output array
|
||||
return array('path' => implode('.', $keys), 'value' => $value);
|
||||
}
|
||||
}
|
||||
|
||||
//return false if not found
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function arrayMergeRecursiveDistinct(array &$array1, array &$array2)
|
||||
{
|
||||
$merged = $array1;
|
||||
|
||||
foreach ($array2 as $key => &$value) {
|
||||
if (is_array($value) && isset ($merged[$key]) && is_array($merged[$key])) {
|
||||
if (is_int($key)) {
|
||||
$merged[] = $this->arrayMergeRecursiveDistinct($merged[$key], $value);
|
||||
} else {
|
||||
$merged[$key] = $this->arrayMergeRecursiveDistinct($merged[$key], $value);
|
||||
}
|
||||
} else {
|
||||
if (is_int($key)) {
|
||||
$merged[] = $value;
|
||||
} else {
|
||||
$merged[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $merged;
|
||||
}
|
||||
|
||||
protected function build()
|
||||
{
|
||||
$directory = __DIR__ . '/../Configs';
|
||||
|
||||
$files = scandir($directory);
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (is_file("$directory/$file")) {
|
||||
$conf = require $directory . "/$file";
|
||||
$this->values[basename($file, '.php')] = $conf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
namespace App\Classes;
|
||||
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Process\Exception\ProcessTimedOutException;
|
||||
use Symfony\Component\Process\InputStream;
|
||||
use Symfony\Component\Process\Process as SymfonyProcess;
|
||||
|
||||
class Process
|
||||
{
|
||||
|
||||
/**
|
||||
* @var SymfonyStyle $io
|
||||
*/
|
||||
private $io;
|
||||
|
||||
/**
|
||||
* @var bool $debug
|
||||
*/
|
||||
private $debug = false;
|
||||
|
||||
/**
|
||||
* Process constructor.
|
||||
* @param SymfonyStyle $io
|
||||
*/
|
||||
public function __construct(SymfonyStyle $io)
|
||||
{
|
||||
$this->io = $io;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a new Process instance
|
||||
*
|
||||
* @param string $cmd
|
||||
* @return string
|
||||
*/
|
||||
public function execute($command, array $input = null, $force = false, $timeout = 3600, $cwd = null, array $env = null)
|
||||
{
|
||||
|
||||
$this->io->writeln("\n<fg=cyan>$command</>");
|
||||
|
||||
$process = new SymfonyProcess($command, $cwd, $env, null, $timeout);
|
||||
$process->setIdleTimeout(900);
|
||||
|
||||
$inputStream = null;
|
||||
if ($input !== null && is_array($input)) {
|
||||
$inputStream = new InputStream();
|
||||
$process->setInput($inputStream);
|
||||
|
||||
!$this->debug ?: $this->io->writeln('[debug] Pty is on');
|
||||
$process->setPty(true);
|
||||
}
|
||||
|
||||
$bar = $this->progressStart();
|
||||
|
||||
$process->start();
|
||||
|
||||
$process->wait(function ($type, $buffer) use ($bar, $input, $inputStream) {
|
||||
!$this->debug ?: $this->io->writeln("[debug] $buffer");
|
||||
|
||||
if ($input !== null && is_array($input)) {
|
||||
|
||||
$last = null;
|
||||
foreach($input as $expect => $send) {
|
||||
if (str_contains($buffer, $expect) && $expect !== $last) {
|
||||
$inputStream->write($send . "\n");
|
||||
$last = $expect;
|
||||
}
|
||||
|
||||
usleep(5000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$bar->advance();
|
||||
usleep(200000);
|
||||
});
|
||||
|
||||
$this->progressStop($bar);
|
||||
$process->stop();
|
||||
|
||||
if (!$process->isSuccessful()) {
|
||||
if (!$force) {
|
||||
$this->io->error($process->getErrorOutput());
|
||||
die();
|
||||
}
|
||||
|
||||
$this->io->writeln("\n<fg=red>[Warning]</> " . $process->getErrorOutput());
|
||||
}
|
||||
|
||||
return $process;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ProgressBar
|
||||
*/
|
||||
protected function progressStart()
|
||||
{
|
||||
$bar = $this->io->createProgressBar();
|
||||
$bar->setBarCharacter('<fg=magenta>=</>');
|
||||
$bar->setFormat('[%bar%] (<fg=cyan>%message%</>)');
|
||||
$bar->setMessage('Please Wait ...');
|
||||
//$bar->setRedrawFrequency(20); todo: may be useful for platforms like CentOS
|
||||
$bar->start();
|
||||
|
||||
return $bar;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $bar
|
||||
*/
|
||||
protected function progressStop(ProgressBar $bar)
|
||||
{
|
||||
$bar->setMessage("<fg=green>Done!</>");
|
||||
$bar->finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $debug
|
||||
*/
|
||||
public function setDebug(bool $debug)
|
||||
{
|
||||
$this->debug = $debug;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
namespace App\Commands;
|
||||
|
||||
use App\Classes\Config;
|
||||
use App\Installer\Database\DatabaseSetup;
|
||||
use App\Installer\Nginx\NginxSetup;
|
||||
use App\Installer\PHP\PhpSetup;
|
||||
use App\Installer\Policies\InstallerPolicies;
|
||||
use App\Installer\Prerequisites\Prerequisites;
|
||||
use App\Installer\Server\ServerSetup;
|
||||
use App\Installer\UNIT3D\Unit3dSetup;
|
||||
use App\Traits\ConsoleTools;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
class InstallCommand extends Command
|
||||
{
|
||||
use ConsoleTools;
|
||||
|
||||
private $steps = [
|
||||
InstallerPolicies::class => 'Validating Installer Policies',
|
||||
ServerSetup::class => 'Server Setup',
|
||||
Prerequisites::class => 'Prerequisites',
|
||||
DatabaseSetup::class => 'Configuring & Securing Database',
|
||||
PhpSetup::class => 'PHP & PHP-FPM Configuration',
|
||||
NginxSetup::class => 'Nginx Setup & Configurations',
|
||||
Unit3dSetup::class => 'UNIT3D-Community-Edition Settings and Configuration',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var SymfonyStyle
|
||||
*/
|
||||
private $io;
|
||||
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
private $config;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('install')
|
||||
->setDescription('Provisions Server')
|
||||
->setHelp('Provisions Server and installs the UNIT3D Platform.');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->io = new SymfonyStyle($input, $output);
|
||||
$this->config = new Config();
|
||||
|
||||
$this->displayIntro();
|
||||
|
||||
foreach ($this->steps as $class => $header) {
|
||||
$this->head($header);
|
||||
|
||||
(new $class($this->io, $this->config))->handle();
|
||||
|
||||
$this->done();
|
||||
}
|
||||
|
||||
$this->head("Finalizing Install");
|
||||
|
||||
$this->info();
|
||||
|
||||
$this->done();
|
||||
}
|
||||
|
||||
private function info()
|
||||
{
|
||||
$db = $this->config->app('db');
|
||||
$dbuser = $this->config->app('dbuser');
|
||||
$dbpass = $this->config->app('dbpass');
|
||||
$domain = $this->config->app('hostname');
|
||||
$owner = $this->config->app('owner');
|
||||
$password = $this->config->app('password');
|
||||
|
||||
$this->io->writeln([
|
||||
'<fg=magenta>Please run "certbot renew --dry-run" manually to test your LetsEncrypt renewal process!!!</>',
|
||||
]);
|
||||
|
||||
$this->io->note([
|
||||
'Database: ' . $db,
|
||||
'Database User: ' . $dbuser,
|
||||
'Database Password: ' . $dbpass
|
||||
]);
|
||||
|
||||
$this->io->writeln([
|
||||
'<fg=green>UNIT3D-Community-Edition has been successfully installed!</>',
|
||||
' ',
|
||||
"Visit <fg=green>$domain</> in a browser",
|
||||
' ',
|
||||
"Login: <fg=green>$owner</>",
|
||||
"Password: <fg=green>$password</>"
|
||||
]);
|
||||
}
|
||||
|
||||
private function done()
|
||||
{
|
||||
$this->io->writeln("<fg=green>[OK] Done!</>");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
use App\Installer\Database\MySqlSetup;
|
||||
|
||||
return [
|
||||
'min_php_version' => '8.2',
|
||||
|
||||
'repository' => 'https://github.com/HDInnovations/UNIT3D-Community-Edition.git',
|
||||
|
||||
'database_installers' => [
|
||||
/**
|
||||
* Map to the Installer class
|
||||
*/
|
||||
'MySql' => MySqlSetup::class,
|
||||
],
|
||||
|
||||
/*
|
||||
* Dynamically set configuration defaults and placeholders
|
||||
*
|
||||
* These do NOT need policy classes
|
||||
*/
|
||||
|
||||
/* Main Server */
|
||||
'server_name' => '',
|
||||
'ip' => '',
|
||||
'hostname' => '',
|
||||
'ssl' => true,
|
||||
'owner' => '',
|
||||
'owner_email' => '',
|
||||
'password' => '',
|
||||
|
||||
/* Database */
|
||||
'database_driver' => 'MySql',
|
||||
|
||||
'db' => '',
|
||||
'dbuser' => '',
|
||||
'dbpass' => '',
|
||||
'dbrootpass' => '',
|
||||
|
||||
/* Mail */
|
||||
'mail_driver' => 'smtp',
|
||||
'mail_host' => '',
|
||||
'mail_port' => '',
|
||||
'mail_username' => '',
|
||||
'mail_password' => '',
|
||||
'mail_from_name' => '',
|
||||
|
||||
/* Chat */
|
||||
'echo-port' => '',
|
||||
|
||||
/* API Keys */
|
||||
'tmdb-key' => '',
|
||||
];
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
* Ubuntu
|
||||
*/
|
||||
'ubuntu' => [
|
||||
'pkg_manager' => 'apt-get',
|
||||
'web-user' => 'www-data',
|
||||
'install_dir' => '/var/www/html',
|
||||
'nginx-sites-available_path' => '/etc/nginx/sites-available',
|
||||
|
||||
'software' => [
|
||||
'build-essential' => 'Basic C/C++ Development Environment',
|
||||
'nginx' => 'Web Server',
|
||||
'mysql-server' => 'Database Server',
|
||||
'supervisor' => 'A Process Control System',
|
||||
'nodejs' => 'JavaScript Run-time Environment (Includes npm)',
|
||||
'git' => 'Version Control',
|
||||
'tmux' => 'Screen Multiplexer',
|
||||
'vim' => 'Text Editor',
|
||||
'wget' => 'Transfer Data From A Server',
|
||||
'zip' => 'Compress Files',
|
||||
'unzip' => 'Decompress Files',
|
||||
'htop' => 'Monitor Server Resources',
|
||||
'cron' => 'Process Scheduling Daemon',
|
||||
],
|
||||
]
|
||||
|
||||
|
||||
];
|
|
@ -0,0 +1,199 @@
|
|||
<?php
|
||||
|
||||
use Symfony\Component\VarDumper\VarDumper;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
if (!function_exists('installed')) {
|
||||
function installed($name)
|
||||
{
|
||||
if (shell_exec("command -v $name") != '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('base_path')) {
|
||||
function base_path($path = '')
|
||||
{
|
||||
return __DIR__ . "/../../$path";
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('resource_path')) {
|
||||
function resource_path($path = '')
|
||||
{
|
||||
return __DIR__ . "/../Resources/$path";
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('str_contains')) {
|
||||
function str_contains($haystack, $needles)
|
||||
{
|
||||
foreach ((array)$needles as $needle) {
|
||||
if ($needle != '' && mb_strpos($haystack, $needle) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('str_random')) {
|
||||
/**
|
||||
* Generate a more truly "random" alpha-numeric string.
|
||||
*
|
||||
* @param int $length
|
||||
* @return string
|
||||
*/
|
||||
function str_random($length = 16)
|
||||
{
|
||||
$string = '';
|
||||
|
||||
while (($len = strlen($string)) < $length) {
|
||||
$size = $length - $len;
|
||||
|
||||
$bytes = random_bytes($size);
|
||||
|
||||
$string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('fqdn')) {
|
||||
function fqdn()
|
||||
{
|
||||
return trim(exec('hostname -f'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('hostname')) {
|
||||
function hostname()
|
||||
{
|
||||
return trim(exec('hostname'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('ip')) {
|
||||
function ip()
|
||||
{
|
||||
return trim(explode(' ', exec('hostname -I'))[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('dd')) {
|
||||
function dd($var)
|
||||
{
|
||||
VarDumper::dump($var);
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('array_find')) {
|
||||
function array_find($array, $searchKey = '')
|
||||
{
|
||||
//create a recursive iterator to loop over the array recursively
|
||||
$iter = new RecursiveIteratorIterator(
|
||||
new RecursiveArrayIterator($array),
|
||||
RecursiveIteratorIterator::SELF_FIRST);
|
||||
|
||||
//loop over the iterator
|
||||
foreach ($iter as $key => $value) {
|
||||
//if the key matches our search
|
||||
if ($key === $searchKey) {
|
||||
//add the current key
|
||||
$keys = array($key);
|
||||
//loop up the recursive chain
|
||||
for ($i = $iter->getDepth() - 1; $i >= 0; $i--) {
|
||||
//add each parent key
|
||||
array_unshift($keys, $iter->getSubIterator($i)->key());
|
||||
}
|
||||
//return our output array
|
||||
return array('path' => implode('.', $keys), 'value' => $value);
|
||||
}
|
||||
}
|
||||
|
||||
//return false if not found
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('distinfo')) {
|
||||
function distinfo()
|
||||
{
|
||||
$distname = strtolower(trim(shell_exec('head -n1 /etc/issue | cut -f 1 -d \' \'')));
|
||||
$distver = trim(shell_exec('head -n1 /etc/issue | cut -f 2 -d \' \''));
|
||||
$lts = (trim(shell_exec('head -n1 /etc/issue | cut -f 3 -d \' \'') === 'LTS'));
|
||||
|
||||
preg_match("/^[0-9]..[0-9]./m", $distver, $matches);
|
||||
$mainver = $matches[0];
|
||||
|
||||
switch ($mainver) {
|
||||
case "24.04":
|
||||
$relname = "(Noble Numbat)";
|
||||
break;
|
||||
case "22.04":
|
||||
$relname = "(Jammy Jellyfish)";
|
||||
break;
|
||||
case "20.04":
|
||||
$relname = "(Focal Fossa)";
|
||||
break;
|
||||
default:
|
||||
$relname = "UNKNOWN";
|
||||
}
|
||||
|
||||
return array(
|
||||
'name' => $distname,
|
||||
'version' => $distver,
|
||||
'mainver' => $mainver,
|
||||
'relname' => $relname,
|
||||
'lts' => $lts
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('distname')) {
|
||||
function distname()
|
||||
{
|
||||
return strtolower(distinfo()['name']);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('distversion')) {
|
||||
function distversion()
|
||||
{
|
||||
return strtolower(distinfo()['version']);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('distmainver')) {
|
||||
function distmainver()
|
||||
{
|
||||
return strtolower(distinfo()['mainver']);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('distrelname')) {
|
||||
function distrelname()
|
||||
{
|
||||
return strtolower(distinfo()['name']);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('distlts')) {
|
||||
function distlts()
|
||||
{
|
||||
return strtolower(distinfo()['lts']);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('memory')) {
|
||||
function memory()
|
||||
{
|
||||
return shell_exec("grep 'MemTotal' /proc/meminfo |tr ' ' '\n' |grep [0-9]") != '';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer;
|
||||
|
||||
use App\Classes\Config;
|
||||
use App\Classes\Process;
|
||||
use App\Traits\ConsoleTools;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
abstract class BaseInstaller
|
||||
{
|
||||
use ConsoleTools;
|
||||
|
||||
/**
|
||||
* @var SymfonyStyle $io
|
||||
*/
|
||||
protected $io;
|
||||
|
||||
/**
|
||||
* @var Process
|
||||
*/
|
||||
protected $process;
|
||||
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var $pkg_manager
|
||||
*/
|
||||
protected $pkg_manager;
|
||||
|
||||
/**
|
||||
* @var int $timeout
|
||||
*/
|
||||
protected $timeout = 15;
|
||||
|
||||
public function __construct(SymfonyStyle $io, Config $config)
|
||||
{
|
||||
$this->io = $io;
|
||||
$this->process = new Process($this->io);
|
||||
$this->config = $config;
|
||||
$this->pkg_manager = $config->get('os.'.distname().'.pkg_manager');
|
||||
}
|
||||
|
||||
abstract public function handle();
|
||||
|
||||
protected function install($pkgs)
|
||||
{
|
||||
$this->process->execute($this->pkg_manager . " install -y $pkgs");
|
||||
}
|
||||
|
||||
protected function process(array $commands, $force = false)
|
||||
{
|
||||
foreach ($commands as $cmd) {
|
||||
$this->process->execute($cmd, null, $force);
|
||||
}
|
||||
}
|
||||
|
||||
protected function createFromStub(array $fr, $stub, $to)
|
||||
{
|
||||
$stub = resource_path() . distname() . '/' . $stub;
|
||||
|
||||
$file = file_get_contents($stub);
|
||||
|
||||
if ($file === false) {
|
||||
$this->throwError("'$stub' error getting file contents. Please report this bug.");
|
||||
}
|
||||
|
||||
$contents = str_replace(array_keys($fr), array_values($fr), $file);
|
||||
|
||||
file_put_contents($to, $contents);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function setTimeout($timeout)
|
||||
{
|
||||
$this->timeout = $timeout;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\Database;
|
||||
|
||||
use App\Installer\BaseInstaller;
|
||||
|
||||
class DatabaseSetup extends BaseInstaller
|
||||
{
|
||||
public function handle()
|
||||
{
|
||||
$driver = $this->config->app('database_driver');
|
||||
$class = $this->config->app('database_installers.' . $driver);
|
||||
|
||||
(new $class($this->io, $this->config))->handle();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\Database;
|
||||
|
||||
use App\Installer\BaseInstaller;
|
||||
|
||||
class MySqlSetup extends BaseInstaller
|
||||
{
|
||||
public function handle()
|
||||
{
|
||||
$root_pass = $this->config->app('dbrootpass');
|
||||
$db = $this->config->app('db');
|
||||
$dbuser = $this->config->app('dbuser');
|
||||
$dbpass = $this->config->app('dbpass');
|
||||
|
||||
switch (true) {
|
||||
case (memory() >= 1200000 && memory() < 3900000):
|
||||
$mycnf = "my-medium.cnf";
|
||||
break;
|
||||
case (memory() >= 3900000):
|
||||
$mycnf = "my-large.cnf";
|
||||
break;
|
||||
default:
|
||||
$mycnf = "my-small.cnf";
|
||||
}
|
||||
|
||||
$this->process(['cp -f ' . resource_path(distname() . "/mysql/$mycnf") . ' /etc/mysql/my.cnf']);
|
||||
|
||||
if (distname() === 'ubuntu') {
|
||||
if ((distmainver() === '20.04' || distmainver() === '22.04' || distmainver() === '24.04') && !is_dir('/var/lib/mysql')) {
|
||||
$this->process([
|
||||
'mkdir /var/lib/mysql',
|
||||
'chown mysql:mysql /var/lib/mysql',
|
||||
'mysqld --initialize-insecure',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->createFromStub(
|
||||
[
|
||||
'{{PASSWORD}}' => $root_pass
|
||||
],
|
||||
'mysql/.my.cnf',
|
||||
'/root/.my.cnf'
|
||||
);
|
||||
|
||||
$this->process([
|
||||
'update-rc.d mysql defaults',
|
||||
'service mysql start',
|
||||
"mysqladmin -u root password $root_pass",
|
||||
'chmod 600 /root/.my.cnf'
|
||||
]);
|
||||
|
||||
/*
|
||||
* Critical
|
||||
*/
|
||||
$this->process([
|
||||
"mysql -e \"DROP USER IF EXISTS $dbuser\"",
|
||||
"mysql -e \"DROP DATABASE IF EXISTS $db\"",
|
||||
"mysql -e \"CREATE DATABASE $db\"",
|
||||
"mysql -e \"CREATE USER '$dbuser'@'localhost' IDENTIFIED BY '$dbpass';\"",
|
||||
"mysql -e \"GRANT ALL PRIVILEGES ON $db . * TO '$dbuser'@'localhost'\"",
|
||||
"mysql -e \"ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '$root_pass'\"",
|
||||
"mysql -e \"DELETE FROM mysql.user WHERE User=''\"",
|
||||
"mysql -e \"DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1')\"",
|
||||
]);
|
||||
|
||||
/*
|
||||
* Non-Critical
|
||||
*/
|
||||
$this->process([
|
||||
"mysql -e \"DROP DATABASE IF EXISTS test\"",
|
||||
"mysql -e \"DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'\"",
|
||||
], true);
|
||||
|
||||
|
||||
$this->process(["mysql -e \"FLUSH PRIVILEGES\""]);
|
||||
|
||||
$this->io->writeln(' ');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\Nginx;
|
||||
|
||||
use App\Installer\BaseInstaller;
|
||||
|
||||
class NginxSetup extends BaseInstaller
|
||||
{
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$default = $this->config->os('nginx-sites-available_path') . '/default';
|
||||
$echo_port = $this->config->app('echo-port');
|
||||
$fqdn = $this->config->app('hostname');
|
||||
$email = $this->config->app('owner_email');
|
||||
$ssl = $this->config->app('ssl');
|
||||
|
||||
if (file_exists($default)) {
|
||||
$this->process(["rm -rf $default"]);
|
||||
}
|
||||
|
||||
$this->createFromStub([
|
||||
'{{FQDN}}' => $fqdn
|
||||
], 'nginx/default.site', $default);
|
||||
|
||||
$this->process([
|
||||
"ufw allow 'Nginx Full'",
|
||||
"ufw delete allow 'Nginx HTTP'",
|
||||
"ufw allow $echo_port",
|
||||
"ufw enable",
|
||||
"systemctl restart nginx"
|
||||
]);
|
||||
|
||||
$this->install('certbot python3-certbot-nginx');
|
||||
|
||||
if ($ssl == 'yes') {
|
||||
$this->process([
|
||||
"certbot --redirect --nginx -n --agree-tos --email=$email -d $fqdn -d www.$fqdn --rsa-key-size 2048",
|
||||
]);
|
||||
}
|
||||
|
||||
$this->io->writeln(' ');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\PHP;
|
||||
|
||||
use App\Installer\BaseInstaller;
|
||||
|
||||
class PhpSetup extends BaseInstaller
|
||||
{
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$fqdn = $this->config->app('hostname');
|
||||
|
||||
$pool = trim(shell_exec('find /etc/php* -type d \( -name "pool.d" -o -name "*fpm.d" \)'));
|
||||
$php_fpm = trim(shell_exec('ls /etc/init.d/php*.*-fpm* |cut -f 4 -d /'));
|
||||
|
||||
$this->createFromStub(
|
||||
[
|
||||
'{{FQDN}}' => $this->config->app('hostname'),
|
||||
'{{WEBUSER}}' => strtolower($this->config->os('web-user')),
|
||||
],
|
||||
'php-fpm/php-fpm.conf',
|
||||
"$pool/$fqdn.conf"
|
||||
);
|
||||
|
||||
//$this->process(["cp -f " . resource_path() . distname() . "/php-fpm/php-fpm.sock $pool/",]);
|
||||
|
||||
if (!is_link("/etc/init.d/php-fpm")) {
|
||||
$this->process(["ln -s /etc/init.d/$php_fpm /etc/init.d/php-fpm > /dev/null 2>&1"]);
|
||||
}
|
||||
|
||||
foreach (explode("\n", trim(shell_exec('find /etc/php* -name php.ini'))) as $file) {
|
||||
$this->process([
|
||||
"sed -i 's/;date.timezone =/date.timezone = UTC/g' $file",
|
||||
"sed -i 's%_open_tag = Off%_open_tag = On%g' $file",
|
||||
"sed -i 's/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=1/g' $file"
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
$this->process([
|
||||
"systemctl restart $php_fpm",
|
||||
"update-rc.d $php_fpm defaults",
|
||||
"service $php_fpm start"
|
||||
]);
|
||||
|
||||
$this->io->writeln(' ');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\Policies;
|
||||
|
||||
class AppKeyExists extends BasePolicy
|
||||
{
|
||||
public function allows($param = null)
|
||||
{
|
||||
if (!array_key_exists('app', $this->config->getValues())) {
|
||||
$this->throwError(
|
||||
"The key 'app' was not found in config!
|
||||
|
||||
Inside the 'Configs' directory there should be a file named 'app.php'
|
||||
with the application configuration."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\Policies;
|
||||
|
||||
class AppNotInstalled extends BasePolicy
|
||||
{
|
||||
public function allows($param = null)
|
||||
{
|
||||
if (is_dir($this->config->os('install_dir') . DIRECTORY_SEPARATOR . 'app')) {
|
||||
$this->throwError('UNIT3D-Community-Edition already installed ... Exiting installer');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\Policies;
|
||||
|
||||
use App\Classes\Config;
|
||||
use App\Traits\ConsoleTools;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
abstract class BasePolicy
|
||||
{
|
||||
use ConsoleTools;
|
||||
|
||||
/**
|
||||
* @var SymfonyStyle
|
||||
*/
|
||||
protected $io;
|
||||
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
public function __construct(SymfonyStyle $io, Config $config)
|
||||
{
|
||||
$this->io = $io;
|
||||
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function handle($param = null)
|
||||
{
|
||||
$this->allows($param);
|
||||
}
|
||||
|
||||
abstract public function allows($param = null);
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\Policies;
|
||||
|
||||
class DatabaseDriverKeyExists extends BasePolicy
|
||||
{
|
||||
public function allows($param = null)
|
||||
{
|
||||
if (!array_key_exists('database_driver', $this->config->get('app'))) {
|
||||
$this->throwError("The key 'database_driver' was not found in 'app' config!
|
||||
|
||||
Please fix this and try again.
|
||||
|
||||
Example: 'database_driver' => 'MySql',"
|
||||
);
|
||||
}
|
||||
|
||||
$driver = $this->config->app('database_driver');
|
||||
|
||||
if (!is_string($driver)) {
|
||||
$this->throwError("The key 'database_driver' is NOT a string in 'app' config!
|
||||
|
||||
'database_driver' should be a string value of the Default database driver.
|
||||
|
||||
Example: 'database_driver' => 'MySql',"
|
||||
);
|
||||
}
|
||||
|
||||
if (!array_key_exists($driver, $this->config->app('database_installers'))) {
|
||||
$this->throwError("The key 'database_driver' is invalid in 'app' config!
|
||||
|
||||
'database_driver' should match a key listed in 'database_installers' array.
|
||||
|
||||
Example: 'database_driver' => 'MySql',
|
||||
|
||||
'database_installers' => [
|
||||
'MySql' => MySqlInstaller::class,
|
||||
],"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\Policies;
|
||||
|
||||
class DatabaseInstallersKeyExists extends BasePolicy
|
||||
{
|
||||
public function allows($param = null)
|
||||
{
|
||||
if (!array_key_exists('database_installers', $this->config->get('app'))) {
|
||||
$this->throwError(
|
||||
"The key 'database_installers' was not found in 'app' config!
|
||||
|
||||
Please fix this and try again.
|
||||
|
||||
Example: 'database_installers' => [
|
||||
'MySql' => MySqlInstaller::class,
|
||||
],"
|
||||
);
|
||||
}
|
||||
|
||||
$database_installers = $this->config->app('database_installers');
|
||||
|
||||
if (!is_array($database_installers)) {
|
||||
$this->throwError(
|
||||
"The key 'database_installers' is NOT an array in 'app' config!
|
||||
|
||||
'database_installers' should be an array of database drivers
|
||||
mapped with their respected installer class.
|
||||
|
||||
Example: 'database_installers' => [
|
||||
'MySql' => MySqlInstaller::class,
|
||||
],"
|
||||
);
|
||||
}
|
||||
|
||||
if (count($database_installers) <= 0) {
|
||||
$this->throwError(
|
||||
"The key 'database_installers' is an empty array in 'app' config!
|
||||
|
||||
'database_installers' should be an array of at least 1 database driver
|
||||
mapped with its respected installer class.
|
||||
|
||||
Example: 'database_installers' => [
|
||||
'MySql' => MySqlInstaller::class,
|
||||
],"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\Policies;
|
||||
|
||||
class InstallDirKeyExists extends BasePolicy
|
||||
{
|
||||
public function allows($param = null)
|
||||
{
|
||||
if (!array_key_exists('install_dir', $this->config->get('os.' . distname()))) {
|
||||
$this->throwError(
|
||||
"The key 'install_dir' was not found in 'app' config!
|
||||
|
||||
Please fix this and try again.
|
||||
|
||||
Example: 'install_dir' => '/var/www/html',"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\Policies;
|
||||
|
||||
class InstallerPolicies extends BasePolicy
|
||||
{
|
||||
private $steps = [
|
||||
/*
|
||||
* Configuration policies
|
||||
*/
|
||||
AppKeyExists::class,
|
||||
InstallDirKeyExists::class,
|
||||
PhpVersionKeyExists::class,
|
||||
DatabaseInstallersKeyExists::class,
|
||||
DatabaseDriverKeyExists::class,
|
||||
|
||||
/*
|
||||
* User and Server State policies
|
||||
*/
|
||||
IsPrivilegedUser::class,
|
||||
AppNotInstalled::class,
|
||||
IsPhpVersionCompat::class
|
||||
];
|
||||
|
||||
public function allows($param = null)
|
||||
{
|
||||
foreach ($this->steps as $class) {
|
||||
(new $class($this->io, $this->config))->allows($param);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\Policies;
|
||||
|
||||
class IsPhpVersionCompat extends BasePolicy
|
||||
{
|
||||
public function allows($param = null)
|
||||
{
|
||||
$phpv = $this->config->app('min_php_version');
|
||||
if (version_compare(PHP_VERSION, $phpv, '<')) {
|
||||
$this->throwError('PHP Version is not compatible with UNIT3D-Community-Edition. Install PHP ' . $phpv . ' or later...');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\Policies;
|
||||
|
||||
class IsPrivilegedUser extends BasePolicy
|
||||
{
|
||||
public function allows($param = null)
|
||||
{
|
||||
$who = trim(shell_exec('whoami'));
|
||||
if ($who !== 'root') {
|
||||
$this->throwError('Must be ran as root or using sudo!');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\Policies;
|
||||
|
||||
class PhpVersionKeyExists extends BasePolicy
|
||||
{
|
||||
public function allows($param = null)
|
||||
{
|
||||
if (!array_key_exists('min_php_version', $this->config->get('app'))) {
|
||||
$this->throwError(
|
||||
"The key 'min_php_version' was not found in 'app' config!
|
||||
|
||||
Please fix this and try again.
|
||||
|
||||
Example: 'min_php_version' => '8.1',"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\Prerequisites;
|
||||
|
||||
use App\Installer\BaseInstaller;
|
||||
|
||||
class Prerequisites extends BaseInstaller
|
||||
{
|
||||
public function handle()
|
||||
{
|
||||
$software = $this->config->os('software');
|
||||
|
||||
$this->io->writeln("<fg=red>!! WARNING !!</> We are preparing to install software on your server. Please review and confirm!\n");
|
||||
$this->seperator();
|
||||
|
||||
foreach ($software as $pkg => $desc) {
|
||||
$this->io->writeln("* <fg=blue>'$pkg':</> <fg=yellow>$desc</>");
|
||||
}
|
||||
$this->seperator();
|
||||
|
||||
if (!$this->io->confirm('Do you wish to continue?', true)) {
|
||||
$this->throwError('Aborted ...');
|
||||
};
|
||||
|
||||
$this->process(['curl -sL https://deb.nodesource.com/setup_20.x | sudo -E bash -']);
|
||||
|
||||
$pkgs = implode(' ', array_keys($software));
|
||||
$this->install($pkgs);
|
||||
|
||||
$this->process(['npm install -g laravel-echo-server']);
|
||||
$this->install('ufw');
|
||||
$this->process(['ufw allow 8443']);
|
||||
|
||||
$this->io->writeln('');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\Server;
|
||||
|
||||
use App\Installer\BaseInstaller;
|
||||
|
||||
class ServerSetup extends BaseInstaller
|
||||
{
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->server();
|
||||
|
||||
$this->user();
|
||||
|
||||
$this->database();
|
||||
|
||||
$this->mail();
|
||||
|
||||
$this->chat();
|
||||
|
||||
$this->apiKeys();
|
||||
}
|
||||
|
||||
protected function server()
|
||||
{
|
||||
$server_name = $this->question('Server Name', hostname());
|
||||
$this->config->app('server_name', trim($server_name));
|
||||
|
||||
do {
|
||||
$hostname = strtolower($this->question('The domain your going to use. ( Example: example.com )', fqdn()));
|
||||
|
||||
$valid = (str_contains($hostname, '.') || $hostname === 'localhost');
|
||||
|
||||
if (!$valid) {
|
||||
$this->warning("Invalid Format:");
|
||||
$this->io->writeln("<fg=blue>Must be a fully qualified domain name. Examples:</>");
|
||||
$this->io->listing([
|
||||
fqdn() . '.com',
|
||||
'server.' . fqdn() . '.com',
|
||||
'example.com',
|
||||
'server.example.com',
|
||||
'localhost'
|
||||
]);
|
||||
}
|
||||
|
||||
} while (!$valid);
|
||||
|
||||
$this->config->app('hostname', trim($hostname));
|
||||
|
||||
$ip = $this->question('Primary IP Address', ip());
|
||||
$this->config->app('ip', trim($ip));
|
||||
|
||||
$ssl = $this->io->choice('Enable SSL (https)', ['yes', 'no'], 'yes');
|
||||
$this->config->app('ssl', $ssl);
|
||||
|
||||
}
|
||||
|
||||
protected function user()
|
||||
{
|
||||
$this->io->writeln('<fg=blue>User Settings</>');
|
||||
$this->seperator();
|
||||
|
||||
$dbowner = $this->question('Owner Username', '');
|
||||
$this->config->app('owner', $dbowner);
|
||||
|
||||
$dbpass = $this->question('Owner Password', '');
|
||||
$this->config->app('password', $dbpass);
|
||||
|
||||
$default = 'admin@' . $this->config->app('hostname');
|
||||
$email = $this->question('Owner Email', $default);
|
||||
$this->config->app('owner_email', trim($email));
|
||||
}
|
||||
|
||||
protected function database()
|
||||
{
|
||||
$this->io->writeln('<fg=blue>Database Settings</>');
|
||||
$this->seperator();
|
||||
|
||||
$driver_choices = array_keys($this->config->app('database_installers'));
|
||||
$default_driver = $this->config->app('database_driver');
|
||||
|
||||
$driver = $this->io->choice('Choose a database driver', $driver_choices, $default_driver);
|
||||
$this->config->app('database_driver', $driver);
|
||||
|
||||
$this->io->writeln('<fg=red>Special Characters Are Not Working At This Time!</>');
|
||||
$db_root_pass = $this->question('DB Server Root Password', '');
|
||||
$this->config->app('dbrootpass', $db_root_pass);
|
||||
|
||||
$db = $this->question('UNIT3D DB Name', 'unit3d');
|
||||
$this->config->app('db', $db);
|
||||
|
||||
$dbuser = $this->question('UNIT3D DB User', 'unit3d');
|
||||
$this->config->app('dbuser', $dbuser);
|
||||
|
||||
$this->io->writeln('<fg=red>Special Characters Are Not Working At This Time!</>');
|
||||
$dbpass = $this->question('UNIT3D DB Password', '');
|
||||
$this->config->app('dbpass', $dbpass);
|
||||
}
|
||||
|
||||
protected function chat()
|
||||
{
|
||||
$this->io->writeln('<fg=blue>Chat Settings</>');
|
||||
$this->seperator();
|
||||
|
||||
$port = $this->question('Chat Listening Port', '8443');
|
||||
$this->config->app('echo-port', $port);
|
||||
}
|
||||
|
||||
protected function apiKeys()
|
||||
{
|
||||
$this->io->writeln('<fg=blue>API Keys</>');
|
||||
$this->seperator();
|
||||
|
||||
$this->io->writeln('<fg=magenta>Obtaining an TMDB Key</>:');
|
||||
$this->io->listing([
|
||||
'Visit <fg=cyan>https://www.themoviedb.org/</>',
|
||||
'Create Free Account',
|
||||
'Visit <fg=cyan>https://www.themoviedb.org/settings/api</>'
|
||||
]);
|
||||
|
||||
$key = $this->question('TMDB Key', '');
|
||||
$this->config->app('tmdb-key', $key);
|
||||
}
|
||||
|
||||
protected function mail()
|
||||
{
|
||||
$this->io->writeln('<fg=blue>Mail Settings</>');
|
||||
$this->io->writeln('(Used for things like invites, registration, ect.)');
|
||||
$this->seperator();
|
||||
|
||||
$this->io->writeln('<fg=blue>/* You will need a provider like Resend. */</>');
|
||||
$this->io->writeln('<fg=cyan>https://resend.com</>');
|
||||
|
||||
$this->io->writeln('Ref: <fg=cyan>https://laravel.com/docs/11.x/mail#introduction</>');
|
||||
|
||||
$value = $this->io->choice('Mail Driver', [
|
||||
"smtp",
|
||||
"sendmail",
|
||||
"mailgun",
|
||||
"mandrill",
|
||||
"ses",
|
||||
"sparkpost",
|
||||
"log",
|
||||
"array"
|
||||
], 'smtp');
|
||||
|
||||
$this->config->app('mail_driver', $value);
|
||||
|
||||
$value = $this->question('Mail Host', '');
|
||||
$this->config->app('mail_host', $value);
|
||||
|
||||
$value = $this->question('Mail Port', '587');
|
||||
$this->config->app('mail_port', $value);
|
||||
|
||||
$value = $this->question('Mail Username', '');
|
||||
$this->config->app('mail_username', $value);
|
||||
|
||||
$value = $this->question('Mail Password', '');
|
||||
$this->config->app('mail_password', $value);
|
||||
|
||||
$value = $this->question('Mail From Name', '');
|
||||
$this->config->app('mail_from_name', $value);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\UNIT3D;
|
||||
|
||||
use App\Installer\BaseInstaller;
|
||||
|
||||
class Unit3dSetup extends BaseInstaller
|
||||
{
|
||||
public function handle()
|
||||
{
|
||||
$this->clone();
|
||||
|
||||
$this->env();
|
||||
|
||||
$this->perms();
|
||||
|
||||
$this->crons();
|
||||
|
||||
$this->setup();
|
||||
|
||||
}
|
||||
|
||||
protected function clone()
|
||||
{
|
||||
$this->io->writeln('<fg=blue>Cloning Source Files</>');
|
||||
$this->seperator();
|
||||
|
||||
$install_dir = $this->config->os('install_dir');
|
||||
$url = $this->config->app('repository');
|
||||
|
||||
if (is_dir($install_dir)) {
|
||||
$this->process(["rm -rf $install_dir"]);
|
||||
}
|
||||
|
||||
$this->process(["git clone $url $install_dir"]);
|
||||
|
||||
if (!is_dir($install_dir)) {
|
||||
$this->throwError('Something went wrong with the cloning process. Please report this bug!');
|
||||
}
|
||||
}
|
||||
|
||||
protected function env()
|
||||
{
|
||||
$this->io->writeln("\n\n<fg=blue>Preparing the '.env' File</>");
|
||||
$this->seperator();
|
||||
|
||||
$install_dir = $this->config->os('install_dir');
|
||||
|
||||
if (file_exists("$install_dir/.env")) {
|
||||
$this->process(["rm $install_dir/.env"]);
|
||||
}
|
||||
|
||||
$this->createFromStub(
|
||||
[
|
||||
'{{PROTOCOL}}' => $this->config->app('ssl') == 'yes' ? 'https' : 'http',
|
||||
'{{FQDN}}' => $this->config->app('hostname'),
|
||||
'{{DBDRIVER}}' => strtolower($this->config->app('database_driver')),
|
||||
'{{DB}}' => $this->config->app('db'),
|
||||
'{{DBUSER}}' => $this->config->app('dbuser'),
|
||||
'{{DBPASS}}' => $this->config->app('dbpass'),
|
||||
'{{OWNER}}' => $this->config->app('owner'),
|
||||
'{{OWNEREMAIL}}' => $this->config->app('owner_email'),
|
||||
'{{OWNERPASSWORD}}' => $this->config->app('password'),
|
||||
'{{TMDBAPIKEY}}' => $this->config->app('tmdb-key'),
|
||||
'{{MAILDRIVER}}' => $this->config->app('mail_driver'),
|
||||
'{{MAILHOST}}' => $this->config->app('mail_host'),
|
||||
'{{MAILPORT}}' => $this->config->app('mail_port'),
|
||||
'{{MAILUSERNAME}}' => $this->config->app('mail_username'),
|
||||
'{{MAILPASSWORD}}' => $this->config->app('mail_password'),
|
||||
'{{MAILFROMNAME}}' => $this->config->app('mail_from_name')
|
||||
],
|
||||
'../.env.stub',
|
||||
"$install_dir/.env"
|
||||
);
|
||||
|
||||
$this->io->writeln('<fg=green>OK</>');
|
||||
}
|
||||
|
||||
protected function perms()
|
||||
{
|
||||
$this->io->writeln("\n<fg=blue>Setting Permissions</>");
|
||||
$this->seperator();
|
||||
|
||||
$install_dir = $this->config->os('install_dir');
|
||||
$web_user = $this->config->os('web-user');
|
||||
|
||||
$this->process([
|
||||
"chown -R $web_user:$web_user /etc/letsencrypt",
|
||||
"chown -R $web_user:$web_user " . dirname($install_dir),
|
||||
"find $install_dir -type d -exec chmod 0775 '{}' + -or -type f -exec chmod 0664 '{}' +",
|
||||
"chmod 750 $install_dir/artisan",
|
||||
"chmod 640 $install_dir/.env"
|
||||
]);
|
||||
}
|
||||
|
||||
protected function setup()
|
||||
{
|
||||
$this->io->writeln("\n\n<fg=blue>Setting Up Web Site</>");
|
||||
$this->seperator();
|
||||
|
||||
$install_dir = $this->config->os('install_dir');
|
||||
$fqdn = $this->config->app('hostname');
|
||||
$web_user = $this->config->os('web-user');
|
||||
$echo_port = $this->config->app('echo-port');
|
||||
$protocol = $this->config->app('ssl') == 'yes' ? 'https' : 'http';
|
||||
|
||||
$this->createFromStub([
|
||||
'{{FQDN}}' => $fqdn,
|
||||
'{{PORT}}' => $echo_port,
|
||||
'{{PROTOCOL}}' => $protocol,
|
||||
], '../laravel-echo-server.stub', '/var/www/html/laravel-echo-server.json');
|
||||
|
||||
$this->process([
|
||||
"chown -R $web_user:$web_user $install_dir/laravel-echo-server.json",
|
||||
]);
|
||||
|
||||
$this->createFromStub([
|
||||
'{{INSTALLDIR}}' => $install_dir,
|
||||
'{{WEBUSER}}' => $web_user,
|
||||
], 'supervisor/app.conf', '/etc/supervisor/conf.d/unit3d.conf');
|
||||
|
||||
$this->process([
|
||||
'supervisorctl reread',
|
||||
'supervisorctl update',
|
||||
'supervisorctl reload'
|
||||
]);
|
||||
|
||||
$www_cmds = [
|
||||
'laravel-echo-server client:add',
|
||||
'composer install -q',
|
||||
'bun install',
|
||||
'bun run build',
|
||||
'php artisan key:generate',
|
||||
'php artisan migrate --seed',
|
||||
'php artisan auto:email-blacklist-update',
|
||||
'php artisan test:email'
|
||||
];
|
||||
|
||||
foreach ($www_cmds as $cmd) {
|
||||
$this->process([
|
||||
"su $web_user -s /bin/bash --command=\"cd $install_dir && $cmd\""
|
||||
], true);
|
||||
}
|
||||
|
||||
$this->io->writeln(' ');
|
||||
}
|
||||
|
||||
protected function crons()
|
||||
{
|
||||
$this->io->writeln("\n\n<fg=blue>Setting Up Crontabs</>");
|
||||
$this->seperator();
|
||||
|
||||
$install_dir = $this->config->os('install_dir');
|
||||
|
||||
$this->process([
|
||||
"(crontab -l ; echo \"* * * * * php $install_dir/artisan schedule:run >> /dev/null 2>&1\") | crontab -"
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Installer\Utilities;
|
||||
|
||||
use App\Installer\BaseInstaller;
|
||||
|
||||
class RestartService extends BaseInstaller
|
||||
{
|
||||
public function handle($target = null, $service = null)
|
||||
{
|
||||
if ($target === null || $service === null) {
|
||||
$this->throwError(
|
||||
"Null Argument supplied in handle method for RestartService::class.
|
||||
|
||||
Expecting string value"
|
||||
);
|
||||
}
|
||||
|
||||
$this->salt->execute($target, 'service.restart', [$service]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
APP_ENV=prod
|
||||
APP_KEY=
|
||||
APP_DEBUG=false
|
||||
APP_URL={{PROTOCOL}}://{{FQDN}}
|
||||
VITE_ECHO_ADDRESS={{PROTOCOL}}://{{FQDN}}:8443
|
||||
|
||||
LOG_CHANNEL=daily
|
||||
LOG_LEVEL=info
|
||||
|
||||
DB_CONNECTION={{DBDRIVER}}
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_DATABASE={{DB}}
|
||||
DB_USERNAME={{DBUSER}}
|
||||
DB_PASSWORD={{DBPASS}}
|
||||
|
||||
BROADCAST_CONNECTION=redis
|
||||
CACHE_STORE=redis
|
||||
QUEUE_CONNECTION=redis
|
||||
|
||||
SESSION_DRIVER=redis
|
||||
SESSION_CONNECTION=session
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_SECURE_COOKIE=true
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_DRIVER={{MAILDRIVER}}
|
||||
MAIL_HOST={{MAILHOST}}
|
||||
MAIL_PORT={{MAILPORT}}
|
||||
MAIL_USERNAME={{MAILUSERNAME}}
|
||||
MAIL_PASSWORD={{MAILPASSWORD}}
|
||||
MAIL_ENCRYPTION=TLS
|
||||
MAIL_FROM_NAME={{MAILFROMNAME}}
|
||||
MAIL_FROM_ADDRESS={{OWNEREMAIL}}
|
||||
|
||||
DEFAULT_OWNER_NAME={{OWNER}}
|
||||
DEFAULT_OWNER_EMAIL={{OWNEREMAIL}}
|
||||
DEFAULT_OWNER_PASSWORD={{OWNERPASSWORD}}
|
||||
|
||||
TMDB_API_KEY={{TMDBAPIKEY}}
|
||||
TWITCH_CLIENT_ID=
|
||||
TWITCH_CLIENT_SECRET=
|
||||
|
||||
TRACKER_KEY=
|
||||
TRACKER_HOST=127.0.0.1
|
||||
TRACKER_PORT=6969
|
|
@ -0,0 +1,14 @@
|
|||
<fg=white>
|
||||
__ __ _ __ ____ ______ _____ ____ ______ _ __ ______ __ _ __ _
|
||||
/ / / // | / // _//_ __/|__ / / __ \ / ____/____ ____ ___ ____ ___ __ __ ____ (_)/ /_ __ __ / ____/____/ /(_)/ /_ (_)____ ____
|
||||
/ / / // |/ / / / / / /_ < / / / /______ / / / __ \ / __ `__ \ / __ `__ \ / / / // __ \ / // __// / / /______ / __/ / __ // // __// // __ \ / __ \
|
||||
/ /_/ // /| /_/ / / / ___/ // /_/ //_____// /___ / /_/ // / / / / // / / / / // /_/ // / / // // /_ / /_/ //_____// /___ / /_/ // // /_ / // /_/ // / / /
|
||||
\____//_/ |_//___/ /_/ /____//_____/ \____/ \____//_/ /_/ /_//_/ /_/ /_/ \__,_//_/ /_//_/ \__/ \__, / /_____/ \__,_//_/ \__//_/ \____//_/ /_/
|
||||
/____/
|
||||
</>
|
||||
|
||||
*-----------------------------------------------*
|
||||
| Copyright: 2017-2024 |
|
||||
| Founder: HDVinnie |
|
||||
| Maintainers: HDVinnie, Roardom & Community |
|
||||
*-----------------------------------------------*
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"authHost": "{{PROTOCOL}}://{{FQDN}}",
|
||||
"authEndpoint": "/broadcasting/auth",
|
||||
"clients": [],
|
||||
"database": "redis",
|
||||
"databaseConfig": {
|
||||
"redis": {},
|
||||
"sqlite": {
|
||||
"databasePath": "/database/laravel-echo-server.sqlite"
|
||||
}
|
||||
},
|
||||
"devMode": false,
|
||||
"host": null,
|
||||
"port": "{{PORT}}",
|
||||
"protocol": "{{PROTOCOL}}",
|
||||
"socketio": {},
|
||||
"sslCertPath": "/etc/letsencrypt/live/{{FQDN}}/cert.pem",
|
||||
"sslKeyPath": "/etc/letsencrypt/live/{{FQDN}}/privkey.pem",
|
||||
"sslCertChainPath": "/etc/letsencrypt/live/{{FQDN}}/fullchain.pem",
|
||||
"sslPassphrase": "",
|
||||
"apiOriginAllow": {
|
||||
"allowCors": false,
|
||||
"allowOrigin": "",
|
||||
"allowMethods": "",
|
||||
"allowHeaders": ""
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
[client]
|
||||
password={{PASSWORD}}
|
|
@ -0,0 +1,40 @@
|
|||
[client]
|
||||
port=3306
|
||||
socket=/var/run/mysqld/mysqld.sock
|
||||
|
||||
[mysqld_safe]
|
||||
socket=/var/run/mysqld/mysqld.sock
|
||||
|
||||
[mysqld]
|
||||
user=mysql
|
||||
pid-file=/var/run/mysqld/mysqld.pid
|
||||
socket=/var/run/mysqld/mysqld.sock
|
||||
port=3306
|
||||
basedir=/usr
|
||||
datadir=/var/lib/mysql
|
||||
tmpdir=/tmp
|
||||
lc-messages-dir=/usr/share/mysql
|
||||
log_error=/var/log/mysql/error.log
|
||||
|
||||
symbolic-links=0
|
||||
|
||||
skip-external-locking
|
||||
key_buffer_size = 256M
|
||||
max_allowed_packet = 32M
|
||||
table_open_cache = 256
|
||||
sort_buffer_size = 1M
|
||||
read_buffer_size = 1M
|
||||
read_rnd_buffer_size = 4M
|
||||
myisam_sort_buffer_size = 64M
|
||||
thread_cache_size = 8
|
||||
|
||||
#innodb_use_native_aio = 0
|
||||
innodb_file_per_table
|
||||
|
||||
max_connections=200
|
||||
max_user_connections=50
|
||||
wait_timeout=10
|
||||
interactive_timeout=50
|
||||
long_query_time=5
|
||||
|
||||
!includedir /etc/mysql/conf.d/
|
|
@ -0,0 +1,40 @@
|
|||
[client]
|
||||
port=3306
|
||||
socket=/var/run/mysqld/mysqld.sock
|
||||
|
||||
[mysqld_safe]
|
||||
socket=/var/run/mysqld/mysqld.sock
|
||||
|
||||
[mysqld]
|
||||
user=mysql
|
||||
pid-file=/var/run/mysqld/mysqld.pid
|
||||
socket=/var/run/mysqld/mysqld.sock
|
||||
port=3306
|
||||
basedir=/usr
|
||||
datadir=/var/lib/mysql
|
||||
tmpdir=/tmp
|
||||
lc-messages-dir=/usr/share/mysql
|
||||
log_error=/var/log/mysql/error.log
|
||||
|
||||
symbolic-links=0
|
||||
|
||||
skip-external-locking
|
||||
key_buffer_size = 16M
|
||||
max_allowed_packet = 16M
|
||||
table_open_cache = 64
|
||||
sort_buffer_size = 512K
|
||||
net_buffer_length = 8K
|
||||
read_buffer_size = 256K
|
||||
read_rnd_buffer_size = 512K
|
||||
myisam_sort_buffer_size = 8M
|
||||
|
||||
#innodb_use_native_aio = 0
|
||||
innodb_file_per_table
|
||||
|
||||
max_connections=70
|
||||
max_user_connections=30
|
||||
wait_timeout=10
|
||||
interactive_timeout=50
|
||||
long_query_time=5
|
||||
|
||||
!includedir /etc/mysql/conf.d/
|
|
@ -0,0 +1,40 @@
|
|||
[client]
|
||||
port=3306
|
||||
socket=/var/run/mysqld/mysqld.sock
|
||||
|
||||
[mysqld_safe]
|
||||
socket=/var/run/mysqld/mysqld.sock
|
||||
|
||||
[mysqld]
|
||||
user=mysql
|
||||
pid-file=/var/run/mysqld/mysqld.pid
|
||||
socket=/var/run/mysqld/mysqld.sock
|
||||
port=3306
|
||||
basedir=/usr
|
||||
datadir=/var/lib/mysql
|
||||
tmpdir=/tmp
|
||||
lc-messages-dir=/usr/share/mysql
|
||||
log_error=/var/log/mysql/error.log
|
||||
|
||||
symbolic-links=0
|
||||
|
||||
skip-external-locking
|
||||
key_buffer_size = 16K
|
||||
max_allowed_packet = 1M
|
||||
table_open_cache = 4
|
||||
sort_buffer_size = 64K
|
||||
read_buffer_size = 256K
|
||||
read_rnd_buffer_size = 256K
|
||||
net_buffer_length = 2K
|
||||
thread_stack = 240K
|
||||
|
||||
#innodb_use_native_aio = 0
|
||||
innodb_file_per_table
|
||||
|
||||
max_connections=30
|
||||
max_user_connections=20
|
||||
wait_timeout=10
|
||||
interactive_timeout=50
|
||||
long_query_time=5
|
||||
|
||||
!includedir /etc/mysql/conf.d/
|
|
@ -0,0 +1,37 @@
|
|||
server {
|
||||
listen 80 default_server;
|
||||
|
||||
root /var/www/html/public;
|
||||
|
||||
index index.php;
|
||||
|
||||
server_name {{FQDN}} www.{{FQDN}};
|
||||
|
||||
location ~* \.(jpg|jpeg|png|gif|ico|svg)$ {
|
||||
expires 365d;
|
||||
}
|
||||
|
||||
location ~* \.(gif|png|jpg|jpeg|svg|css|js|ico)$ {
|
||||
valid_referers none blocked {{FQDN}} www.{{FQDN}};
|
||||
if ($invalid_referer) {
|
||||
return 403;
|
||||
}
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
include snippets/fastcgi-php.conf;
|
||||
fastcgi_pass unix:/var/run/php/{{FQDN}}.sock;
|
||||
}
|
||||
|
||||
location /index.php {
|
||||
return 301 {{FQDN}};
|
||||
}
|
||||
|
||||
location ~* ^.*(\.(?:git|svn|htaccess|github))$ {
|
||||
return 403;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
[{{FQDN}}]
|
||||
|
||||
listen = /var/run/php/{{FQDN}}.sock
|
||||
listen.backlog = 511
|
||||
listen.owner = {{WEBUSER}}
|
||||
listen.group = {{WEBUSER}}
|
||||
listen.mode=0660
|
||||
|
||||
user = {{WEBUSER}}
|
||||
group = {{WEBUSER}}
|
||||
|
||||
pm = ondemand
|
||||
pm.max_children = 10
|
||||
pm.process_idle_timeout = 10
|
||||
pm.max_requests = 0
|
||||
chdir = /
|
|
@ -0,0 +1,20 @@
|
|||
[program:unit3d-queue]
|
||||
process_name=%(program_name)s_%(process_num)02d
|
||||
command=php {{INSTALLDIR}}/artisan queue:work --tries=1 --max-jobs=1000 --max-time=3600
|
||||
startsecs = 0
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stopasgroup=true
|
||||
killasgroup=true
|
||||
user=www-data
|
||||
numprocs=4
|
||||
redirect_stderr=true
|
||||
stopwaitsecs=3600
|
||||
|
||||
[program:unit3d-chat-server]
|
||||
process_name=%(program_name)s_%(process_num)02d
|
||||
command=/usr/bin/node /usr/bin/laravel-echo-server start --dir={{INSTALLDIR}}
|
||||
autostart=true
|
||||
autorestart=true
|
||||
user={{WEBUSER}}
|
||||
numprocs=1
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
trait ConsoleTools
|
||||
{
|
||||
|
||||
/**
|
||||
* Formats a warning output
|
||||
*
|
||||
* @param string $msg
|
||||
*/
|
||||
protected function warning($msg)
|
||||
{
|
||||
$this->io->writeln("<bg=white;fg=yellow>[Warning] $msg</>\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the installer error and exits the installer
|
||||
*
|
||||
* @param array|string $error
|
||||
*/
|
||||
protected function throwError($error = 'Unknown Error ...')
|
||||
{
|
||||
$this->io->writeln("<fg=red>$error</>");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
protected function dump($var)
|
||||
{
|
||||
$this->io->writeln('<fg=red>---VAR DUMP---');
|
||||
var_dump($var);
|
||||
$this->io->writeln('--------------</>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a the intro
|
||||
*/
|
||||
protected function displayIntro()
|
||||
{
|
||||
$stub = file_get_contents(__DIR__ . '/../Resources/intro.stub');
|
||||
|
||||
$this->io->text($stub);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a seperator
|
||||
*/
|
||||
protected function seperator()
|
||||
{
|
||||
$this->io->writeln(str_repeat('=', 80));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a header
|
||||
*
|
||||
* @param string $text
|
||||
*/
|
||||
protected function head($text)
|
||||
{
|
||||
if ($text !== null) {
|
||||
$this->io->writeln("\n<fg=blue>" . str_repeat('=', 80));
|
||||
$this->io->writeln(' ' . $text . str_repeat(' ', (76 - strlen($text))));
|
||||
$this->io->writeln(str_repeat('=', 80) . "</>\n");
|
||||
}
|
||||
}
|
||||
|
||||
protected function success()
|
||||
{
|
||||
$this->io->writeln("\n<fg=white;bg=green>[OK] Done!" . str_repeat(' ', 70) . "</>");
|
||||
}
|
||||
|
||||
protected function question($question, $default = '')
|
||||
{
|
||||
do {
|
||||
$answer = $this->io->ask($question, $default);
|
||||
|
||||
$valid = ($answer !== '' && strpos($answer, ' ') === false);
|
||||
|
||||
if (!$valid) {
|
||||
$this->warning('Cannot be empty or contain spaces!');
|
||||
}
|
||||
|
||||
} while (!$valid);
|
||||
|
||||
return trim($answer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace App\Tests;
|
||||
|
||||
use App\BaseTestCase;
|
||||
|
||||
class MasterBootstrapperTest extends BaseTestCase
|
||||
{
|
||||
/** @test */
|
||||
public function it_installed()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
// include the composer autoloader
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
// include our custom helpers
|
||||
require_once __DIR__ . '/../src/Helpers/helpers.php';
|
|
@ -0,0 +1,74 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Reset
|
||||
Color_Off='\033[0m' # Text Reset
|
||||
|
||||
# Regular Colors
|
||||
Black='\033[0;30m' # Black
|
||||
Red='\033[0;31m' # Red
|
||||
Green='\033[0;32m' # Green
|
||||
Yellow='\033[0;33m' # Yellow
|
||||
Blue='\033[0;34m' # Blue
|
||||
Purple='\033[0;35m' # Purple
|
||||
Cyan='\033[0;36m' # Cyan
|
||||
White='\033[0;37m' # White
|
||||
|
||||
# Bold
|
||||
BBlack='\033[1;30m' # Black
|
||||
BRed='\033[1;31m' # Red
|
||||
BGreen='\033[1;32m' # Green
|
||||
BYellow='\033[1;33m' # Yellow
|
||||
BBlue='\033[1;34m' # Blue
|
||||
BPurple='\033[1;35m' # Purple
|
||||
BCyan='\033[1;36m' # Cyan
|
||||
BWhite='\033[1;37m' # White
|
||||
|
||||
# Underline
|
||||
UBlack='\033[4;30m' # Black
|
||||
URed='\033[4;31m' # Red
|
||||
UGreen='\033[4;32m' # Green
|
||||
UYellow='\033[4;33m' # Yellow
|
||||
UBlue='\033[4;34m' # Blue
|
||||
UPurple='\033[4;35m' # Purple
|
||||
UCyan='\033[4;36m' # Cyan
|
||||
UWhite='\033[4;37m' # White
|
||||
|
||||
# Background
|
||||
On_Black='\033[40m' # Black
|
||||
On_Red='\033[41m' # Red
|
||||
On_Green='\033[42m' # Green
|
||||
On_Yellow='\033[43m' # Yellow
|
||||
On_Blue='\033[44m' # Blue
|
||||
On_Purple='\033[45m' # Purple
|
||||
On_Cyan='\033[46m' # Cyan
|
||||
On_White='\033[47m' # White
|
||||
|
||||
# High Intensity
|
||||
IBlack='\033[0;90m' # Black
|
||||
IRed='\033[0;91m' # Red
|
||||
IGreen='\033[0;92m' # Green
|
||||
IYellow='\033[0;93m' # Yellow
|
||||
IBlue='\033[0;94m' # Blue
|
||||
IPurple='\033[0;95m' # Purple
|
||||
ICyan='\033[0;96m' # Cyan
|
||||
IWhite='\033[0;97m' # White
|
||||
|
||||
# Bold High Intensity
|
||||
BIBlack='\033[1;90m' # Black
|
||||
BIRed='\033[1;91m' # Red
|
||||
BIGreen='\033[1;92m' # Green
|
||||
BIYellow='\033[1;93m' # Yellow
|
||||
BIBlue='\033[1;94m' # Blue
|
||||
BIPurple='\033[1;95m' # Purple
|
||||
BICyan='\033[1;96m' # Cyan
|
||||
BIWhite='\033[1;97m' # White
|
||||
|
||||
# High Intensity backgrounds
|
||||
On_IBlack='\033[0;100m' # Black
|
||||
On_IRed='\033[0;101m' # Red
|
||||
On_IGreen='\033[0;102m' # Green
|
||||
On_IYellow='\033[0;103m' # Yellow
|
||||
On_IBlue='\033[0;104m' # Blue
|
||||
On_IPurple='\033[0;105m' # Purple
|
||||
On_ICyan='\033[0;106m' # Cyan
|
||||
On_IWhite='\033[0;107m' # White
|
|
@ -0,0 +1,138 @@
|
|||
#!/usr/bin/env bash
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
export DEBCONF_NOWARNINGS=yes
|
||||
|
||||
source tools/colors.sh
|
||||
|
||||
rm -rf /var/lib/dpkg/lock
|
||||
rm -rf /var/cache/debconf/*.*
|
||||
|
||||
echo -e "\n\n$Purple Preparing Environment For The Installer ... $Color_Off"
|
||||
echo "============================================="
|
||||
|
||||
check_locale() {
|
||||
|
||||
echo -e "\n$Cyan Setting UTF8 ...$Color_Off"
|
||||
|
||||
apt-get -qq update
|
||||
apt-get install -qq apt-utils language-pack-en-base > /dev/null
|
||||
export LC_ALL=en_US.UTF-8
|
||||
export LANG=en_US.UTF-8
|
||||
apt-get install -qq software-properties-common > /dev/null
|
||||
|
||||
echo -e "$IGreen OK $Color_Off"
|
||||
}
|
||||
|
||||
# Adds PPA's
|
||||
add_ppa() {
|
||||
echo -e "\n$Cyan Adding PPA Repositories ... $Color_Off"
|
||||
|
||||
for ppa in "$@"; do
|
||||
add-apt-repository -y $ppa > /dev/null 2>&1
|
||||
check $? "Adding $ppa Failed!"
|
||||
done
|
||||
|
||||
echo -e "$IGreen OK $Color_Off"
|
||||
}
|
||||
|
||||
# Installs Environment Prerequisites
|
||||
add_pkgs() {
|
||||
# Update apt
|
||||
echo -e "\n$Cyan Updating Packages ... $Color_Off"
|
||||
|
||||
apt-get -qq update > /dev/null
|
||||
check $? "Updating packages Failed!"
|
||||
|
||||
echo -e "$IGreen OK $Color_Off"
|
||||
|
||||
# PHP
|
||||
echo -e "\n$Cyan Installing PHP ... $Color_Off"
|
||||
|
||||
apt-get -qq install curl php-pear php8.3-common php8.3-cli php8.3-fpm php8.3-{redis,bcmath,curl,dev,gd,igbinary,intl,mbstring,mysql,opcache,readline,xml,zip} > /dev/null
|
||||
check $? "Installing PHP Failed!"
|
||||
|
||||
echo -e "$IGreen OK $Color_Off"
|
||||
|
||||
# Redis
|
||||
echo -e "\n$Cyan Installing Redis ... $Color_Off"
|
||||
|
||||
curl -fsSL https://packages.redis.io/gpg | sudo gpg --yes --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
|
||||
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list > /dev/null
|
||||
apt-get -qq update > /dev/null
|
||||
apt-get -qq install redis > /dev/null
|
||||
|
||||
echo -e "$IGreen OK $Color_Off"
|
||||
|
||||
# Symlink Redis and Enable
|
||||
echo -e "\n$Cyan Symlink and Enabling Redis ... $Color_Off"
|
||||
|
||||
systemctl -q enable --now redis-server
|
||||
systemctl is-active --quiet redis-server && echo -e "$IGreen OK $Color_Off"||echo -e "$IRed NOK $Color_Off"
|
||||
|
||||
# PHP Redis
|
||||
echo -e "\n$Cyan Installing PHP Redis ... $Color_Off"
|
||||
|
||||
printf "\n" | pecl install redis > /dev/null
|
||||
|
||||
echo -e "$IGreen OK $Color_Off"
|
||||
|
||||
# Update Dependencies
|
||||
echo -e "\n$Cyan Updating Dependencies ... $Color_Off"
|
||||
|
||||
apt-get -qq upgrade > /dev/null
|
||||
|
||||
echo -e "$IGreen OK $Color_Off"
|
||||
|
||||
# Bun
|
||||
echo -e "\n$Cyan Installing Bun ... $Color_Off"
|
||||
|
||||
apt-get -qq install unzip > /dev/null
|
||||
curl -fsSL https://bun.sh/install | bash >/dev/null 2>&1
|
||||
mv /root/.bun/bin/bun /usr/local/bin/
|
||||
chmod a+x /usr/local/bin/bun
|
||||
. ~/.bashrc
|
||||
|
||||
echo -e "$IGreen OK $Color_Off"
|
||||
}
|
||||
|
||||
# Installs Composer
|
||||
install_composer() {
|
||||
echo -e "\n$Cyan Installing Composer ... $Color_Off"
|
||||
|
||||
php -r "readfile('http://getcomposer.org/installer');" | sudo php -- --install-dir=/usr/bin/ --filename=composer > /dev/null
|
||||
check $? "Installing Composer Failed!"
|
||||
|
||||
echo -e "$IGreen OK $Color_Off"
|
||||
}
|
||||
|
||||
# Adds installer packages
|
||||
installer_pkgs() {
|
||||
echo -e "\n$Cyan Adding Installer Packages ... $Color_Off"
|
||||
|
||||
composer install -qq > /dev/null 2>&1
|
||||
check $? "Adding Installer Packages Failed!"
|
||||
|
||||
echo -e "$IGreen OK $Color_Off"
|
||||
}
|
||||
|
||||
# Checks the returned code
|
||||
check() {
|
||||
if [ $1 -ne 0 ]; then
|
||||
echo -e "$Red Error: $2 \n Please try re-running the script via 'sudo ./install.sh' $Color_Off"
|
||||
exit $1
|
||||
fi
|
||||
}
|
||||
|
||||
check_locale
|
||||
|
||||
add_ppa ppa:ondrej/php
|
||||
|
||||
add_pkgs
|
||||
|
||||
install_composer
|
||||
|
||||
installer_pkgs
|
||||
|
||||
echo -e "\n$Purple Launching The Installer ... $Color_Off"
|
||||
echo "============================================="
|
||||
php artisan install
|
Loading…
Reference in New Issue