mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-08 22:00:50 +08:00
docs: serve uncached random readme badge
This commit is contained in:
parent
c2fa93a604
commit
23c976ab07
6 changed files with 127 additions and 2 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
|||
node_modules/
|
||||
.wrangler/
|
||||
coverage/
|
||||
.env
|
||||
.env.*
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<p>
|
||||
<a href="LICENSE"><img src="https://img.shields.io/github/license/xmanrui/dsh-im" alt="MIT license"></a>
|
||||
<img src="https://img.shields.io/badge/agent-DeepSeek%20Harness-5865f2" alt="DeepSeek Harness">
|
||||
<img src="https://redirectwizard.com/redirect/MD1G1" alt="滑动变祖器:今天是梁子或今天是梁圣(随机)">
|
||||
<img src="https://dsh-im-random-badge.xmanrui-dsh-im.workers.dev" alt="滑动变祖器:今天是梁子或今天是梁圣(随机)">
|
||||
</p>
|
||||
|
||||
<p><a href="README.md">简体中文</a> · <strong>English</strong></p>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<p>
|
||||
<a href="LICENSE"><img src="https://img.shields.io/github/license/xmanrui/dsh-im" alt="MIT 许可证"></a>
|
||||
<img src="https://img.shields.io/badge/agent-DeepSeek%20Harness-5865f2" alt="DeepSeek Harness">
|
||||
<img src="https://redirectwizard.com/redirect/MD1G1" alt="滑动变祖器:今天是梁子或今天是梁圣(随机)">
|
||||
<img src="https://dsh-im-random-badge.xmanrui-dsh-im.workers.dev" alt="滑动变祖器:今天是梁子或今天是梁圣(随机)">
|
||||
</p>
|
||||
|
||||
<p><strong>简体中文</strong> · <a href="README.en.md">English</a></p>
|
||||
|
|
|
|||
55
test/random-badge-worker.test.mjs
Normal file
55
test/random-badge-worker.test.mjs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
|
||||
import worker, {
|
||||
createBadgeResponse,
|
||||
pickBadgeValue,
|
||||
renderBadge,
|
||||
} from '../worker/random-badge.mjs';
|
||||
|
||||
test('random badge selects both supported values at the sample boundary', () => {
|
||||
assert.equal(pickBadgeValue(() => 0), '今天是梁子');
|
||||
assert.equal(pickBadgeValue(() => 0.499999), '今天是梁子');
|
||||
assert.equal(pickBadgeValue(() => 0.5), '今天是梁圣');
|
||||
assert.equal(pickBadgeValue(() => 0.999999), '今天是梁圣');
|
||||
});
|
||||
|
||||
test('random badge rejects invalid random samples and badge values', () => {
|
||||
assert.throws(() => pickBadgeValue(() => -0.1), RangeError);
|
||||
assert.throws(() => pickBadgeValue(() => 1), RangeError);
|
||||
assert.throws(() => renderBadge('今天是别人'), RangeError);
|
||||
});
|
||||
|
||||
test('random badge reproduces the Shields layout for either value', () => {
|
||||
for (const value of ['今天是梁子', '今天是梁圣']) {
|
||||
const svg = renderBadge(value);
|
||||
|
||||
assert.match(svg, /width="130" height="20"/);
|
||||
assert.match(svg, /<rect width="65" height="20" fill="#555"\/>/);
|
||||
assert.match(svg, /<rect x="65" width="65" height="20" fill="#f39c12"\/>/);
|
||||
assert.match(svg, new RegExp(`<title>滑动变祖器: ${value}</title>`));
|
||||
}
|
||||
});
|
||||
|
||||
test('worker returns a fresh SVG and explicitly disables every cache layer', async () => {
|
||||
const response = await worker.fetch(new Request('https://badge.example/'));
|
||||
const svg = await response.text();
|
||||
|
||||
assert.equal(response.status, 200);
|
||||
assert.equal(response.headers.get('content-type'), 'image/svg+xml; charset=utf-8');
|
||||
assert.equal(response.headers.get('cache-control'), 'no-store, no-cache, must-revalidate, max-age=0');
|
||||
assert.equal(response.headers.get('cdn-cache-control'), 'no-store');
|
||||
assert.equal(response.headers.get('cloudflare-cdn-cache-control'), 'no-store');
|
||||
assert.equal(response.headers.get('surrogate-control'), 'no-store');
|
||||
assert.match(svg, /今天是梁(?:子|圣)/);
|
||||
});
|
||||
|
||||
test('worker supports HEAD and rejects state-changing methods', async () => {
|
||||
const headResponse = createBadgeResponse('HEAD');
|
||||
const postResponse = createBadgeResponse('POST');
|
||||
|
||||
assert.equal(headResponse.status, 200);
|
||||
assert.equal(await headResponse.text(), '');
|
||||
assert.equal(postResponse.status, 405);
|
||||
assert.equal(postResponse.headers.get('allow'), 'GET, HEAD');
|
||||
});
|
||||
61
worker/random-badge.mjs
Normal file
61
worker/random-badge.mjs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
const BADGE_LABEL = '滑动变祖器';
|
||||
const BADGE_VALUES = Object.freeze(['今天是梁子', '今天是梁圣']);
|
||||
|
||||
function secureRandom() {
|
||||
const sample = new Uint32Array(1);
|
||||
crypto.getRandomValues(sample);
|
||||
return sample[0] / 0x1_0000_0000;
|
||||
}
|
||||
|
||||
export function pickBadgeValue(random = secureRandom) {
|
||||
const sample = random();
|
||||
|
||||
if (!Number.isFinite(sample) || sample < 0 || sample >= 1) {
|
||||
throw new RangeError('Random samples must be finite numbers from 0 (inclusive) to 1 (exclusive).');
|
||||
}
|
||||
|
||||
return BADGE_VALUES[Math.floor(sample * BADGE_VALUES.length)];
|
||||
}
|
||||
|
||||
export function renderBadge(value) {
|
||||
if (!BADGE_VALUES.includes(value)) {
|
||||
throw new RangeError(`Unsupported badge value: ${value}`);
|
||||
}
|
||||
|
||||
const accessibleLabel = `${BADGE_LABEL}: ${value}`;
|
||||
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" width="130" height="20" role="img" aria-label="${accessibleLabel}"><title>${accessibleLabel}</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="130" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="65" height="20" fill="#555"/><rect x="65" width="65" height="20" fill="#f39c12"/><rect width="130" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="335" y="150" fill-opacity=".8" filter="url(#blur)" textLength="550">${BADGE_LABEL}</text><text x="335" y="150" fill-opacity=".3" textLength="550">${BADGE_LABEL}</text></g><text x="335" y="140" textLength="550">${BADGE_LABEL}</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="965" y="150" fill-opacity=".8" filter="url(#blur)" textLength="550">${value}</text><text x="965" y="150" fill-opacity=".3" textLength="550">${value}</text></g><text x="965" y="140" textLength="550">${value}</text></g></g></svg>`;
|
||||
}
|
||||
|
||||
const NO_CACHE_HEADERS = {
|
||||
'Cache-Control': 'no-store, no-cache, must-revalidate, max-age=0',
|
||||
'CDN-Cache-Control': 'no-store',
|
||||
'Cloudflare-CDN-Cache-Control': 'no-store',
|
||||
'Content-Type': 'image/svg+xml; charset=utf-8',
|
||||
Expires: '0',
|
||||
Pragma: 'no-cache',
|
||||
'Surrogate-Control': 'no-store',
|
||||
'X-Content-Type-Options': 'nosniff',
|
||||
};
|
||||
|
||||
export function createBadgeResponse(method = 'GET') {
|
||||
if (method !== 'GET' && method !== 'HEAD') {
|
||||
return new Response('Method Not Allowed', {
|
||||
status: 405,
|
||||
headers: {
|
||||
...NO_CACHE_HEADERS,
|
||||
Allow: 'GET, HEAD',
|
||||
'Content-Type': 'text/plain; charset=utf-8',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const body = method === 'HEAD' ? null : renderBadge(pickBadgeValue());
|
||||
return new Response(body, { headers: NO_CACHE_HEADERS });
|
||||
}
|
||||
|
||||
export default {
|
||||
fetch(request) {
|
||||
return createBadgeResponse(request.method);
|
||||
},
|
||||
};
|
||||
8
wrangler.jsonc
Normal file
8
wrangler.jsonc
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/cloudflare/workers-sdk/main/packages/wrangler/config-schema.json",
|
||||
"name": "dsh-im-random-badge",
|
||||
"main": "worker/random-badge.mjs",
|
||||
"compatibility_date": "2026-08-18",
|
||||
"workers_dev": true,
|
||||
"preview_urls": false
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue