diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2297ea2..b1c91b5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,34 +1,17 @@ -# Contributing +# contributing -Thanks for your interest in contributing to Cap! +## development setup -## Development setup +- you need to have the latest version of [Bun](https://bun.com/) installed for building the widget, server, standalone, solver and docs, and Rust for WASM -**Notes**: +- zed is the recommended IDE for working on Cap, but VSCode also works well. -- You need to have the latest release version of [Bun](https://bun.com/) installed. For WASM, Rust is also required. +- for writing docs, make sure to place them inside of `docs/guide` and make sure to update the sidebar in `docs/.vitepress/config.mjs`. -- VSCode is the recommended IDE for working on Cap. +run `bun install` in each Bun package you want to work on to install dependencies. -- For writing docs, make sure to place them inside of `docs/guide` and make sure to update the sidebar in `docs/.vitepress/config.mjs`. +## tips -Run `bun install` in each Bun package you want to work on to install dependencies. +- we recommend [Biome](https://biomejs.dev/) for formatting and linting. please fix all errors and warnings before submitting a PR -## Tips - -- Formatting uses [Biome](https://biomejs.dev/). Make sure there are no warnings or errors before submitting a PR. - -- We prefer conventional commits, but they are not enforced. See [here](https://www.conventionalcommits.org/en/v1.0.0/) for reference. - -- No AI-generated code please. AI autocomplete is fine, but not fully AI code. If you submit AI-generated code without making it clear, it may be rejected. - -- Type files are auto-generated from JSDoc comments - - -## Code of Conduct - -* Be respectful to others. Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. - -* Any spamming, trolling, flaming, or baiting is not welcome. - -* Keep contributions constructive and in good faith. \ No newline at end of file +- [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) are preferred \ No newline at end of file diff --git a/docs/guide/standalone/options.md b/docs/guide/standalone/options.md index 4a1a950..33a9315 100644 --- a/docs/guide/standalone/options.md +++ b/docs/guide/standalone/options.md @@ -58,4 +58,8 @@ If you would like to use a different database, you can set the `DB_URL` environm We recommend keeping this to the default value, which is `sqlite://./.data/db.sqlite.`. However, using Postgres, MySQL, or any other database supported by Bun SQL might be possible, even though not officially supported or tested: - `postgres://user:pass@localhost:5432/mydb` -- `mysql://user:password@localhost:3306/database` \ No newline at end of file +- `mysql://user:password@localhost:3306/database` + +## Error messages + +Error messages are redacted by default and instead logged to the console. To disable error logging, set `DISABLE_ERROR_LOGGING=true`. To disable error message redaction, set `SHOW_ERRORS=true`. \ No newline at end of file diff --git a/standalone/package.json b/standalone/package.json index 7777adb..4645383 100644 --- a/standalone/package.json +++ b/standalone/package.json @@ -1,6 +1,6 @@ { "name": "cap-standalone", - "version": "2.1.2", + "version": "2.1.3", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "bun run --watch src/index.js", diff --git a/standalone/src/index.js b/standalone/src/index.js index a886e28..86ec173 100644 --- a/standalone/src/index.js +++ b/standalone/src/index.js @@ -60,6 +60,35 @@ new Elysia({ .onBeforeHandle(({ set }) => { set.headers["X-Powered-By"] = "Cap Standalone"; }) + .onError(({ error, code }) => { + if (["VALIDATION", "NOT_FOUND"].includes(code)) { + return { + success: false, + error: error.code || "Internal server error", + detail: error + } + } + + const errorId = Bun.randomUUIDv7().split("-").pop(); + console.error(`[${error.code || "ERR"} ${errorId}]`, JSON.stringify({ + timestamp: new Date().toISOString(), + error, + env: { + bun: process.versions.bun, + platform: process.platform, + mem: process.memoryUsage() + } + })); + + return { + success: false, + error: error.code || "Internal server error", + detail: { + troubleshooting: "http://capjs.js.org/guide/standalone/options.html#error-messages", + id: errorId + } + } + }) .use(staticPlugin()) .get("/", async ({ cookie }) => { return file( diff --git a/standalone/src/siteverify.js b/standalone/src/siteverify.js index f510c37..0943820 100644 --- a/standalone/src/siteverify.js +++ b/standalone/src/siteverify.js @@ -38,7 +38,7 @@ export const siteverifyServer = new Elysia({ set.headers["X-RateLimit-Limit"] = "1"; set.headers["X-RateLimit-Remaining"] = "0"; set.headers["X-RateLimit-Reset"] = Math.ceil(unblockTime / 1000).toString(); - return { error: "You were temporarily for using an invalid secret key. Please try again later." }; + return { "success":false, error: "You were temporarily blocked for using an invalid secret key. Please try again later." }; } const sitekey = params.siteKey; @@ -46,14 +46,14 @@ export const siteverifyServer = new Elysia({ if (!sitekey || !secret || !response) { set.status = 400; - return { error: "Missing required parameters" }; + return { "success":false, error: "Missing required parameters" }; } const [keyData] = await db`SELECT * FROM keys WHERE siteKey = ${sitekey}`; const keyHash = keyData?.secretHash; if (!keyHash || !secret) { set.status = 404; - return { error: "Invalid site key or secret" }; + return { "success":false, error: "Invalid site key or secret" }; } const isValidSecret = await Bun.password.verify(secret, keyHash); @@ -61,20 +61,20 @@ export const siteverifyServer = new Elysia({ if (!isValidSecret) { blockedIPs.set(ip, now + 250); set.status = 403; - return { error: "Invalid site key or secret" }; + return { "success":false, error: "Invalid site key or secret" }; } const [token] = await db`SELECT * FROM tokens WHERE siteKey = ${params.siteKey} AND token = ${response}`; if (!token) { set.status = 404; - return { error: "Token not found" }; + return { "success":false, error: "Token not found" }; } if (token.expires < Date.now()) { await db`DELETE FROM tokens WHERE siteKey = ${params.siteKey} AND token = ${response}`; set.status = 403; - return { error: "Token expired" }; + return { "success":false,error: "Token expired" }; } await db`DELETE FROM tokens WHERE siteKey = ${params.siteKey} AND token = ${response}`;