Skip to content

Commit 89ca045

Browse files
committed
feat: add schemaVersion and accessCounter to paste metadata
1 parent 91b318e commit 89ca045

File tree

4 files changed

+59
-20
lines changed

4 files changed

+59
-20
lines changed

.github/workflows/deploy.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ on:
33
push:
44
branches:
55
- goshujin
6-
- dev
76

87
jobs:
98
deploy:
@@ -26,7 +25,6 @@ jobs:
2625
yarn test
2726
2827
- name: "Deploy"
29-
if: github.ref == 'refs/heads/main'
3028
env:
3129
CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
3230
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}

src/handlers/handleDelete.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { parsePath, WorkerError } from "../common.js"
2-
import { deletePaste, getPaste } from "../storage/storage.js"
2+
import {deletePaste, getPasteMetadata} from "../storage/storage.js"
33

44
export async function handleDelete(request: Request, env: Env, _: ExecutionContext) {
55
const url = new URL(request.url)
66
const { nameFromPath, passwd } = parsePath(url.pathname)
7-
const item = await getPaste(env, nameFromPath)
8-
if (item === null) {
7+
const metadata = await getPasteMetadata(env, nameFromPath)
8+
if (metadata === null) {
99
throw new WorkerError(404, `paste of name '${nameFromPath}' not found`)
1010
} else {
11-
if (passwd !== item.metadata.passwd) {
11+
if (passwd !== metadata.passwd) {
1212
throw new WorkerError(403, `incorrect password for paste '${nameFromPath}`)
1313
} else {
1414
await deletePaste(env, nameFromPath)

src/handlers/handleWrite.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
parsePath,
1010
WorkerError,
1111
} from "../common.js"
12-
import {createPaste, getPaste, pasteNameAvailable, updatePaste} from "../storage/storage.js";
12+
import {createPaste, getPasteMetadata, pasteNameAvailable, updatePaste} from "../storage/storage.js";
1313

1414
type PasteResponse = {
1515
url: string,
@@ -106,18 +106,18 @@ export async function handlePostOrPut(request: Request, env: Env, ctx: Execution
106106
let now = new Date()
107107
if (isPut) {
108108
const { nameFromPath, passwd } = parsePath(url.pathname)
109-
const item = await getPaste(env, nameFromPath)
109+
const originalMetadata = await getPasteMetadata(env, nameFromPath)
110110

111-
if (item === null) {
111+
if (originalMetadata === null) {
112112
throw new WorkerError(404, `paste of name '${nameFromPath}' is not found`)
113113
} else if (passwd === undefined) {
114114
throw new WorkerError(403, `no password for paste '${nameFromPath}`)
115-
} else if (passwd !== item.metadata.passwd) {
115+
} else if (passwd !== originalMetadata.passwd) {
116116
throw new WorkerError(403, `incorrect password for paste '${nameFromPath}`)
117117
} else {
118118
let pasteName = nameFromPath || genRandStr(isPrivate ? params.PRIVATE_PASTE_NAME_LEN : params.PASTE_NAME_LEN)
119119
let newPasswd = passwdFromForm || passwd
120-
await updatePaste(env, pasteName, content, item.metadata, {
120+
await updatePaste(env, pasteName, content, originalMetadata, {
121121
expirationSeconds,
122122
now,
123123
passwd: newPasswd,

src/storage/storage.ts

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import {dateToUnix, genRandStr, params, WorkerError} from "../common.js";
1+
import {dateToUnix, WorkerError} from "../common.js";
22

33
export type PasteMetadata = {
4+
schemaVersion: number,
45
passwd: string,
56

67
lastModifiedAtUnix: number,
78
createdAtUnix: number,
89
willExpireAtUnix: number,
910

11+
accessCounter: number, // a counter representing how frequent it is accessed, to administration usage
1012
filename?: string,
1113
}
1214

@@ -19,17 +21,50 @@ export async function getPaste(env: Env, short: string): Promise<PasteWithMetada
1921
let item = await env.PB.getWithMetadata<PasteMetadata>(short, {type: "arrayBuffer"});
2022

2123
if (item.value === null) {
22-
throw new WorkerError(404, `paste of name '${short}' not found`)
24+
return null
2325
} else if (item.metadata === null) {
2426
throw new WorkerError(500, `paste of name '${short}' has no metadata`)
2527
} else {
2628
if (item.metadata.willExpireAtUnix < new Date().getTime() / 1000) {
27-
throw new WorkerError(404, `paste of name '${short}' not found`)
29+
return null
2830
}
31+
32+
// update counter with probability 1%
33+
if (Math.random() < 0.01) {
34+
item.metadata.accessCounter += 1
35+
try {
36+
env.PB.put(short, item.value, {
37+
metadata: item.metadata,
38+
expiration: item.metadata.willExpireAtUnix,
39+
})
40+
} catch (e) {
41+
// ignore rate limit message
42+
if (!(e as Error).message.includes( "KV PUT failed: 429 Too Many Requests")) {
43+
throw e
44+
}
45+
}
46+
}
47+
2948
return {paste: item.value, metadata: item.metadata}
3049
}
3150
}
3251

52+
// we separate usage of getPasteMetadata and getPaste to make access metric more reliable
53+
export async function getPasteMetadata(env: Env, short: string): Promise<PasteMetadata | null> {
54+
let item = await env.PB.getWithMetadata<PasteMetadata>(short, {type: "stream"});
55+
56+
if (item.value === null) {
57+
return null
58+
} else if (item.metadata === null) {
59+
throw new WorkerError(500, `paste of name '${short}' has no metadata`)
60+
} else {
61+
if (item.metadata.willExpireAtUnix < new Date().getTime() / 1000) {
62+
return null
63+
}
64+
return item.metadata
65+
}
66+
}
67+
3368
export async function updatePaste(
3469
env: Env,
3570
pasteName: string,
@@ -41,16 +76,19 @@ export async function updatePaste(
4176
passwd: string,
4277
filename?: string,
4378
}) {
44-
const putOptions: { metadata: PasteMetadata, expirationTtl: number } = {
79+
const expirationUnix = dateToUnix(options.now) + options.expirationSeconds
80+
const putOptions: KVNamespacePutOptions = {
4581
metadata: {
82+
schemaVersion: 0,
4683
filename: options.filename || originalMetadata.filename,
4784
passwd: options.passwd,
4885

4986
lastModifiedAtUnix: dateToUnix(options.now),
5087
createdAtUnix: originalMetadata.createdAtUnix,
51-
willExpireAtUnix: dateToUnix(options.now) + options.expirationSeconds,
88+
willExpireAtUnix: expirationUnix,
89+
accessCounter: originalMetadata.accessCounter,
5290
},
53-
expirationTtl: options.expirationSeconds,
91+
expiration: expirationUnix,
5492
}
5593

5694
await env.PB.put(pasteName, content, putOptions)
@@ -66,16 +104,19 @@ export async function createPaste(
66104
passwd: string,
67105
filename?: string,
68106
}) {
69-
const putOptions: { metadata: PasteMetadata, expirationTtl: number } = {
107+
const expirationUnix = dateToUnix(options.now) + options.expirationSeconds
108+
const putOptions: KVNamespacePutOptions = {
70109
metadata: {
110+
schemaVersion: 0,
71111
filename: options.filename,
72112
passwd: options.passwd,
73113

74114
lastModifiedAtUnix: dateToUnix(options.now),
75115
createdAtUnix: dateToUnix(options.now),
76-
willExpireAtUnix: dateToUnix(options.now) + options.expirationSeconds,
116+
willExpireAtUnix: expirationUnix,
117+
accessCounter: 0,
77118
},
78-
expirationTtl: options.expirationSeconds,
119+
expiration: expirationUnix,
79120
}
80121

81122
await env.PB.put(pasteName, content, putOptions)

0 commit comments

Comments
 (0)