@@ -17,7 +17,7 @@ document.addEventListener("DOMContentLoaded", async () => {
17
17
const version = window . location . hash === "" ? 0 : parseInt ( window . location . hash . slice ( 1 ) ) ;
18
18
const params = new URLSearchParams ( window . location . search ) ;
19
19
if ( params . has ( "token" ) ) {
20
- setUpdateToken ( key , params . get ( "token" ) ) ;
20
+ setToken ( key , params . get ( "token" ) ) ;
21
21
}
22
22
23
23
document . querySelector ( "#nav-btn" ) . checked = false ;
@@ -89,8 +89,8 @@ document.querySelector("#code-edit").addEventListener("keyup", (event) => {
89
89
document . querySelector ( "#edit" ) . addEventListener ( "click" , async ( ) => {
90
90
if ( document . querySelector ( "#edit" ) . disabled ) return ;
91
91
92
- const { key, version , content, language} = getState ( ) ;
93
- const { newState, url} = createState ( getUpdateToken ( key ) === "" ? "" : key , 0 , "edit" , content , language ) ;
92
+ const { key, content, language} = getState ( ) ;
93
+ const { newState, url} = createState ( hasPermission ( getToken ( key ) , "write" ) ? key : "" , 0 , "edit" , content , language ) ;
94
94
updateCode ( newState ) ;
95
95
updatePage ( newState ) ;
96
96
window . history . pushState ( newState , "" , url ) ;
@@ -100,17 +100,17 @@ document.querySelector("#save").addEventListener("click", async () => {
100
100
if ( document . querySelector ( "#save" ) . disabled ) return ;
101
101
const { key, mode, content, language} = getState ( )
102
102
if ( mode !== "edit" ) return ;
103
- const updateToken = getUpdateToken ( key ) ;
103
+ const token = getToken ( key ) ;
104
104
const saveButton = document . querySelector ( "#save" ) ;
105
105
saveButton . classList . add ( "loading" ) ;
106
106
107
107
let response ;
108
- if ( key && updateToken ) {
108
+ if ( key && token ) {
109
109
response = await fetch ( `/documents/${ key } ` , {
110
110
method : "PATCH" ,
111
111
body : content ,
112
112
headers : {
113
- Authorization : updateToken ,
113
+ Authorization : `Bearer ${ token } ` ,
114
114
Language : language
115
115
}
116
116
} ) ;
@@ -132,9 +132,10 @@ document.querySelector("#save").addEventListener("click", async () => {
132
132
return ;
133
133
}
134
134
135
- const { newState, url} = createState ( body . key , body . version , "view" , content , language ) ;
136
- setUpdateToken ( body . key , body . update_token ) ;
137
-
135
+ const { newState, url} = createState ( body . key , 0 , "view" , content , language ) ;
136
+ if ( body . token ) {
137
+ setToken ( body . key , body . token ) ;
138
+ }
138
139
const inputElement = document . createElement ( "input" )
139
140
const labelElement = document . createElement ( "label" )
140
141
@@ -166,10 +167,8 @@ document.querySelector("#delete").addEventListener("click", async () => {
166
167
if ( document . querySelector ( "#delete" ) . disabled ) return ;
167
168
168
169
const { key} = getState ( ) ;
169
- const updateToken = getUpdateToken ( key ) ;
170
- if ( updateToken === "" ) {
171
- return ;
172
- }
170
+ const token = getToken ( key ) ;
171
+ if ( ! token ) return ;
173
172
174
173
const deleteConfirm = window . confirm ( "Are you sure you want to delete this document? This action cannot be undone." )
175
174
if ( ! deleteConfirm ) return ;
@@ -179,7 +178,7 @@ document.querySelector("#delete").addEventListener("click", async () => {
179
178
let response = await fetch ( `/documents/${ key } ` , {
180
179
method : "DELETE" ,
181
180
headers : {
182
- Authorization : updateToken
181
+ Authorization : `Bearer ${ token } `
183
182
}
184
183
} ) ;
185
184
deleteButton . classList . remove ( "loading" ) ;
@@ -190,7 +189,7 @@ document.querySelector("#delete").addEventListener("click", async () => {
190
189
console . error ( "error deleting document:" , response ) ;
191
190
return ;
192
191
}
193
- deleteUpdateToken ( ) ;
192
+ deleteToken ( ) ;
194
193
const { newState, url} = createState ( "" , 0 , "edit" , "" , "" ) ;
195
194
updateCode ( newState ) ;
196
195
updatePage ( newState ) ;
@@ -217,43 +216,63 @@ document.querySelector("#share").addEventListener("click", async () => {
217
216
if ( document . querySelector ( "#share" ) . disabled ) return ;
218
217
219
218
const { key} = getState ( ) ;
220
- const updateToken = getUpdateToken ( key ) ;
221
- if ( updateToken === "" ) {
219
+ const token = getToken ( key ) ;
220
+ if ( ! hasPermission ( token , "share" ) ) {
222
221
await navigator . clipboard . writeText ( window . location . href ) ;
223
222
return ;
224
223
}
225
224
226
- document . querySelector ( "#share-permissions" ) . checked = false ;
227
- document . querySelector ( "#share-url" ) . value = window . location . href ;
225
+ document . querySelector ( "#share-permissions-write" ) . checked = false ;
226
+ document . querySelector ( "#share-permissions-delete" ) . checked = false ;
227
+ document . querySelector ( "#share-permissions-share" ) . checked = false ;
228
+
228
229
document . querySelector ( "#share-dialog" ) . showModal ( ) ;
229
230
} ) ;
230
231
231
232
document . querySelector ( "#share-dialog-close" ) . addEventListener ( "click" , ( ) => {
232
233
document . querySelector ( "#share-dialog" ) . close ( ) ;
233
234
} ) ;
234
235
235
- document . querySelector ( "#share-permissions" ) . addEventListener ( "change" , ( event ) => {
236
- const { key} = getState ( ) ;
237
- const updateToken = getUpdateToken ( key ) ;
238
- if ( updateToken === "" ) {
239
- return ;
236
+ document . querySelector ( "#share-copy" ) . addEventListener ( "click" , async ( ) => {
237
+ const permissions = [ ] ;
238
+ if ( document . querySelector ( "#share-permissions-write" ) . checked ) {
239
+ permissions . push ( "write" ) ;
240
+ }
241
+ if ( document . querySelector ( "#share-permissions-delete" ) . checked ) {
242
+ permissions . push ( "delete" ) ;
243
+ }
244
+ if ( document . querySelector ( "#share-permissions-share" ) . checked ) {
245
+ permissions . push ( "share" ) ;
240
246
}
241
247
242
- const shareUrl = document . querySelector ( "#share-url" ) ;
243
- if ( event . target . checked ) {
244
- shareUrl . value = ` ${ window . location . href } ?token= ${ updateToken } ` ;
248
+ if ( permissions . length === 0 ) {
249
+ await navigator . clipboard . writeText ( window . location . href ) ;
250
+ document . querySelector ( "#share-dialog" ) . close ( ) ;
245
251
return ;
246
252
}
247
- shareUrl . value = window . location . href ;
248
- } ) ;
249
253
250
- document . querySelector ( "#share-url" ) . addEventListener ( "click" , ( ) => {
251
- document . querySelector ( "#share-url" ) . select ( ) ;
252
- } ) ;
254
+ const { key} = getState ( ) ;
255
+ const token = getToken ( key ) ;
253
256
254
- document . querySelector ( "#share-copy" ) . addEventListener ( "click" , async ( ) => {
255
- const shareUrl = document . querySelector ( "#share-url" ) ;
256
- await navigator . clipboard . writeText ( shareUrl . value ) ;
257
+ const response = await fetch ( `/documents/${ key } /share` , {
258
+ method : "POST" ,
259
+ body : JSON . stringify ( { permissions : permissions } ) ,
260
+ headers : {
261
+ "Content-Type" : "application/json" ,
262
+ Authorization : `Bearer ${ token } `
263
+ }
264
+ } ) ;
265
+
266
+ if ( ! response . ok ) {
267
+ const body = await response . json ( ) ;
268
+ showErrorPopup ( body . message || response . statusText )
269
+ console . error ( "error sharing document:" , response ) ;
270
+ return ;
271
+ }
272
+
273
+ const body = await response . json ( )
274
+ const shareUrl = window . location . href + "?token=" + body . token ;
275
+ await navigator . clipboard . writeText ( shareUrl ) ;
257
276
document . querySelector ( "#share-dialog" ) . close ( ) ;
258
277
} ) ;
259
278
@@ -272,9 +291,9 @@ document.querySelector("#style").addEventListener("change", (event) => {
272
291
document . querySelector ( "#versions" ) . addEventListener ( "click" , async ( event ) => {
273
292
if ( event . target && event . target . matches ( "input[type='radio']" ) ) {
274
293
const { key, version} = getState ( ) ;
275
- let newVersion = event . target . value ;
276
- if ( event . target . parentElement . children . item ( 0 ) . value === newVersion ) {
277
- newVersion = ""
294
+ let newVersion = parseInt ( event . target . value ) ;
295
+ if ( event . target . parentElement . children . item ( 0 ) . value === ` ${ newVersion } ` ) {
296
+ newVersion = 0 ;
278
297
}
279
298
if ( newVersion === version ) return ;
280
299
const { newState, url} = await fetchVersion ( key , newVersion )
@@ -314,26 +333,26 @@ function createState(key, version, mode, content, language) {
314
333
return { newState : { key, version, mode, content : content . trim ( ) , language} , url : `/${ key } ${ version ? `#${ version } ` : "" } ` } ;
315
334
}
316
335
317
- function getUpdateToken ( key ) {
336
+ function getToken ( key ) {
318
337
const documents = localStorage . getItem ( "documents" )
319
338
if ( ! documents ) return ""
320
- const updateToken = JSON . parse ( documents ) [ key ]
321
- if ( ! updateToken ) return ""
339
+ const token = JSON . parse ( documents ) [ key ]
340
+ if ( ! token ) return ""
322
341
323
- return updateToken
342
+ return token
324
343
}
325
344
326
- function setUpdateToken ( key , updateToken ) {
345
+ function setToken ( key , token ) {
327
346
let documents = localStorage . getItem ( "documents" )
328
347
if ( ! documents ) {
329
348
documents = "{}"
330
349
}
331
350
const parsedDocuments = JSON . parse ( documents )
332
- parsedDocuments [ key ] = updateToken
351
+ parsedDocuments [ key ] = token
333
352
localStorage . setItem ( "documents" , JSON . stringify ( parsedDocuments ) )
334
353
}
335
354
336
- function deleteUpdateToken ( ) {
355
+ function deleteToken ( ) {
337
356
const { key} = getState ( ) ;
338
357
const documents = localStorage . getItem ( "documents" ) ;
339
358
if ( ! documents ) return ;
@@ -342,6 +361,13 @@ function deleteUpdateToken() {
342
361
localStorage . setItem ( "documents" , JSON . stringify ( parsedDocuments ) ) ;
343
362
}
344
363
364
+ function hasPermission ( token , permission ) {
365
+ if ( ! token ) return false ;
366
+ const tokenSplit = token . split ( "." )
367
+ if ( tokenSplit . length !== 3 ) return false ;
368
+ return JSON . parse ( atob ( tokenSplit [ 1 ] ) ) . permissions . includes ( permission ) ;
369
+ }
370
+
345
371
function updateCode ( state ) {
346
372
const { mode, content} = state ;
347
373
@@ -365,7 +391,7 @@ function updateCode(state) {
365
391
366
392
function updatePage ( state ) {
367
393
const { key, mode, content} = state ;
368
- const updateToken = getUpdateToken ( key ) ;
394
+ const token = getToken ( key ) ;
369
395
// update page title
370
396
if ( key ) {
371
397
document . title = `gobin - ${ key } ` ;
@@ -386,9 +412,7 @@ function updatePage(state) {
386
412
saveButton . style . display = "none" ;
387
413
editButton . disabled = false ;
388
414
editButton . style . display = "block" ;
389
- if ( updateToken ) {
390
- deleteButton . disabled = false ;
391
- }
415
+ deleteButton . disabled = ! hasPermission ( token , "delete" ) ;
392
416
copyButton . disabled = false ;
393
417
rawButton . disabled = false ;
394
418
shareButton . disabled = false ;
0 commit comments