@@ -36,11 +36,12 @@ import (
36
36
const defaultHostname = "go"
37
37
38
38
var (
39
- verbose = flag .Bool ("verbose" , false , "be verbose" )
40
- sqlitefile = flag .String ("sqlitedb" , "" , "path of SQLite database to store links" )
41
- dev = flag .String ("dev-listen" , "" , "if non-empty, listen on this addr and run in dev mode; auto-set sqlitedb if empty and don't use tsnet" )
42
- snapshot = flag .String ("snapshot" , "" , "file path of snapshot file" )
43
- hostname = flag .String ("hostname" , defaultHostname , "service name" )
39
+ verbose = flag .Bool ("verbose" , false , "be verbose" )
40
+ sqlitefile = flag .String ("sqlitedb" , "" , "path of SQLite database to store links" )
41
+ dev = flag .String ("dev-listen" , "" , "if non-empty, listen on this addr and run in dev mode; auto-set sqlitedb if empty and don't use tsnet" )
42
+ snapshot = flag .String ("snapshot" , "" , "file path of snapshot file" )
43
+ hostname = flag .String ("hostname" , defaultHostname , "service name" )
44
+ resolveFromBackup = flag .String ("resolve-from-backup" , "" , "resolve a link from snapshot file and exit" )
44
45
)
45
46
46
47
var stats struct {
@@ -66,6 +67,16 @@ var localClient *tailscale.LocalClient
66
67
func Run () error {
67
68
flag .Parse ()
68
69
70
+ // if resolving from backup, set sqlitefile and snapshot flags to
71
+ // restore links into an in-memory sqlite database.
72
+ if * resolveFromBackup != "" {
73
+ * sqlitefile = ":memory:"
74
+ snapshot = resolveFromBackup
75
+ if flag .NArg () != 1 {
76
+ log .Fatal ("--resolve-from-backup also requires a link to be resolved" )
77
+ }
78
+ }
79
+
69
80
if * sqlitefile == "" {
70
81
if devMode () {
71
82
tmpdir , err := ioutil .TempDir ("" , "golink_dev_*" )
@@ -102,6 +113,16 @@ func Run() error {
102
113
log .Printf ("initializing stats: %v" , err )
103
114
}
104
115
116
+ // if link specified on command line, resolve and exit
117
+ if flag .NArg () > 0 {
118
+ destination , err := resolveLink (flag .Arg (0 ))
119
+ if err != nil {
120
+ log .Fatal (err )
121
+ }
122
+ fmt .Println (destination )
123
+ os .Exit (0 )
124
+ }
125
+
105
126
// flush stats periodically
106
127
go flushStatsLoop ()
107
128
@@ -588,3 +609,19 @@ func restoreLastSnapshot() error {
588
609
}
589
610
return bs .Err ()
590
611
}
612
+
613
+ func resolveLink (link string ) (string , error ) {
614
+ // if link specified as "go/name", trim "go" prefix.
615
+ // Remainder will parse as URL with no scheme or host
616
+ link = strings .TrimPrefix (link , * hostname )
617
+ u , err := url .Parse (link )
618
+ if err != nil {
619
+ return "" , err
620
+ }
621
+ short , remainder , _ := strings .Cut (strings .TrimPrefix (u .RequestURI (), "/" ), "/" )
622
+ l , err := db .Load (short )
623
+ if err != nil {
624
+ return "" , err
625
+ }
626
+ return expandLink (l .Long , expandEnv {Now : time .Now ().UTC (), Path : remainder })
627
+ }
0 commit comments