# Technical documentation — filmspace (Everyspace)

Evidence date: 2026-10-15. Source: `git@bitbucket.org:overdrive/filmspace.git`, `main` at `f19c1dc` (2024-11-14).

Secrets are named by file. Values are not copied.

## What this repository is

A single Meteor application. Package name `filmspace-meteor_react` (`package.json`). Release file `.meteor/release` is `METEOR@2.4`. Client and server entry modules are declared in `package.json` `meteor.mainModule`: `client/main.js` and `server/main.js`. Test module: `tests/main.js`.

UI is React 16 mounted with `react-mounter` into `#react-target`. Routes use `ostrio:flow-router-extra`. Data uses Meteor methods, publications, and `aldeed:collection2` / `simpl-schema`.

## Repository map

| Path | Role |
|---|---|
| `client/` | HTML shell, client startup |
| `server/main.js` | Imports server API modules |
| `imports/api/` | Domain modules: Users, Listings, Bookings, Messaging, Reviews, Admin, PeachPayments, ActiveCampaign, Intercom, JobQueue, Activities, Counter, App |
| `imports/routes/` | Flow Router groups |
| `imports/ui/` | React pages, layouts, components |
| `imports/libs/countries.js` | Country list |
| `private/email-templates/` | Handlebars HTML email |
| `public/` | Fonts, images, legal PDFs |
| `db/meteor/` | BSON dumps and metadata for categories, forms, images, listings, users |
| `db/scripts/` | Mongo shell scripts for pricing migration and test data |
| `.meteor/` | Release, package list, versions, platforms |
| `packages/meteor-accounts-merge/` | Local Meteor package `mikael:accounts-merge` is also in the package list |
| `src/`, `gulpfile.babel.js`, `config.yml` | Foundation static pipeline |
| `chart/` | GitLab auto-deploy Helm chart `auto-deploy-meteor` 0.0.1 |
| `Dockerfile` | Multi-stage Meteor build |
| `.gitlab-ci.yml` | Build, staging, manual production |
| `pm2.json` | PM2 process named Everyspace |
| `deploy.bat` | `meteor deploy` to Galaxy |
| `deploy.sh` | Empty file |
| `tests/main.js` | Three assertions |

`__CODEOWNERS` defaults ownership to `@dharb`. Other ownership lines are commented out (`@matthew97` appears in comments).

## Languages and frameworks

| Item | Version evidence |
|---|---|
| Meteor | 2.4 (`.meteor/release`) |
| Node in Docker runtime | `node:14.18.1-alpine` |
| Node in README | "10.x", and a note to install on 12.13.0 then gulp on 10.16.0 |
| React / React DOM | `^16.12.0` |
| TypeScript | Meteor package `typescript@4.3.5`. Some listing, search, and activity files are `.ts` / `.tsx`. `tsconfig.json` is present |
| Flow Router | `ostrio:flow-router-extra`, plus title and meta packages |
| Roles | `alanning:roles` |
| Files | `ostrio:files` |
| Mongo | Meteor `mongo` package |
| CSS pipeline | gulp 4, gulp-sass, Foundation Sites `^6.6.3` |
| Image processing | `sharp` `^0.29.3` |
| PDF | `puppeteer` `^2.1.0` |
| HTTP client to Peach | `meteor/http` |

Dockerfile comment says the Node image matches Meteor 1.11.1. The release file says 2.4. **Requires Confirmation** which pair is actually built in CI. The first stage uses `geoffreybooth/meteor-base:2.4`.

`npm start` runs `meteor run --settings dev.settings.json --exclude-archs web.browser.legacy,web.cordova`. `start-dev-db` sets `MONGO_URL` in the script. That URL contains a password and is not reproduced here.

## Application structure

Server startup imports, in order: App, Users, Admin, Listings, ActiveCampaign, Intercom, Bookings, Messaging, PeachPayments, Reviews, Counter, JobQueue, Activities. ServerRender is commented out.

Client startup imports the matching client barrels and `/dist/assets/css/app.css`.

Router waits until the roles subscription is ready, then initialises Flow Router (`imports/routes/router.js`).

## Data stores

Mongo collections constructed in code:

| Collection | Module |
|---|---|
| `newsletter` | Users |
| `user_reviews`, `listing_reviews` | Reviews |
| `message_threads` | Messaging |
| `listings`, `categories`, `forms_for_categories`, `locations`, `spaces_tags`, `spaces_tags_images_links` | Listings |
| `filters.categories`, `filters.features`, `filters.styles` | Client-only collection names |
| `activities` | Activities |
| `pre_bookings`, `bookings` | Bookings |
| `change_requests` | Admin |
| `job_queue` | JobQueue |
| `counter` | Counter |
| Meteor `users` | accounts |

`DocumentsDB` is an `ostrio:files` collection used for generated PDFs (`imports/api/Bookings/server/methods/bookings.js`). Listing images use a files collection in `imports/api/Listings/server/collections/images.js`.

Listings dump metadata shows a `2dsphere` index on `address`. Search uses `$geoNear` on that key.

