Skip to content

Commit

Permalink
add support for running in read-only mode
Browse files Browse the repository at this point in the history
Right now, running in read-only mode displays a banner stating that
links can be resolved but not created or updated, as well as actively
blocking requests to modify links.

Update #118

Signed-off-by: Will Norris <will@tailscale.com>
  • Loading branch information
willnorris committed Apr 2, 2024
1 parent c66cbb8 commit b1d4d28
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 34 deletions.
30 changes: 22 additions & 8 deletions golink.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ var (
hostname = flag.String("hostname", defaultHostname, "service name")
resolveFromBackup = flag.String("resolve-from-backup", "", "resolve a link from snapshot file and exit")
allowUnknownUsers = flag.Bool("allow-unknown-users", false, "allow unknown users to save links")
readonly = flag.Bool("readonly", false, "start golink server in read-only mode")
)

var stats struct {
Expand Down Expand Up @@ -269,10 +270,11 @@ type visitData struct {

// homeData is the data used by homeTmpl.
type homeData struct {
Short string
Long string
Clicks []visitData
XSRF string
Short string
Long string
Clicks []visitData
XSRF string
ReadOnly bool
}

// deleteData is the data used by deleteTmpl.
Expand Down Expand Up @@ -443,10 +445,11 @@ func serveHome(w http.ResponseWriter, r *http.Request, short string) {
return
}
homeTmpl.Execute(w, homeData{
Short: short,
Long: long,
Clicks: clicks,
XSRF: xsrftoken.Generate(xsrfKey, cu.login, newShortName),
Short: short,
Long: long,
Clicks: clicks,
XSRF: xsrftoken.Generate(xsrfKey, cu.login, newShortName),
ReadOnly: *readonly,
})
}

Expand Down Expand Up @@ -743,6 +746,10 @@ func userExists(ctx context.Context, login string) (bool, error) {
var reShortName = regexp.MustCompile(`^\w[\w\-\.]*$`)

func serveDelete(w http.ResponseWriter, r *http.Request) {
if *readonly {
http.Error(w, "golink is in read-only mode", http.StatusMethodNotAllowed)
return
}
short := strings.TrimPrefix(r.URL.Path, "/.delete/")
if short == "" {
http.Error(w, "short required", http.StatusBadRequest)
Expand Down Expand Up @@ -793,6 +800,10 @@ func serveDelete(w http.ResponseWriter, r *http.Request) {
// long URL are validated for proper format. Existing links may only be updated
// by their owner.
func serveSave(w http.ResponseWriter, r *http.Request) {
if *readonly {
http.Error(w, "golink is in read-only mode", http.StatusMethodNotAllowed)
return
}
short, long := r.FormValue("short"), r.FormValue("long")
if short == "" || long == "" {
http.Error(w, "short and long required", http.StatusBadRequest)
Expand Down Expand Up @@ -871,6 +882,9 @@ func serveSave(w http.ResponseWriter, r *http.Request) {
// Admin users can edit all links.
// Non-admin users can only edit their own links or links without an active owner.
func canEditLink(ctx context.Context, link *Link, u user) bool {
if *readonly {
return false
}
if link == nil || link.Owner == "" {
// new or unowned link
return true
Expand Down
15 changes: 15 additions & 0 deletions static/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -1351,6 +1351,11 @@ select {
border-color: rgb(178 45 48 / var(--tw-border-opacity));
}

.border-orange-50 {
--tw-border-opacity: 1;
border-color: rgb(254 227 192 / var(--tw-border-opacity));
}

.bg-gray-100 {
--tw-bg-opacity: 1;
background-color: rgb(247 245 244 / var(--tw-bg-opacity));
Expand All @@ -1366,6 +1371,11 @@ select {
background-color: rgb(178 45 48 / var(--tw-bg-opacity));
}

.bg-orange-0 {
--tw-bg-opacity: 1;
background-color: rgb(255 250 238 / var(--tw-bg-opacity));
}

.p-2 {
padding: 0.5rem;
}
Expand All @@ -1390,6 +1400,11 @@ select {
padding-bottom: 1rem;
}

.py-3 {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
}

.pt-6 {
padding-top: 1.5rem;
}
Expand Down
32 changes: 21 additions & 11 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,36 @@ module.exports = {
800: "rgba(90, 0, 0)",
900: "rgba(66, 0, 0)",
},
white: '#fff',
current: 'currentColor',
orange: {
0: "rgba(255, 250, 238)",
50: "rgba(254, 227, 192)",
100: "rgba(248, 184, 134)",
200: "rgba(245, 146, 94)",
300: "rgba(229, 111, 74)",
400: "rgba(196, 76, 52)",
500: "rgba(158, 47, 40)",
600: "rgba(126, 30, 35)",
700: "rgba(93, 22, 27)",
800: "rgba(66, 14, 17)",
900: "rgba(66, 14, 17)",
},
white: "#fff",
current: "currentColor",
},
extend: {
typography: {
DEFAULT: {
css: {
'code::before': {
'content': '',
"code::before": {
content: "",
},
'code::after': {
'content': '',
"code::after": {
content: "",
},
},
},
},
},
},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
],
}
plugins: [require("@tailwindcss/forms"), require("@tailwindcss/typography")],
};
34 changes: 19 additions & 15 deletions tmpl/home.html
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
{{ define "main" }}
<h2 class="text-xl font-bold pb-2">Create a new link</h2>
{{ if .ReadOnly }}
<p class="rounded-md py-3 px-4 bg-orange-0 border border-orange-50">golink is running in read-only mode. Links can be resolved, but not created or updated.</p>
{{ else }}
<h2 class="text-xl font-bold pb-2">Create a new link</h2>

{{ with .Long }}
<p class="">Did you mean <a class="text-blue-600 hover:underline" href="{{.}}">{{.}}</a> ? Create a go link for it now:</p>
{{ with .Long }}
<p class="">Did you mean <a class="text-blue-600 hover:underline" href="{{.}}">{{.}}</a> ? Create a go link for it now:</p>
{{ end }}
<form method="POST" action="/" class="flex flex-wrap">
<input type="hidden" name="xsrf" value="{{ .XSRF }}" />
<div class="flex">
<label for=short class="flex my-2 px-2 items-center bg-gray-100 border border-r-0 border-gray-300 rounded-l-md text-gray-700">http://go/</label>
<input id=short name=short required type=text size=15 placeholder="shortname" value="{{.Short}}" pattern="\w[\w\-\.]*" title="Must start with letter or number; may contain letters, numbers, dashes, and periods."
class="p-2 my-2 rounded-r-md border-gray-300 placeholder:text-gray-400">
<span class="flex m-2 items-center">&rarr;</span>
</div>
<input name=long required type=text size=40 placeholder="https://destination-url"{{if .Short}} value="{{.Long}}" autofocus{{end}} class="p-2 my-2 mr-2 max-w-full rounded-md border-gray-300 placeholder:text-gray-400">
<button type=submit class="py-2 px-4 my-2 rounded-md bg-blue-500 border-blue-500 text-white hover:bg-blue-600 hover:border-blue-600">Create</button>
</form>
<p class="text-sm text-gray-500"><a class="text-blue-600 hover:underline" href="/.help">Help and advanced options</a></p>
{{ end }}
<form method="POST" action="/" class="flex flex-wrap">
<input type="hidden" name="xsrf" value="{{ .XSRF }}" />
<div class="flex">
<label for=short class="flex my-2 px-2 items-center bg-gray-100 border border-r-0 border-gray-300 rounded-l-md text-gray-700">http://go/</label>
<input id=short name=short required type=text size=15 placeholder="shortname" value="{{.Short}}" pattern="\w[\w\-\.]*" title="Must start with letter or number; may contain letters, numbers, dashes, and periods."
class="p-2 my-2 rounded-r-md border-gray-300 placeholder:text-gray-400">
<span class="flex m-2 items-center">&rarr;</span>
</div>
<input name=long required type=text size=40 placeholder="https://destination-url"{{if .Short}} value="{{.Long}}" autofocus{{end}} class="p-2 my-2 mr-2 max-w-full rounded-md border-gray-300 placeholder:text-gray-400">
<button type=submit class="py-2 px-4 my-2 rounded-md bg-blue-500 border-blue-500 text-white hover:bg-blue-600 hover:border-blue-600">Create</button>
</form>
<p class="text-sm text-gray-500"><a class="text-blue-600 hover:underline" href="/.help">Help and advanced options</a></p>

<h2 class="text-xl font-bold pt-6 pb-2">Popular Links</h2>
<table class="table-auto ">
Expand Down

0 comments on commit b1d4d28

Please sign in to comment.