Skip to content

Refactor generation flow and upgrade docs#1

Merged
AmaLS367 merged 23 commits into
mainfrom
refactor/generate-api-hardening
May 13, 2026
Merged

Refactor generation flow and upgrade docs#1
AmaLS367 merged 23 commits into
mainfrom
refactor/generate-api-hardening

Conversation

@AmaLS367
Copy link
Copy Markdown
Owner

Summary

  • Refactors the document generation flow into focused services for amoCRM access, template rendering, quoting, field mapping, security, cache, notes, and logging.
  • Fixes frontend API path handling and moves quote/totals preview to backend-calculated data.
  • Reworks documentation into a GitHub showcase README, split English/Russian docs, visual assets, diagrams, and community health files.

User Impact

  • Browser generation now requires a server-issued generate_token from prefill, while HMAC remains available for trusted server-to-server clients.
  • amoCRM custom fields are mapped by field ID instead of fragile display names.
  • Documentation is easier to navigate and includes setup, API, templates, operations, security, and contribution guidance.

Test Plan

  • ./vendor/bin/phpunit / .\vendor\bin\phpunit — OK (35 tests, 86 assertions)
  • Markdown relative links checked
  • Issue template structure checked
  • PHP syntax checked earlier in branch: 32 files OK

Notes

  • config/config.php remains ignored because it may contain secrets; tracked defaults live in config/config.example.php.
  • GitHub social preview asset is referenced at docs/assets/github-social-preview.png.

@AmaLS367 AmaLS367 marked this pull request as ready for review May 13, 2026 14:24
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the AmoDocsEngine into a modular service-oriented architecture, introducing dedicated classes for amoCRM integration, document generation, security, and storage. Key additions include a new quote calculation endpoint, a token-based authentication system for browser requests, and a comprehensive PHPUnit test suite. Feedback identifies several opportunities to improve robustness by replacing the error suppression operator (@) with explicit error handling and validation for filesystem operations in the document generation, logging, and storage services.


$templatePath = $this->templateRegistry->path($template);
$documentDir = rtrim((string)$this->config['document_path'], '/');
@is_dir($documentDir) || @mkdir($documentDir, (int)$this->config['dir_mode'], true);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using the error control operator (@) for mkdir can hide filesystem permission issues. If the directory for documents cannot be created, the subsequent saveAs call on the template processor will fail, and the root cause might not be obvious. Explicitly checking for success and throwing an exception would make this more robust.

if (!is_dir($documentDir) && !mkdir($documentDir, (int)$this->config['dir_mode'], true) && !is_dir($documentDir)) {
    throw new \RuntimeException(sprintf('Document directory "%s" could not be created.', $documentDir));
}

Comment thread src/Support/JsonLogger.php Outdated
Comment on lines +31 to +36
@is_dir($dir) || @mkdir($dir, $this->dirMode, true);
file_put_contents(
$this->path,
json_encode($event, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n",
FILE_APPEND
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Silently suppressing errors from mkdir and file_put_contents can be problematic for a logger. If logging fails due to permission issues, you won't know about it, which defeats the purpose of logging. It's better to let the errors surface by removing the @ operator and checking function return values. Using trigger_error is a good way to report this without halting execution by throwing an exception.

if (!is_dir($dir) && !mkdir($dir, $this->dirMode, true) && !is_dir($dir)) {
    trigger_error("Could not create log directory: {$dir}", E_USER_WARNING);
    return;
}
$result = file_put_contents(
    $this->path,
    json_encode($event, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n",
    FILE_APPEND
);
if ($result === false) {
    trigger_error("Could not write to log file: {$this->path}", E_USER_WARNING);
}

Comment thread src/AmoCrm/AmoCrmNoteService.php Outdated

public function replaceDocumentNote(int $leadId, string $template, string $url): void
{
@is_dir($this->metaDir) || @mkdir($this->metaDir, 0775, true);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The use of the error control operator (@) for mkdir can hide filesystem permission issues. If the directory for metadata cannot be created, file_put_contents will fail, and the root cause might not be obvious. This could lead to issues with note tracking that are hard to debug.

if (!is_dir($this->metaDir) && !mkdir($this->metaDir, 0775, true) && !is_dir($this->metaDir)) {
    throw new \RuntimeException(sprintf('Directory "%s" was not created', $this->metaDir));
}

Comment thread src/Security/GenerateTokenStore.php Outdated
Comment on lines +101 to +102
@is_dir($dir) || @mkdir($dir, 0775, true);
file_put_contents($this->path, json_encode($tokens, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The use of the error control operator (@) for mkdir and the lack of checking the return value of file_put_contents can mask underlying filesystem issues, such as incorrect permissions. This could lead to silent failures where tokens are not saved, causing authorization problems that are difficult to debug. It would be more robust to check the return values and throw an exception on failure.

if (!is_dir($dir) && !mkdir($dir, 0775, true) && !is_dir($dir)) {
    throw new \RuntimeException(sprintf('Directory "%s" was not created', $dir));
}
if (file_put_contents($this->path, json_encode($tokens, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)) === false) {
    throw new \RuntimeException(sprintf('Unable to write to file "%s"', $this->path));
}

Comment thread src/Storage/PrefillCache.php Outdated
Comment on lines +52 to +58
@is_dir($this->cacheDir) || @mkdir($this->cacheDir, $this->dirMode, true);
file_put_contents($this->path($leadId), json_encode([
'saved_at' => time(),
'template' => $template,
'discount' => $discount,
'products' => $products,
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using the error control operator (@) here can suppress important filesystem errors. If the cache directory cannot be created, file_put_contents will fail silently (or with a warning that isn't handled), which could lead to data not being cached as expected. Explicit error handling would make this more robust.

if (!is_dir($this->cacheDir) && !mkdir($this->cacheDir, $this->dirMode, true) && !is_dir($this->cacheDir)) {
    throw new \RuntimeException(sprintf('Cache directory "%s" could not be created.', $this->cacheDir));
}
$data = json_encode([
    'saved_at' => time(),
    'template' => $template,
    'discount' => $discount,
    'products' => $products,
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
if (file_put_contents($this->path($leadId), $data) === false) {
    throw new \RuntimeException(sprintf('Unable to write to cache file: %s', $this->path($leadId)));
}

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7996817dab

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

private function authMode(): string
{
$security = is_array($this->config['security'] ?? null) ? $this->config['security'] : [];
$mode = (string)($security['generate_auth_mode'] ?? 'browser_token');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve legacy HMAC mode when a secret exists

Existing deployments that followed the previous configuration only have a top-level hmac_secret; before this change, generate.php enforced and accepted X-Signature whenever that value was non-empty. With this default, those same signed requests now fall into browser_token mode and are rejected unless the client also sends a freshly minted generate_token, so upgrading breaks server-to-server generation for any install that has not added the new security.generate_auth_mode key. Default to HMAC/either when the legacy secret is present, or migrate the old key explicitly.

Useful? React with 👍 / 👎.

@AmaLS367 AmaLS367 merged commit 09df5ea into main May 13, 2026
1 check passed
@AmaLS367 AmaLS367 deleted the refactor/generate-api-hardening branch May 13, 2026 14:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant