This application was inspired by Hkirat’s video below.
https://www.youtube.com/watch?v=c8_tafixiAs&t=7138s
This is the code description of the video.
https://projects.100xdevs.com/tracks/ZSQI8YNE0iL6sT1hJpts/vercel-1
See the video on Youtube and code in Github below
To see the video of this project
Github repository
Below are diagrams to describe upload
, deploy
, and request
section briefly.
What I did (especially different way from Hkirat’s one)
Of course JS(typescript) and PHP though.
About Environment
I used sail to leverage the benefit of Laravel and I made the way the web socket
server in the one of sail containers, which I will write later. About the det\ail how I could build a web socket server is written below Github repository link. Using sail and Laravel project structure I was able to do almost same thing within one Laravel project described by Hkirat’s video.
https://github.com/memorandumtk/sail-websockets
For the cloud storage, I was going to use AWS S3
and tried it. However I was a little bit scared of exceeding the amount of free use, then I shifted to use CloudFlare R2
which is compatible for S3.
I have to say that I used Windows 11 to edit my code and check the result on browser and WSL2 to have sail environment and use DNS server, dnsmasq
and systemd-resolved
. I’ve set up my DNS like this my twitter post to be able to detect subdomain to get id like below. But saying this, such this URL is used a request to get deployed React files After deployed.
Route::domain('{id}.vercel-local.com')->group(function () {
Upload – To upload folders and files cloned from github repository which comes from a request into R2.
I did this by using Laravel Process and Storage facade. I used the former to clone git repository entered to input form in the browser and the latter to move files (those content) between local storage and R2 storage. I changed the storage setting for it in config/filesystems.php
. When the upload process has done, using Redis built on sail, it is going to be saved ‘status’ key, then dispatches ProcessBuild
job into the queue.
Example of R2 setting
'r2' => [
'driver' => 's3',
'key' => env('CLOUDFLARE_R2_ACCESS_KEY_ID'),
'secret' => env('CLOUDFLARE_R2_SECRET_ACCESS_KEY'),
'region' => env('CLOUDFLARE_R2_DEFAULT_REGION'),
'bucket' => env('CLOUDFLARE_R2_BUCKET'),
'endpoint' => env('CLOUDFLARE_R2_ENDPOINT'),
'url' => env('CLOUDFLARE_R2_URL'),
// Optional: To use path-style endpoint, which might be required for some configurations
'use_path_style_endpoint' => env('CLOUDFLARE_R2_USE_PATH_STYLE_ENDPOINT', false),
'throw' => false,
],
Deploy – To deploy the React project and trigger the web socket event after deployed.
This part is the same as upload part for moving files and registering status
in Redis. In addition, I use Process facade to run this command (npm install && npm run build) and fired new web sockets message to let the front code know deploy process has done. The code firing the message is in the DeployService.php
event(new DeployStatusMessage('deployed', $id));
About Requests
Sending a request of github repository URL which will be cloned.
I made this flow within resources/views/deploy/form-of-deploy.blade.php
and resources/js/send-giturl-form.js
After the deploy process has done, what I made are:
- To have a request from front-end to get a content of React project. This logic is in
app/Http/Controllers/RequestController.php
. - To change the display of front page with making
status-of-deploy
visible after receiving the web sockets event saying ‘deployed’ and verifying the id is correct using session storage value of id. This logic is written inresources/js/receive-status-ws.js
. - Getting the deployed URL, to be able to access the URL. The URL would be like:
http://{id}.vercel-local.com/{file}
{file} part can be index.html(initially), js file, css file and other requested based on index.html. These file path handled by below code, which is in web.php
.
Route::domain('{id}.vercel-local.com')->group(function () {
// Define other routes that should match the subdomain here
Route::get('/{file}', [\App\Http\Controllers\RequestController::class, 'request'])
->where('file', '.*'); // The '.*' is a wildcard to match any file path
});
Comments