@@ -4,9 +4,6 @@ import { checkQuota, updateQuotas } from '../utils/quotas.ts'
4
4
import { recordDeletion , recordDownload , recordUpload } from '../utils/stats.ts'
5
5
6
6
const MAX_FILE_SIZE = parseInt ( Deno . env . get ( 'MAX_FILE_SIZE' ) || '10485760' ) // 10MB default
7
- const FILENAME = 'only_you_know'
8
-
9
- // Initialize KV store
10
7
const kv = await Deno . openKv ( )
11
8
12
9
interface FileData {
@@ -16,37 +13,29 @@ interface FileData {
16
13
deletionKey : string
17
14
}
18
15
19
- async function storeFileData ( hash : string , data : FileData ) {
20
- await kv . set ( [ 'files' , hash ] , data )
16
+ async function storeFileData ( key : string , data : FileData ) {
17
+ await kv . set ( [ 'files' , key ] , data )
21
18
}
22
19
23
- async function getFileData ( hash : string ) : Promise < FileData | null > {
24
- const res = await kv . get < FileData > ( [ 'files' , hash ] )
20
+ async function getFileData ( key : string ) : Promise < FileData | null > {
21
+ const res = await kv . get < FileData > ( [ 'files' , key ] )
25
22
return res . value
26
23
}
27
24
28
- async function deleteFileData ( hash : string ) {
29
- await kv . delete ( [ 'files' , hash ] )
25
+ async function deleteFileData ( key : string ) {
26
+ await kv . delete ( [ 'files' , key ] )
30
27
}
31
28
32
29
export const handler : Handlers = {
33
30
async PUT ( req , ctx ) {
34
31
try {
35
- const filename = ctx . params . filename
36
-
37
- if ( ! filename . endsWith ( '.enc' ) ) {
38
- return new Response ( 'Only encrypted files (.enc) are accepted' , {
39
- status : 400 ,
40
- } )
41
- }
32
+ const key = ctx . params . filename // This is our storage key
42
33
43
34
const size = req . headers . get ( 'content-length' )
44
35
if ( ! size || parseInt ( size ) > MAX_FILE_SIZE ) {
45
36
return new Response (
46
37
`File too large. Maximum size is ${ MAX_FILE_SIZE / 1024 / 1024 } MB` ,
47
- {
48
- status : 413 ,
49
- } ,
38
+ { status : 413 }
50
39
)
51
40
}
52
41
@@ -55,31 +44,24 @@ export const handler: Handlers = {
55
44
if ( ! quotaOk ) {
56
45
return new Response (
57
46
'Service quota exceeded (storage or transfer limit reached)' ,
58
- {
59
- status : 507 ,
60
- } ,
47
+ { status : 507 }
61
48
)
62
49
}
63
50
64
51
// Read the request body
65
52
const arrayBuffer = await req . arrayBuffer ( )
66
53
const fileData = new Uint8Array ( arrayBuffer )
67
54
68
- // Calculate SHA-256 hash of the file content
69
- const hash = await crypto . subtle . digest ( 'SHA-256' , fileData )
70
- const hashHex = Array . from ( new Uint8Array ( hash ) )
71
- . map ( ( b ) => b . toString ( 16 ) . padStart ( 2 , '0' ) )
72
- . join ( '' )
73
-
74
- // Extract key and IV from the filename
75
- const authKey = filename . slice ( - 64 - 32 - 4 , - 4 ) // 64 for key, 32 for iv, 4 for .enc
55
+ // Extract IV from the request - it's added to the key for deletion auth
56
+ const iv = key . slice ( 64 , 96 )
57
+ const deletionKey = key + iv // Full key+iv for deletion auth
76
58
77
59
// Store file data and metadata in KV
78
- await storeFileData ( hashHex , {
60
+ await storeFileData ( key , {
79
61
content : fileData ,
80
62
size : fileSize ,
81
63
created : new Date ( ) . toISOString ( ) ,
82
- deletionKey : authKey ,
64
+ deletionKey
83
65
} )
84
66
85
67
await updateQuotas ( fileSize , true )
@@ -88,10 +70,10 @@ export const handler: Handlers = {
88
70
// Auto-deletion after 24h
89
71
setTimeout ( async ( ) => {
90
72
try {
91
- const fileInfo = await getFileData ( hashHex )
73
+ const fileInfo = await getFileData ( key )
92
74
if ( fileInfo ) {
93
75
await recordDeletion ( fileInfo . size )
94
- await deleteFileData ( hashHex )
76
+ await deleteFileData ( key )
95
77
}
96
78
} catch {
97
79
// Ignore deletion errors
@@ -107,18 +89,9 @@ export const handler: Handlers = {
107
89
108
90
async GET ( _req , ctx ) {
109
91
try {
110
- const filename = ctx . params . filename
111
-
112
- if ( ! filename . endsWith ( '.enc' ) ) {
113
- return new Response ( 'Only encrypted files (.enc) are allowed' , {
114
- status : 400 ,
115
- } )
116
- }
117
-
118
- // Get file hash from the start of the filename
119
- const fileHash = filename . slice ( 0 , 64 ) // SHA-256 hash is 64 hex chars
120
-
121
- const fileData = await getFileData ( fileHash )
92
+ const key = ctx . params . filename
93
+ const fileData = await getFileData ( key )
94
+
122
95
if ( ! fileData ) {
123
96
return new Response ( 'File not found' , { status : 404 } )
124
97
}
@@ -130,16 +103,15 @@ export const handler: Handlers = {
130
103
start ( controller ) {
131
104
controller . enqueue ( fileData . content )
132
105
controller . close ( )
133
- } ,
106
+ }
134
107
} )
135
108
136
109
return new Response ( stream , {
137
110
headers : {
138
111
'content-type' : 'application/octet-stream' ,
139
112
'cache-control' : 'no-store' ,
140
- 'content-disposition' : `attachment; filename="${ FILENAME } "` ,
141
- 'content-length' : fileData . size . toString ( ) ,
142
- } ,
113
+ 'content-length' : fileData . size . toString ( )
114
+ }
143
115
} )
144
116
} catch ( err ) {
145
117
console . error ( 'Download error:' , err )
@@ -149,20 +121,15 @@ export const handler: Handlers = {
149
121
150
122
async DELETE ( req , ctx ) {
151
123
try {
152
- const filename = ctx . params . filename
153
- if ( ! filename . endsWith ( '.enc' ) ) {
154
- return new Response ( 'Invalid file type' , { status : 400 } )
155
- }
156
-
157
- const fileHash = filename . slice ( 0 , 64 )
124
+ const key = ctx . params . filename
158
125
159
126
const authHeader = req . headers . get ( 'Authorization' )
160
127
if ( ! authHeader ?. startsWith ( 'Bearer ' ) ) {
161
128
return new Response ( 'Authorization required' , { status : 401 } )
162
129
}
163
130
const deletionKey = authHeader . slice ( 7 )
164
131
165
- const fileData = await getFileData ( fileHash )
132
+ const fileData = await getFileData ( key )
166
133
if ( ! fileData ) {
167
134
return new Response ( 'File not found' , { status : 404 } )
168
135
}
@@ -172,12 +139,12 @@ export const handler: Handlers = {
172
139
}
173
140
174
141
await recordDeletion ( fileData . size )
175
- await deleteFileData ( fileHash )
142
+ await deleteFileData ( key )
176
143
177
144
return new Response ( 'File deleted' , { status : 200 } )
178
145
} catch ( err ) {
179
146
console . error ( 'Delete error:' , err )
180
147
return new Response ( 'Delete failed' , { status : 500 } )
181
148
}
182
- } ,
183
- }
149
+ }
150
+ }
0 commit comments