Backends can register subcommands for cli.php by overriding cliUsage() and cli($args) on the base backend class. Only the backends below do so in this tree (as of the current code).
Invocation:
php server/cli.php <backendKeyFromConfig> --flag ...
<backendKeyFromConfig> is the key under config.json → "backends" (e.g. files, not the PHP folder mongo).
| Backend key | PHP | Role |
|---|---|---|
files |
backends/files/mongo/mongo.php |
GridFS indexes, expiry cleanup, bulk expire edits, migration to extfs |
extfs |
backends/extfs/internal/internal.php |
Delete orphan files on disk that GridFS no longer references |
mkb |
backends/mkb/internal/internal.php |
Text + field indexes on one MKB user Mongo collection |
users |
backends/users/internal/internal.php |
Admin disable 2FA by login |
households |
backends/households/internal/internal.php |
Import RFID keys from CSV into a house |
tt |
backends/tt/tt.php + tt/internal/internal.php |
Export TT assets to disk; sync viewers from disk; rebuild TT issue indexes |
dvrExports |
backends/dvrExports/dvrExports.php |
Run one DVR export job and notify via inbox |
All other backends keep the default empty cliUsage() / no-op cli().
Context: MongoDB database name comes from backend config; collection is standard GridFS fs.files / fs.chunks. Several commands align with cron-driven cleanup / compact behavior.
Calls listIndexes() on fs.files, prints each index name (one per line), then a total count. Exits 0.
- Scans all GridFS file documents in pages of 1024 (
searchFiles), collecting everymetadata.*field name seen across files. - Builds a list:
filename,uploadDate,md5, plus eachmetadata.<key>(unique). - For each field, creates an ascending index named
index_<field>onfs.files(errors swallowed per index). - Prints how many indexes were (re)created. Exits 0.
Lists indexes on fs.files, drops every index whose name starts with index_ (default GridFS/system indexes with other names are left). Prints count dropped. Exits 0.
Parses comma-separated field names, builds one compound ascending index on fs.files named manual_index_<_field1_field2...>. Prints 0 or 1 created. Exits 0.
Lists fs.files indexes and drops the index whose name exactly equals the argument. Prints count dropped (0 or 1 in normal cases). Exits 0.
Runs cleanup(): finds fs.files documents with metadata.expire less than current Unix time, deletes each via deleteFile() (removes GridFS file + chunks). Exits 0 (output comes from delete path). Same logic as cron 5min for this backend.
Requires loadBackend("extfs") to succeed. Optional --query=<json> merges an extra Mongo filter (must be valid JSON; otherwise exits 1).
Selection: files with length > 0, no metadata.expire, and metadata.external missing or false. Optionally AND your JSON filter.
Per file: sets metadata.external = true, copies uploadDate to metadata.realUploadDate, reads the GridFS stream, calls addFile() (writes into configured storage — typically extfs when that backend is active), then deleteFile() on the GridFS id. Prints . per file. Runs Mongo compact on fs.chunks in an inner loop and again after the batch. Prints total files moved. Exits 0 (or prints “extfs is not available” if backend missing).
Parses <date> with strtotime(). If invalid, falls through to parent::cli().
Otherwise optional --query=<json> (invalid JSON → exit 1). Builds a query: documents that have metadata.expire and where expire is not already the new timestamp (checks both integer and string inequality). AND-merge optional filter. updateMany sets metadata.expire to the new Unix time. Prints matched vs modified counts. Exits 0.
Runs cleanup():
- Loads the
filesbackend. - Recursively walks the configured
extfsroot directory. - For each regular file, treats the filename as the md5 id used in
metadata.md5idsearches. - If
files->searchFiles([ "metadata.md5id" => $uuid ])returns no rows, printsunused file found …, deletes the file from disk, increments counter.
Prints total files deleted. Exits 0.
Meaning: after GridFS/metadata and extfs paths diverge (e.g. DB row removed but file left on disk), this reclaims disk space.
Mongo database name from config; collection name equals the MKB login string passed on the CLI.
Calls private createIndexes($login):
- Creates a text index named
fullTextover subject/body/tags/comments/subtasks fields (language fromconfig["language"]oren). - Creates ascending indexes
index_<field>for:type,author,name,subject,color,body,desk,date,inbox,done.
Prints count of indexes created (or 0 if the helper returned true only). Exits 0.
Lists indexes on collection $login, drops every index except _id_. Prints how many dropped. Exits 0.
Resolves login → uid via getUidByLogin. If missing, dies with user not found. Calls twoFa($uid, false) to clear 2FA state. Prints success or failure. Exits 0.
- Loads flats for the house via
getFlats("houseId", houseId); builds a map flat number → flatId. Dies if no flats. - Dies if CSV path does not exist.
- Reads CSV lines; splits by comma. Without
--rf-first: column 0 = flat number, column 1 = RFID key. With--rf-first: column 0 = key, column 1 = flat. - For each pair, if flat exists, calls
addKey($key, 2, $flatId, "imported <timestamp>")(accessType2 = RFID-style access to flat). Counts successes; prints per-row messages and total keys imported. Exits 0.
addKey validates RFID string length (6–32) and inserts into houses_rfids, then notifies queue if present.
Dispatch order: internal’s cli() runs first (index commands), then parent::cli($args) (tt handles export/replace). Any tt “files” flag is handled only after index branches do not match.
Paths are under server/data/files/ (relative to tt.php location: ../../data/files/...).
| Flag | Behavior |
|---|---|
--export-workflows |
getWorkflows() then for each workflow id getWorkflow($id) → write workflows/<id>.lua. |
--export-filters |
Each filter id → getFilter($id) → filters/<id>.json. |
--export-viewers |
Each viewer → write viewers/<filename>.js from stored code. |
--replace-viewer=<name.js> |
Finds viewer whose filename + .js equals the argument; if matching file exists on disk, reads it and putViewer(field, name, code). |
--replace-all-viewers |
Scans viewers/ directory; for each .js file that matches a known viewer filename, putViewer from disk content. Prints how many replaced. |
All exit 0 after work (or on first hard failure in replace paths).
Uses Mongo collections named by project acronym (--project=<acronym> where required).
| Flag | Behavior |
|---|---|
--list-indexes |
Requires --project. Lists index names on that collection. |
--create-indexes |
reCreateIndexes(): for every TT project, rebuilds fullText index from project settings (subject/description/comments/custom searchable fields); stores a hash in Redis FTS:<acronym> to skip unchanged definitions. Then ensures standard single-field indexes (index_*) exist for assigned, author, catalog, created, etc., plus custom fields marked indexed — drops obsolete index_* not in the desired set. Prints total index operations counted. |
--drop-indexes |
For each project, drops indexes whose names start with index_ (fullText handled separately in recreate). |
--create-index=<fields> |
Requires --project. Compound index manual_index_<fields> on that collection. |
--drop-index=<name> |
Requires --project. Drops index by exact name. |
Casts id to int. Calls abstract runDownloadRecordTask($recordId) on the concrete dvrExports variant. If it returns a uuid:
- Loads
inboxandfiles. - Reads file metadata via
files->getFileMetadata($uuid). - Sends a localized inbox message to
subscriberId(mobile API link text includes config).
Exits 0 (also exits 0 when task does not return uuid — no inbox in that case).