Skip to content

Feat/add rust fs and local storage support#1670

Draft
sujitaw wants to merge 2 commits into
mainfrom
feat/add_rust_fs_and_local_storage_support
Draft

Feat/add rust fs and local storage support#1670
sujitaw wants to merge 2 commits into
mainfrom
feat/add_rust_fs_and_local_storage_support

Conversation

@sujitaw
Copy link
Copy Markdown
Contributor

@sujitaw sujitaw commented Jun 5, 2026

What

  • added file storage support for
  • rust fs
  • local machine storage

Summary by CodeRabbit

  • New Features

    • Added local file storage capability, enabling files to be stored and served from the local filesystem as an alternative to cloud storage.
    • Added RustFS integration support with configurable endpoints and credentials.
  • Chores

    • Updated configuration with local storage toggles and RustFS integration settings.

sujitaw added 2 commits June 4, 2026 13:24
Signed-off-by: sujitaw <sujit.sutar@ayanworks.com>
Signed-off-by: sujitaw <sujit.sutar@ayanworks.com>
@sujitaw sujitaw self-assigned this Jun 5, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 5, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR enables local filesystem storage as an alternative to AWS S3 throughout the platform. Environment flags (IS_LOCAL_FS, IS_LOCAL_RUSTFS) control the behavior globally. AwsService implements conditional logic in upload, retrieval, deletion, and persistence operations. Files are written to disk with timestamped names when local mode is enabled. URLs are constructed to point to the local PLATFORM_URL instead of S3.

Changes

Local Filesystem Storage Support

Layer / File(s) Summary
Configuration and Environment Setup
.env.demo
New toggles and RustFS connection settings for local filesystem and RustFS modes, with guidance for required AWS bucket variables.
Local Filesystem Storage Implementation
libs/aws/src/aws.service.ts
AwsService constructor reads local-mode flags and initializes directory paths. Methods uploadFileToS3Bucket, uploadCsvFile, getFile, deleteFile, and storeObject each add conditional local-filesystem branches that write/read from disk with timestamped or JSON filenames; non-local paths remain S3-based.
URL Construction and File Serving
apps/utility/src/utilities.service.ts, apps/api-gateway/src/main.ts
Utilities service returns uploadResult.Location directly for local modes, bypassing the SHORTENED_URL_DOMAIN URL construction. API gateway mounts uploadedFiles/ directory as a static HTTP route.
Issuance Service CSV Template Adjustment
apps/issuance/src/issuance.service.ts
Minor localized edit in the INDY branch of uploadCSVTemplate between schemaName assignment and file loading.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

The PR adds conditional local-filesystem logic across multiple methods in AwsService with varying complexity (simple reads/writes, JSON serialization, URL construction). Changes are consistent in pattern but spread across five files with distinct roles. Requires verification that all file operations (upload, read, delete, persist) correctly switch between local and S3 modes, and that URL construction works for both paths.

Suggested reviewers

  • shitrerohit
  • tipusinghaw

Poem

🐰 Files now rest on disk, no cloud to roam,
A toggle switches storage, local feels like home,
AWS gives way to folders deep and wide,
Timestamps mark each upload with rabbit pride! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: adding RustFS and local storage support. It aligns with the primary purpose of the pull request across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/add_rust_fs_and_local_storage_support

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jun 5, 2026

@sujitaw
Copy link
Copy Markdown
Contributor Author

sujitaw commented Jun 5, 2026

@coderabbitai fullreview

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 5, 2026

@sujitaw Sure! I'll redo the full review of this PR now.

✅ Action performed

Full review finished.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.env.demo:
- Around line 137-139: Replace the concrete credentials in the .env template by
removing the weak defaults and using empty placeholders for RUSTFS_ACCESS_KEY_ID
and RUSTFS_SECRET_ACCESS_KEY (e.g. set them to empty values) and keep
RUSTFS_REGION as a default region if desired; also add a short inline comment in
the file near RUSTFS_ACCESS_KEY_ID / RUSTFS_SECRET_ACCESS_KEY that clearly
states these must be supplied at deploy time and must not be checked in. This
ensures the environment variables RUSTFS_ACCESS_KEY_ID and
RUSTFS_SECRET_ACCESS_KEY are not hard-coded and documents the requirement to
provide real secrets during deployment.

