During my internship at Entreredes I independently built AutoLanding, an internal tool for the company. The code and the tool belong to Entreredes, so there's no code or screenshots here: I describe what it did, how it was built and what I learned.
What it solved
Preparing a proposal for a local business took hours: finding its details, writing the copy, getting images and laying out the page. AutoLanding automated it: from a single business, it generated a complete landing page ready to show the client.
A pipeline of chained jobs
Creating a lead triggered three chained queue jobs: extracting the business data from Google Places (address, phone, reviews and photos), generating the copy section by section with Gemini, adapted to the sector, and resolving the images, combining real photos with generated ones. The result was reviewed in a preview, shared with the client through a signed, expiring link, and could be exported.
Everything was managed from a Filament admin panel, with 14 business sectors with their own styles and Excel import and export of leads.
Heavy work, always in queues
Calls to Google and Gemini are slow and can fail, so they never ran during the user's request: they went into Redis queues, processed by a separate worker, with a scheduler for periodic tasks. The interface always responded, even when a generation took a while.
Security from the start
The application took in external data and sent it to an AI model, so security couldn't be left for the end: rate limiting on sensitive endpoints, sanitising the text sent to the model against prompt injection, validating download destinations to prevent SSRF, HMAC-signed links, neutralising formulas in exported CSV files, and CSP and HSTS headers.
225 tests before every deployment
With PHPUnit, including break-it tests that try to bring the application down with malicious or unexpected input, load tests and fuzzing, and a full pipeline check with mocked or real APIs. No deployment went out without passing them.
In production with Docker
It was deployed on the company's server with Docker Compose: the application, MySQL, Redis, the queue worker and the scheduler, each in its own container, behind a proxy with automatic SSL.
What I learned: designing for failure
It was the first time a whole project depended on me: designing it, testing it, deploying it and answering when something failed. I learned to treat every external API as something that will fail and to design for it, and that break-it tests find what normal tests miss. What I build today, like leo-mcp, starts from that way of working.