BSON dumps live in `db/meteor/`. Category metadata namespace is `filmspace.categories`. Listings metadata namespace is `meteor.listings`. The dumps are not applied by app startup. README restore command: `mongorestore -h 127.0.0.1 --port 3001 --db meteor ..\dump\filmspace`. That path `..\dump\filmspace` is not the `db/meteor` folder. **Unknown** whether both snapshots are the same data.

Scripts in `db/scripts/`: `pricing-migrate.js`, `pricing-minMax.js`, `random-activities-for-testing.js`, `category-slug.js`, `listing-calendar.js`, `number-of-listings-for-user.js`, `remove-mobile-category.js`, `remove-threads-where-listings-dont-exist.js`, `review-createdAt.js`.

## APIs

There is no separate HTTP API product. The client calls Meteor methods. Route table (prefixes):

| Prefix or path | Group |
|---|---|
| `/` | home |
| `/legal`, `/contact`, `/loader`, `/testing`, `/dev-test` | general |
| `/create-account`, `/register`, `/login`, reset and verify paths | auth |
| `/listing/...` | listing wizard |
| `/search/:page?`, `/filmspace-detail/:listingId` | search and detail |
| `/dashboard/my-listings/...` | host listings |
| `/dashboard/favourites`, `/dashboard/profile` | guest and profile |
| `/dashboard/account-settings/payments`, `password-change`, `notifications` | settings |
| `/dashboard/bookings/...` | bookings |
| `/dashboard/messages/...` | messages |
| `/booking/payment-result/...`, `/booking/agreement/...`, `/booking/receipt/...`, `/invoice/...` | payments and PDFs |
| `/admin/...` | admin |

Method names are listed in `features.md` and `prd.md`. Two server files both register `listings.getLocation`: `imports/api/Listings/server/methods/search.ts` and `_search.js`.

## External integrations

| Integration | Code | Notes |
|---|---|---|
| Peach Payments OPPWA | `PeachPaymentsService.js` | Live host `https://oppwa.com`. Token and entity id are constants. Sandbox block is commented |
| MongoDB Atlas and other URLs | `settings.json` galaxy env, `dev.settings.json` via npm script, `pm2.json`, `chart/values.yaml`, README docker example | Several different hosts. All embedded URLs include credentials |
| SMTP | `MAIL_URL` in settings, pm2, and README | Gmail SMTP |
| Twilio | `imports/api/Users/server/init/sms.js` | Sid, token, and from-number are constants. Sending number is a +27 number |
| ActiveCampaign | settings keys `ACTIVE_CAMPAIGN_*` | Host `filmspace.api-us1.com` |
| Intercom | `imports/api/Intercom/` | Events from bookings, listings, messages |
| AWS SDK S3 | `digital-ocean-spaces-client.js` | Region `af-south-1`. Endpoint line for DigitalOcean is commented. Bucket `filmspace-assets` |
| Google Maps | `public.GOOGLE_API_KEY` | In dev settings |
| reCAPTCHA | `recaptchaVerify` | Secret constant. Dev settings set `DISABLE_RECAPTCHA` to 1 |
| Google OAuth | `authentication_services.js` | Client id and secret upserted every startup |
| Facebook OAuth | same | App id and secret upserted every startup |
| Google Tag Manager | `router.js` | `UA-158596565-1` |
| Facebook pixel | `router.js` | `fbq("track", "PageView")` |

Development SMS does not leave the process (`SMSNotifications`).

## Infrastructure and deployment

Three deploy descriptions are in git:

1. **Galaxy.** `deploy.bat` sets `DEPLOY_HOSTNAME=eu-west-1.galaxy.meteor.com` and runs `meteor deploy filmspace.codeswop.com --settings settings.json`. `settings.json` has a `galaxy.meteor.com.env` block.
2. **VM and PM2.** README copies a meteor build tarball to `codeswop@filmspace.codeswop.com` or `codeswop@filmspace.co.za` and runs `pm2 restart`. `pm2.json` runs `/home/ubuntu/everyspace/bundle/main.js` on port 3000, `ROOT_URL` `https://everyspace.co.za/`, `BIND_IP` `127.0.0.1`, Mongo host `10.0.20.5`.
3. **GitLab Kubernetes.** `.gitlab-ci.yml` builds with Docker 20.10, exports `app.tar.gz`, pushes the image, deploys staging on the default branch with auto-deploy image `v2.18.1`, and production only `when: manual`. Chart name `auto-deploy-meteor`. Ingress TLS via `kubernetes.io/tls-acme`. Service external port 5000, internal 3000. Liveness and readiness hit `/`. HPA is disabled. Postgres is disabled. `application.mongo_url` in `values.yaml` points at a public IP with a credential.

Dockerfile stages: `geoffreybooth/meteor-base:2.4` builds the bundle (`meteor npm run build` is allowed to fail with `exit 0`), then `node:14.18.1-alpine` installs server npm dependencies and rebuilds sharp, then a final stage installs Chromium for Puppeteer (`PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser`) and tars the bundle to `/app.tar.gz`. Entrypoint `/docker/entrypoint.sh`, command `node main.js`.

`deploy.sh` is an empty file.