In `@apps/api-gateway/src/main.ts`:
- Line 125: The unconditional mount app.use('/uploadedFiles',
express.static('uploadedFiles')) exposes the whole storage tree; change this to
only mount explicit safe subdirectories (e.g., '/uploadedFiles/public' ->
express.static(path.join('uploadedFiles','public'))) and/or wrap the mount in a
feature-flag check (e.g., isLocalStorageEnabled() or
process.env.LOCAL_STORAGE_ENABLED) so the static middleware is registered only
when local storage serving is allowed; ensure you reference and update the
app.use call and the '/uploadedFiles' mount point accordingly and prefer
path.join for safe paths and a whitelist of allowed subfolders.

In `@libs/aws/src/aws.service.ts`:
- Around line 20-26: The constructor returns early when this.isLocalFs is true
but later code still expects AWS_BUCKET, AWS_ORG_LOGO_BUCKET_NAME, and
AWS_S3_STOREOBJECT_BUCKET to be set, causing path.join with undefined; update
the constructor (around the this.isLocalFs / localStoragePath logic) to validate
those env vars when this.isLocalFs is true and fail fast by throwing a clear
error (or set safe defaults) before returning so subsequent local file path
operations (that use those bucket envs) won’t receive undefined.
- Around line 155-158: The local delete in aws.service.ts currently does await
fs.unlink(filePath) inside the isLocalFs branch which will throw if the file
doesn't exist; make this idempotent like S3 by wrapping the unlink in a
try/catch inside the same method (the isLocalFs branch) and swallow errors whose
code === 'ENOENT' but rethrow any other errors so only missing-file cases are
ignored; ensure the try/catch surrounds the call referencing the same
localStoragePath/process.env.AWS_BUCKET/key resolution used to build filePath.
- Around line 81-87: Validate and normalize any user-supplied path segments
(pathAWS, key, objKey, filename) before joining them into filesystem paths:
strip or reject path traversal like "../" (e.g., replace with "" or use
path.basename for filename segments), use path.normalize on the combined path,
then compute the final filePath by path.join(bucketDir, normalizedSegment) and
verify the resulting absolute path starts with the bucketDir absolute path
(using path.resolve) — if it does not, throw an error. Apply this same
validation/sanitization logic wherever pathAWS/key/objKey are used (references:
bucketDir, fileKey, filePath, ensureDir, localStoragePath) to prevent escaping
uploadedFiles/<bucket> in local mode. Ensure returned URL still uses the
original safe URL-encoded filename but only after the filesystem checks pass.
- Around line 116-120: uploadCsvFile currently only ensures the bucket root dir
(using ensureDir on bucketDir) but then writes filePath directly, which fails
for nested keys like "foo/bar.csv"; update uploadCsvFile to create parent
directories for the final file by calling ensureDir on the file's directory
(e.g. path.dirname(filePath)) before calling fs.writeFile so nested prefixes in
key behave like S3; reference the existing localStoragePath,
process.env.AWS_BUCKET, key, filePath, ensureDir, and fs.writeFile when making
this change.
- Around line 49-64: The S3 clients (this.s3, this.s4, this.s3StoreObject)
always read AWS_*_REGION env vars but when IS_LOCAL_RUSTFS is set you should
prefer RUSTFS_REGION; update the region selection logic in the constructor
(where these S3 instances are created) to use process.env.RUSTFS_REGION when
process.env.IS_LOCAL_RUSTFS is truthy, falling back to the existing AWS_*_REGION
values otherwise so RustFS mode uses the correct region.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b0c19a79-f348-4b2d-9ee3-990730efae19

📥 Commits

Reviewing files that changed from the base of the PR and between 921c73c and 5de6aec.

📒 Files selected for processing (5)
  • .env.demo
  • apps/api-gateway/src/main.ts
  • apps/issuance/src/issuance.service.ts
  • apps/utility/src/utilities.service.ts
  • libs/aws/src/aws.service.ts
💤 Files with no reviewable changes (1)
  • apps/issuance/src/issuance.service.ts

Comment thread .env.demo
Comment on lines +137 to +139
RUSTFS_ACCESS_KEY_ID=admin
RUSTFS_SECRET_ACCESS_KEY=supersecretpassword
RUSTFS_REGION=us-east-1
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid committing weak/default RustFS credentials in the shared env template.

Line 137 and Line 138 currently set concrete credentials (admin / supersecretpassword). This can be accidentally deployed as-is and weakens baseline security. Use empty placeholders and document that deploy-time secrets are required.

🔧 Suggested change
-RUSTFS_ACCESS_KEY_ID=admin
-RUSTFS_SECRET_ACCESS_KEY=supersecretpassword
+RUSTFS_ACCESS_KEY_ID=
+RUSTFS_SECRET_ACCESS_KEY=
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
RUSTFS_ACCESS_KEY_ID=admin
RUSTFS_SECRET_ACCESS_KEY=supersecretpassword
RUSTFS_REGION=us-east-1
RUSTFS_ACCESS_KEY_ID=
RUSTFS_SECRET_ACCESS_KEY=
RUSTFS_REGION=us-east-1
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 137-137: [UnorderedKey] The RUSTFS_ACCESS_KEY_ID key should go before the RUSTFS_ENDPOINT key

