Everything AI – TOC

This blog has plenty of posts about AI, some are about AI tools, others are about installing AI locally, so this post is where I am putting all the AI stuff I have ever blogged about in one place !

The section Local AI is about creating your own AI server using freely available sources, the API section lists all the services that provide an API that can be used remotely (Most can not be installed locally anyway), and the Online Services is where you can get things done via AI online (That can be used using your browser, whether they provide an API or not, and whether they can be installed locally or not is a different story)

Continue reading “Everything AI – TOC”

Creating ISO with large files

xorriso -as mkisofs \
-o /hds/1tb/tmp/mystuff.iso \
-iso-level 3 \
-full-iso9660-filenames \
-volid MyStuff \
/hds/12tb/mystuff

As simple as that, now you can just mount the ISO image and files larger than 4GB will just work, but it seems that filenames are correct in Linux, but in ALL CAPS in windows

Should start experimenting with

xorriso -as mkisofs \
-o /hds/1tb/tmp/mystuff.iso \
-iso-level 3 \
-full-iso9660-filenames \
-J \
-R \
-volid MyStuff \
/hds/12tb/mystuff

I am pasting this here because all the other methods (Mounting and writing to, or using the traditional tools seem to fail for some reason)

Laravel Socialite with socialite-ui

In a previous post, I explained how to install the third party (socialite-ui) on your website, which itself was written to help people with the “Gradually migrating to Laravel” post, but it seems it is not enough, so here is how to fully get socialite working, much of the work is outside your website though, and i won’t be covering that, all you need to do is ask google, for example, how do i enable social logins with google or facebook etc…

The redirect problem

After registering or logging in, your user will be redirected to an absolutely empty “/dashboard“, which is already handled by myrouter.php

Email verification

Before doing anything, let us check that email is working using your SMTP credentials

Create the following route for testing

use Illuminate\Support\Facades\Mail;

Route::get('/test-email', function () {
Mail::raw('This is a test email.', function ($message) {
$message->to('test@example.com')
->subject('Test Email');
});
return 'Email sent!';
});

socialite-ui does not come with email verification (during registration) by default, you will need to enable it, this is simply by following the instructions here

