MaxMind was removed by us; the getGeoForIp closure still carried the
fallback block and depended on a 'geodb' container entry that resolved
to null (no register binding remains). Callers relied solely on the
appwrite-geo HTTP service path, making the fallback unreachable.
Removes:
- Local MaxMind fallback block in getGeoForIp
- 'geodb' dep from the getGeoForIp deps array and closure signature
- Dead 'geodb' container binding in init/resources.php that only
wrapped a never-registered register->get('geodb')
_APP_GEO_ENDPOINT and _APP_GEO_SECRET already exist in .env and
docker-compose; appwrite container keeps _APP_GEO_SECRET so it can
send it as Bearer to appwrite-geo.
Raises `phpstan.neon` level from 3 to 4 and fixes the 549 new errors
that level 4 surfaces across 157 files. Fixes are root-cause — no
`@phpstan-ignore`, no `@var` casts, no baseline entries, no widened
types. A handful of latent bugs were fixed along the way:
- `app/controllers/general.php`: path-traversal guard was negating
`\substr(...)` before the strict comparison (`!\substr(...) === $base`
was always `false === $base`). Rewritten as `\substr(...) !== $base`.
- `src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php`
and `.../TablesDB/Logs/XList.php`: were importing the raw Matomo
`DeviceDetector` (whose `getDevice()` returns `?int`) but treating the
result as an array with `deviceName/deviceBrand/deviceModel` keys.
Swapped to `Appwrite\Detector\Detector`, matching the wrapper already
used a few lines below for `$os`/`$client`.
- `src/Appwrite/Platform/Modules/Functions/Workers/Builds.php`: a match
key was checking `$resourceKey === 'functions'` when `$resourceKey`
is `'functionId'|'siteId'` — always false. Switched to the intended
`$resource->getCollection() === 'functions'` check.
- `src/Appwrite/OpenSSL/OpenSSL.php`: `encrypt()` return type tightened
to `string|false` to match `openssl_encrypt`; this lets callers'
`=== false` error handling remain meaningful.
- `app/controllers/api/messaging.php`: removed a dead
`array_key_exists('from', [])` branch in the Msg91 provider (empty
array literal; branch was unreachable).
Large cleanup categories across the 549 fixes:
- Removed redundant `?? default` on array offsets and expressions that
PHPStan now knows are non-nullable.
- Removed unreachable statements (mostly `return;` after `throw` or
`markTestSkipped()`).
- Removed redundant `is_array`/`is_string`/`is_bool`/`instanceof` checks
on already-narrowed types.
- Added `default =>` arms (or throwing arms) to non-exhaustive matches
on `string`/`mixed` input.
- Removed dead `$document === false` branches where method return types
were tightened to non-nullable `Document`.
- Removed unused properties (`$version` on Etsy/Zoom OAuth2, `$paths` on
Installer State, `$source` on MigrationsWorker, `$account2` on two
GraphQL auth tests), unused traits (`ApiVectorsDB`, `DatabaseFixture`),
and an unused `cleanupStaleExecutions` task method.
- Replaced `assertTrue(true)` and redundant `assertIsArray`/`assertIsString`/
`assertNotNull` assertions with `addToAssertionCount(1)` or
`assertNotEmpty` where the runtime type was already known.
Resolve conflicts in app/controllers/api/account.php by keeping HEAD's
GeoRecord injection (the branch's core feature) and adopting main's Bus
pattern for session alerts:
- Drop the local sendSessionAlert() helper function; session alerts are
now dispatched via Bus
- $createSession signature: replace Mail $queueForMails with Bus $bus
- Same swap in the email/password login action and the deprecated magic
URL session update action
- Two inline sendSessionAlert() calls replaced with
$bus->dispatch(new SessionCreated(user, project, session, locale))
- Add 'use Appwrite\Bus\Events\SessionCreated;' and 'use Utopia\Bus\Bus;'
imports
- Other Mail $queueForMails injections (Magic URL, OTP, verification,
recovery emails) are unaffected — they remain on the Mail queue
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Make geodb resource return null gracefully when MaxMind DB is not
registered (geo service replaces it)
- Rename $geodb -> $geoRecord in magic-url session endpoint for clarity
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These resources depend on the request object which is only available
in the per-request container scope. Moving them from resources.php to
resources/request.php fixes "Failed to find resource: geoRecord" error.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolve merge conflicts:
- Adapt geo resources to use $container->set() DI pattern
- Keep GeoRecord type hints while adding domainVerification/cookieDomain params
- Remove duplicate resources already moved to request.php
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add restart policy and logging config to appwrite-geo docker service
- Fix auth denylist condition bug (wrong parentheses) and normalize country codes
- Use geoRecord->isEu() directly instead of re-checking config in general.php
- Add array type guard for geo service JSON response in resources.php
- Add getGeoForIp callable resource with per-IP caching for log endpoints
- Fix all log endpoints to look up geo per log entry IP instead of using
the viewer's geoRecord, which was returning the same country for all logs
https://claude.ai/code/session_01JUg4bHzgpHrkvZdPeKsTVM
Resolve conflicts in docker-compose.yml, Documents/Logs/XList.php,
and TablesDB/Rows/Logs/XList.php. Keep geoRecord (new geo service)
while integrating getDatabasesDB from 1.9.x. Fix 6 additional
auto-merged files that incorrectly used geodb/locale instead of
geoRecord.
https://claude.ai/code/session_01Pp3FnMwnJqph6ohBatUFYL
Since setDocumentType('users', User::class) is registered on all
database instances, getDocument('users', ...) already returns User
instances. The new User($doc->getArrayCopy()) pattern was redundant
and could lose internal state managed by the database layer.
https://claude.ai/code/session_01JLPDurUgyj7qViA8JqQFTH
The user resource and realtime handlers return Document objects from
getDocument(), but isPrivileged()/isApp() are now instance methods on
the User class. Wrapping results with new User() ensures the correct
type is returned for all code paths.
https://claude.ai/code/session_01JLPDurUgyj7qViA8JqQFTH
Upgrade utopia-php/framework from 0.33.x to 0.34.x which removes the
static Http::setResource() API. Resources are now registered on a
Utopia\DI\Container instance.
- Replace 81 Http::setResource() calls in resources.php with $container->set()
- Refactor http.php to use Swoole HttpServer adapter with shared container
- Refactor realtime.php to use FPM adapter with global container
- Refactor cli.php to use direct $cli->setResource() calls
- Update Specs.php to use local container + FPM adapter
- Update Migrate.php to inject console document instead of creating Http instance
- Update GraphQL Schema.php to use instance setResource()
The team resource query was changed from $internalId to $sequence,
breaking console session authentication (401 on project creation).
Also removed duplicate utopia-php/servers entry in composer.json
and updated composer.lock hash.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>