Skip to content

feat: cross post flow - #1

Open
petrubraha wants to merge 7 commits into
mainfrom
posts
Open

petrubraha wants to merge 7 commits into
mainfrom
posts

Conversation

@petrubraha

@petrubraha petrubraha commented Jul 20, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Added a new cross-posts/ mock Node.js “Varful Cross-Post Partner API Demo”.
  • Implemented an Express server (cross-posts/partner.js) showcasing a cross-post flow:
    • POST /api/partner/posts: accepts a post payload, generates OAuth2 PKCE code_verifier/code_challenge, then redirects the user to Varful’s /o/authorize/ endpoint.
    • GET /partner/posts: when called with ?code=..., exchanges the authorization code + PKCE code_verifier for an access token, forwards the previously submitted post to Varful (POST /api/posts/), then clears in-memory state.
    • GET /partner/posts without a code: fetches latest posts using a client-credentials token and renders them into cross-posts/user.html.
  • Added cross-posts/user.html: a simple partner website form that submits user, image, location, location_slug, and caption to the partner backend.
  • Added local setup scaffolding:
    • cross-posts/.env.example with required OAuth2 variables and VARFUL_HOST
    • cross-posts/.gitignore to ignore secrets/deps
    • cross-posts/README.md with purpose, OAuth flow description, required fields, and run steps
    • cross-posts/package.json (mock-partner, npm run dev runs node partner.js)

Security considerations

  • Auth correlation is not per-user/session: PKCE and the pending post are stored in module-level in-memory variables (codeVerifier, codeChallenge, postData), which can break under concurrent users and are lost on restart.
  • No state parameter: the authorization redirect does not include or validate an OAuth2 state value, weakening CSRF/callback integrity guarantees.
  • Potential XSS via HTML injection: generateDynamicHtml() inserts post.location, post.user, and post.caption directly into HTML without escaping; post.image is also interpolated into an <img src=...> attribute without validation/escaping.
  • Weak input validation: the server accepts req.body and forwards it to Varful without schema/type/length validation or sanitization.
  • Over-permissive CORS: app.use(cors()) enables default permissive CORS behavior.
  • Error information leakage: the global error handler returns err.message to the client and logs details to console.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a Node.js Express demo for cross-posting content to Varful through OAuth2 Authorization Code with PKCE. The server validates environment configuration, accepts post data, redirects through authorization, exchanges tokens, forwards posts to the remote API, and renders post content into an HTML template. Supporting package metadata, setup documentation, environment examples, ignore rules, and a pre-filled browser form are included.

Estimated code review effort: 3 (Moderate) | ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and clearly refers to the new cross-posting flow implemented in this PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch posts

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.

