|
65 | 65 | configDir = flag.String("config-dir", "", `tsnet configuration directory ("" to use default)`) |
66 | 66 | resolveFromBackup = flag.String("resolve-from-backup", "", "resolve a link from snapshot file and exit") |
67 | 67 | allowUnknownUsers = flag.Bool("allow-unknown-users", false, "allow unknown users to save links") |
| 68 | + readonly = flag.Bool("readonly", false, "start golink server in read-only mode") |
68 | 69 | ) |
69 | 70 |
|
70 | 71 | var stats struct { |
@@ -271,10 +272,11 @@ type visitData struct { |
271 | 272 |
|
272 | 273 | // homeData is the data used by homeTmpl. |
273 | 274 | type homeData struct { |
274 | | - Short string |
275 | | - Long string |
276 | | - Clicks []visitData |
277 | | - XSRF string |
| 275 | + Short string |
| 276 | + Long string |
| 277 | + Clicks []visitData |
| 278 | + XSRF string |
| 279 | + ReadOnly bool |
278 | 280 | } |
279 | 281 |
|
280 | 282 | // deleteData is the data used by deleteTmpl. |
@@ -479,10 +481,11 @@ func serveHome(w http.ResponseWriter, r *http.Request, short string) { |
479 | 481 | return |
480 | 482 | } |
481 | 483 | homeTmpl.Execute(w, homeData{ |
482 | | - Short: short, |
483 | | - Long: long, |
484 | | - Clicks: clicks, |
485 | | - XSRF: xsrftoken.Generate(xsrfKey, cu.login, newShortName), |
| 484 | + Short: short, |
| 485 | + Long: long, |
| 486 | + Clicks: clicks, |
| 487 | + XSRF: xsrftoken.Generate(xsrfKey, cu.login, newShortName), |
| 488 | + ReadOnly: *readonly, |
486 | 489 | }) |
487 | 490 | } |
488 | 491 |
|
@@ -798,6 +801,10 @@ func userExists(ctx context.Context, login string) (bool, error) { |
798 | 801 | var reShortName = regexp.MustCompile(`^\w[\w\-\.]*$`) |
799 | 802 |
|
800 | 803 | func serveDelete(w http.ResponseWriter, r *http.Request) { |
| 804 | + if *readonly { |
| 805 | + http.Error(w, "golink is in read-only mode", http.StatusMethodNotAllowed) |
| 806 | + return |
| 807 | + } |
801 | 808 | short := strings.TrimPrefix(r.URL.Path, "/.delete/") |
802 | 809 | if short == "" { |
803 | 810 | http.Error(w, "short required", http.StatusBadRequest) |
@@ -848,6 +855,10 @@ func serveDelete(w http.ResponseWriter, r *http.Request) { |
848 | 855 | // long URL are validated for proper format. Existing links may only be updated |
849 | 856 | // by their owner. |
850 | 857 | func serveSave(w http.ResponseWriter, r *http.Request) { |
| 858 | + if *readonly { |
| 859 | + http.Error(w, "golink is in read-only mode", http.StatusMethodNotAllowed) |
| 860 | + return |
| 861 | + } |
851 | 862 | short, long := r.FormValue("short"), r.FormValue("long") |
852 | 863 | if short == "" || long == "" { |
853 | 864 | http.Error(w, "short and long required", http.StatusBadRequest) |
@@ -934,6 +945,9 @@ func serveSave(w http.ResponseWriter, r *http.Request) { |
934 | 945 | // Admin users can edit all links. |
935 | 946 | // Non-admin users can only edit their own links or links without an active owner. |
936 | 947 | func canEditLink(ctx context.Context, link *Link, u user) bool { |
| 948 | + if *readonly { |
| 949 | + return false |
| 950 | + } |
937 | 951 | if link == nil || link.Owner == "" { |
938 | 952 | // new or unowned link |
939 | 953 | return true |
|
0 commit comments