EasyEngine

Habilitar acceso externo a mysql

Editar el fichero /opt/easyengine/services/docker-compose.yml

En el bloque de “global-db:”
Añadir ( teniendo en cuenta las tabulaciones o dará error el compose)
ports:
- “3306:3306”

Esto mapeará el puerto 3306 del docker de mysql al puerto 3306 de la maquina principal. Por defecto ya permite la conexión desde cualquier host y no hay que tocar ninguna configuración especial de mysql ni nada.

Rehacer docker con el siguiente comando dentro de esa misma carpeta
docker-compose up -d


Para activar las admin tools y poder acceder a phpmyadmin y demás herramientas hay que utilizar el siguiente comando

ee admin-tools enable example.com

Las credenciales de acceso serán las indicadas en el sitio que pueden visualizarse con

ee site info example.com

Todo tiene una autenticación previa por htpasswd para dar mayor seguridad y cuyas credenciales pueden visualizarse ejecutando

ee auth list global

Recaptcha v2 y Livewire

Para poner en livewire un recaptcha v2, se puede hacer de dos formas.

Poniendo directamente el texto:

<div id="captcha" class="mt-4" wire:ignore></div>

                                @error('captcha')
                                    <p class="mt-3 text-sm text-red-600 text-left">
                                        {{ $message }}
                                    </p>
                                @enderror
                            
                            <script src="https://www.google.com/recaptcha/api.js?onload=handle&render=explicit"
                                async
                                defer>
                            </script>
                            
                            <script>
                                var  handle = function(e) {
                                    widget = grecaptcha.render('captcha', {
                                        'sitekey': '{{ env('RECAPTCHAV2_SITEKEY') }}',
                                        'theme': 'light', // you could switch between dark and light mode.
                                        'callback': verify
                                    });
                            
                                }
                                var verify = function (response) {
                                    @this.set('captcha', response)
                                }
                            </script>Copiando directamente el código completo 

Si hubiera problemas porque hay «modals raros» habría que utilizar esta segunda versión, ya que hay que cargar el script de una manera especial. El div como tal es lo mismo pero cambia el javascript

En la vista:

   <script>
        var  handle = function(e) {
            widget = grecaptcha.render('captcha', {
                'sitekey': '{{ env('RECAPTCHAV2_SITEKEY') }}',
                'theme': 'light', // you could switch between dark and light mode.
                'callback': verify
            });

        }
        var verify = function (response) {
            @this.set('captcha', response)
        }
    </script>
    <script>

        document.addEventListener('DOMContentLoaded', () => {
            
            {{-- Para abrir el modal de alerta de precios --}}
            var myModal_alerta = new bootstrap.Modal(document.getElementById('modal_alerta_precio'));

            window.addEventListener('abrir_modal_alerta_precio', event => {
                var script = document.createElement('script');
                script.src = 'https://www.google.com/recaptcha/api.js?onload=handle&render=explicit';
                document.body.appendChild(script);
                myModal_alerta.show();
            });
            window.addEventListener('cerrar_modal_alerta_precio', event => {
                myModal_alerta.hide();
                var alerta_precio = new bootstrap.Modal('#aviso_alerta_precio');
                alerta_precio.show();

            });
            {{-- /Para abrir el modal de alerta de precios --}}
        });

Y luego en el componente

public $captcha = null;
 
public function updatedCaptcha($token)
{
    $response = Http::post(
        'https://www.google.com/recaptcha/api/siteverify?secret='.
        env('CAPTCHA_SECRET_KEY').
        '&response='.$token
    );
 
    $success = $response->json()['success'];
 
    if (! $success) {
        throw ValidationException::withMessages([
            'captcha' => __('Google thinks, you are a bot, please refresh and try again!'),
        ]);
    } else {
        $this->captcha = true;
    }
}
 
// validate the captcha rule
protected function rules()
{
    return [
        'captcha' => ['required'],
        // ...
    ];
}