@petrubraha
petrubraha marked this pull request as ready for review July 27, 2026 12:50

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@cross-posts/.gitignore`:
- Line 3: Remove package-lock.json from .gitignore and generate or retain the
npm lockfile in the repository so the OAuth demo’s dependency graph is committed
and reproducible.

In `@cross-posts/package.json`:
- Around line 7-10: Add an engines constraint to cross-posts/package.json
declaring Node.js 18 or newer, alongside the existing package metadata. Do not
alter the main entry point or dev script.

In `@cross-posts/partner.js`:
- Around line 236-241: Update the post rendering template in the post mapping
callback to HTML-escape post.location, post.user, and post.caption before
interpolation, and validate post.image so only https: URLs are used in the img
src attribute; reject or omit invalid image URLs rather than inserting them
directly.
- Around line 56-59: Replace the process-global codeVerifier and postData flow
state with per-session or cryptographically random state-keyed storage in the
authorization and callback handlers. Generate and include a state value in the
OAuth authorization URL, validate it on callback before exchanging the code,
retrieve the matching verifier and post payload, and delete only that flow’s
entry after use.
- Around line 108-117: Update the token exchange flow in the partner posts
handler around exchangeCredentialsForToken so the client-credentials token is
reused across feed requests instead of fetched every time. Store the token with
its expiry and refresh it only when expired or within a small refresh skew,
while preserving the existing credential exchange and fetchWithAccess flow;
align the reuse duration with the 10-hour requirement documented in the README.
- Around line 154-164: Apply the same finite timeout signal to all three
outbound fetch calls in cross-posts/partner.js: the client-credentials exchange
at lines 154-164, the authorization-code exchange at lines 179-193, and the
remote posts API request at lines 213-217. Add the bounded deadline to each
fetch options object while preserving their existing request behavior.

In `@cross-posts/README.md`:
- Around line 30-33: Update the “Configure Environment” instructions in
README.md to show `cp .env.example .env` as the primary macOS/Linux command, and
retain `copy .env.example .env` as the Windows alternative.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5f0f1f66-f754-4e0a-b68f-0a1c40662454

📥 Commits

Reviewing files that changed from the base of the PR and between 852337a and 0bff3af.

📒 Files selected for processing (6)
  • cross-posts/.env.example
  • cross-posts/.gitignore
  • cross-posts/README.md
  • cross-posts/package.json
  • cross-posts/partner.js
  • cross-posts/user.html
📜 Review details
🧰 Additional context used
🪛 ast-grep (0.44.1)
cross-posts/partner.js

[warning] 232-232: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(path.join(__dirname, "user.html"), "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename)

🪛 dotenv-linter (4.0.0)
cross-posts/.env.example

[warning] 2-2: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 3-3: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 4-4: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 5-5: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)

🪛 GitHub Check: SonarCloud Code Analysis
cross-posts/partner.js

[warning] 36-36: This framework implicitly discloses version information by default. Make sure it is safe here.

See more on https://sonarcloud.io/project/issues?id=petrubraha_varful.ro-api-demo&issues=AZ-A9mVcMaC8Fe_7tDqo&open=AZ-A9mVcMaC8Fe_7tDqo&pullRequest=1


[warning] 43-43: Make sure that enabling CORS is safe here.

See more on https://sonarcloud.io/project/issues?id=petrubraha_varful.ro-api-demo&issues=AZ-A9mVcMaC8Fe_7tDqp&open=AZ-A9mVcMaC8Fe_7tDqp&pullRequest=1

🪛 markdownlint-cli2 (0.23.0)
cross-posts/README.md

[warning] 31-31: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 33-33: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 35-35: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 40-40: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 42-42: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 44-44: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 46-46: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

🔇 Additional comments (2)
cross-posts/.env.example (1)

1-5: LGTM!

cross-posts/user.html (1)

1-41: LGTM!

Comment thread cross-posts/.gitignore
Comment thread cross-posts/package.json
Comment on lines +7 to +10
"type": "commonjs",
"main": "partner.js",
"scripts": {
"dev": "node partner.js"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

fd -HI -t f '^(package\.json|\.nvmrc|\.node-version|\.tool-versions|Dockerfile)$' .
rg -n -C2 '"engines"|FROM node:|NODE_VERSION|node-version' .

Repository: petrubraha/varful.ro-api-demo

Length of output: 219


🌐 Web query:

For the Node.js version intended by this project, verify support for global fetch, btoa, and crypto.createHash(...).digest("base64url").

💡 Result:

As of July 2026, Node.js provides the following levels of support for the requested APIs: 1. Global fetch: Supported. It is available as a global object in Node.js, with the implementation originating from the Undici library [1][2]. It is no longer experimental, having been enabled by default since Node.js v18.0.0 and reaching stable status in v21.0.0 [3][1]. 2. Global btoa: Supported, but marked as legacy. While it is available as a global function, Node.js documentation categorizes it as legacy and recommends using Buffer.toString('base64') instead [4][5]. 3. crypto.createHash(...).digest("base64url"): Supported. The hash.digest method accepts 'base64url' as an encoding argument to return the digest in a URL-safe Base64 format [6][7][8]. This functionality is part of the standard node:crypto module [6].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

printf '--- candidate package/tooling files ---\n'
git ls-files | rg '(^|/)(package\.json|package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb|\.nvmrc|\.node-version|\.tool-versions|Dockerfile|docker-compose.*|.*\.yml|.*\.yaml)$' || true

printf '\n--- root package.json ---\n'
if [ -f package.json ]; then cat -n package.json; fi

printf '\n--- cross-posts package.json ---\n'
cat -n cross-posts/package.json

printf '\n--- root tooling/version references ---\n'
rg -n -C2 '"engines"|node:"?' package.json | head -n 80 || true
rg -n -C2 'node-version|setup-node|FROM node:|node:|NODE_VERSION|NODEJS_VERSION|node_modules|npm ci|npm install|devDependencies|dependencies' -g '*.yml' -g '*.yaml' -g 'Dockerfile*' -g 'package.json' . | head -n 160 || true

Repository: petrubraha/varful.ro-api-demo

Length of output: 258


🌐 Web query:

Node.js digest base64url support initial version createHash base64url

💡 Result:

In Node.js, the base64url encoding for digests was introduced in Node.js version 15.7.0 [1]. Before this official addition, Node.js had long supported base64url input when creating Buffers (via the base64 encoding option) [2][3], which was documented as a feature in 2016 [4][3]. However, developers could not explicitly request base64url output from cryptographic methods like hash.digest() or Buffer.toString() until the release of v15.7.0 [5][1]. Before this, developers typically had to perform manual string replacements—such as .toString('base64').replaceAll('+', '-').replaceAll('/', '_')—to achieve the desired output format [5]. Since v15.7.0, base64url is a first-class encoding option throughout the Node.js API, including the crypto module's hash.digest('base64url') and various Buffer operations [5][1].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- candidate package/tooling files ---'
git ls-files | rg '(^|/)(package\.json|package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb|\.nvmrc|\.node-version|\.tool-versions|Dockerfile|docker-compose.*|.*\.yml|.*\.yaml)$' || true

printf '%s\n' ''
printf '%s\n' '--- root package.json ---'
if [ -f package.json ]; then cat -n package.json; fi

printf '%s\n' ''
printf '%s\n' '--- cross-posts package.json ---'
cat -n cross-posts/package.json

printf '%s\n' ''
printf '%s\n' '--- root tooling/version references ---'
rg -n -C2 '"engines"|node:"?' package.json || true
rg -n -C2 'node-version|setup-node|FROM node:|node:|NODE_VERSION|NODEJS_VERSION|node_modules|npm ci|npm install|devDependencies|dependencies' -g '*.yml' -g '*.yaml' -g 'Dockerfile*' -g 'package.json' . || true

Repository: petrubraha/varful.ro-api-demo

Length of output: 1508


Declare the supported Node.js version.

cross-posts/package.json has no engines field, node tooling/version files, or CI Docker/runtime references. Since this script uses Node globals, add an engines constraint that covers Node 18+ so unsupported runtimes fail early.

🤖 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 `@cross-posts/package.json` around lines 7 - 10, Add an engines constraint to
cross-posts/package.json declaring Node.js 18 or newer, alongside the existing
package metadata. Do not alter the main entry point or dev script.

Comment thread cross-posts/partner.js
Comment on lines +56 to +59
// Internal storage (in-memory) to link authorization redirects with original data.
let codeVerifier;
let codeChallenge;
let postData;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Bind each OAuth flow to its initiating browser session.

codeVerifier and postData are process-global: a second POST overwrites an in-flight flow, causing the first callback to exchange its code with the wrong verifier. Unauthenticated requests can repeatedly disrupt every active authorization. The authorization URL also lacks state, so the callback is not bound to its initiator.

Store the verifier and post payload per session (or keyed by a cryptographically random state), send state, validate it on callback, then delete only that flow’s entry.

Also applies to: 80-81, 122-130

🤖 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 `@cross-posts/partner.js` around lines 56 - 59, Replace the process-global
codeVerifier and postData flow state with per-session or cryptographically
random state-keyed storage in the authorization and callback handlers. Generate
and include a state value in the OAuth authorization URL, validate it on
callback before exchanging the code, retrieve the matching verifier and post
payload, and delete only that flow’s entry after use.

Comment thread cross-posts/partner.js
Comment on lines +108 to +117
if (!code) {
console.debug("[GET /partner/posts] Exchanging credentials for token...");
const encodedCredentials = btoa(
`${config.clientId}:${config.clientSecret}`,
);
const accessToken = await exchangeCredentialsForToken(encodedCredentials);

// const posts = mockPosts();
console.debug("[GET /partner/posts] Retrieving posts with access token...");
const posts = await fetchWithAccess(accessToken, "GET");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Cache the client-credentials token.

Every feed request calls the token endpoint. Cache the token with its expiry (and a small refresh skew) before fetching posts; this currently contradicts the 10-hour reuse requirement in cross-posts/README.md line 7.

🤖 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 `@cross-posts/partner.js` around lines 108 - 117, Update the token exchange
flow in the partner posts handler around exchangeCredentialsForToken so the
client-credentials token is reused across feed requests instead of fetched every
time. Store the token with its expiry and refresh it only when expired or within
a small refresh skew, while preserving the existing credential exchange and
fetchWithAccess flow; align the reuse duration with the 10-hour requirement
documented in the README.

Comment thread cross-posts/partner.js
Comment on lines +154 to +164
const response = await fetch(config.varfulTokenUrl, {
method: "POST",
headers: {
Authorization: `Basic ${encodedCredentials}`,
"Cache-Control": "no-cache",
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
grant_type: "client_credentials",
}),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For the Node.js version declared for this project, what supported mechanism cancels a global fetch request after a deadline?

💡 Result:

For the current Node.js versions (which include the v26.x and v24.x lines as of July 2026) [1][2], the supported mechanism to cancel a global fetch request after a specific deadline is the AbortSignal.timeout(ms) method [3][4]. This method, which is part of the Web API standard implemented in Node.js [5], creates an AbortSignal that automatically aborts after the specified duration in milliseconds [6][7]. You pass this signal to the fetch request via the signal property in the options object [6][4]. Example usage: const response = await fetch('https://api.example.com/data', { signal: AbortSignal.timeout(5000), // Cancels after 5000ms }); When the deadline is reached, the fetch request is automatically cancelled, and the promise rejects with a TimeoutError [6][7]. This approach is preferred over manually managing an AbortController and setTimeout, as it eliminates the need for manual cleanup and prevents memory leaks [3][6][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package files =="
git ls-files | grep -E '(^|/)(package.json|package-lock.json|pnpm-lock.yaml|yarn.lock|bun.lockb)$' || true

echo "== node engine / dependencies snippets =="
for f in $(git ls-files | grep -E '(^|/)package\.json$'); do
  echo "--- $f"
  sed -n '1,220p' "$f" | grep -nE '"(engines|version|exports|type|dependencies|devDependencies|peerDependencies|imports)"|fetch|AbortSignal|node-fetch|undici' -C 2 || true
done

echo "== cross-posts/partner.js relevant sections =="
if [ -f cross-posts/partner.js ]; then
  wc -l cross-posts/partner.js
  sed -n '130,230p' cross-posts/partner.js
  echo "== fetch occurrences in cross-posts/partner.js =="
  nl -ba cross-posts/partner.js | grep -n 'fetch\|Abort' -C 2 || true
else
  echo "missing cross-posts/partner.js"
fi

echo "== lockfile env hints if present =="
for f in $(git ls-files | grep -E '(^|/)(package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb)$'); do
  echo "--- $f"
  grep -n 'node@|lts/|Node.js|nodejs.org|engines:' "$f" -C 1 | head -80 || true
done

Repository: petrubraha/varful.ro-api-demo

Length of output: 3940


Apply a finite timeout to every outbound OAuth/API request.

A stalled remote peer can leave these requests pending indefinitely and exhaust server resources. Add the same bounded deadline to the client-credentials exchange, authorization-code exchange, and remote posts API request, such as signal: AbortSignal.timeout(...) in each fetch.

📍 Affects 1 file
  • cross-posts/partner.js#L154-L164 (this comment)
  • cross-posts/partner.js#L179-L193
  • cross-posts/partner.js#L213-L217
🤖 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 `@cross-posts/partner.js` around lines 154 - 164, Apply the same finite timeout
signal to all three outbound fetch calls in cross-posts/partner.js: the
client-credentials exchange at lines 154-164, the authorization-code exchange at
lines 179-193, and the remote posts API request at lines 213-217. Add the
bounded deadline to each fetch options object while preserving their existing
request behavior.

Comment thread cross-posts/partner.js
Comment on lines +236 to +241
(post) => `
<div>
<p>${post.location}</p>
<p><strong>${post.user}</strong>: ${post.caption}</p>
<img src="${post.image}" width="100" />
</div>`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Escape remote post content before rendering HTML.

location, user, and caption are inserted as HTML, while image is inserted into an attribute without validation. A post containing markup or an event-handler payload can execute in every feed viewer. HTML-escape text values and allow only validated https: image URLs before interpolation.

🤖 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 `@cross-posts/partner.js` around lines 236 - 241, Update the post rendering
template in the post mapping callback to HTML-escape post.location, post.user,
and post.caption before interpolation, and validate post.image so only https:
URLs are used in the img src attribute; reject or omit invalid image URLs rather
than inserting them directly.

Comment thread cross-posts/README.md
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@cross-posts/user.html`:
- Around line 12-13: Update the form action in the cross-posts template to avoid
hardcoding the localhost backend origin; use the Express-served relative partner
posts endpoint, or the configured partner API base URL when this page is
separately hosted, while preserving the POST target.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 17bf5ce6-08eb-4c3d-9d4a-1c8734fa41b4