Which of the three paths is production: **Unknown / Requires Confirmation**.

## Environment configuration

| Source | Used when |
|---|---|
| `dev.settings.json` | `npm start` (`meteor run --settings`) |
| `settings.json` | `deploy.bat` Galaxy deploy |
| Process env `MONGO_URL`, `MAIL_URL`, `DO_ACCESS_KEY`, `DO_SECRET`, `ACTIVE_CAMPAIGN_*`, `ROOT_URL`, `PORT` | `pm2.json`, Docker README, and production branches in `digital-ocean-spaces-client.js` |
| GitLab CI variables | `CI_REGISTRY_*`, `KUBE_INGRESS_BASE_DOMAIN`, auto-deploy secrets |

`digital-ocean-spaces-client.js` reads `Meteor.settings.DO_SPACES` in development and `process.env.DO_ACCESS_KEY` / `DO_SECRET` otherwise.

Do not copy these files into a new environment without rotating every key.

## Auth

- Meteor accounts: password, Google, Facebook.
- Roles package for `admin`, `host`, and per-listing `editor`.
- Email verification gate on password login.
- Ban flag.
- reCAPTCHA helper method. Disabled flag in dev public settings.
- Admin users created in `create_admin.js` if missing.

OAuth and reCAPTCHA and Twilio secrets are in source, not only in settings files.

## Background work

`node-cron` schedule `0 8 * * *` calls `enqueueJobs` (`imports/api/JobQueue/server/init/job-queue.js`). No timezone argument is passed. node-cron then uses the server's local timezone.

On startup the queue is reset as described in business rule BR-038. Due jobs run with `Meteor.setTimeout`. Success deletes the job. Failure logs and leaves the job scheduled in the collection (the timeout is not retried by this callback).

Jobs found:

| Method | Module | When |
|---|---|---|
| `bookings.onHostApprovalTimeExpires` | Bookings | 3 days after pre-authorisation |
| `bookings.24hourBookingRequestReminder` | Bookings | 24 hours after pre-authorisation |
| `bookings.onBookingEndTime` | Bookings | listing end, scheduled on accept |
| `bookings.48hourReminder` | Bookings | 48 hours before start, if start is more than 96 hours away |
| `cleanCalendar` | Listings | every 30 minutes |
| `notifyOfNewMessage` | Messaging | 5 minutes after a message |
| `notifyOfRecentDiscussionUnreadMessages` | Messaging | 25 minutes after the 5-minute job |
| `notifyOfUnreadMessages` | Messaging | next 15:00, then reschedules |
| `sendFilmmakerHasBeenReviewedNotification`, `sendHostHasBeenReviewedNotification` | Reviews | when scheduled by review email methods |

## Storage

Images and PDFs go to bucket `filmspace-assets` via the AWS SDK client above. Watermarks use `private/images/watermark.png` and sharp. A commented client URL mentions the DigitalOcean Frankfurt CDN hostname. **Requires Confirmation** of the live provider.

## Monitoring

No application monitoring package was found. Helm values set `prometheus.metrics: false`. PM2 is configured with `log_date_format`. GitLab environments record a URL. Intercom and ActiveCampaign receive product events. Google Tag Manager and Facebook receive page views.

## Security

Committed credentials (rotate; do not duplicate the values):

- `settings.json`, `dev.settings.json`, `pm2.json`, `chart/values.yaml`, README Docker example.
- `PeachPaymentsService.js`, `authentication_services.js`, `sms.js`, `recaptchaVerify` in `imports/api/Users/server/methods/users.js`.
- `create_admin.js` password `admin`.
- Commented mail URL and user id in `server/main.js`.

Other notes from the code, not a pentest:

- Several booking mutation methods do not check that the caller is the host or the guest.
- `toggleUserIsHost` does not check the caller.
- `bookings.setExpiresIn` and `bookings.setEndDateIn` do not check the caller.
- `/testing` and `/dev-test` are public.
- `bookings.delete` removes a booking and edits the calendar with no role check in the method body that was read.
- Dockerfile Puppeteer launch in the app uses `--no-sandbox`.
- GitLab production deploy is manual. Staging deploys on every default-branch pipeline.

## Development and test process

README: `npm install`, `npm start`. Node version notes conflict with Docker, as above. Email template note points at an external `eml_to_html` tool.

Scripts: `test` is `meteor test --once --driver-package meteortesting:mocha`. `test-app` watches the full app. The only test file asserts the package name and `Meteor.isClient` / `Meteor.isServer`.

`db.bat` exists for local database tasks (not re-run for this document). `random-activities-for-testing.js` is explicitly a test-data script.

CI on GitLab builds a Docker image and deploys. There is no test job in `.gitlab-ci.yml`.

## Duplicate and stale code worth knowing

- `listing.delete` is defined on both client and server.
- `listings.getLocation` is defined in `search.ts` and `_search.js`.
- `PricingInputGroup` exists as `.jsx` and `.tsx`.
- `src/` Foundation site and the Meteor UI both exist.
- Product strings Filmspace and Everyspace are mixed, including the detail route `/filmspace-detail/:listingId` on an Everyspace landing page.