(UnorderedKey)


[warning] 139-139: [UnorderedKey] The RUSTFS_REGION key should go before the RUSTFS_SECRET_ACCESS_KEY key

(UnorderedKey)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.env.demo around lines 137 - 139, Replace the concrete credentials in the
.env template by removing the weak defaults and using empty placeholders for
RUSTFS_ACCESS_KEY_ID and RUSTFS_SECRET_ACCESS_KEY (e.g. set them to empty
values) and keep RUSTFS_REGION as a default region if desired; also add a short
inline comment in the file near RUSTFS_ACCESS_KEY_ID / RUSTFS_SECRET_ACCESS_KEY
that clearly states these must be supplied at deploy time and must not be
checked in. This ensures the environment variables RUSTFS_ACCESS_KEY_ID and
RUSTFS_SECRET_ACCESS_KEY are not hard-coded and documents the requirement to
provide real secrets during deployment.

app.use(express.static('invoice-pdf'));
app.use(express.static('uploadedFiles/bulk-verification-templates'));
app.use(express.static('uploadedFiles/import'));
app.use('/uploadedFiles', express.static('uploadedFiles'));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid unconditional public exposure of the entire uploadedFiles tree.

Line 125 serves all files under uploadedFiles over HTTP. That broadens access beyond previously scoped static directories and can expose internal objects written by local storage flows.

Limit this mount to explicit safe subdirectories and/or gate it behind local-storage flags.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api-gateway/src/main.ts` at line 125, The unconditional mount
app.use('/uploadedFiles', express.static('uploadedFiles')) exposes the whole
storage tree; change this to only mount explicit safe subdirectories (e.g.,
'/uploadedFiles/public' -> express.static(path.join('uploadedFiles','public')))
and/or wrap the mount in a feature-flag check (e.g., isLocalStorageEnabled() or
process.env.LOCAL_STORAGE_ENABLED) so the static middleware is registered only
when local storage serving is allowed; ensure you reference and update the
app.use call and the '/uploadedFiles' mount point accordingly and prefer
path.join for safe paths and a whitelist of allowed subfolders.

Comment on lines +20 to +26
this.isLocalFs = 'true' === process.env.IS_LOCAL_FS;
this.isLocal = 'true' === process.env.IS_LOCAL_RUSTFS;
this.localStoragePath = path.resolve(process.cwd(), 'uploadedFiles');

if (this.isLocalFs) {
return;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail fast on missing bucket envs in local filesystem mode.

When IS_LOCAL_FS=true, the constructor returns early (Line 24-Line 26), but local branches later rely on AWS_BUCKET, AWS_ORG_LOGO_BUCKET_NAME, and AWS_S3_STOREOBJECT_BUCKET. Missing values will cause runtime path errors (path.join with undefined).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/aws/src/aws.service.ts` around lines 20 - 26, The constructor returns
early when this.isLocalFs is true but later code still expects AWS_BUCKET,
AWS_ORG_LOGO_BUCKET_NAME, and AWS_S3_STOREOBJECT_BUCKET to be set, causing
path.join with undefined; update the constructor (around the this.isLocalFs /
localStoragePath logic) to validate those env vars when this.isLocalFs is true
and fail fast by throwing a clear error (or set safe defaults) before returning
so subsequent local file path operations (that use those bucket envs) won’t
receive undefined.

Comment on lines +49 to +64
region: process.env.AWS_REGION,
...localOverrides
});

this.s4 = new S3({
accessKeyId: process.env.AWS_PUBLIC_ACCESS_KEY,
secretAccessKey: process.env.AWS_PUBLIC_SECRET_KEY,
region: process.env.AWS_PUBLIC_REGION
accessKeyId: publicAccessKey,
secretAccessKey: publicSecretKey,
region: process.env.AWS_PUBLIC_REGION,
...localOverrides
});