Cogido de referencia de la web: https://coderflex.com/blog/enhancing-security-and-user-experience-leveraging-recaptcha-with-laravel-and-livewire#step-9-add-google-recaptcha

Instalar certificado intermedio SSL

Para instalar un certificado intermedio simplemente hay que fusionar la clave pública con la clave intermedia proporcionada por el proveedor de SSL.

En globalsign es esta URL para sacar el certificado.
https://support.globalsign.com/ca-certificates/intermediate-certificates/organizationssl-intermediate-certificates

Simplemente se combinan ambos en un sólo fichero crt con el comando

cat clavepublic.crt claveintermedia.crt > certificado_generado_a_instalar.crt

Al hacer esto dentro del fichero certificado_generado_a_instalar.crt están uno detras del otro (eso es bien).

Si estamos utilizando por ejemplo Laravel Forge, copia la clave privada en el textarea de Private Key, y copiamos el crt generado en el textarea de Certificate.

Oracle S3 en Laravel

Para usar el «S3» de Oracle, hay que crear al igual que en Amazon una clave y secret para indicarla en el .env.

Hay que tener en cuenta 3 cosas:

  1. Hay que poner a true el AWS_USE_PATH_STYLE_ENDPOINT.
  2. Hay que personalizar el AWS_URL con el ENDPOINT + el NOMBREDELBUCKET
  3. La región podemos cogerla del OCID.

Toda esta información se localiza fácilmente en la pantalla del Bucket.

Así quedaría un .env operativo para Oracle Cloud

AWS_ACCESS_KEY_ID=386***************************

AWS_SECRET_ACCESS_KEY=7CG**********************************************

AWS_DEFAULT_REGION=eu-paris-1

AWS_BUCKET=NOMBREDELBUCKET

AWS_ENDPOINT=https://NOMBREDELNAMESPACE.compat.objectstorage.eu-paris-1.oraclecloud.com

AWS_URL=https://NOMBREDELNAMESPACE.compat.objectstorage.eu-paris-1.oraclecloud.com/NOMBREDELBUCKET/

AWS_USE_PATH_STYLE_ENDPOINT=true

Amazon S3 en Laravel

Para empezar a utilizar S3 de Amazon en Laravel es muy fácil.

Lo primero es crear un usuario en el apartado de IAM con permisos de s3full

Después creamos unas credenciales para acceder desde fuera y el KEY y el SECRET es lo que tenemos que poner en el .env

OJO hay que poner en el .env también el bucket y la region (ojo a esto).

Por ejemplo:

AWS_ACCESS_KEY_ID=A**************
AWS_SECRET_ACCESS_KEY=z****************************
AWS_DEFAULT_REGION=eu-west-1
AWS_BUCKET=exports-tentless-chaps-nerine ( el nombre de tu bucket )
AWS_USE_PATH_STYLE_ENDPOINT=false

Muy importante, por defecto aunque se ponga a público el bucket, cada vez que se sube un objeto por defecto se pone privado, hay que meter esta política específicamente para que cada vez que se haga una subida por defecto el fichero/foto se muestre sin problemas usando el Storage::disk(‘s3’)->url($row->imagen).

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "ARNDELBUCKET QUE SE VE EN LA PANTALLA DE AWA ARRIBA/*"
        }
    ]
}

Certificados nginx client

Para nosotros

  1. Generamos la key
    openssl genrsa -des3 -out ca.key 4096
  2. Creamos nuestro certificado CRT con esa llave. Tendremos que responder varias preguntas que deben ser las mismas que si en un futuro lo queremos renovar (la renovación es la generación en sí de uno nuevo)
    Generar
    openssl req -new -x509 -days 365 -key ca.key -out ca.crt

    Ver información introducida
    openssl x509 -in ca.crt -noout -text