📥 Commits

Reviewing files that changed from the base of the PR and between 0bff3af and 96031d8.

📒 Files selected for processing (2)
  • cross-posts/README.md
  • cross-posts/user.html
📜 Review details
🔇 Additional comments (1)
cross-posts/README.md (1)

32-32: LGTM!

Comment thread cross-posts/user.html
Comment on lines +12 to +13
<!-- localhost is your backend, the partner backend -->
<form action="http://localhost:3000/api/partner/posts" method="POST">

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not hardcode the backend origin in the form.

When this page is served outside http://localhost:3000—or over HTTPS—the browser submits to the visitor’s local machine instead of the partner backend. Use a relative action when the template is served by Express, or inject a configured partner API base URL for separately hosted pages.

Proposed fix
-    <form action="http://localhost:3000/api/partner/posts" method="POST">
+    <form action="/api/partner/posts" method="POST">
📝 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
<!-- localhost is your backend, the partner backend -->
<form action="http://localhost:3000/api/partner/posts" method="POST">
<!-- localhost is your backend, the partner backend -->
<form action="/api/partner/posts" method="POST">
🤖 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 `@cross-posts/user.html` around lines 12 - 13, Update the form action in the
cross-posts template to avoid hardcoding the localhost backend origin; use the
Express-served relative partner posts endpoint, or the configured partner API
base URL when this page is separately hosted, while preserving the POST target.

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