now edit the file (/config/socialite-ui.php) to enable the social logins you want enabled, and alter the other settings as per the page her (https://github.com/Laravel-Uis/socialite-ui)

php artisan config:clear
php artisan cache:clear

installing Laravel 12 with social login

Normally this would not have been worthy of a blog post of its own, there are plenty of posts that explain how to do this, but because I don’t want the post on “Gradually migrating to laravel” to be too long, I have decided to move the part on how to setup a vanilla install of laravel to its own post.

Rather than having you setup sociallite in laravel, there is an amazing starter kit on github (https://github.com/Laravel-Uis/socialite-ui) that we will be using to hit the ground running, this is not an official starter kit, it is by a third party, namely Joel Butcher, the same person behind the good old, now unmaintained socialstream (Socialite and JetStream)

So, let’s get down to business, I assume you have a debian machine up and running

apt install php-fpm nginx libnginx-mod-http-headers-more-filter openssl
apt install php-{dev,common,xml,tokenizer,zip,mysql,curl,mbstring,mysql,opcache,gd,intl,json,xsl,bcmath,imap,soap,readline,sqlite3,gmp,guzzlehttp-guzzle}

Now, make sure the following PHP extensions are enabled (i am pretty sure they are after the commands above) but to be safe, check in php.ini

extension=fileinfo
extension=mbstring
extension=openssl

Remember to “mariadb-secure-installation”, then create a username and database in mysql for laravel ! you can either use PHPMYADMIN or from the command line, makes no difference, remember to flush privileges

Install composer and nodejs

apt install composer nodejs npm
composer --version

Now, I am assuming your web directory is owned by the web server user ! first, install laravel globally, this is a new tool we will use to create laravel installations from now on

su - webdev (Whatever user you want to execute under)
composer global require laravel/installer

Now, add laravel to your path in .bashrc

export PATH="$HOME/.config/composer/vendor/bin:$PATH"
source ~/.bashrc
echo $PATH

IMPORTANT: In my case, all my websites are in folders that follow the patern “/var/www/vhosts/com.websitename.www/public”… hence, i execute the lines below from within the “/var/www/vhosts/” directory ! once i execute the below line, i will have a folder called my-app, I usually rename that to com.websitename.www and there is already a public folder in there, The long and short of this story is, your whole project folder with the public directory inside is creating from the command below, if you do it wrong, you can simply move the files and folders in that folder to where they should be

And now, in your project directory (Which is one level below your public web directory), execute the following, the laravel command is the new way, and if you are not looking for Livewire Volt, just visit the github page to see your 3 other options (Vue, React, and Livewire without volt)

laravel new my-app --using=laravel-uis/socialite-ui-livewire-starter-kit

During the install, it will ask you what testing framework you want, You can pick whatever one you prefer, then it will ask you if you would like to run “npm install and npm run build?”, You can hit no, and run the following command yourself

npm install && npm run build

Configure

Now, edit your .env ! the default one uses sqlite, if you want to use mysql / mariadb (like most PHP people), just make sure this becomes your database in your .env file

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=

Now, run the following commands

php artisan migrate
The following commands are for reference only
php artisan session:table
php artisan queue:table
php artisan cache:table
php artisan migrate
php artisan config:clear
php artisan route:clear
php artisan view:clear
php artisan cache:clear
php artisan migrate:status

Behind Proxy

If you are behind a proxy, you might need to tell laravel 12 to trust proxies… (Edit /bootstrap/app.php), and add the parts

first, add this line at the top

use Illuminate\Http\Request;
$middleware->trustProxies(
//at: '*', // Trust all proxies (use for dynamic/cloud setups like Nginx on the same server; see security notes below)
at: ['127.0.0.1', '192.168.1.0/24', 'xxx.xxx.xxx.xxx'],
headers: Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB // Include if using AWS; optional for standard Nginx
);

Right where there is an empt comment inside

->withMiddleware(function (Middleware $middleware) {

Sanity check for BZ2 files before decompression

To check if a file integrity is intact without expanding it, you can try the obvious send to /dev/null or use a built in equivilent

bzip2 -dc file.bz2 > /dev/null
bzip2 -tv file.bz2

Now, the problem can be that the split files were not complete ! so when i executed

cat file.bz2.001 file.bz2.002 file.bz2.003 file.bz2.004 > file.bz2

I thought that 004 was the last part, but it was not, how can i know without wasting CPU cycles ! let me show you

NOTE: If you have not yet concatenated the files, you can do the header checks on the first file, and the footer checks on the last 😉 in my case, I already have them concatenated (Not a problem, i can add more files to it later) so i am doing both checks on the same file

Check the header, Every bzip2 file starts with: 42 5a 68 xx

head -c4 file.bz2 | hexdump -C

Should result in

00000000  42 5a 68 39                                       |BZh9|

And it does

But when i check the footer with the command

tail -c64 file.tar.bz2 | hexdump -C

The first line of that output should be

00000000  17 72 45 38 50 90 00 00  00 00 00 00 00 00 00 00  |.rE8P...........|

But it is not, in my case it was

00000000  e4 fc 2e 36 2f e7 d5 bf  74 d5 3b 0f ef 4a 61 15  |...6/...t.;..Ja.|

Which looks like random compressed data

In conclusion, I should find all the other parts to be concatinated to this before wasting time decompressing it

Gradually Migrating a legacy system to Laravel !

This looks long, but it’s simpler than it seems.

Scenario: You have an existing legacy PHP system, and you would like to gradually migrate to Laravel, you don’t want to migrate everything to laravel from the word go (because you either don’t have the time, or you need features from laravel but don’t want to learn laravel all at once, or maybe developing with laravel is your favorite way of learning laravel), whichever it may be, this tutorial is for you.

To get on track, I will assume that all you want for the time being is to have laravel (with socialite) take care of registering and authenticating users, and pass the logged in user data to your system… and leave other migrations for later, this is a good way to get started.

The steps

1- Install Laravel 12

Just a plain old vanilla setup. If you don’t know how to do this, I have the following post for you (Installing laravel 12 with social login), you don’t need to modify anything for this setup to work, yet you might find some stuff that needs some modification (Regardless of this setup), so here are a few modifications you may want to make

all you need now is to make sure you can register as a user, either directly or using social login (Or whatever you want), to speed things up, you might want to use this amazing starter kit here (https://github.com/Laravel-Uis/socialite-ui), bottom line is  a clean Laravel install — nothing fancy..

2- One of the following two options depending on how your system is setup

Option 1– If your system already uses rewrite, have nginx rewrite all your requests to myrouter.php instead of index.php, here is the nginx config file I use, and here is the myrouter.php file

Elaboration: Normally, in a Laravel installation, nginx would rewrite all requests to laravel’s own index.php, what we have done with those two files is have it rewrite it to our own router (myrouter.php), if our existing system knows how to handle the request, it would, if it doesn’t , then it is probably a laravel page that the user is seeking (such as registration page), so we pass control to laravel.

Option 2– If you are not using rewrite in your legacy system, and PHP pages are accessed directly, the laravel recommended nginx config already deals with this since it checks for file existence before rewriting the request to index.php, all you will need to do at this stage is include this small file in your PHP files (require_once rather than include)

3- Integrating the auth system into ours

Our options: What we will do is create an endpoint on Laravel and have our system call it internally

What we won’t do: it is worth mentioning that there are quite a few ways besides the above, but none of them is straight forward, one of them is to include laravel’s internals (the console bootstrap path… what Artisan uses) into our own system, but Laravel’s sessions are serialized and encrypted ( with Illuminate\Encryption\Encrypter and the APP_KEY) so you will have to replicate Laravel’s decryption/unserialization logic,so we will stick to the auth endpoint… and optimize it by only firing it up when needed (To not load laravel for every walk in visitor to the website)

Note: If you will be going by my advice to develop them separately, what comes below needs laravel to function/test/develop, you will need to create a file that checks if laravel is available before firing up, and if not, send back a dummy testing user (or whatever you need), you can make that easier by toggling a “production” flag, and when in production it calls upon laravel, when in development, it serves the dummy user.

3.1 : Create an endpoint in laravel, very simple, Just edit laravel’s routes file ( routes/web.php) and add the following, it is also wrap it in middleware that only allows access from localhost, even though that is not really needed, but keeping security tight is a good idea (You can remove the extra wrapper by deleting everything from ->middleware through the end and putting a semi colon instead, up to you)

Note: I personally added this to /routes/auth.php alongside the login and registration features since it is authentication specific, but technically it makes absolutely no difference

use Illuminate\Support\Facades\Route;

Route::get('/auth/me', function () {
    return response()->json([
        'user' => auth()->check() ? auth()->user() : null,
    ]);
})->middleware(function ($request, $next) {
    if ($request->server->get('SERVER_ADDR') !== $request->ip()) {
        abort(403);
    }
    return $next($request);
});

Now, we need a function to talk to laravel internally and get the user ! we will fire up laravel, take the user data, and go back to our own system

And we also need to know when to call that function, there is no need to fire up Laravel then tear it all down when there is no chance that this might be a logged in user !

Previously, i had a function that fired up laravel internally, but after some digging, I realized that the overhead of calling it over http is very low, and that it will future proof the system (When laravel decide to change things)


function currentUser($http_port) {
    static $cachedUser = null;
    static $resolved = false;

    if (!$resolved) {
        $cachedUser = null; // default

        if (!empty($_COOKIE['voodoo_session'])) {
            //$ch = curl_init("https://www.voodoo.business/auth/me"); // Using your FQDN
            $ch = curl_init("http://127.0.0.1:{$http_port}/auth/me");
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_HEADER, false);
            curl_setopt($ch, CURLOPT_TIMEOUT, 5); // optional timeout
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
                'Host: www.voodoo.business',
                'Cookie: voodoo_session=' . $_COOKIE['voodoo_session']
            ]);

            $response = curl_exec($ch);

            if ($response === false) {
                error_log('currentUser cURL error: ' . curl_error($ch));
            } else {
                $data = json_decode($response, true);

                if (json_last_error() !== JSON_ERROR_NONE) {
                    error_log('currentUser JSON decode error: ' . json_last_error_msg());
                } else {
                    $cachedUser = $data['user'] ?? null;
                }
            }

            curl_close($ch);
        }

        $resolved = true;
    }

    return $cachedUser;
}

Now you can simply call the function currentUser as follows, as many times as you wish, it will only invoke laravel once per web request

$user = currentUser();
if ($user) {
    echo "Logged in as {$user['name']}";
} else {
    echo "Not logged in";
}

Improvement – Optimization: You can short circuit earlier by simply connecting to the database and checking if the session id ($sessionId = $_COOKIE[‘laravel_session’];) exists in the database or the file or memory store, then, if it exists, invoke the currentUser function… this will ( check that the session file/row/key exists, not that it’s valid), but getting into this now will complicate the tutorial because there are 3 different scenarios and the solution we are at right now is good enough for most low to medium traffic websites

The logout button

Laravel does not have a page that logs you out as soon as you visit it, this will effectively disable CSRF ! and allow forced logout, so the answer is to create a logout page in Laravel and visit that page, it is very easy

The route file

/routes/web.php

Route::get('/log_me_out', function () {
return view('logout'); // blade template with the button
});

The Blade template

resources/views/logout.blade.php (If you are not familiar with laravel, the part of the file name that is before “.blade.php” is the view name, so the view in the route takes us here

<h1>Click below to log out</h1>

<form method="POST" action="{{ route('logout') }}">
@csrf
<button type="submit">Logout</button>
</form>

All you need to do now is link to /log_me_out, and the user will be presented with the option to logout

Thoughts and ideas

I do not practice what I preach, my projects live inside of each other, the outer project is laravel, and my project lives inside a folder inside laravel (Not inside the public folder, just inside laravel’s project folder), at which point, myrouter.php either reads static files and serves them, or includes php files from my projects folder, so I only have 1 file inside the public folder….

So, in my projects folder, i do the regular steps below

cd /var/www/laravel/innderproject (This is the whole story, running composer inside the project folder)
composer init
composer require nikic/php-parser:^5.6 vlucas/phpdotenv:^5.6

And inside that folder, all files include the code

require __DIR__ . '/vendor/autoload.php';

This loads from the inner vendors folder, laravel knows how to reach the outer one

BMW F10 M5 (2014), the everything

So, a good friend of mine bought a used but perfect condition M5, he is so happy about it, as a testament to how personal BMW cars can be, you can feel the adrenaline in his voice days before the handover date, and his happiness is contagious in a weird way, hence this page.

The hunt

1- Diagnostic software (And hardware)
2- Service manuals
3- Aftermarket infotainment
4- OEM upgrades
5- Aftermarket upgrades

Online tools

MDECODER: Should be able to show all the specs about the car

Vindecoderz : Good for finding the recalls

Bimmer.Work:

Android car units

I recently bought an android system for an old car, and to my surprise, the RAM installed on it was half of what was advertised !

It is easy to check, here are the commands you need

On linux

Get connected to phone for ADB , then

Screen resolution before upscaling
adb shell wm size

Ram and other features

getprop ro.boot.dramsize
adb shell getprop ro.build.version.sdk
adb shell getprop (Just show Everything)

On windows, you will execute all this differently

adb shell
getprop ro.build.version.sdk
getprop

This also applies to home android TV set top boxes ! I checked

Trixie… php8.4-imap missing

Error: Unable to locate package php8.4-imap
Error: Couldn’t find any package by glob ‘php8.4-imap’

the debian package maintainer (OndÅ™ej Surý) has released it on his repository, but it is nowhere to be found for Debian 13 on “https://packages.debian.org/“, trixie didn’t trickle down to the website yet, but in SID, it reads “The extension is unbundled from php-src as of PHP 8.4.0.”

I am not yet sure what this means, there is a chance it means we should install it using PECL, I will not do that, I would rather installwith apt, but if i wanted to, i would execute

sudo apt install php-pear php8.4-dev
sudo pecl install imap

There is a chance that it will be coming back to debian’s repositories since Sury (The package maintainer) has it, but I have no idea when

So, let us install it (the IMAP extension and nothing else) from the sury repository to make things simpler when debian has it again

Let us start by adding

sudo apt install -y lsb-release apt-transport-https ca-certificates curl gnupg2
echo "deb https://packages.sury.org/php $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/sury-php.list
curl -fsSL https://packages.sury.org/php/apt.gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/sury-php.gpg
sudo apt update

Now we have added Sury’s repository to apt, let us go ahead and pin it so that only php8.4-imap is sourced from that repo !

sudo tee /etc/apt/preferences.d/sury-php.pref >/dev/null <<'EOF'
Package: *
Pin: release o=deb.sury.org
Pin-Priority: 1

Package: php8.4-imap
Pin: release o=deb.sury.org
Pin-Priority: 990
EOF
sudo apt update

Now install

sudo apt install php8.4-imap

To make sure everything is correct

apt policy php8.4-imap

Now in my case, I need to restart PHP (systemctl restart php8.4), in your case, you might need to restart apache or something else, when in doubt, restart the machine 😛

Once the package is in debian repositories again, we will simply remove /etc/apt/preferences.d/sury-php.pref and /etc/apt/sources.list.d/sury-php.list, then reinstall php8.4-imap.

Failing spark plugs

My 2014 Toyota Prius uses iridium-tipped spark plugs such as the Denso SK16R11 or the NGK IFR5A11, both usually set you back around $20, but if you buy them from the official toyota dealer, they will set you back $150 ! but I have no choice, as the market is flooded with counterfeit spark plugs, and changing the spark plugs on the prius requiers the removal of the wiper area !

What about those $150 spark plugs (I will check the exact price and update this soon, so when I took them out after less than 5000 KMs and found two of them broken ! specifically the Insulator Nose (shield) around the copper core !

I had a failing head gasket, and the removal was part of fixing that, so I thought that the seeping oil or water into the piston might have been the problem, but since i maintained their order after removing them, It turns out that the one that has the inner shield fully broken was in a piston that did not suffer any head gasket leak !

Prius 2014 head gasket replacement

Seems the Prius is known to blow head gaskets, reason being attributed to the hybrid system, the engine switches off and on so often, heating up and cooling down all the time.

I am using the official TIS repair manuals. I am warning you, this is a lengthy job, it is not something you can finish in a day.

Tools

There are plenty of tools that you will need along the way, everything from the tools needed to take out the EGR, to a triple square bit to remove the cylinder head bolts, in any case, here is a list of tools i recall needing

  • A ratchet, breaker bar, and a cheater bar
  • A torque wrench (I use ACDelco’s ARM302-4s)
  • Oil filter removal tool
  • 10mm bi-hexagon wrench (An M12 (12mm triple square) bit will do if you fail to obtain the bi-hexagon ! but you will feel a bit of wiggle in it)
  • A pair of pliers
  • 12mm socket (Many screws)
  • 14mm socket (Many screws)
  • 19mm socket (The harmonizer / crank pulley)
  • Feeler gauges down to 0.004 (0.004 is the maximum acceptable engine warp)
  • A super straight bar to assess the engine against (Laser cut)
  • Harmonic Damper removal tool such as Schley 64300 (I made my own simple tool that works)
  • A tool to take out the valve stem seals (I made my own)
  • Patience

The Harmonic Damper removal tool

There are many ways to remove the pulley (Harmonic Damper), 1- the most common of which is using a torque impact drill, it just works, but my gut feeling tells me it might not be wise, if it was, Toyota would have approved it, 2- another popular way is to use a belt or strap and tie it to a strong part of the vehicle and around the pulley to use friction to hold the pulley in place, problem with this method is that if it is not 100% horizontally aligned, and the pulley is being pulled in or out, it might cause damage, 3- and there is the Toyota way, which is based on using a tool to hold the pulley in place while you loosen the screw, examples of such a tool is the Schley Products 64300, I don’t have access to such a tool, so I improvised a tool that holds the pulley against the engine mount

Parts

Distilled water

7 Liters of distilled water, You are probably going to need to use distilled water before engine coolant to flush the engine from the contaminated coolant in the car, it was contaminated because your head gasket broke, and exhaust went through that coolant.

Engine Coolant

(Applies to both Fix and repair)
If the coolant has been contaminated with engine exhaust, you will need to flush and add distilled water, then after a day (a day of light use, and zero days of the car sitting), remove as much of the water as you can and add the SLLC coolant (Do not run on distilled water for more than 1 day).

Totota’s coolant is super expensive, if you are looking for something other than the OEM, or want to understand the risks and advantages, check out my Toyota’s coolants post

The car takes 6.5 liters of coolant, every container is 2 liters !

Oil and oil filter

(Applies to both Fix and repair)

if the engine oil has been contaminated (And it has), you will need a new filter and new oils

Expected cost (35JD)

Gasket kit

PN: 04111-37316

The repair manual states that you will need a new gasket kit, the gasket kit has the following parts.

FIPG 103

PN: 00295-00103

The form in place gaskets from Toyota are best, but they are very pricey, I went with an aftermarket German brand (Victor Reinz Reinzosil) that is high quality (Used by German brands as OEM !) and have my fingers crossed, the Toyota FIPG costs around $50, the Victor Reinz Reinzosil (Universal) aftermarket brand costs less than 6 dollars

Cylinder head bolts

(90910‑02164 and later was superseded by 90910‑A2009)

Those are Torque to yield bolts, when you install them, they are stretched, and should not be used again, In my case unfortunately, Al markazeyah Toyota does not provide them, I was told today that they did have them at one point, and they cost $5 each (10 of them is $50), but they no longer carry them ! yes, I know, what the duck, as an exclusive dealer in Jordan, they absolutely should have them.

The Toyota repair manual often tells you to measure the bolts after removal, and replace them if they are out of spec — a classic sign of TTY design, even if not spelled out.

The process

Removing the spark plugs

Getting started with a big surprise !

I started by removing the spark plugs, like you normally would, and to my surprise, those brand new spark plugs (Less than 5000 KMs) are broken, and the ones that were broken are not even the ones where the head gasket leaked into ! Here is more about the failing spark plugs