Skip to content

feat: add vitest test infrastructure and configuration (STARTUP-139)#50

Open
ian wants to merge 4 commits into
mainfrom
startup-139-startupkit-pro
Open

feat: add vitest test infrastructure and configuration (STARTUP-139)#50
ian wants to merge 4 commits into
mainfrom
startup-139-startupkit-pro

Conversation

@ian
Copy link
Copy Markdown
Owner

@ian ian commented Apr 7, 2026

Summary

  • Add vitest configuration to packages/mcp-pro, packages/pro, and workers/pro
  • Add initial test files for auth, schema, domains, credits, trends, and middleware
  • Add deploy-pro-workers.yml GitHub Actions workflow
  • Add .dev.vars.example for workers/pro

Open with Devin

ian added 2 commits April 5, 2026 22:22
- Pro dashboard with credit tracking and tool catalog
- MCP server for AI agent integration
- CLI tool for pro features
- Cloudflare Workers backend for API integrations
- AI agent skills documentation
- Domain provider integrations (Porkbun, Namecheap)
- Credit-based usage model with multiple plans

Issues: STARTUP-139
@linear
Copy link
Copy Markdown

linear Bot commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 4 potential issues.

View 7 additional findings in Devin Review.

Open in Devin Review

Comment thread workers/pro/src/domains.ts Outdated
Comment on lines +50 to +51
const apiUser = process.env.DATAFORSEO_LOGIN;
const apiKey = process.env.DATAFORSEO_KEY;
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot Apr 7, 2026

Choose a reason for hiding this comment

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

🔴 Keywords handler uses process.env instead of Workers env bindings — always returns mock data

The fetchKeywords function at workers/pro/src/keywords.ts:69-70 reads API credentials via process.env.DATAFORSEO_LOGIN and process.env.DATAFORSEO_KEY. In Cloudflare Workers, environment bindings (secrets/vars) are accessed via c.env, not process.env — even with nodejs_compat enabled. Compare with the SEO handler (workers/pro/src/seo.ts:62-64) which correctly uses env.DATA_FOR_SEO_API_LOGIN. Additionally, the variable names are wrong: the Env interface and wrangler.toml define DATA_FOR_SEO_API_LOGIN/DATA_FOR_SEO_API_KEY, not DATAFORSEO_LOGIN/DATAFORSEO_KEY. This means the keywords endpoint will never use real DataForSEO data and will always fall back to mock/random data in production.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread workers/pro/src/credits.ts Outdated
Comment thread workers/pro/src/apps.ts Outdated
- Add UNIQUE constraint on (user_id, provider) for domain_provider_settings
- Fix ON CONFLICT SQL in domains.ts to use (user_id, provider)
- Use c.env for DataForSEO credentials instead of process.env
- Fix credits /deduct endpoint to properly deduct from bonus_credits
- Fix iOS installCount mapping (trackContentRating is age rating)
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 7, 2026

Deploying startupkit with  Cloudflare Pages  Cloudflare Pages

Latest commit: 8c67476
Status: ✅  Deploy successful!
Preview URL: https://f55ab8c8.startupkit-975.pages.dev
Branch Preview URL: https://startup-139-startupkit-pro.startupkit-975.pages.dev

View logs

devin-ai-integration[bot]

This comment was marked as resolved.

@socket-security
Copy link
Copy Markdown

socket-security Bot commented Apr 7, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​types/​node@​20.19.371001008196100
Added@​modelcontextprotocol/​sdk@​1.29.09910010098100
Added@​cloudflare/​workers-types@​4.20260402.1100100100100100

View full report

@socket-security
Copy link
Copy Markdown

socket-security Bot commented Apr 7, 2026

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report

- Fix auth route mismatch: mount authRouter at both /auth and /api/auth
- Fix CLI auth URLs to use base URL without /api suffix
- Add missing await on response.json() in pro dashboard
- Fix credits /balance to return total = balance + used
- Fix MCP credits tool double-counting (available = balance, not balance - used)
- Fix Porkbun provider to use domain check API for real availability
- Fix TOCTOU race condition in deductCredits using D1 batch for atomic deduction
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

View 13 additional findings in Devin Review.

Open in Devin Review

Comment on lines +17 to +32
const result = await db.batch([
db
.prepare(
`UPDATE users SET
credits = CASE WHEN credits >= ? THEN credits - ? ELSE 0 END,
bonus_credits = CASE WHEN credits >= ? THEN bonus_credits ELSE bonus_credits - (? - credits) END
WHERE id = ? AND (credits + bonus_credits) >= ?`,
)
.bind(amount, amount, amount, amount, userId, amount),
db
.prepare(
`INSERT INTO credit_transactions (id, user_id, amount, type, tool, description)
VALUES (?, ?, ?, 'debit', ?, ?)`,
)
.bind(transactionId, userId, -amount, tool, description || null),
]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Phantom debit transactions inserted on insufficient credits

The deductCredits function uses db.batch() which executes both the UPDATE and INSERT atomically. When the user has insufficient credits, the UPDATE's WHERE clause (credits + bonus_credits) >= ? doesn't match any rows (0 changes), but the INSERT into credit_transactions still succeeds unconditionally within the same batch. The code then checks rowsAffected === 0 and returns success: false, but the debit transaction record has already been persisted. Over time, this accumulates phantom debit records that corrupt the transaction history and could skew the /credits/history endpoint at workers/pro/src/credits.ts:45-69.

Prompt for agents
In workers/pro/src/lib/credits.ts, the deductCredits function uses db.batch() to execute an UPDATE and INSERT together. The problem is that when the UPDATE matches 0 rows (insufficient credits), the INSERT still runs, creating a phantom debit transaction.

The fix should ensure the INSERT only happens when the UPDATE succeeds. Two approaches:

1. Check credits before the batch: Query the user's credits first, and only execute the batch if they have enough. This still has a small race window but is simpler.

2. Separate the operations: First execute the UPDATE alone, check rowsAffected, and only INSERT the transaction record if the UPDATE succeeded.

Approach 2 is preferred:
- Execute the UPDATE with the WHERE clause
- Check result[0].meta.changes
- Only if changes > 0, then INSERT the credit_transaction record
- This ensures transaction records are only created for actual deductions
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread workers/pro/src/index.ts
Comment on lines +31 to +33
app.route("/auth", authRouter);
app.route("/api/auth", authRouter);
app.use("/api/*", authMiddleware);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 CLI whoami always fails: /auth/me route lacks auth middleware

The worker mounts auth routes at both /auth and /api/auth (workers/pro/src/index.ts:31-32), but the auth middleware only applies to /api/* (workers/pro/src/index.ts:33). The /auth/me endpoint at workers/pro/src/auth.ts:121 expects c.get("user") to be populated by the middleware, but since no middleware runs on /auth/*, user is always null and it returns 401.

The CLI's whoami function at packages/pro/src/lib/auth.ts:56-57 strips /api from the base URL and calls ${baseUrl}/auth/me — this hits the unprotected /auth/me route which always returns 401. The error handler then calls config.clear(), wiping the user's stored credentials. This means any whoami call will fail and log the user out.

Prompt for agents
The problem is a mismatch between the CLI's URL construction and the server's middleware configuration.

In workers/pro/src/index.ts, auth routes are mounted at /auth (unprotected) and /api/auth (protected by authMiddleware). The /auth/me endpoint needs the user to be set by auth middleware, but /auth/* routes don't go through it.

The CLI (packages/pro/src/lib/auth.ts) strips /api from the base URL for auth endpoints, meaning it calls /auth/me instead of /api/auth/me.

Fix options:
1. Change the CLI's whoami function to NOT strip /api, so it calls /api/auth/me which goes through auth middleware. But this would also affect login/register which need to be unprotected.
2. Better approach: In the worker, add auth middleware specifically for /auth/me. For example, split the auth router into public routes (register, login, logout) and protected routes (me), and only apply middleware to the protected ones.
3. Alternatively, have the /auth/me handler manually parse the Authorization header and look up the user, bypassing the middleware pattern.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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