clone

push a directory of html from the terminal and get a four-character url back. a deno cli on cloudflare

fix: preserve site policy on repush

Dami committed 930bc04
Keep existing TTL and password settings when push flags are omitted, and report the resulting policy in commit responses and CLI output.
commit
930bc04a0db9b6e1cfc986bb0509da1d55a654f5
parent
b73fe3a8f8f4214a0d0411af6d15184e21407744
author
Dami <dami.etoile@gmail.com>
7 files changed, +114 -13
Mcli/README.md6++--
Mcli/commands/push.ts13++++++-
Acli/commands/push_test.ts22+++++++++++
Mshared/types.ts4++-
Mworker/src/api/push.ts22++++++++----
Mworker/src/api/sites.ts2+
Mworker/test/index.runtime.test.ts58++++++++++++++++++++++++++++++
M cli/README.md +3 -3
M cli/commands/push.ts +11 -2
A cli/commands/push_test.ts +22 -0
M shared/types.ts +3 -1
M worker/src/api/push.ts +15 -7
@@ -104,13 +104,13 @@ export async function handleCommit(
104104 );105105 }106106 107- const expiresAt = ttlToExpiry(body.ttl);108107 const target = body.target as Target;109108 let address: number;110109 let alias: string | null = null;111110 let isNew: boolean;111+ let existingSite: Awaited<ReturnType<typeof getSiteByAddress>> = null;112112 if ("address" in target) {113- const existing = await getSiteByAddress(env, target.address);113+ existingSite = await getSiteByAddress(env, target.address);114114 const slot = vaultSlotOf(target.address);115115 if (!(await getVaultBySlot(env, slot))) {116116 throw new ApiError(
@@ -119,8 +119,8 @@ export async function handleCommit(
119119 );120120 }121121 address = target.address;122- alias = existing?.alias ?? null;123- isNew = !existing;122+ alias = existingSite?.alias ?? null;123+ isNew = !existingSite;124124 } else {125125 const vault = await getVaultByName(env, target.vault);126126 if (!vault) throw new ApiError(404, `unknown vault: ${target.vault}`);
@@ -128,11 +128,11 @@ export async function handleCommit(
128128 if (alias !== null && !isValidName(alias)) {129129 throw new ApiError(400, `invalid alias: ${alias}`);130130 }131- const existing = alias === null131+ existingSite = alias === null132132 ? null133133 : await resolveTarget(env, { vault: target.vault, alias });134- if (existing) {135- address = existing.address;134+ if (existingSite) {135+ address = existingSite.address;136136 isNew = false;137137 } else {138138 address = await allocateAddress(env, vault.slot);
@@ -140,6 +140,13 @@ export async function handleCommit(
140140 }141141 }142142 143+ const expiresAt = body.ttl === undefined && existingSite144+ ? existingSite.expires_at145+ : ttlToExpiry(body.ttl);146+ const protectedSite = passwordHash === undefined && existingSite147+ ? existingSite.password_hash !== null148+ : passwordHash !== null && passwordHash !== undefined;149+143150 if (!(await env.CONTENT.head(`manifest/${hash}`))) {144151 await env.CONTENT.put(`manifest/${hash}`, bytes);145152 }
@@ -160,6 +167,7 @@ export async function handleCommit(
160167 alias,161168 manifestHash: hash,162169 expiresAt,170+ protected: protectedSite,163171 seq,164172 });165173 }
M worker/src/api/sites.ts +2 -0
M worker/test/index.runtime.test.ts +58 -0
@@ -200,6 +200,64 @@ describe("Worker runtime", () => {
200200 expect((await response.json<{ version: string }>()).version).toBe("0.7.1");201201 });202202 203+ it("preserves TTL and password settings when repushing without policy flags", async () => {204+ const address = 0xeffe;205+ const expiresAt = Math.floor(Date.now() / 1000) + 90 * 86_400;206+ const emptyHash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";207+ const oldManifest = "a".repeat(64);208+ await env.DB.batch([209+ env.DB.prepare(210+ "INSERT INTO vaults (slot, name, created_at) VALUES (?, ?, ?)",211+ ).bind(14, "repush-policy", Math.floor(Date.now() / 1000)),212+ env.DB.prepare(213+ `INSERT INTO sites214+ (address, vault_slot, alias, current_manifest, created_at, updated_at, expires_at, password_hash)215+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,216+ ).bind(217+ address,218+ 14,219+ "keep-settings",220+ oldManifest,221+ Math.floor(Date.now() / 1000),222+ Math.floor(Date.now() / 1000),223+ expiresAt,224+ "existing-password-hash",225+ ),226+ ]);227+ await env.CONTENT.put(`blob/${emptyHash}`, new Uint8Array());228+229+ const response = await SELF.fetch("https://share.demo.dev/api/push/commit", {230+ method: "POST",231+ headers: {232+ authorization: "Bearer runtime-test-token",233+ "content-type": "application/json",234+ },235+ body: JSON.stringify({236+ manifest: {237+ v: 1,238+ files: {239+ "index.html": { h: emptyHash, s: 0, ct: "text/html; charset=utf-8" },240+ },241+ },242+ target: { address },243+ }),244+ });245+246+ expect(response.status).toBe(200);247+ expect(await response.json()).toMatchObject({248+ address: "effe",249+ expiresAt,250+ protected: true,251+ });252+ const stored = await env.DB.prepare(253+ "SELECT expires_at, password_hash FROM sites WHERE address = ?",254+ ).bind(address).first<{ expires_at: number | null; password_hash: string | null }>();255+ expect(stored).toEqual({256+ expires_at: expiresAt,257+ password_hash: "existing-password-hash",258+ });259+ });260+203261 it("creates, lists, renames, and redescribes vaults", async () => {204262 const headers = {205263 authorization: "Bearer runtime-test-token",