-
Notifications
You must be signed in to change notification settings - Fork 7
Adds secret service for plugin config loading #173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
* Get basic working * adds rss feeds * added stablewatch founder and stablecoin intern from messaria as approvers on stablecoins feed * fixes to config for grants, sui, and telegram channels (#102) * add query param for selective processing (#103) * adds query param to process * add query param for processing * simplify * add tags * Feat: implement frontend leaderboard (#93) * feat: implement frontend leaderboard * feat: implement a leaderboard in frontend * feat: implemented leaderboard * fix: rebuild implement leaderboard * fix: prettier * fix: prettier * fix: reimplement frontend leaderboard * fix: implement frontend leaderboard * approval rate * sets approval rate and hides curator --------- Co-authored-by: Elliot Braem <elliot@ejlbraem.com> * remove tailwind-scrollbar * added bob to desci feed * Get basic working * set .env.example * nitpicks --------- Co-authored-by: Elliot Braem <elliot@ejlbraem.com> Co-authored-by: codingshot <45281667+codingshot@users.noreply.github.com> Co-authored-by: Elliot Braem <elliot@everything.dev> Co-authored-by: Louis <112561517+louisdevzz@users.noreply.github.com>
* Explore Page - commit-1 * Explore Page - commit-2 * explore page - commit-3 * explore page - commit - 4 * explore page - commit - prettier * explore page responsiveness + code Rabbit Comments * code Rabbit Comments resolved * css updates * css changes 2 * header update + mobile responsive * conflicts resolved * Rebase and changes * Fix fmt
* Explore Page - commit-1 * Explore Page - commit-2 * explore page - commit-3 * explore page - commit - 4 * explore page - commit - prettier * explore page responsiveness + code Rabbit Comments * code Rabbit Comments resolved * css updates * css changes 2 * header update + mobile responsive * conflicts resolved * Rebase and changes * Fix fmt * Header Updates + Web3Auth getUserInfo + Explore Page changes * fmt * coderabbit comments resolved
* Update the FE to have the profile page (header and tabs init) * Move tabs to it's own component * Add stats and top badges to overview * Finish the overview tab * Update overview page and init content page * feat(profile-page): add Content and My Feed tags * feat(profile-page): finish profile page static UI * refactor: fmt
* Curate Engine Step 1 * content-progress-configuration step-1 * Curation Settings Part 2 and 3 * CodeRabbit Comments Resolved + Mobile Responsive * Responsiveness: empty State, JSON check --------- Co-authored-by: Elliot Braem <elliot@everything.dev>
* Submissions Page + Feed Page + Mobile Responsiveness * fixes * craete-feed authenticated user condition * fmt * remove sqlite --------- Co-authored-by: Elliot Braem <elliot@ejlbraem.com>
* feat: Add connect button to feed page * fix: recommit * added back to stablecoins feed since stablewatch forked their own * fix packages, update pg, and ignore cloudflare sockets * fmt --------- Co-authored-by: ethnclark <ethanclark1310@gmail.com> Co-authored-by: codingshot <45281667+codingshot@users.noreply.github.com> Co-authored-by: Elliot Braem <elliot@ejlbraem.com>
This reverts commit cd12908.
* fix: Sort By Oldest * fix: Sort By Oldest * fix: Fix RecentSubmissions Sort Order Update * fix: All feed should be hidden, remove double title --------- Co-authored-by: vohuunhan1310@gmail.com <ethanclark1310@gmail.com>
* UI fixes * fmt --------- Co-authored-by: Elliot Braem <elliot@everything.dev>
* fix: Leaderboard improvements * fix: fmt * reorganize * remove unused * rename * clean up * fmt --------- Co-authored-by: Elliot Braem <elliot@ejlbraem.com>
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughA new secret management service ("secret-service") is introduced, including its Docker, database schema, API, encryption, and service logic. The main application is updated to support secret hydration in plugins, propagating Changes
Sequence Diagram(s)sequenceDiagram
participant API_Server
participant SecretServiceApiClient
participant SecretService
participant Database
API_Server->>SecretServiceApiClient: getPlaintextSecret(feedId, keyName)
SecretServiceApiClient->>SecretService: POST /v1/secrets/get-plaintext
SecretService->>Database: SELECT secret WHERE feedId, keyName
Database-->>SecretService: Encrypted secret row
SecretService->>SecretService: Decrypt secret
SecretService-->>SecretServiceApiClient: Plaintext secret
SecretServiceApiClient-->>API_Server: Plaintext secret
API_Server->>Plugin: Hydrate config with secret
sequenceDiagram
participant PluginService
participant SecretServiceApiClient
PluginService->>SecretServiceApiClient: getPlaintextSecret(feedId, VAR_NAME)
SecretServiceApiClient-->>PluginService: Secret Value
PluginService->>Plugin: Initialize with hydrated config
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 12
🔭 Outside diff range comments (1)
apps/api/src/services/plugin.service.ts (1)
1-1:⚠️ Potential issueFix code formatting issues.
The pipeline indicates formatting issues in this file.
Run the following command to fix formatting:
pnpm prettier --write apps/api/src/services/plugin.service.ts🧰 Tools
🪛 GitHub Actions: CI
[warning] 1-1: Prettier formatting check failed. Run 'prettier --write' to fix code style issues in this file.
🧹 Nitpick comments (17)
apps/secret-service/.gitignore (1)
1-29: Comprehensive ignore patterns—consider build artifacts.
The file correctly ignores editor, dependency, env, and log files. To prevent accidental commits of compiled outputs, adddist/and TypeScript incremental build files (*.tsbuildinfo).apps/secret-service/README.md (1)
1-11: Enrich README with setup and env details.
Consider documenting prerequisites (Node/PNPM versions), required environment variables (e.g.,SECRET_SERVICE_INTERNAL_API_KEY), and example API endpoints. This will streamline onboarding and usage.apps/api/src/services/feed.service.ts (1)
71-75: Redundant round-trip to DB forfeedConfigYou already fetched the
feedrecord one call earlier. If theFeedentity embeds its config, doing a second query (getFeedConfig) inside the same request adds latency and extra I/O.
Consider:-const feedConfig = await this.feedRepository.getFeedConfig(feedId); +const feedConfig = feed.config ?? await this.feedRepository.getFeedConfig(feedId);If the repository intentionally separates the two, add a quick comment explaining why so future readers don’t assume this is an oversight.
apps/api/src/services/transformation.service.ts (1)
50-65: Uncaught plugin hydration failures bubble as generic errors
pluginService.getPluginmay now throw for missing secrets.
Consider wrapping it to attachstage/transformIndexcontext similar to the later error handling, so operators can immediately see which transform failed to hydrate.-const plugin = await this.pluginService.getPlugin( +const plugin = await this.pluginService.getPlugin( … ) // wrap in try/catch and convert to PluginErrorapps/api/src/env.ts (1)
28-33: Mismatch between “required” typing and runtime fallback handling
SECRET_SERVICE_URLandSECRET_SERVICE_INTERNAL_API_KEYare declared mandatory here, yetServiceProviderstill contains a fallbacklogger.errorbranch and proceeds with!non-null assertions. Either keep them mandatory (preferred) and drop the fallback logic, or mark them.optional()and handle absence robustly.apps/secret-service/src/utils/crypto-helpers.ts (1)
15-17: Add minimal hex input validation
Buffer.from(hexString, "hex")silently ignores odd-length or non-hex characters, which can lead to silent corruption. Consider validating the input first:-export function hexToBuffer(hexString: string): Buffer { - return Buffer.from(hexString, "hex"); +const HEX_RE = /^[0-9a-fA-F]+$/; +export function hexToBuffer(hexString: string): Buffer { + if (hexString.length % 2 !== 0 || !HEX_RE.test(hexString)) { + throw new Error("Invalid hex string"); + } + return Buffer.from(hexString, "hex"); }apps/secret-service/package.json (1)
1-31: Minor QoL scripts
Apreparescript ("prepare": "npm run build") would letnpm link/ local installs compile automatically, andlintcould be included in CI. Optional but useful.apps/secret-service/src/utils/logger.ts (2)
1-6: Loosen log method typings to accept any valueRestricting the first argument to
stringforces callers to stringify objects up-front and loses rich console inspection. Acceptunknown(orany) to mirrorconsole.*signatures.-export interface Logger { - info: (message: string, ...args: any[]) => void; - warn: (message: string, ...args: any[]) => void; - error: (message: string, ...args: any[]) => void; - debug: (message: string, ...args: any[]) => void; +export interface Logger { + info: (message: unknown, ...args: unknown[]) => void; + warn: (message: unknown, ...args: unknown[]) => void; + error: (message: unknown, ...args: unknown[]) => void; + debug: (message: unknown, ...args: unknown[]) => void; }
18-25:debugstill fires in production whenDEBUG=trueThe current guard prints debug logs in prod if someone sets
DEBUG=true, which may leak sensitive data from the secret-service. Consider also checkingNODE_ENV !== "production".- if ( - process.env.NODE_ENV === "development" || - process.env.DEBUG === "true" - ) { + if ( + (process.env.NODE_ENV ?? "production") !== "production" && + process.env.DEBUG === "true" + ) {apps/api/src/services/processor.service.ts (1)
12-13: Inconsistent import extensions may break TS ↔ JS path mappingThis file imports
import { TransformationService } from "./transformation.service.js";but other sibling imports omit extensions. Under
moduleResolution: "NodeNext"this will compile, yet the.jssuffix will be emitted into JS and may diverge from internal paths that still use bare specifiers. Align on one style (prefer suffix‐less within TS), or update all internal imports to.js.-import { TransformationService } from "./transformation.service.js"; +import { TransformationService } from "./transformation.service";apps/secret-service/src/services/encryption.service.ts (2)
60-66: Optimize encryption by working directly with Buffers.The current implementation converts to hex strings during encryption and then back to Buffer, which is inefficient.
Apply this diff to work directly with Buffers:
- let encrypted = cipher.update(plaintext, "utf8", "hex"); - encrypted += cipher.final("hex"); + const encrypted = Buffer.concat([ + cipher.update(plaintext, "utf8"), + cipher.final() + ]); const authTag = cipher.getAuthTag(); return { - encryptedValue: Buffer.from(encrypted, "hex"), + encryptedValue: encrypted, iv: iv, authTag: authTag, };
83-88: Optimize decryption by working directly with Buffers.Similar to the encrypt method, avoid unnecessary hex string conversions.
Apply this diff to work directly with Buffers:
- let decrypted = decipher.update( - encryptedValue.toString("hex"), - "hex", - "utf8", - ); // Provide 'hex' as inputEncoding for encryptedValue - decrypted += decipher.final("utf8"); + let decrypted = decipher.update(encryptedValue, null, "utf8"); + decrypted += decipher.final("utf8");apps/api/src/services/secret-service-client.ts (1)
23-77: Consider RESTful conventions for API design.While using POST for all operations provides consistency and avoids logging sensitive data in URLs, it deviates from RESTful conventions where GET is typically used for retrieval operations.
Consider:
- If security is the primary concern (avoiding sensitive data in URLs), document this design decision
- If following REST conventions, use GET for retrieval with proper authentication headers
- The empty object return on line 64 might be unexpected - consider returning null or undefined for truly empty responses
apps/secret-service/src/index.ts (1)
1-249: Consider adding rate limiting and request validation for production readiness.The service has good security foundations with API key authentication and structured logging. For production deployment, consider adding:
- Rate limiting middleware to prevent abuse
- Request body size limits
- Input validation middleware (e.g., using Zod schemas)
- Structured error responses with correlation IDs
- Metrics/monitoring endpoints
apps/secret-service/Dockerfile (3)
1-8: Consider leveraging Corepack for pnpm instead of global installsYou currently install
pnpmandturboglobally vianpm install -g, which adds extra layers. You can use Corepack to managepnpmwithout a global install:RUN corepack enable \ && corepack prepare pnpm@latest --activateThis reduces image size and simplifies your toolchain setup.
26-54: Consolidate COPY directives in builder stageMultiple
COPYsteps (manifests then full source) create extra layers. You can merge relatedCOPYinstructions or refine.dockerignoreto exclude unnecessary files—this will speed up cache hits and reduce image size.🧰 Tools
🪛 Hadolint (2.12.0)
[error] 42-42: COPY with more than 2 arguments requires the last argument to end with /
(DL3021)
84-85: Copy only built artifacts to reduce runtime image bloatInstead of copying the entire
apps/secret-servicedirectory, limit to the build output and essential files:-COPY --from=builder --chown=app:app /app/apps/secret-service ./apps/secret-service +COPY --from=builder --chown=app:app /app/apps/secret-service/dist ./apps/secret-service/dist +COPY --from=builder --chown=app:app /app/apps/secret-service/package.json ./apps/secret-service/package.jsonThis avoids shipping source files and speeds up deployments.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (26)
apps/api/src/env.ts(1 hunks)apps/api/src/services/distribution.service.ts(3 hunks)apps/api/src/services/feed.service.ts(2 hunks)apps/api/src/services/plugin.service.ts(7 hunks)apps/api/src/services/processor.service.ts(10 hunks)apps/api/src/services/secret-service-client.ts(1 hunks)apps/api/src/services/transformation.service.ts(2 hunks)apps/api/src/utils/service-provider.ts(4 hunks)apps/secret-service/.dockerignore(1 hunks)apps/secret-service/.gitignore(1 hunks)apps/secret-service/Dockerfile(1 hunks)apps/secret-service/README.md(1 hunks)apps/secret-service/package.json(1 hunks)apps/secret-service/src/db/index.ts(1 hunks)apps/secret-service/src/index.ts(1 hunks)apps/secret-service/src/services/encryption.service.ts(1 hunks)apps/secret-service/src/services/secret-store.service.ts(1 hunks)apps/secret-service/src/utils/crypto-helpers.ts(1 hunks)apps/secret-service/src/utils/logger.ts(1 hunks)apps/secret-service/tsconfig.json(1 hunks)packages/shared-db/src/schema/index.ts(1 hunks)packages/shared-db/src/schema/secrets.ts(1 hunks)packages/shared-db/src/validators.ts(2 hunks)packages/shared-db/tsconfig.json(1 hunks)packages/utils/tsconfig.json(1 hunks)tsconfig.json(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (7)
apps/secret-service/src/db/index.ts (1)
packages/shared-db/src/validators.ts (1)
DB(10-10)
packages/shared-db/src/schema/secrets.ts (1)
packages/shared-db/src/schema/common.ts (1)
timestamps(14-22)
apps/secret-service/src/utils/logger.ts (1)
apps/api/src/services/processor.service.ts (1)
process(34-131)
packages/shared-db/src/validators.ts (1)
packages/shared-db/src/schema/secrets.ts (1)
secrets(37-54)
apps/secret-service/src/services/encryption.service.ts (1)
apps/secret-service/src/utils/logger.ts (1)
logger(8-26)
apps/api/src/services/secret-service-client.ts (1)
apps/secret-service/src/utils/logger.ts (2)
Logger(1-6)logger(8-26)
apps/api/src/services/processor.service.ts (1)
packages/shared-db/src/validators.ts (1)
RichSubmission(122-125)
🪛 GitHub Actions: CI
apps/api/src/utils/service-provider.ts
[warning] 1-1: Prettier formatting check failed. Run 'prettier --write' to fix code style issues in this file.
packages/shared-db/src/schema/secrets.ts
[warning] 1-1: Prettier formatting check failed. Run 'prettier --write' to fix code style issues in this file.
packages/shared-db/src/validators.ts
[warning] 1-1: Prettier formatting check failed. Run 'prettier --write' to fix code style issues in this file.
apps/secret-service/src/services/secret-store.service.ts
[warning] 1-1: Prettier formatting check failed. Run 'prettier --write' to fix code style issues in this file.
apps/api/src/services/plugin.service.ts
[warning] 1-1: Prettier formatting check failed. Run 'prettier --write' to fix code style issues in this file.
🪛 Hadolint (2.12.0)
apps/secret-service/Dockerfile
[error] 42-42: COPY with more than 2 arguments requires the last argument to end with /
(DL3021)
🔇 Additional comments (17)
packages/utils/tsconfig.json (2)
3-4: ReorderedcompilerOptionsfor clarity.
Moving"rootDir"and"outDir"to the top aligns with other packages and highlights input/output paths.
3-4: Confirm removal of explicittarget.
By omitting"target", this package inherits the monorepo default (es2022). Verify that all downstream consumers support this target.tsconfig.json (1)
4-6: Includesecret-servicein the project references.
Adding{ "path": "./apps/secret-service" }integrates the new service into the monorepo build. Verify thatapps/secret-service/tsconfig.jsonexists and is configured correctly for incremental builds.packages/shared-db/src/schema/index.ts (1)
9-9: Expose the newsecretsschema.
Exporting./secretsmakes the secrets table available via the shared schema index. Ensuresrc/schema/secrets.tsand its corresponding validators invalidators.tsare implemented and tested.apps/api/src/services/feed.service.ts (1)
157-158: 💡 Verification agent🧩 Analysis chain
Signature drift check
processorService.processnow acceptsfeedId. Make sure all call-sites (cron jobs, tests, CLI scripts) were updated; otherwise the type-check may pass (viaany) but runtime will fail with an “undefined argument” error.
🏁 Script executed:
#!/bin/bash # Verify every call passes three args rg --no-heading -n $'\\.process\\(' | gawk '/process\\(/ && NF {print}'Length of output: 128
Refined signature‐drift verification – ensure every call to
processorService.processpasses three arguments:
🏁 Script executed:
#!/bin/bash # List all calls to processorService.process and flag any with ≠3 args rg --no-heading -n 'processorService\.process\(' | while IFS=: read -r file line text; do # extract argument list between the first pair of parentheses args=$(echo "$text" | sed -E 's/.*processorService\.process\((.*)\).*/\1/') # count commas (args = commas+1) count=$(( $(grep -o "," <<<"$args" | wc -l) + 1 )) if [ "$count" -ne 3 ]; then echo "Incorrect arg count in $file:$line → found $count args" fi doneLength of output: 782
Let’s inspect the
processorService.processcall inmoderation.service.tsto confirm its argument list:
🏁 Script executed:
#!/bin/bash # Show 3 lines before/after each processorService.process usage rg -n -C3 'processorService\.process' apps/api/src/services/moderation.service.tsLength of output: 367
Add the missing
feedIdargument in moderation.service.tsThe call to
processorService.processinapps/api/src/services/moderation.service.tsonly passes two arguments but the signature now requires three. Update it to include thefeedId.• File: apps/api/src/services/moderation.service.ts
Lines: 166–169- await this.processorService.process( - submission, - feedConfig.outputs.stream, - ); + await this.processorService.process( + submission, + feedConfig.outputs.stream, + feedEntry.feedId, + );Likely an incorrect or invalid review comment.
apps/api/src/services/distribution.service.ts (1)
21-37:Details
❓ Verification inconclusive
New mandatory
feedId– compile vs. runtime safetyThe extra parameter is great for secret hydration, but any downstream caller missing it will now silently compile if their
feedIdis typed asany/unknown.
Run a quick repo-wide grep to guarantee there are zero 2-arg invocations left.
🏁 Script executed:
#!/bin/bash # Expect no matches rg --no-heading -n $'distributeContent\\([^,]+,[^,]+\\)' | grep -v ',[^,]+,[^,]+'Length of output: 80
To catch any calls with exactly two arguments (including multi-line invocations), let’s use an AST-based search:
🏁 Script executed:
#!/bin/bash # Find any distributeContent calls with exactly two args ast-grep --pattern $'$.distributeContent($_, $_)'Length of output: 50
Verify zero 2-arg
distributeContentcalls
The newfeedIdparameter is mandatory; any call still passing only two arguments (especially if typed asany/unknown) will compile but fail at runtime. Please run a repo-wide search to ensure there are no invocations with fewer than three arguments:rg -n "distributeContent\s*\(" | grep -E -v "distributeContent\([^,]+,[^,]+,[^,]+\)"If you find any matches, update them to include the
feedId.apps/api/src/services/transformation.service.ts (1)
10-11:.jsextension import – verify path after emitThe switch to
./plugin.service.jsonly works if:
moduleisNodeNext/Node16andoutDirpreserves relative structure.If any consumer compiles to CJS (see
shared-dbchange), this path will resolve incorrectly. Double-check the emitted file layout or revert to extension-less import and rely on TS path mapping.apps/secret-service/.dockerignore (1)
1-42: Concise & well-scoped.dockerignore
The ignore list covers the usual heavyweight & developer-only artifacts and keeps the production image lean. No issues spotted.apps/api/src/utils/service-provider.ts (1)
22-220: Prettier check failed – run formatter
CI flaggedservice-provider.ts. A quickpnpm prettier --write apps/api/src/utils/service-provider.ts(or equivalent) will unblock the pipeline.packages/shared-db/src/schema/secrets.ts (1)
1-55: Prettier check failing – run formatterCI flagged this file. Running
prettier --write packages/shared-db/src/schema/secrets.tswill fix spacing and trailing commas (e.g. after spread...timestamps).🧰 Tools
🪛 GitHub Actions: CI
[warning] 1-1: Prettier formatting check failed. Run 'prettier --write' to fix code style issues in this file.
packages/shared-db/src/validators.ts (1)
180-191: Missing formatting & trailing newlineThe same Prettier failure applies here. Formatting will also add a newline at EOF, keeping git diffs clean.
prettier --write packages/shared-db/src/validators.tsapps/secret-service/src/services/secret-store.service.ts (1)
27-127: Well-implemented secret storage service.The implementation demonstrates good practices:
- Proper use of upsert pattern for idempotent operations
- Comprehensive error handling and logging
- Buffer type validation before decryption
- Returns null instead of throwing on retrieval failures
apps/secret-service/src/index.ts (2)
113-126: Excellent security measure for protecting private keys.The check to prevent retrieval of private keys through the plaintext endpoint is a crucial security control. This ensures private keys can only be used for signing operations.
203-204: Good validation logic for plaintextValue.Using
plaintextValue === undefinedinstead of a falsy check correctly allows empty strings as valid secret values while rejecting undefined.apps/secret-service/Dockerfile (3)
11-23: Validate the prune scopes include all dependenciesThe
turbo prune --scope=@curatedotfun/secret-servicecommand must capture all direct workspace dependencies (e.g.,shared-db,types,utils). Please verify and, if needed, add additional--scopeflags or update your workspace configuration.
66-74: Ignore Hadolint DL3021 false positive on COPYThe flagged
COPYcommands use exactly two arguments (source and destination), which is valid Docker syntax. You can safely disregard the DL3021 warning in this context.
79-80: Verify pnpm filter triple-dot syntaxEnsure that
--filter=@curatedotfun/secret-service...(with the trailing...) correctly includes transitive workspace dependencies during the production install.
| // "extends": "../../tsconfig.json", | ||
| "compilerOptions": { | ||
| "rootDir": "src", | ||
| "outDir": "dist", | ||
| "noEmit": false, // This package needs to emit JS and declaration files | ||
| "declaration": true, // Generate .d.ts files | ||
| "declarationMap": true, // Generate sourcemaps for .d.ts files | ||
| "composite": true, // Required for project references and building this package | ||
| "module": "ESNext", | ||
| "moduleResolution": "bundler", | ||
| "baseUrl": "." | ||
| "module": "CommonJS", | ||
| "moduleResolution": "Node", | ||
| "strict": true, | ||
| "esModuleInterop": true, | ||
| "skipLibCheck": true, | ||
| "forceConsistentCasingInFileNames": true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Configuration drift from monorepo baseline
Commenting out the "extends" line and flipping to "module": "CommonJS" / "moduleResolution": "Node" breaks the prior ESM/bundler alignment that other packages still rely on.
This will:
- Force consumers that compile as ESM to pull in CommonJS artifacts (node resolution hates mixed graphs).
- Duplicate compiler options that may go stale compared to the root config.
Unless you require CJS emission here, keep the inheritance and just override the handful of flags:
-// "extends": "../../tsconfig.json",
+"extends": "../../tsconfig.json",
"compilerOptions": {
- "module": "CommonJS",
- "moduleResolution": "Node",
+ "module": "CommonJS", // if absolutely needed
+ "moduleResolution": "Node",Add a comment explaining why this package is special to avoid accidental re-alignment later.
📝 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.
| // "extends": "../../tsconfig.json", | |
| "compilerOptions": { | |
| "rootDir": "src", | |
| "outDir": "dist", | |
| "noEmit": false, // This package needs to emit JS and declaration files | |
| "declaration": true, // Generate .d.ts files | |
| "declarationMap": true, // Generate sourcemaps for .d.ts files | |
| "composite": true, // Required for project references and building this package | |
| "module": "ESNext", | |
| "moduleResolution": "bundler", | |
| "baseUrl": "." | |
| "module": "CommonJS", | |
| "moduleResolution": "Node", | |
| "strict": true, | |
| "esModuleInterop": true, | |
| "skipLibCheck": true, | |
| "forceConsistentCasingInFileNames": true | |
| "extends": "../../tsconfig.json", | |
| "compilerOptions": { | |
| "rootDir": "src", | |
| "outDir": "dist", | |
| "noEmit": false, // This package needs to emit JS and declaration files | |
| "declaration": true, // Generate .d.ts files | |
| "declarationMap": true, // Generate sourcemaps for .d.ts files | |
| "composite": true, // Required for project references and building this package | |
| "module": "CommonJS", // if absolutely needed | |
| "moduleResolution": "Node", | |
| "strict": true, | |
| "esModuleInterop": true, | |
| "skipLibCheck": true, | |
| "forceConsistentCasingInFileNames": true |
🤖 Prompt for AI Agents
In packages/shared-db/tsconfig.json lines 2 to 15, the "extends" line is
commented out and the module settings are changed to CommonJS, causing
configuration drift from the monorepo baseline and breaking ESM alignment.
Restore the "extends" line to inherit from the root tsconfig.json, override only
the necessary compilerOptions like "rootDir", "outDir", and "declaration" flags,
and add a comment explaining why this package requires these specific overrides
to prevent accidental future changes.
| const pool = new Pool({ | ||
| connectionString: process.env.DATABASE_URL!, | ||
| }); | ||
| const db: DB = drizzle(pool, { schema }); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fail-fast env validation
Using process.env.DATABASE_URL! will crash after the Pool is constructed (or worse, hang).
Guard early:
-const pool = new Pool({
- connectionString: process.env.DATABASE_URL!,
-});
+const { DATABASE_URL } = process.env;
+if (!DATABASE_URL) {
+ throw new Error("DATABASE_URL env var is required for Secret Service DB");
+}
+const pool = new Pool({ connectionString: DATABASE_URL });Also expose a helper to gracefully pool.end() during service shutdown to avoid connection leaks in tests.
📝 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.
| const pool = new Pool({ | |
| connectionString: process.env.DATABASE_URL!, | |
| }); | |
| const db: DB = drizzle(pool, { schema }); | |
| const { DATABASE_URL } = process.env; | |
| if (!DATABASE_URL) { | |
| throw new Error("DATABASE_URL env var is required for Secret Service DB"); | |
| } | |
| const pool = new Pool({ connectionString: DATABASE_URL }); | |
| const db: DB = drizzle(pool, { schema }); |
🤖 Prompt for AI Agents
In apps/secret-service/src/db/index.ts around lines 5 to 9, the code uses
process.env.DATABASE_URL! without checking if it is defined, which can cause
crashes or hangs after Pool construction. Add an early guard to validate that
DATABASE_URL is set before creating the Pool, throwing a clear error if missing.
Additionally, export a helper function that calls pool.end() to allow graceful
shutdown of the database connection, preventing connection leaks during tests or
service shutdown.
| if (!env.SECRET_SERVICE_URL || !env.SECRET_SERVICE_INTERNAL_API_KEY) { | ||
| logger.error( | ||
| "ServiceProvider: SECRET_SERVICE_URL or SECRET_SERVICE_INTERNAL_API_KEY is not defined. SecretServiceApiClient may not function correctly.", | ||
| ); | ||
| } | ||
| const secretServiceApiClient = new SecretServiceApiClient( | ||
| env.SECRET_SERVICE_URL!, | ||
| env.SECRET_SERVICE_INTERNAL_API_KEY!, | ||
| logger, | ||
| ); | ||
| this.services.set("secretServiceApiClient", secretServiceApiClient); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Runtime will still crash if the env vars are missing
You log an error but immediately bypass it with non-null assertions, so the constructor will throw later anyway. If you want a graceful degradation, guard the instantiation:
-if (!env.SECRET_SERVICE_URL || !env.SECRET_SERVICE_INTERNAL_API_KEY) {
- logger.error(
- "ServiceProvider: SECRET_SERVICE_URL or SECRET_SERVICE_INTERNAL_API_KEY is not defined. SecretServiceApiClient may not function correctly.",
- );
-}
-const secretServiceApiClient = new SecretServiceApiClient(
- env.SECRET_SERVICE_URL!,
- env.SECRET_SERVICE_INTERNAL_API_KEY!,
- logger,
-);
-this.services.set("secretServiceApiClient", secretServiceApiClient);
+if (env.SECRET_SERVICE_URL && env.SECRET_SERVICE_INTERNAL_API_KEY) {
+ const secretServiceApiClient = new SecretServiceApiClient(
+ env.SECRET_SERVICE_URL,
+ env.SECRET_SERVICE_INTERNAL_API_KEY,
+ logger,
+ );
+ this.services.set("secretServiceApiClient", secretServiceApiClient);
+} else {
+ logger.error(
+ "ServiceProvider: Missing SECRET_SERVICE_URL or SECRET_SERVICE_INTERNAL_API_KEY – SecretServiceApiClient disabled.",
+ );
+}📝 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.
| if (!env.SECRET_SERVICE_URL || !env.SECRET_SERVICE_INTERNAL_API_KEY) { | |
| logger.error( | |
| "ServiceProvider: SECRET_SERVICE_URL or SECRET_SERVICE_INTERNAL_API_KEY is not defined. SecretServiceApiClient may not function correctly.", | |
| ); | |
| } | |
| const secretServiceApiClient = new SecretServiceApiClient( | |
| env.SECRET_SERVICE_URL!, | |
| env.SECRET_SERVICE_INTERNAL_API_KEY!, | |
| logger, | |
| ); | |
| this.services.set("secretServiceApiClient", secretServiceApiClient); | |
| if (env.SECRET_SERVICE_URL && env.SECRET_SERVICE_INTERNAL_API_KEY) { | |
| const secretServiceApiClient = new SecretServiceApiClient( | |
| env.SECRET_SERVICE_URL, | |
| env.SECRET_SERVICE_INTERNAL_API_KEY, | |
| logger, | |
| ); | |
| this.services.set("secretServiceApiClient", secretServiceApiClient); | |
| } else { | |
| logger.error( | |
| "ServiceProvider: Missing SECRET_SERVICE_URL or SECRET_SERVICE_INTERNAL_API_KEY – SecretServiceApiClient disabled.", | |
| ); | |
| } |
🤖 Prompt for AI Agents
In apps/api/src/utils/service-provider.ts around lines 37 to 48, the code logs
an error if SECRET_SERVICE_URL or SECRET_SERVICE_INTERNAL_API_KEY are missing
but then uses non-null assertions to instantiate SecretServiceApiClient, which
will cause a runtime crash. To fix this, add a conditional check to only create
and set secretServiceApiClient if both environment variables are defined,
preventing the constructor from being called with undefined values and allowing
graceful degradation.
| "compilerOptions": { | ||
| "rootDir": "./src", | ||
| "outDir": "./dist", | ||
| "noEmit": false, | ||
| "module": "NodeNext", | ||
| "moduleResolution": "NodeNext", | ||
| "target": "ESNext", | ||
| "strict": true, | ||
| "baseUrl": ".", | ||
| "esModuleInterop": true, | ||
| "skipLibCheck": true, | ||
| "forceConsistentCasingInFileNames": true, | ||
| "resolveJsonModule": true, | ||
| "verbatimModuleSyntax": true, | ||
| "types": ["node"], | ||
| "paths": { | ||
| "@curatedotfun/shared-db": ["../../packages/shared-db/src"], | ||
| "*": ["src/*"] | ||
| } | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enable composite & incremental for project-references build
Because this project is referenced by packages/shared-db, the recommended pattern for a TypeScript monorepo is to mark every referenced tsconfig.json as composite (and usually incremental) so that tsc -b works end-to-end and emits build-info files for fast rebuilds.
"compilerOptions": {
+ "composite": true,
+ "incremental": true,
"rootDir": "./src",
"outDir": "./dist",Without these flags, tsc -b will silently skip building this sub-project and type-checking may become inconsistent.
📝 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.
| "compilerOptions": { | |
| "rootDir": "./src", | |
| "outDir": "./dist", | |
| "noEmit": false, | |
| "module": "NodeNext", | |
| "moduleResolution": "NodeNext", | |
| "target": "ESNext", | |
| "strict": true, | |
| "baseUrl": ".", | |
| "esModuleInterop": true, | |
| "skipLibCheck": true, | |
| "forceConsistentCasingInFileNames": true, | |
| "resolveJsonModule": true, | |
| "verbatimModuleSyntax": true, | |
| "types": ["node"], | |
| "paths": { | |
| "@curatedotfun/shared-db": ["../../packages/shared-db/src"], | |
| "*": ["src/*"] | |
| } | |
| }, | |
| "compilerOptions": { | |
| "composite": true, | |
| "incremental": true, | |
| "rootDir": "./src", | |
| "outDir": "./dist", | |
| "noEmit": false, | |
| "module": "NodeNext", | |
| "moduleResolution": "NodeNext", | |
| "target": "ESNext", | |
| "strict": true, | |
| "baseUrl": ".", | |
| "esModuleInterop": true, | |
| "skipLibCheck": true, | |
| "forceConsistentCasingInFileNames": true, | |
| "resolveJsonModule": true, | |
| "verbatimModuleSyntax": true, | |
| "types": ["node"], | |
| "paths": { | |
| "@curatedotfun/shared-db": ["../../packages/shared-db/src"], | |
| "*": ["src/*"] | |
| } | |
| }, |
🤖 Prompt for AI Agents
In apps/secret-service/tsconfig.json around lines 3 to 22, the compilerOptions
section lacks the "composite" and "incremental" flags needed for proper
TypeScript project references. Add "composite": true and "incremental": true to
the compilerOptions to enable correct build behavior with tsc -b, ensuring this
sub-project is built and type-checked consistently within the monorepo.
| // Define a custom type for bytea, assuming it will be handled as Buffer in JS/TS | ||
| const byteaType = customType<{ data: Buffer; driverData: string }>({ | ||
| dataType() { | ||
| return "bytea"; | ||
| }, | ||
| toDriver(value: Buffer): string { | ||
| // Convert Buffer to hex string for storage, or let the driver handle it | ||
| // For node-postgres, it can often handle Buffers directly. | ||
| // If direct Buffer handling is problematic, convert to hex: value.toString('hex') | ||
| // However, for now, let's assume the driver handles Buffer correctly. | ||
| // If not, this mapping function would be: `return '\\x' + value.toString('hex');` | ||
| return value as any; | ||
| }, | ||
| fromDriver(value: string | Buffer): Buffer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
customType generic mismatched – Buffer returned where string expected
driverData is declared as string, yet toDriver returns a Buffer, forcing an as any cast and defeating type-safety. Change the generic to reflect reality (or convert to hex).
-const byteaType = customType<{ data: Buffer; driverData: string }>({
+const byteaType = customType<{ data: Buffer; driverData: Buffer }>({
...
- toDriver(value: Buffer): string {
- return value as any;
+ toDriver(value: Buffer): Buffer {
+ return value;
},This removes the unsafe cast and prevents accidental misuse.
🤖 Prompt for AI Agents
In packages/shared-db/src/schema/secrets.ts around lines 10 to 23, the generic
type for customType incorrectly declares driverData as string while toDriver
returns a Buffer, causing an unsafe cast. Fix this by updating the generic type
to have driverData as Buffer instead of string, or alternatively convert the
Buffer to a hex string in toDriver to match the string type. This will remove
the unsafe cast and ensure type safety.
| @@ -0,0 +1,127 @@ | |||
| import { | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix code formatting issues.
The pipeline indicates formatting issues in this file.
Run the following command to fix formatting:
pnpm prettier --write apps/secret-service/src/services/secret-store.service.ts🧰 Tools
🪛 GitHub Actions: CI
[warning] 1-1: Prettier formatting check failed. Run 'prettier --write' to fix code style issues in this file.
🤖 Prompt for AI Agents
In apps/secret-service/src/services/secret-store.service.ts at line 1, there are
code formatting issues detected by the pipeline. To fix this, run the command
"pnpm prettier --write apps/secret-service/src/services/secret-store.service.ts"
to automatically format the file according to the project's style guidelines.
| const keyPair = KeyPair.fromString(privateKey as any); // Cast to satisfy KeyPairString type | ||
| const signature = keyPair.sign(new TextEncoder().encode(payload)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid using as any cast for type safety.
The as any cast bypasses TypeScript's type checking. Consider properly typing the privateKey or using a type assertion that's more specific.
- const keyPair = KeyPair.fromString(privateKey as any); // Cast to satisfy KeyPairString type
+ // Ensure privateKey is properly typed or use a more specific type assertion
+ const keyPair = KeyPair.fromString(privateKey);If the type mismatch is due to the KeyPair API, consider:
- Updating the type definitions
- Using a more specific type assertion
- Adding runtime validation before the call
📝 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.
| const keyPair = KeyPair.fromString(privateKey as any); // Cast to satisfy KeyPairString type | |
| const signature = keyPair.sign(new TextEncoder().encode(payload)); | |
| // Ensure privateKey is properly typed or use a more specific type assertion | |
| const keyPair = KeyPair.fromString(privateKey); | |
| const signature = keyPair.sign(new TextEncoder().encode(payload)); |
🤖 Prompt for AI Agents
In apps/secret-service/src/index.ts around lines 168 to 169, avoid using the `as
any` cast on `privateKey` to maintain type safety. Instead, ensure `privateKey`
is properly typed as a string or the expected KeyPairString type before this
code. If the KeyPair API types are incorrect, update the type definitions
accordingly or use a more specific type assertion that matches the expected
input. Additionally, add runtime validation to confirm `privateKey` is valid
before calling `KeyPair.fromString`.
| logger.warn( | ||
| `Unauthorized access attempt to secret-service. Provided key: ${providedKey ? providedKey.substring(0, 5) + "..." : "None"}`, | ||
| ); | ||
| return c.json({ error: "Unauthorized" }, 401); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Security: Avoid logging partial API keys.
Logging even partial API keys (first 5 characters) could aid attackers in brute-force attempts or social engineering.
- logger.warn(
- `Unauthorized access attempt to secret-service. Provided key: ${providedKey ? providedKey.substring(0, 5) + "..." : "None"}`,
- );
+ logger.warn(
+ `Unauthorized access attempt to secret-service. API key provided: ${providedKey ? "Yes" : "No"}`,
+ );📝 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.
| logger.warn( | |
| `Unauthorized access attempt to secret-service. Provided key: ${providedKey ? providedKey.substring(0, 5) + "..." : "None"}`, | |
| ); | |
| return c.json({ error: "Unauthorized" }, 401); | |
| logger.warn( | |
| `Unauthorized access attempt to secret-service. API key provided: ${providedKey ? "Yes" : "No"}`, | |
| ); | |
| return c.json({ error: "Unauthorized" }, 401); |
🤖 Prompt for AI Agents
In apps/secret-service/src/index.ts around lines 79 to 82, the code logs the
first 5 characters of the provided API key during unauthorized access attempts,
which poses a security risk. Remove the partial API key from the log message
entirely and replace it with a generic message that does not reveal any part of
the key to prevent aiding attackers.
| // The spec mentions publicKey here, but SecretStoreService.setSecret doesn't use it. | ||
| // If publicKey needs to be stored, the service and schema would need an update. | ||
| // For now, matching the service method. | ||
| const { feedId, keyName, plaintextValue } = await c.req.json<{ | ||
| feedId: string; | ||
| keyName: string; | ||
| plaintextValue: string; | ||
| publicKey?: string; | ||
| }>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Unused publicKey parameter in API design.
The comment indicates that publicKey is accepted in the request but not used by the SecretStoreService. This creates confusion about the API contract.
Either remove the unused parameter from the API:
- const { feedId, keyName, plaintextValue } = await c.req.json<{
+ const { feedId, keyName, plaintextValue } = await c.req.json<{
feedId: string;
keyName: string;
plaintextValue: string;
- publicKey?: string;
}>();Or update the SecretStoreService to handle the publicKey parameter if it's intended for future use.
🤖 Prompt for AI Agents
In apps/secret-service/src/index.ts around lines 194 to 202, the publicKey
parameter is extracted from the request JSON but not used in the
SecretStoreService.setSecret method, causing confusion about the API contract.
To fix this, either remove the publicKey parameter from the request parsing and
related API definitions if it is not needed, or update the SecretStoreService
and its schema to accept and handle the publicKey parameter properly if it is
intended for future use.
| app.use("*", async (c, next) => { | ||
| // Service initialization logic (lazy, on first request) | ||
| // This ensures env vars are loaded via Hono's adapter before services use them. | ||
| if (!encryptionService) { | ||
| const { ENCRYPTION_MASTER_KEY, DATABASE_URL } = env<AppEnv>(c); | ||
| if (!ENCRYPTION_MASTER_KEY) { | ||
| logger.error( | ||
| "FATAL: ENCRYPTION_MASTER_KEY is not defined in the environment.", | ||
| ); | ||
| return c.json( | ||
| { error: "Server configuration error: Missing master key." }, | ||
| 500, | ||
| ); | ||
| } | ||
| if (!DATABASE_URL) { | ||
| // Though db/index.js checks, good to be defensive | ||
| logger.error( | ||
| "FATAL: DATABASE_URL is not defined in the environment for service initialization.", | ||
| ); | ||
| return c.json( | ||
| { error: "Server configuration error: Missing database URL." }, | ||
| 500, | ||
| ); | ||
| } | ||
| try { | ||
| encryptionService = new EncryptionService(ENCRYPTION_MASTER_KEY); | ||
| secretStoreService = new SecretStoreService( | ||
| db, | ||
| encryptionService, | ||
| logger, | ||
| ); | ||
| logger.info("Services initialized successfully."); | ||
| } catch (error) { | ||
| logger.error("FATAL: Failed to initialize services.", { error }); | ||
| return c.json({ error: "Server initialization failed." }, 500); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential race condition in service initialization.
The lazy initialization pattern could lead to race conditions if multiple requests arrive simultaneously before the services are initialized. Multiple concurrent requests could trigger multiple initialization attempts.
Consider initializing services once during startup or using a proper synchronization mechanism:
-// Initialize services - these will be set in the 'request' hook or a startup function
-let encryptionService: EncryptionService;
-let secretStoreService: SecretStoreService;
+// Initialize services with proper synchronization
+let encryptionService: EncryptionService;
+let secretStoreService: SecretStoreService;
+let initializationPromise: Promise<void> | null = null;
// Middleware for API Key Authentication & Service Initialization
app.use("*", async (c, next) => {
// Service initialization logic (lazy, on first request)
// This ensures env vars are loaded via Hono's adapter before services use them.
if (!encryptionService) {
+ if (!initializationPromise) {
+ initializationPromise = (async () => {
const { ENCRYPTION_MASTER_KEY, DATABASE_URL } = env<AppEnv>(c);
// ... rest of initialization logic
+ })();
+ }
+ try {
+ await initializationPromise;
+ } catch (error) {
+ return c.json({ error: "Server initialization failed." }, 500);
+ }
}📝 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.
| app.use("*", async (c, next) => { | |
| // Service initialization logic (lazy, on first request) | |
| // This ensures env vars are loaded via Hono's adapter before services use them. | |
| if (!encryptionService) { | |
| const { ENCRYPTION_MASTER_KEY, DATABASE_URL } = env<AppEnv>(c); | |
| if (!ENCRYPTION_MASTER_KEY) { | |
| logger.error( | |
| "FATAL: ENCRYPTION_MASTER_KEY is not defined in the environment.", | |
| ); | |
| return c.json( | |
| { error: "Server configuration error: Missing master key." }, | |
| 500, | |
| ); | |
| } | |
| if (!DATABASE_URL) { | |
| // Though db/index.js checks, good to be defensive | |
| logger.error( | |
| "FATAL: DATABASE_URL is not defined in the environment for service initialization.", | |
| ); | |
| return c.json( | |
| { error: "Server configuration error: Missing database URL." }, | |
| 500, | |
| ); | |
| } | |
| try { | |
| encryptionService = new EncryptionService(ENCRYPTION_MASTER_KEY); | |
| secretStoreService = new SecretStoreService( | |
| db, | |
| encryptionService, | |
| logger, | |
| ); | |
| logger.info("Services initialized successfully."); | |
| } catch (error) { | |
| logger.error("FATAL: Failed to initialize services.", { error }); | |
| return c.json({ error: "Server initialization failed." }, 500); | |
| } | |
| } | |
| // Initialize services with proper synchronization | |
| let encryptionService: EncryptionService; | |
| let secretStoreService: SecretStoreService; | |
| let initializationPromise: Promise<void> | null = null; | |
| // Middleware for API Key Authentication & Service Initialization | |
| app.use("*", async (c, next) => { | |
| // Service initialization logic (lazy, on first request) | |
| // This ensures env vars are loaded via Hono's adapter before services use them. | |
| if (!encryptionService) { | |
| if (!initializationPromise) { | |
| initializationPromise = (async () => { | |
| const { ENCRYPTION_MASTER_KEY, DATABASE_URL } = env<AppEnv>(c); | |
| if (!ENCRYPTION_MASTER_KEY) { | |
| logger.error( | |
| "FATAL: ENCRYPTION_MASTER_KEY is not defined in the environment.", | |
| ); | |
| return c.json( | |
| { error: "Server configuration error: Missing master key." }, | |
| 500, | |
| ); | |
| } | |
| if (!DATABASE_URL) { | |
| // Though db/index.js checks, good to be defensive | |
| logger.error( | |
| "FATAL: DATABASE_URL is not defined in the environment for service initialization.", | |
| ); | |
| return c.json( | |
| { error: "Server configuration error: Missing database URL." }, | |
| 500, | |
| ); | |
| } | |
| try { | |
| encryptionService = new EncryptionService(ENCRYPTION_MASTER_KEY); | |
| secretStoreService = new SecretStoreService( | |
| db, | |
| encryptionService, | |
| logger, | |
| ); | |
| logger.info("Services initialized successfully."); | |
| } catch (error) { | |
| logger.error("FATAL: Failed to initialize services.", { error }); | |
| throw error; | |
| } | |
| })(); | |
| } | |
| try { | |
| await initializationPromise; | |
| } catch (error) { | |
| return c.json({ error: "Server initialization failed." }, 500); | |
| } | |
| } | |
| // Proceed with authentication, routing, etc. | |
| return next(); | |
| }); |
🤖 Prompt for AI Agents
In apps/secret-service/src/index.ts between lines 27 and 63, the current lazy
initialization of encryptionService and secretStoreService inside the middleware
can cause race conditions when multiple requests trigger initialization
concurrently. To fix this, move the service initialization to the application
startup phase before handling requests, or implement a synchronization mechanism
such as a mutex or a promise lock to ensure only one initialization occurs at a
time and other requests wait for it to complete.
Summary by CodeRabbit