World Vibe Web
The distributed app store for vibe-coded projects. Browse, discover, and install open-source apps from across GitHub.
Search and filter rental properties in Botswana by location, price and amenities
Translation pending
AI summary · website and README
CommercePython · HTMLGitHub @TrodSticksOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
A web application for renting property in Botswana. Renters search, filter and browse listings on a list or a map and message landlords. Landlords publish listings with photo galleries. Administrators handle reports and landlord verification.
Built with Flask, SQLAlchemy and Flask-Migrate.Read the full README ↗A web application for renting property in Botswana. Renters search, filter and browse listings on a list or a map and message landlords. Landlords publish listings with photo galleries. Administrators handle reports and landlord verification.
Built with Flask, SQLAlchemy and Flask-Migrate.
/admin). A queue of reports, where administrators record an outcome and can hide or restore listings. Hidden listings disappear from the list, the map, recommendations and favourites.The rule-based search parser always runs. An optional local model (LiquidAI LFM2.5-350M) can fill in what the rules miss. It is off by default; see USE_LLM_SEARCH below.
Install dependencies
pip install -r requirements.txt
For spaCy-based search and the optional AI model, also run pip install -r requirements-ai.txt. Without them, search uses the rule-based parser.
Create your local settings file
Copy .env.example to .env. For work on your own computer the one line that matters is:
APP_ENV=development
.env is ignored by git. Never commit it.
Create sample data (optional). This deletes everything in the database first.
python create_sample_data.py
Run the application
python app.py
Open http://localhost:5000. The database is created or brought up to date automatically.
Create the first administrator. The account must already exist (sign up first, or use a demo account).
flask --app app make-admin [email protected]
Other commands: flask --app app list-admins and flask --app app revoke-admin EMAIL. These commands are the only way to grant admin access; nothing on the website can.
The sample data script creates these, all with the password demo123 and a confirmed email:
[email protected], [email protected][email protected], [email protected]Verification and password-reset emails are plain text and are sent through one of these backends:
| Backend | When it is used | What happens |
|---|---|---|
smtp | MAIL_SERVER is set | Emails are sent through your mail server |
file | APP_ENV=development and no MAIL_SERVER | Emails are saved in mail_outbox/ and shown at /dev/mailbox |
disabled | Production with no MAIL_SERVER | Nothing is sent; the site says so on the relevant pages |
With APP_ENV=development, open http://localhost:5000/dev/mailbox to read the emails the app would have sent and open their links. That page only opens from the computer running the app, and does not exist in production. mail_outbox/ is ignored by git.
Set these environment variables on the server (never in the repository):
APP_ENV=production
SECRET_KEY=<a long random value>
MAIL_SERVER=smtp.your-provider.example
MAIL_PORT=587
MAIL_USERNAME=<from your email provider>
MAIL_PASSWORD=<from your email provider>
MAIL_USE_TLS=1
MAIL_FROM=Rent Me <[email protected]>
PUBLIC_BASE_URL=https://your-domain.example
Any provider that offers SMTP works. Use MAIL_USE_SSL=1 and MAIL_PORT=465 if your provider needs SSL instead of STARTTLS.
Not yet tested against a real mail server. The SMTP code is exercised by automated tests using a stand-in server, and the development mailbox was tested by hand. After configuring a provider, sign up with a real address and check that the email arrives before relying on it.
Email bodies contain sign-in links, so the app never writes them to its log.
vercel_app.py is the entry point on Vercel (set in pyproject.toml). It keeps the database and uploads in /tmp and loads the demo data on start-up, so changes made on the demo are temporary, unless you connect Supabase as described below: then data and photos are kept, and the demo data is only loaded into an empty database. Set SECRET_KEY in the Vercel project's environment variables so logins survive restarts.
On a host without a lasting disk, such as Vercel, the SQLite file and uploaded photos disappear whenever the host starts a fresh copy of the app. Point the app at Supabase to keep them: a Postgres database for accounts, listings and messages, and a Storage bucket for photos. Nothing changes on your own computer unless you set these variables.
[YOUR-PASSWORD] with the database password. The pooler is the right choice for Vercel: the direct connection needs IPv6, which Vercel doesn't offer.property-photos and switch on Public bucket, so browsers can show the photos.sb_secret_). Either works.In the Vercel project, open Settings → Environment Variables and add, for Production (and Preview if you want previews to share the data):
| Variable | Value |
|---|---|
DATABASE_URL | The transaction pooler connection string from step 2 |
SUPABASE_URL | The Project URL, e.g. https://abcd1234.supabase.co |
SUPABASE_SERVICE_ROLE_KEY | The service_role key |
SUPABASE_STORAGE_BUCKET | Only if you named the bucket something other than property-photos |
SECRET_KEY | A long random value, if it isn't set already |
Then redeploy. On its first start the app creates its tables in Supabase (one copy at a time, even when Vercel starts several) and from then on keeps them up to date.
0004_lock_down_data_api) with no policies, so the public key can't read them, while the app itself, which connects as the database owner, works as before.create_sample_data.py deletes everything. With DATABASE_URL pointing at Supabase it wipes the live data, so only run it against a database you mean to reset.The app keeps its database up to date by itself: every time it starts, it applies any migration in migrations/versions/ that the database has not had yet. Existing data is kept. A database from before migrations existed is adopted automatically.
Useful commands:
flask --app app db current # which version the database is at
flask --app app db upgrade # apply pending migrations by hand
flask --app app db migrate -m "describe it" # create a migration after changing models.py
flask --app app db downgrade 0002_property_coordinates # go back one version
Set AUTO_MIGRATE=0 to stop the app upgrading the database at startup and run db upgrade yourself.
Back up the database file before upgrading a live site.
python -m pytest
The suite uses an in-memory database and a temporary folder for photos. It includes tests that generate a few thousand listings and count how many the app reads from the database, so a page that loaded everything and then discarded most of it would fail. The two tests that run the optional AI model are skipped unless you run python -m pytest -m model.
These can go in .env or in the real environment. Real environment variables win.
| Variable | What it does | Default |
|---|---|---|
APP_ENV | development or production. Development turns on the local mailbox and allows login cookies over http. | production |
SECRET_KEY | Signs login cookies and emailed links. Set this on any real server. | A random key saved to a git-ignored .secret_key file |
DATABASE_URL | Database connection string. postgres:// and postgresql:// addresses (as Supabase gives them) are used with the psycopg driver. | Local app.db SQLite file |
SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY | Store photos in Supabase Storage instead of static/property_pics/ (see "Keeping data on a hosted site") | Not set: photos stay on disk |
SUPABASE_STORAGE_BUCKET | The public Storage bucket for photos | property-photos |
AUTO_MIGRATE | Set to 0 to stop the app updating the database when it starts | On |
MAIL_SERVER, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD | SMTP server details | None; port 587 |
MAIL_USE_TLS / MAIL_USE_SSL | STARTTLS (port 587) or SSL (port 465) | TLS on, SSL off |
MAIL_FROM | The "From" address on emails | Rent Me <no-reply@localhost> |
MAIL_BACKEND | Force smtp, file or disabled. file is ignored outside development. | Chosen automatically |
PUBLIC_BASE_URL | The site's public address, used in emailed links | The address of the request |
SESSION_COOKIE_SECURE | Send the login cookie over https only | On, except in development |
TRUSTED_PROXY_COUNT | Number of reverse proxies in front of the app, so visitor addresses are read correctly for rate limits |
APP_ENV=production, SECRET_KEY, the MAIL_* variables and PUBLIC_BASE_URL.python app.py.TRUSTED_PROXY_COUNT, or every visitor will appear to come from the proxy and share one rate limit.flask --app app make-admin.static/property_pics/ and the default database is a single SQLite file. Make sure your host keeps both between restarts, or use Supabase (see "Keeping data on a hosted site").search_engine.py turns a plain-English search into validated filters (type, town, bedrooms, bathrooms, price, amenities, leftover keywords). routes.search_conditions combines those with the drop-down filters into one set of database conditions used by the list, the map and the town counts./api/map-pins asks the database for the listings inside the visible area, capped at 300, with a separate count.SECRET_KEY and expire (24 hours and 1 hour). A reset link stops working once the password changes, and a verification link stops working if the email address changes. Changing a password signs out the account's other sessions.rent_me_app/
├── app.py # App factory, database preparation
├── config.py # Settings, .env loading
├── models.py # Database models and shared choices (statuses, amenities)
├── routes.py # Browsing, search, map, favourites, messaging
├── listings.py # Listing form, photos, property page, dashboard, reporting
├── accounts.py # Sign-up, sign-in, email verification, password recovery
├── admin.py # Report queue, hiding listings, landlord verification
├── cli.py # make-admin, revoke-admin, list-admins
├── security.py # Emailed-link tokens, rate limits, password rules, permission checks
├── mailer.py # Sending email (smtp, development mailbox)
├── photos.py # Storing and removing photos (disk or Supabase Storage)
├── locations.py # Towns and map positions
├── search_engine.py # Plain-English search parser
├── llm_parser.py # Optional LLM search parser (off by default)
├── download_model.py # Downloads the optional LLM model
├── create_sample_data.py # Demo accounts and listings (wipes the database)
├── .env.example # Template for local settings
├── migrations/ # Database changes, applied automatically at startup
├── tests/ # Pytest test suite
├── templates/ # HTML templates
└── static/
├── style.css # Design system: palette, components
├── script.js # Shared behaviour
├── map.js # Map view (Leaflet + OpenStreetMap)
├── gallery.js # Property page gallery and full-screen viewer
├── photo_manager.js # Listing form: reorder, cover, remove, preview
├── pin_picker.js # Listing form: mark a location on a map
├── manifest.webmanifest
├── icons/ # App icon (SVG and PNG sizes)
└── property_pics/ # Uploaded photos (ignored by git, except default.jpg)
GET / - Listings, with search, filters and paginationGET /map - Map viewGET /api/map-pins?bbox=south,west,north,east - Listings inside part of the mapGET /api/search-suggestions - Search suggestionsGET /property/<id> - Property pageGET/POST /register, GET/POST /login, GET /logoutGET/POST /account, POST /account/passwordGET /verify-email/<token>, POST /verify-email/resendGET/POST /forgot-password, GET/POST /reset-password/<token>GET /dev/mailbox - Development onlyGET /dashboardGET/POST /property/new, GET/POST /property/<id>/updatePOST /property/<id>/status, POST /property/<id>/deleteGET /favorites, POST /favorite/<id>POST /property/<id>/reportGET /inbox, GET /conversation/<user_id>GET/POST /message/<recipient_id>, POST /send_reply/<recipient_id>GET /admin/reports, POST /admin/reports/<id>/resolvePOST /admin/listings/<id>/hide, POST /admin/listings/<id>/restoreGET /admin/landlords, POST /admin/landlords/<id>/verify, POST /admin/landlords/<id>/revokeThis project is licensed under the MIT License.
0 |
HOST / PORT | Address and port the app listens on | 0.0.0.0 / 5000 |
FLASK_DEBUG | Set to 1 for debug mode while developing. Never on a public server. | Off |
USE_LLM_SEARCH | Set to 1 to turn on the optional AI search model (run python download_model.py first, about 700 MB) | Off |