- TypeScript 46.8%
- Go 31.2%
- HTML 15.8%
- Python 3.2%
- CSS 2.3%
- Other 0.7%
| .claude | ||
| cmd/server | ||
| internal | ||
| scripts | ||
| web | ||
| .air.toml | ||
| .dockerignore | ||
| .env | ||
| .gitignore | ||
| CLAUDE.md | ||
| compose.yaml | ||
| Dockerfile | ||
| flake.lock | ||
| flake.nix | ||
| go.mod | ||
| package-lock.json | ||
| package.json | ||
| presets.json | ||
| README.md | ||
| tsconfig.json | ||
PaperTracker - Econadzor's correspondence visualizer
WARNING: This project is AI-generated using Claude Code
Configuration
All configuration is supplied through environment variables, read once at startup
(internal/config/config.go). For Docker, set them in .env / docker-compose.yml;
for local development the Nix dev shell exports sensible defaults. Changing any value
requires a restart.
| Variable | Default (dev) | Default (container) | Description |
|---|---|---|---|
PORT |
8080 |
8080 |
TCP port the HTTP server listens on. |
DB_PATH |
./dev-data/papertracker.db |
/data/papertracker.db |
Path to the SQLite database file. Created on first run; WAL files live alongside it. |
DOCS_PATH |
./docs |
/docs |
Root folder containing the document tree. Each project's folder is a subdirectory of this path. Mounted into the container (read-write — uploads are written into project folders). |
PROJECT_STATUSES |
active,closed,archived |
active,closed,archived |
Comma-separated list of allowed project statuses. The first entry is the default for new projects. |
MAX_UPLOAD_MB |
25 |
25 |
Maximum upload size in megabytes. 0 = unlimited. Invalid values fall back to 25. |
UPLOAD_EXTENSIONS |
pdf,doc,docx,odt,png,jpg,jpeg,bmp,gif,webp,tiff |
same | Comma-separated allow-list of upload file extensions (case-insensitive, leading dots optional). Empty = allow any type. |
FILE_NAME_MASK |
(unset) | (unset) | Go text/template controlling how uploaded files are renamed. Applies to the main document and is the fallback for attachments whose ATTACHMENT_NAME_MASK is unset. Unset = keep the original filename. The extension is appended automatically; the mask renders the stem only. See File rename mask. |
PRESETS_FILE |
(unset) | /config/presets.json |
Path to a JSON file holding sender/recipient presets. Preferred over PRESETS; if set but unreadable, falls back to PRESETS, then to the built-in default. See Presets. |
PRESETS |
(unset) | (unset) | Inline JSON presets, same format as PRESETS_FILE. Used only when PRESETS_FILE is unset or unreadable. |
Notes:
- The Nix dev shell (
flake.nix) exportsPORT,DB_PATH, andDOCS_PATHand createsdev-data/anddocs/, sogo run ./cmd/serverworks out of the box. - Do not set
DOCS_PATHin your shell when runningdocker compose— it overrides the value in.envand can break the volume mount.
Presets
A preset groups a list of senders, a list of recipients and (optionally) two topic lists
under a key (e.g. ОЭК). Each project selects one preset; its communication nodes then pick
a sender and a recipient from that preset's party lists, and optionally a topic. The first
preset in the list is the UI default; order is preserved.
Presets are loaded from PRESETS_FILE (preferred) or the PRESETS env var. Both use the
same compact JSON: an array of presets whose senders/recipients are
{short_name: full_name} maps and whose topics_sent/topics_received are plain string
arrays.
[
{
"key": "ОЭК",
"senders": { "МОО": "Межрегиональная общественная организация Эконадзор" },
"recipients": { "ГП РФ": "Генеральная прокуратура" },
"topics_sent": ["Жалоба", "Запрос информации"],
"topics_received": ["Ответ на запрос"]
}
]
The full name is what's stored on a node and shown by default; the short name is used by the
UI display setting (Short / Full / Both) and by the rename mask's party variables. Parties
and topics keep the order they appear in the JSON. The two topic lists are optional and are
offered by node type — topics_sent for sent nodes, topics_received for received ones. The
node's topic field is a dropdown: pick one from the list, choose Custom… to type a
free-form one, or leave it as None. If neither source is valid, a built-in ОЭК preset
is used so the app always works.
File rename mask
FILE_NAME_MASK is a Go text/template. The rendered result is the file's stem; the real
extension is appended automatically. Path separators produced by the mask are collapsed, and
an empty result is rejected. It renames the main document and is also the fallback for
attachments when ATTACHMENT_NAME_MASK is unset.
| Variable | Value |
|---|---|
{{.Name}} |
Original filename without extension. |
{{.Slug}} |
Name lowercased, spaces → -, unsafe characters stripped. |
{{.Input}} |
User-entered name (spaces → _). Referencing this prompts the user for a name at upload time. |
{{.Folder}} |
Project folder name. |
{{.ProjectID}} |
Numeric project id. |
{{.SenderShort}} |
Sender short name from the project's preset (empty if no match). |
{{.SenderName}} |
Sender full name as selected on the node. |
{{.RecipientShort}} |
Recipient short name from the project's preset (empty if no match). |
{{.RecipientName}} |
Recipient full name as selected on the node. |
{{.Topic}} |
Node topic (from the preset list or free-form), spaces → _. Empty if none chosen. |
{{.Date}} |
Real (upload) date, YYYY-MM-DD. |
{{.DateU}} |
Real (upload) date, YYYY_MM_DD (underscores). |
{{.Time}} |
Real (upload) time, HH-MM-SS. |
{{.DateTime}} |
Date_Time. |
{{.Unix}} |
Real (upload) epoch seconds. |
{{.NodeDate}} |
The communication's own date, YYYY-MM-DD (empty if unset). |
{{.NodeDateU}} |
The communication's own date, YYYY_MM_DD (underscores). |
{{.UUID}} |
8-character random hex. |
{{.Rand}} |
4-character random hex. |
Example:
- FILE_NAME_MASK={{.Date}}_{{.SenderShort}}-{{.RecipientShort}}_{{.Input}}
→ 2026-06-16_МОО-ГП_РФ_my-file.pdf