Rename to uncensored-send
This commit is contained in:
+1
-1
@@ -1,2 +1,2 @@
|
||||
/send
|
||||
/uncensored-send
|
||||
/data/
|
||||
|
||||
@@ -13,8 +13,8 @@ Built for sharing large binaries with friends so that I don't have to manually m
|
||||
|
||||
```
|
||||
go build
|
||||
./send token add me --vanity --admin
|
||||
./send
|
||||
./uncensored-send token add me --vanity --admin
|
||||
./uncensored-send
|
||||
```
|
||||
|
||||
Open <http://localhost:8080>, click **Log in**, paste the token. That's it.
|
||||
@@ -25,9 +25,9 @@ upload limit.
|
||||
|
||||
## Options
|
||||
|
||||
**Read `./send --help` rather than this file.**
|
||||
**Read `./uncensored-send --help` rather than this file.**
|
||||
|
||||
Every option also reads from `SEND_`-prefixed environment variables.
|
||||
Every option also reads from `UNCENSORED_SEND_`-prefixed environment variables.
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"send/internal/config"
|
||||
"uncensored-send/internal/config"
|
||||
)
|
||||
|
||||
// tokenFilePerm is deliberately stricter than the rest of the data directory:
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"send/internal/config"
|
||||
"uncensored-send/internal/config"
|
||||
)
|
||||
|
||||
func defaults() *config.Config {
|
||||
|
||||
@@ -35,7 +35,7 @@ type Config struct {
|
||||
trustedProxyNets []*net.IPNet
|
||||
}
|
||||
|
||||
const EnvPrefix = "SEND_"
|
||||
const EnvPrefix = "UNCENSORED_SEND_"
|
||||
|
||||
// Register wires every option onto s. Short forms exist only for the options
|
||||
// reached often; everything else is long-only, by design.
|
||||
|
||||
@@ -86,7 +86,7 @@ func TestFormatRoundTrips(t *testing.T) {
|
||||
func TestHyphenConvention(t *testing.T) {
|
||||
newSet := func() (*Set, *string) {
|
||||
var listen string
|
||||
s := NewSet("test", "SEND_TEST_")
|
||||
s := NewSet("test", "UNCENSORED_SEND_TEST_")
|
||||
s.String(&listen, "listen", "l", "default", "ADDR", "listen address")
|
||||
return s, &listen
|
||||
}
|
||||
@@ -116,9 +116,9 @@ func TestHyphenConvention(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEnvFallback(t *testing.T) {
|
||||
t.Setenv("SEND_TEST_MAX_SIZE", "4GiB")
|
||||
t.Setenv("UNCENSORED_SEND_TEST_MAX_SIZE", "4GiB")
|
||||
var size int64
|
||||
s := NewSet("test", "SEND_TEST_")
|
||||
s := NewSet("test", "UNCENSORED_SEND_TEST_")
|
||||
s.Size(&size, "max-size", "s", "2GiB", "cap")
|
||||
if err := s.Parse(nil); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -128,7 +128,7 @@ func TestEnvFallback(t *testing.T) {
|
||||
}
|
||||
|
||||
// An explicit flag still wins over the environment.
|
||||
s = NewSet("test", "SEND_TEST_")
|
||||
s = NewSet("test", "UNCENSORED_SEND_TEST_")
|
||||
s.Size(&size, "max-size", "s", "2GiB", "cap")
|
||||
if err := s.Parse([]string{"--max-size", "1GiB"}); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -52,7 +52,7 @@ func (s *Set) note(long, short string) {
|
||||
}
|
||||
}
|
||||
|
||||
// envName maps --max-size to SEND_MAX_SIZE.
|
||||
// envName maps --max-size to UNCENSORED_SEND_MAX_SIZE.
|
||||
func (s *Set) envName(long string) string {
|
||||
return s.envPrefix + strings.ToUpper(strings.ReplaceAll(long, "-", "_"))
|
||||
}
|
||||
@@ -183,7 +183,7 @@ func (s *Set) checkConvention(args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyEnv seeds every option from its SEND_-prefixed environment variable.
|
||||
// applyEnv seeds every option from its UNCENSORED_SEND_-prefixed environment variable.
|
||||
// Called before parsing so an explicit command-line flag always wins.
|
||||
func (s *Set) applyEnv() error {
|
||||
for _, sp := range s.specs {
|
||||
@@ -214,7 +214,7 @@ func (s *Set) Parse(args []string) error {
|
||||
// Changed reports whether an option was given on the command line.
|
||||
//
|
||||
// Values picked up from the environment do not count: a command that acts only
|
||||
// on the options you actually typed must not quietly act on a SEND_ variable
|
||||
// on the options you actually typed must not quietly act on a UNCENSORED_SEND_ variable
|
||||
// exported for the server.
|
||||
func (s *Set) Changed(long string) bool {
|
||||
if s.fromEnv[long] {
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"send/internal/config"
|
||||
"uncensored-send/internal/config"
|
||||
)
|
||||
|
||||
// adminPage is the one view that shows every object, regardless of who
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
// reason to prefer it over localStorage, where any injected script could
|
||||
// exfiltrate the credential. The page never needs to see the value: the server
|
||||
// resolves it and renders who the caller is.
|
||||
const tokenCookie = "send_token"
|
||||
const tokenCookie = "uncensored_send_token"
|
||||
|
||||
// rememberFor is how long a persisted login survives. Tokens are revoked by
|
||||
// deleting them from the token file, so a long window costs nothing.
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"send/internal/auth"
|
||||
"send/internal/store"
|
||||
"uncensored-send/internal/auth"
|
||||
"uncensored-send/internal/store"
|
||||
)
|
||||
|
||||
// handleDelete removes an object early. Three credentials are accepted: the
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"send/internal/store"
|
||||
"uncensored-send/internal/store"
|
||||
)
|
||||
|
||||
// downloadCSP is as inert as a policy gets. Combined with the attachment
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"send/internal/config"
|
||||
"uncensored-send/internal/config"
|
||||
)
|
||||
|
||||
// loginPage backs both the form and its error redisplay.
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"send/internal/auth"
|
||||
"send/internal/config"
|
||||
"send/internal/store"
|
||||
"uncensored-send/internal/auth"
|
||||
"uncensored-send/internal/config"
|
||||
"uncensored-send/internal/store"
|
||||
)
|
||||
|
||||
func writeJSON(w http.ResponseWriter, status int, v any) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"send/internal/config"
|
||||
"uncensored-send/internal/config"
|
||||
)
|
||||
|
||||
// limiter is a per-key token bucket. It is deliberately small: there is no
|
||||
|
||||
@@ -11,10 +11,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"send/internal/auth"
|
||||
"send/internal/config"
|
||||
"send/internal/store"
|
||||
"send/web"
|
||||
"uncensored-send/internal/auth"
|
||||
"uncensored-send/internal/config"
|
||||
"uncensored-send/internal/store"
|
||||
"uncensored-send/web"
|
||||
)
|
||||
|
||||
// Server holds everything the handlers need. It is safe for concurrent use.
|
||||
|
||||
@@ -19,9 +19,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"send/internal/auth"
|
||||
"send/internal/config"
|
||||
"send/internal/store"
|
||||
"uncensored-send/internal/auth"
|
||||
"uncensored-send/internal/config"
|
||||
"uncensored-send/internal/store"
|
||||
)
|
||||
|
||||
// clock is the fixed instant tests start from; s.now is swapped so expiry can
|
||||
@@ -42,7 +42,7 @@ func newHarness(t *testing.T, tweak func(*config.Config)) *harness {
|
||||
dir := t.TempDir()
|
||||
|
||||
cfg := config.Config{}
|
||||
fs := config.NewSet("test", "SEND_TEST_")
|
||||
fs := config.NewSet("test", "UNCENSORED_SEND_TEST_")
|
||||
cfg.Register(fs)
|
||||
if err := fs.Parse(nil); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"send/internal/auth"
|
||||
"send/internal/config"
|
||||
"send/internal/store"
|
||||
"uncensored-send/internal/auth"
|
||||
"uncensored-send/internal/config"
|
||||
"uncensored-send/internal/store"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Command send is a small self-hosted file drop: uploads land in a flat data
|
||||
// Command uncensored-send is a small self-hosted file drop: uploads land in a flat data
|
||||
// directory, expire on their own, and are served back as inert attachments.
|
||||
package main
|
||||
|
||||
@@ -15,10 +15,10 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"send/internal/auth"
|
||||
"send/internal/config"
|
||||
"send/internal/server"
|
||||
"send/internal/store"
|
||||
"uncensored-send/internal/auth"
|
||||
"uncensored-send/internal/config"
|
||||
"uncensored-send/internal/server"
|
||||
"uncensored-send/internal/store"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -26,7 +26,7 @@ func main() {
|
||||
if errors.Is(err, flag.ErrHelp) {
|
||||
os.Exit(0)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "send: "+err.Error())
|
||||
fmt.Fprintln(os.Stderr, "uncensored-send: "+err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -38,13 +38,13 @@ func run(args []string) error {
|
||||
return serve(args)
|
||||
}
|
||||
|
||||
const serveUsage = `send - a small self-hosted file drop
|
||||
const serveUsage = `uncensored-send - a small self-hosted file drop
|
||||
|
||||
Usage:
|
||||
send [options] run the server
|
||||
send token add <name> [opts] mint a token, printing it once
|
||||
send token list list tokens
|
||||
send token rm <name> remove a token
|
||||
uncensored-send [options] run the server
|
||||
uncensored-send token add <name> [opts] mint a token, printing it once
|
||||
uncensored-send token list list tokens
|
||||
uncensored-send token rm <name> remove a token
|
||||
|
||||
Options take one hyphen with a single letter (-s) and two with a full
|
||||
word (--max-size). Every option can also be set from the environment.
|
||||
@@ -54,7 +54,7 @@ Options:
|
||||
|
||||
func serve(args []string) error {
|
||||
var cfg config.Config
|
||||
fs := config.NewSet("send", config.EnvPrefix)
|
||||
fs := config.NewSet("uncensored-send", config.EnvPrefix)
|
||||
fs.SetOutput(os.Stderr)
|
||||
cfg.Register(fs)
|
||||
|
||||
|
||||
@@ -12,19 +12,19 @@ import (
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"send/internal/auth"
|
||||
"send/internal/config"
|
||||
"uncensored-send/internal/auth"
|
||||
"uncensored-send/internal/config"
|
||||
)
|
||||
|
||||
const tokenUsage = `send token - manage upload credentials
|
||||
const tokenUsage = `uncensored-send token - manage upload credentials
|
||||
|
||||
Usage:
|
||||
send token add <name> [options] generate a token
|
||||
send token add <name> --token - read a chosen one from stdin
|
||||
send token rotate <name> [--token -] replace the secret, keep everything else
|
||||
send token set <name> [options] change limits and flags in place
|
||||
send token list [options]
|
||||
send token rm <name> [options]
|
||||
uncensored-send token add <name> [options] generate a token
|
||||
uncensored-send token add <name> --token - read a chosen one from stdin
|
||||
uncensored-send token rotate <name> [--token -] replace the secret, keep everything else
|
||||
uncensored-send token set <name> [options] change limits and flags in place
|
||||
uncensored-send token list [options]
|
||||
uncensored-send token rm <name> [options]
|
||||
|
||||
A token grants its own size and lifetime limits. Any limit left unset is
|
||||
inherited from the running server's defaults, so a token with no options
|
||||
@@ -136,7 +136,7 @@ type tokenOptions struct {
|
||||
}
|
||||
|
||||
func (o *tokenOptions) register() *config.Set {
|
||||
fs := config.NewSet("send token", config.EnvPrefix)
|
||||
fs := config.NewSet("uncensored-send token", config.EnvPrefix)
|
||||
fs.String(&o.dataDir, "data", "d", "./data", "DIR", "directory holding the data")
|
||||
fs.String(&o.tokensPath, "tokens", "", "", "FILE", "token file location (default <data>/tokens.json)")
|
||||
fs.String(&o.maxSize, "max-size", "s", "", "SIZE", "per-upload cap for this token; 'unlimited' to remove it")
|
||||
@@ -296,7 +296,7 @@ func tokenSet(file *auth.File, name string, opts tokenOptions, fs *config.Set) e
|
||||
changes = append(changes, fmt.Sprintf("--%s=%t", f.flag, f.value))
|
||||
}
|
||||
if fs.Changed("token") {
|
||||
return errors.New("use \"send token rotate\" to change the secret")
|
||||
return errors.New("use \"uncensored-send token rotate\" to change the secret")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<section class="card">
|
||||
<h2>Tokens</h2>
|
||||
<p class="hint">
|
||||
Tokens are minted, rotated and revoked with <code>./send token</code> on the
|
||||
Tokens are minted, rotated and revoked with <code>./uncensored-send token</code> on the
|
||||
server. They are deliberately not manageable from here: nothing that hands
|
||||
out credentials should be reachable over the network.
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user