Para cada cliente

  1. Generamos la key para el cliente
    openssl genrsa -des3 -out ayuntamientoquesea.key 4096
  2. Generamos su certificado CSR. Hay que responder varias preguntas; responde a cada una, incluido el nombre común (CN) y la dirección de correo electrónico.
    openssl req -new -key ayuntamientoquesea.key -out ayuntamientoquesea.csr
  3. Firmar el CSR con nuestro certificado y llave. Por lo general, hay que incrementar el número de serie con cada firma. Una vez que el certificado caduca, no es necesario volver a crear una nueva CSR; se puede firmar el mismo, lo que creará un nuevo certificado vinculado a esa clave pública. Si queremos 14 meses serían 410 días.
    openssl x509 -req -days 365-in ayuntamientoquesea.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out ayuntamientoquesea.crt
  4. Crear el PFX que pueda importar el usuario en el navegador
    openssl pkcs12 -export -out ayuntamientoquesea.pfx -inkey ayuntamientoquesea.key -in ayuntamientoquesea.crt -certfile ca.crt

Configurar NGINX para DOBLE FACTOR

Las líneas básicas para activar el doble factor deben aplicar en cada fichero de cada web del sites_availables, indicando la ruta exacta de donde está ubicado el certificado propio nuestro.

Si estamos usando HestiaCP hay que tocar las plantillas y configurar el sitio para que use esta plantilla. Normalmente lo que se suele hacer es clonar la plantilla que estamos usando (por ejemplo laravel) y a esa plantilla clonada meterle estas líneas. Las plantillas en hestia se encuentran en

cd /usr/local/hestia/data/templates/web/nginx/php-fpm/

Las líneas son las siguientes, a completar con lo indicado en el ENS

ssl_client_certificate /etc/nginx/innova/ca.crt;
ssl_verify_client on;

El nombre del ayuntamiento hay que meterlo en Section

El nombre de swpolicial en «Unidad Organizativa Software«

En el «asunto» que es «Company» hay que poner el «nombre del ayuntamiento – swpolicial«

El emisor si está bien poner «SWPOLICIAL»

El número de serie podría ser el 0000 (0007 almonte) IDAYUNTAMIENTO YMD de cuando se genera

Laravel Vite dominio propio

Use the following steps to install Bootstrap 5 in the laravel 9 with Vite.

  1. Install Laravel Project
  2. Install Laravel UI For Bootstrap 5
  3. Setup Auth Scaffolding with Bootstrap 5
  4. Install NPM Dependencies
  5. Import vite.config.js Bootstrap 5 Path
  6. Update bootstrap.js
  7. Import Bootstrap 5 SCSS in JS Folder
  8. Replace mix() with @vite Blade directive
  9. Running Vite Command to build Asset File
  10. Start the Local server

Step 1: Install Laravel Project

Installing a fresh new laravel application, so head over to the terminal, type the command, and create a new laravel app.

composer create-project --prefer-dist laravel/laravel:^9.0 laravel9-bootstrap5-vite

or, if you have installed the Laravel Installer as a global composer dependency:

laravel new laravel9-bootstrap5-vite

Step 2: Install Laravel UI For Bootstrap 5

Next, you need to run the below command in your terminal

composer require laravel/ui

Step 3: Setup Auth Scaffolding with Bootstrap 5

php artisan ui bootstrap --auth

 Step 4: Install NPM Dependencies

Run the following command to install frontend dependencies:

npm install

Step 5: Import vite.config.js Bootstrap 5 Path

First, you need to change vite.config.js and add the bootstrap 5 path & remove resources/css/app.css

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import path from 'path'

export default defineConfig({
    plugins: [
        laravel([
            'resources/js/app.js',
        ]),
    ],
    resolve: {
        alias: {
            '~bootstrap': path.resolve(__dirname, 'node_modules/bootstrap'),
        }
    },
});

Step 6: Update bootstrap.js

We need to use import instead of require.

import loadash from 'lodash'
window._ = loadash