this.s3StoreObject = new S3({
accessKeyId: process.env.AWS_S3_STOREOBJECT_ACCESS_KEY,
secretAccessKey: process.env.AWS_S3_STOREOBJECT_SECRET_KEY,
region: process.env.AWS_S3_STOREOBJECT_REGION
accessKeyId: storeAccessKey,
secretAccessKey: storeSecretKey,
region: process.env.AWS_S3_STOREOBJECT_REGION,
...localOverrides
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use RUSTFS_REGION when RustFS mode is enabled.

Line 49, Line 56, and Line 63 always read AWS_*_REGION, but RustFS mode is keyed off IS_LOCAL_RUSTFS and .env.demo introduces RUSTFS_REGION. In RustFS mode this can leave region unset/mismatched.

🔧 Suggested change
     this.s3 = new S3({
       accessKeyId: accessKey,
       secretAccessKey: secretKey,
-      region: process.env.AWS_REGION,
+      region: this.isLocal ? process.env.RUSTFS_REGION : process.env.AWS_REGION,
       ...localOverrides
     });

     this.s4 = new S3({
       accessKeyId: publicAccessKey,
       secretAccessKey: publicSecretKey,
-      region: process.env.AWS_PUBLIC_REGION,
+      region: this.isLocal ? process.env.RUSTFS_REGION : process.env.AWS_PUBLIC_REGION,
       ...localOverrides
     });

     this.s3StoreObject = new S3({
       accessKeyId: storeAccessKey,
       secretAccessKey: storeSecretKey,
-      region: process.env.AWS_S3_STOREOBJECT_REGION,
+      region: this.isLocal ? process.env.RUSTFS_REGION : process.env.AWS_S3_STOREOBJECT_REGION,
       ...localOverrides
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
region: process.env.AWS_REGION,
...localOverrides
});
this.s4 = new S3({
accessKeyId: process.env.AWS_PUBLIC_ACCESS_KEY,
secretAccessKey: process.env.AWS_PUBLIC_SECRET_KEY,
region: process.env.AWS_PUBLIC_REGION
accessKeyId: publicAccessKey,
secretAccessKey: publicSecretKey,
region: process.env.AWS_PUBLIC_REGION,
...localOverrides
});
this.s3StoreObject = new S3({
accessKeyId: process.env.AWS_S3_STOREOBJECT_ACCESS_KEY,
secretAccessKey: process.env.AWS_S3_STOREOBJECT_SECRET_KEY,
region: process.env.AWS_S3_STOREOBJECT_REGION
accessKeyId: storeAccessKey,
secretAccessKey: storeSecretKey,
region: process.env.AWS_S3_STOREOBJECT_REGION,
...localOverrides
region: this.isLocal ? process.env.RUSTFS_REGION : process.env.AWS_REGION,
...localOverrides
});
this.s4 = new S3({
accessKeyId: publicAccessKey,
secretAccessKey: publicSecretKey,
region: this.isLocal ? process.env.RUSTFS_REGION : process.env.AWS_PUBLIC_REGION,
...localOverrides
});
this.s3StoreObject = new S3({
accessKeyId: storeAccessKey,
secretAccessKey: storeSecretKey,
region: this.isLocal ? process.env.RUSTFS_REGION : process.env.AWS_S3_STOREOBJECT_REGION,
...localOverrides
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/aws/src/aws.service.ts` around lines 49 - 64, The S3 clients (this.s3,
this.s4, this.s3StoreObject) always read AWS_*_REGION env vars but when
IS_LOCAL_RUSTFS is set you should prefer RUSTFS_REGION; update the region
selection logic in the constructor (where these S3 instances are created) to use
process.env.RUSTFS_REGION when process.env.IS_LOCAL_RUSTFS is truthy, falling
back to the existing AWS_*_REGION values otherwise so RustFS mode uses the
correct region.

Comment on lines +81 to +87
const bucketDir = path.join(this.localStoragePath, bucketName);
await this.ensureDir(path.join(bucketDir, pathAWS));
const timestamp = Date.now();
const fileKey = `${pathAWS}/${encodeURIComponent(filename)}-${timestamp}.${ext}`;
const filePath = path.join(bucketDir, fileKey);
await fs.writeFile(filePath, fileBuffer);
return `${process.env.PLATFORM_URL}/uploadedFiles/${bucketName}/${fileKey}`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Block path traversal in local filesystem key/path handling.

pathAWS, key, and objKey are joined directly into filesystem paths. Values containing ../ can escape uploadedFiles/<bucket> and allow unintended file read/write/delete in local mode.

🔒 Suggested hardening pattern
+  private resolveUnderBase(baseDir: string, relativePath: string): string {
+    const resolvedBase = path.resolve(baseDir);
+    const resolvedTarget = path.resolve(baseDir, relativePath);
+    if (!resolvedTarget.startsWith(`${resolvedBase}${path.sep}`) && resolvedTarget !== resolvedBase) {
+      throw new HttpException('Invalid file key/path', HttpStatus.BAD_REQUEST);
+    }
+    return resolvedTarget;
+  }
-      const filePath = path.join(bucketDir, fileKey);
+      const filePath = this.resolveUnderBase(bucketDir, fileKey);
-      const filePath = path.join(this.localStoragePath, process.env.AWS_BUCKET, key);
+      const bucketDir = path.join(this.localStoragePath, process.env.AWS_BUCKET);
+      const filePath = this.resolveUnderBase(bucketDir, key);

Also applies to: 138-140, 156-157, 177-181

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/aws/src/aws.service.ts` around lines 81 - 87, Validate and normalize any
user-supplied path segments (pathAWS, key, objKey, filename) before joining them
into filesystem paths: strip or reject path traversal like "../" (e.g., replace
with "" or use path.basename for filename segments), use path.normalize on the
combined path, then compute the final filePath by path.join(bucketDir,
normalizedSegment) and verify the resulting absolute path starts with the
bucketDir absolute path (using path.resolve) — if it does not, throw an error.
Apply this same validation/sanitization logic wherever pathAWS/key/objKey are
used (references: bucketDir, fileKey, filePath, ensureDir, localStoragePath) to
prevent escaping uploadedFiles/<bucket> in local mode. Ensure returned URL still
uses the original safe URL-encoded filename but only after the filesystem checks
pass.

Comment on lines +116 to +120
const bucketDir = path.join(this.localStoragePath, process.env.AWS_BUCKET);
await this.ensureDir(bucketDir);
const filePath = path.join(bucketDir, key);
const data = 'string' === typeof body ? body : body.toString();
await fs.writeFile(filePath, data);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Create parent directories for nested CSV keys in local mode.

uploadCsvFile only ensures the bucket root dir. If key includes folders (foo/bar.csv), writeFile fails with ENOENT, while S3 accepts key prefixes.

🔧 Suggested change
       const bucketDir = path.join(this.localStoragePath, process.env.AWS_BUCKET);
-      await this.ensureDir(bucketDir);
       const filePath = path.join(bucketDir, key);
+      await this.ensureDir(path.dirname(filePath));
       const data = 'string' === typeof body ? body : body.toString();
       await fs.writeFile(filePath, data);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const bucketDir = path.join(this.localStoragePath, process.env.AWS_BUCKET);
await this.ensureDir(bucketDir);
const filePath = path.join(bucketDir, key);
const data = 'string' === typeof body ? body : body.toString();
await fs.writeFile(filePath, data);
const bucketDir = path.join(this.localStoragePath, process.env.AWS_BUCKET);
const filePath = path.join(bucketDir, key);
await this.ensureDir(path.dirname(filePath));
const data = 'string' === typeof body ? body : body.toString();
await fs.writeFile(filePath, data);
🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis

[warning] 119-119: 'body' will use Object's default stringification format ('[object Object]') when stringified.

See more on https://sonarcloud.io/project/issues?id=credebl_platform&issues=AZ6XUSxs4lgwc59G6VOC&open=AZ6XUSxs4lgwc59G6VOC&pullRequest=1670

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/aws/src/aws.service.ts` around lines 116 - 120, uploadCsvFile currently
only ensures the bucket root dir (using ensureDir on bucketDir) but then writes
filePath directly, which fails for nested keys like "foo/bar.csv"; update
uploadCsvFile to create parent directories for the final file by calling
ensureDir on the file's directory (e.g. path.dirname(filePath)) before calling
fs.writeFile so nested prefixes in key behave like S3; reference the existing
localStoragePath, process.env.AWS_BUCKET, key, filePath, ensureDir, and
fs.writeFile when making this change.

Comment on lines +155 to +158
if (this.isLocalFs) {
const filePath = path.join(this.localStoragePath, process.env.AWS_BUCKET, key);
await fs.unlink(filePath);
return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Keep local delete semantics idempotent like S3 delete.

In local mode, fs.unlink throws when the file does not exist; S3 deleteObject is effectively idempotent. Swallowing ENOENT avoids unnecessary failures during retries/cleanup flows.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/aws/src/aws.service.ts` around lines 155 - 158, The local delete in
aws.service.ts currently does await fs.unlink(filePath) inside the isLocalFs
branch which will throw if the file doesn't exist; make this idempotent like S3
by wrapping the unlink in a try/catch inside the same method (the isLocalFs
branch) and swallow errors whose code === 'ENOENT' but rethrow any other errors
so only missing-file cases are ignored; ensure the try/catch surrounds the call
referencing the same localStoragePath/process.env.AWS_BUCKET/key resolution used
to build filePath.

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