25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

412 lines
9.4 KiB

  1. package cmd
  2. import (
  3. "fmt"
  4. "log"
  5. "os"
  6. "strings"
  7. "github.com/dutchcoders/transfer.sh/server"
  8. "github.com/dutchcoders/transfer.sh/server/storage"
  9. "github.com/fatih/color"
  10. "github.com/urfave/cli"
  11. "google.golang.org/api/googleapi"
  12. )
  13. var Version = "1.1.4"
  14. var helpTemplate = `NAME:
  15. {{.Name}} - {{.Usage}}
  16. DESCRIPTION:
  17. {{.Description}}
  18. USAGE:
  19. {{.Name}} {{if .Flags}}[flags] {{end}}command{{if .Flags}}{{end}} [arguments...]
  20. COMMANDS:
  21. {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
  22. {{end}}{{if .Flags}}
  23. FLAGS:
  24. {{range .Flags}}{{.}}
  25. {{end}}{{end}}
  26. VERSION:
  27. ` + Version +
  28. `{{ "\n"}}`
  29. var globalFlags = []cli.Flag{
  30. cli.StringFlag{
  31. Name: "listener",
  32. Usage: "127.0.0.1:8080",
  33. Value: "127.0.0.1:8080",
  34. },
  35. // redirect to https?
  36. // hostnames
  37. cli.StringFlag{
  38. Name: "profile-listener",
  39. Usage: "127.0.0.1:6060",
  40. Value: "",
  41. },
  42. cli.BoolFlag{
  43. Name: "force-https",
  44. Usage: "",
  45. },
  46. cli.StringFlag{
  47. Name: "tls-listener",
  48. Usage: "127.0.0.1:8443",
  49. Value: "",
  50. },
  51. cli.BoolFlag{
  52. Name: "tls-listener-only",
  53. Usage: "",
  54. },
  55. cli.StringFlag{
  56. Name: "tls-cert-file",
  57. Value: "",
  58. },
  59. cli.StringFlag{
  60. Name: "tls-private-key",
  61. Value: "",
  62. },
  63. cli.StringFlag{
  64. Name: "temp-path",
  65. Usage: "path to temp files",
  66. Value: os.TempDir(),
  67. },
  68. cli.StringFlag{
  69. Name: "web-path",
  70. Usage: "path to static web files",
  71. Value: "",
  72. },
  73. cli.StringFlag{
  74. Name: "proxy-path",
  75. Usage: "path prefix when service is run behind a proxy",
  76. Value: "",
  77. },
  78. cli.StringFlag{
  79. Name: "ga-key",
  80. Usage: "key for google analytics (front end)",
  81. Value: "",
  82. },
  83. cli.StringFlag{
  84. Name: "uservoice-key",
  85. Usage: "key for user voice (front end)",
  86. Value: "",
  87. },
  88. cli.IntFlag{
  89. Name: "lifetime",
  90. Usage: "default file lifetime in days",
  91. Value: 14,
  92. },
  93. cli.StringFlag{
  94. Name: "provider",
  95. Usage: "s3|gdrive|local",
  96. Value: "",
  97. },
  98. cli.StringFlag{
  99. Name: "s3-endpoint",
  100. Usage: "",
  101. Value: "",
  102. EnvVar: "S3_ENDPOINT",
  103. },
  104. cli.StringFlag{
  105. Name: "s3-region",
  106. Usage: "",
  107. Value: "eu-west-1",
  108. EnvVar: "S3_REGION",
  109. },
  110. cli.StringFlag{
  111. Name: "aws-access-key",
  112. Usage: "",
  113. Value: "",
  114. EnvVar: "AWS_ACCESS_KEY",
  115. },
  116. cli.StringFlag{
  117. Name: "aws-secret-key",
  118. Usage: "",
  119. Value: "",
  120. EnvVar: "AWS_SECRET_KEY",
  121. },
  122. cli.StringFlag{
  123. Name: "bucket",
  124. Usage: "",
  125. Value: "",
  126. EnvVar: "BUCKET",
  127. },
  128. cli.BoolFlag{
  129. Name: "s3-no-multipart",
  130. Usage: "Disables S3 Multipart Puts",
  131. },
  132. cli.BoolFlag{
  133. Name: "s3-path-style",
  134. Usage: "Forces path style URLs, required for Minio.",
  135. },
  136. cli.StringFlag{
  137. Name: "gdrive-client-json-filepath",
  138. Usage: "",
  139. Value: "",
  140. },
  141. cli.StringFlag{
  142. Name: "gdrive-local-config-path",
  143. Usage: "",
  144. Value: "",
  145. },
  146. cli.IntFlag{
  147. Name: "gdrive-chunk-size",
  148. Usage: "",
  149. Value: googleapi.DefaultUploadChunkSize / 1024 / 1024,
  150. },
  151. cli.IntFlag{
  152. Name: "rate-limit",
  153. Usage: "requests per minute",
  154. Value: 0,
  155. EnvVar: "",
  156. },
  157. cli.StringFlag{
  158. Name: "lets-encrypt-hosts",
  159. Usage: "host1, host2",
  160. Value: "",
  161. EnvVar: "HOSTS",
  162. },
  163. cli.StringFlag{
  164. Name: "log",
  165. Usage: "/var/log/transfersh.log",
  166. Value: "",
  167. },
  168. cli.StringFlag{
  169. Name: "basedir",
  170. Usage: "path to storage",
  171. Value: "",
  172. },
  173. cli.IntFlag{
  174. Name: "cleanup-interval",
  175. Usage: "interval to clean up expired files from local storage",
  176. Value: 1,
  177. },
  178. cli.StringFlag{
  179. Name: "clamav-host",
  180. Usage: "clamav-host",
  181. Value: "",
  182. EnvVar: "CLAMAV_HOST",
  183. },
  184. cli.StringFlag{
  185. Name: "virustotal-key",
  186. Usage: "virustotal-key",
  187. Value: "",
  188. EnvVar: "VIRUSTOTAL_KEY",
  189. },
  190. cli.BoolFlag{
  191. Name: "profiler",
  192. Usage: "enable profiling",
  193. },
  194. cli.StringFlag{
  195. Name: "http-auth-user",
  196. Usage: "user for http basic auth",
  197. Value: "",
  198. },
  199. cli.StringFlag{
  200. Name: "http-auth-pass",
  201. Usage: "pass for http basic auth",
  202. Value: "",
  203. },
  204. cli.StringFlag{
  205. Name: "ip-whitelist",
  206. Usage: "comma separated list of ips allowed to connect to the service",
  207. Value: "",
  208. },
  209. cli.StringFlag{
  210. Name: "ip-blacklist",
  211. Usage: "comma separated list of ips not allowed to connect to the service",
  212. Value: "",
  213. },
  214. }
  215. type Cmd struct {
  216. *cli.App
  217. }
  218. func VersionAction(c *cli.Context) {
  219. fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh: Easy file sharing from the command line, version: %s", c.App.Version)))
  220. }
  221. func New() *Cmd {
  222. logger := log.New(os.Stdout, "[transfer.sh]", log.LstdFlags)
  223. app := cli.NewApp()
  224. app.Name = "transfer.sh"
  225. app.Author = ""
  226. app.Usage = "transfer.sh"
  227. app.Description = `Easy file sharing from the command line`
  228. app.Version = Version
  229. app.Flags = globalFlags
  230. app.CustomAppHelpTemplate = helpTemplate
  231. app.Commands = []cli.Command{
  232. {
  233. Name: "version",
  234. Action: VersionAction,
  235. },
  236. }
  237. app.Before = func(c *cli.Context) error {
  238. return nil
  239. }
  240. app.Action = func(c *cli.Context) {
  241. var options []server.OptionFn
  242. if v := c.String("listener"); v != "" {
  243. options = append(options, server.Listener(v))
  244. }
  245. if v := c.String("tls-listener"); v == "" {
  246. } else if c.Bool("tls-listener-only") {
  247. options = append(options, server.TLSListener(v, true))
  248. } else {
  249. options = append(options, server.TLSListener(v, false))
  250. }
  251. if v := c.String("profile-listener"); v != "" {
  252. options = append(options, server.ProfileListener(v))
  253. }
  254. if v := c.String("web-path"); v != "" {
  255. options = append(options, server.WebPath(v))
  256. }
  257. if v := c.String("proxy-path"); v != "" {
  258. options = append(options, server.ProxyPath(v))
  259. }
  260. if v := c.String("ga-key"); v != "" {
  261. options = append(options, server.GoogleAnalytics(v))
  262. }
  263. if v := c.String("uservoice-key"); v != "" {
  264. options = append(options, server.UserVoice(v))
  265. }
  266. if v := c.String("temp-path"); v != "" {
  267. options = append(options, server.TempPath(v))
  268. }
  269. if v := c.String("log"); v != "" {
  270. options = append(options, server.LogFile(logger, v))
  271. } else {
  272. options = append(options, server.Logger(logger))
  273. }
  274. if v := c.String("lets-encrypt-hosts"); v != "" {
  275. options = append(options, server.UseLetsEncrypt(strings.Split(v, ",")))
  276. }
  277. if v := c.String("virustotal-key"); v != "" {
  278. options = append(options, server.VirustotalKey(v))
  279. }
  280. if v := c.String("clamav-host"); v != "" {
  281. options = append(options, server.ClamavHost(v))
  282. }
  283. if v := c.Int("rate-limit"); v > 0 {
  284. options = append(options, server.RateLimit(v))
  285. }
  286. if cert := c.String("tls-cert-file"); cert == "" {
  287. } else if pk := c.String("tls-private-key"); pk == "" {
  288. } else {
  289. options = append(options, server.TLSConfig(cert, pk))
  290. }
  291. if c.Bool("profiler") {
  292. options = append(options, server.EnableProfiler())
  293. }
  294. if c.Bool("force-https") {
  295. options = append(options, server.ForceHTTPs())
  296. }
  297. if httpAuthUser := c.String("http-auth-user"); httpAuthUser == "" {
  298. } else if httpAuthPass := c.String("http-auth-pass"); httpAuthPass == "" {
  299. } else {
  300. options = append(options, server.HttpAuthCredentials(httpAuthUser, httpAuthPass))
  301. }
  302. applyIPFilter := false
  303. ipFilterOptions := server.IPFilterOptions{}
  304. if ipWhitelist := c.String("ip-whitelist"); ipWhitelist != "" {
  305. applyIPFilter = true
  306. ipFilterOptions.AllowedIPs = strings.Split(ipWhitelist, ",")
  307. ipFilterOptions.BlockByDefault = true
  308. }
  309. if ipBlacklist := c.String("ip-blacklist"); ipBlacklist != "" {
  310. applyIPFilter = true
  311. ipFilterOptions.BlockedIPs = strings.Split(ipBlacklist, ",")
  312. }
  313. if applyIPFilter {
  314. options = append(options, server.FilterOptions(ipFilterOptions))
  315. }
  316. if lifetime := c.Int("lifetime"); lifetime > 0 {
  317. server.LifeTime(lifetime)
  318. } else {
  319. panic("lifetime not greater than 0")
  320. }
  321. switch provider := c.String("provider"); provider {
  322. case "s3":
  323. if accessKey := c.String("aws-access-key"); accessKey == "" {
  324. panic("access-key not set.")
  325. } else if secretKey := c.String("aws-secret-key"); secretKey == "" {
  326. panic("secret-key not set.")
  327. } else if bucket := c.String("bucket"); bucket == "" {
  328. panic("bucket not set.")
  329. } else if awsStorage, err := storage.NewS3Storage(accessKey, secretKey, bucket, c.String("s3-region"),
  330. c.String("s3-endpoint"), logger, c.Bool("s3-no-multipart"), c.Bool("s3-path-style")); err != nil {
  331. panic(err)
  332. } else {
  333. options = append(options, server.UseStorage(awsStorage))
  334. }
  335. case "gdrive":
  336. chunkSize := c.Int("gdrive-chunk-size")
  337. if clientJsonFilepath := c.String("gdrive-client-json-filepath"); clientJsonFilepath == "" {
  338. panic("client-json-filepath not set.")
  339. } else if localConfigPath := c.String("gdrive-local-config-path"); localConfigPath == "" {
  340. panic("local-config-path not set.")
  341. } else if basedir := c.String("basedir"); basedir == "" {
  342. panic("basedir not set.")
  343. } else if gStorage, err := storage.NewGDriveStorage(clientJsonFilepath, localConfigPath, basedir, chunkSize, logger); err != nil {
  344. panic(err)
  345. } else {
  346. options = append(options, server.UseStorage(gStorage))
  347. }
  348. case "local":
  349. if v := c.String("basedir"); v == "" {
  350. panic("basedir not set.")
  351. } else if cleanUp := c.Int("cleanup-interval"); cleanUp <= 0 {
  352. panic("cleanup-interval invalid.")
  353. } else if localStorage, err := storage.NewLocalStorage(v, cleanUp, logger); err != nil {
  354. panic(err)
  355. } else {
  356. options = append(options, server.UseStorage(localStorage))
  357. }
  358. default:
  359. panic("Provider not set or invalid.")
  360. }
  361. srvr, err := server.New(
  362. options...,
  363. )
  364. if err != nil {
  365. logger.Println(color.RedString("Error starting server: %s", err.Error()))
  366. return
  367. }
  368. srvr.Run()
  369. }
  370. return &Cmd{
  371. App: app,
  372. }
  373. }