import * as Popper from '@popperjs/core'
window.Popper = Popper

import 'bootstrap'


/**
 * We'll load the axios HTTP library which allows us to easily issue requests
 * to our Laravel back-end. This library automatically handles sending the
 * CSRF token as a header based on the value of the "XSRF" token cookie.
 */

import axios from 'axios'
window.axios = axios

window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

/**
 * Echo exposes an expressive API for subscribing to channels and listening
 * for events that are broadcast by Laravel. Echo and event broadcasting
 * allows your team to easily build robust real-time web applications.
 */

// import Echo from 'laravel-echo';

// window.Pusher = require('pusher-js');

// window.Echo = new Echo({
//     broadcaster: 'pusher',
//     key: process.env.MIX_PUSHER_APP_KEY,
//     cluster: process.env.MIX_PUSHER_APP_CLUSTER,
//     forceTLS: true
// });

Step 7: Import Bootstrap 5 SCSS in JS Folder

Now you need to import bootstrap 5 SCSS path in you resources/js/app.js or resources/js/bootstrap.js

resources/js/app.js

import './bootstrap';

import '../sass/app.scss'

Step 8: Replace mix() with @vite Blade directive

When using Vite, you will need to use the @vite Blade directive instead of the mix() helper. remove mix helper and add @vite directive.

<link rel="stylesheet" href="{{ mix('css/app.css') }}">
<script src="{{ mix('js/app.js') }}" defer></script>

use @vite directive

@vite(['resources/js/app.js'])

views/layouts/app.blade

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="//fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">

    @vite(['resources/js/app.js'])

</head>
<body>
    <div id="app">
        <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
            <div class="container">
                <a class="navbar-brand" href="{{ url('/') }}">
                    {{ config('app.name', 'Laravel') }}
                </a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                    <span class="navbar-toggler-icon"></span>
                </button>

                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <!-- Left Side Of Navbar -->
                    <ul class="navbar-nav me-auto">

                    </ul>

                    <!-- Right Side Of Navbar -->
                    <ul class="navbar-nav ms-auto">
                        <!-- Authentication Links -->
                        @guest
                            @if (Route::has('login'))
                                <li class="nav-item">
                                    <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
                                </li>
                            @endif

                            @if (Route::has('register'))
                                <li class="nav-item">
                                    <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
                                </li>
                            @endif
                        @else
                            <li class="nav-item dropdown">
                                <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                                    {{ Auth::user()->name }}
                                </a>

                                <div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
                                    <a class="dropdown-item" href="{{ route('logout') }}"
                                       onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                        {{ __('Logout') }}
                                    </a>

                                    <form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
                                        @csrf
                                    </form>
                                </div>
                            </li>
                        @endguest
                    </ul>
                </div>
            </div>
        </nav>

        <main class="py-4">
            @yield('content')
        </main>
    </div>
</body>
</html>

Step 9: Running Vite Command to build Asset File

You need to npm run build command to create asset bootstrap 5.

npm run build

Step 10: Start the Local server

Now open a new terminal and execute the following command from your terminal to run the development server.

php artisan serve

Para hacerlo funcionar en desarrollo hay que apuntar un subdominio específico para el vite e instalar el plugin de ssl del mkcert que genera un certificado auto signed para ese subdominio.

Igualmente hay que ver el código fuente y darle al menos una vez a aceptar el certificado.

https://github.com/liuweiGL/vite-plugin-mkcert

npm install vite-plugin-mkcert -D

My final vite.config.js file looked like this:

import laravel from 'laravel-vite-plugin'
import { defineConfig } from 'vite'
import mkcert from'vite-plugin-mkcert'

export default defineConfig({
    server: {
      https: true,
      host: 'vite.myproject.test',
    },
    plugins: [
        mkcert(),
        laravel({
          input: [
            'resources/css/app.css',
            'resources/js/app.js',
          ],
          refresh: true,
        }),
    ],
})