Contains the Concourse pipeline definition for building a line-server container
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

284 lines
9.0 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. package main
  2. import (
  3. "flag"
  4. "log"
  5. "net"
  6. "net/http"
  7. "net/http/fcgi"
  8. "net/url"
  9. "os"
  10. "regexp"
  11. "strconv"
  12. "strings"
  13. "time"
  14. "github.com/GeertJohan/go.rice"
  15. "github.com/andreimarcu/linx-server/backends"
  16. "github.com/andreimarcu/linx-server/backends/localfs"
  17. "github.com/andreimarcu/linx-server/backends/metajson"
  18. "github.com/flosch/pongo2"
  19. "github.com/vharitonsky/iniflags"
  20. "github.com/zenazn/goji/graceful"
  21. "github.com/zenazn/goji/web"
  22. "github.com/zenazn/goji/web/middleware"
  23. )
  24. type headerList []string
  25. func (h *headerList) String() string {
  26. return strings.Join(*h, ",")
  27. }
  28. func (h *headerList) Set(value string) error {
  29. *h = append(*h, value)
  30. return nil
  31. }
  32. var Config struct {
  33. bind string
  34. filesDir string
  35. metaDir string
  36. siteName string
  37. siteURL string
  38. sitePath string
  39. selifPath string
  40. certFile string
  41. keyFile string
  42. contentSecurityPolicy string
  43. fileContentSecurityPolicy string
  44. referrerPolicy string
  45. fileReferrerPolicy string
  46. xFrameOptions string
  47. maxSize int64
  48. maxExpiry uint64
  49. realIp bool
  50. noLogs bool
  51. allowHotlink bool
  52. fastcgi bool
  53. remoteUploads bool
  54. authFile string
  55. remoteAuthFile string
  56. addHeaders headerList
  57. noDirectAgents bool
  58. }
  59. var Templates = make(map[string]*pongo2.Template)
  60. var TemplateSet *pongo2.TemplateSet
  61. var staticBox *rice.Box
  62. var timeStarted time.Time
  63. var timeStartedStr string
  64. var remoteAuthKeys []string
  65. var metaStorageBackend backends.MetaStorageBackend
  66. var metaBackend backends.MetaBackend
  67. var fileBackend backends.StorageBackend
  68. func setup() *web.Mux {
  69. mux := web.New()
  70. // middleware
  71. mux.Use(middleware.RequestID)
  72. if Config.realIp {
  73. mux.Use(middleware.RealIP)
  74. }
  75. if !Config.noLogs {
  76. mux.Use(middleware.Logger)
  77. }
  78. mux.Use(middleware.Recoverer)
  79. mux.Use(middleware.AutomaticOptions)
  80. mux.Use(ContentSecurityPolicy(CSPOptions{
  81. policy: Config.contentSecurityPolicy,
  82. referrerPolicy: Config.referrerPolicy,
  83. frame: Config.xFrameOptions,
  84. }))
  85. mux.Use(AddHeaders(Config.addHeaders))
  86. if Config.authFile != "" {
  87. mux.Use(UploadAuth(AuthOptions{
  88. AuthFile: Config.authFile,
  89. UnauthMethods: []string{"GET", "HEAD", "OPTIONS", "TRACE"},
  90. }))
  91. }
  92. // make directories if needed
  93. err := os.MkdirAll(Config.filesDir, 0755)
  94. if err != nil {
  95. log.Fatal("Could not create files directory:", err)
  96. }
  97. err = os.MkdirAll(Config.metaDir, 0700)
  98. if err != nil {
  99. log.Fatal("Could not create metadata directory:", err)
  100. }
  101. if Config.siteURL != "" {
  102. // ensure siteURL ends wth '/'
  103. if lastChar := Config.siteURL[len(Config.siteURL)-1:]; lastChar != "/" {
  104. Config.siteURL = Config.siteURL + "/"
  105. }
  106. parsedUrl, err := url.Parse(Config.siteURL)
  107. if err != nil {
  108. log.Fatal("Could not parse siteurl:", err)
  109. }
  110. Config.sitePath = parsedUrl.Path
  111. } else {
  112. Config.sitePath = "/"
  113. }
  114. Config.selifPath = strings.TrimLeft(Config.selifPath, "/")
  115. if lastChar := Config.selifPath[len(Config.selifPath)-1:]; lastChar != "/" {
  116. Config.selifPath = Config.selifPath + "/"
  117. }
  118. metaStorageBackend = localfs.NewLocalfsBackend(Config.metaDir)
  119. metaBackend = metajson.NewMetaJSONBackend(metaStorageBackend)
  120. fileBackend = localfs.NewLocalfsBackend(Config.filesDir)
  121. // Template setup
  122. p2l, err := NewPongo2TemplatesLoader()
  123. if err != nil {
  124. log.Fatal("Error: could not load templates", err)
  125. }
  126. TemplateSet := pongo2.NewSet("templates", p2l)
  127. err = populateTemplatesMap(TemplateSet, Templates)
  128. if err != nil {
  129. log.Fatal("Error: could not load templates", err)
  130. }
  131. staticBox = rice.MustFindBox("static")
  132. timeStarted = time.Now()
  133. timeStartedStr = strconv.FormatInt(timeStarted.Unix(), 10)
  134. // Routing setup
  135. nameRe := regexp.MustCompile("^" + Config.sitePath + `(?P<name>[a-z0-9-\.]+)$`)
  136. selifRe := regexp.MustCompile("^" + Config.sitePath + Config.selifPath + `(?P<name>[a-z0-9-\.]+)$`)
  137. selifIndexRe := regexp.MustCompile("^" + Config.sitePath + Config.selifPath + `$`)
  138. torrentRe := regexp.MustCompile("^" + Config.sitePath + `(?P<name>[a-z0-9-\.]+)/torrent$`)
  139. if Config.authFile == "" {
  140. mux.Get(Config.sitePath, indexHandler)
  141. mux.Get(Config.sitePath+"paste/", pasteHandler)
  142. } else {
  143. mux.Get(Config.sitePath, http.RedirectHandler(Config.sitePath+"API", 303))
  144. mux.Get(Config.sitePath+"paste/", http.RedirectHandler(Config.sitePath+"API/", 303))
  145. }
  146. mux.Get(Config.sitePath+"paste", http.RedirectHandler(Config.sitePath+"paste/", 301))
  147. mux.Get(Config.sitePath+"API/", apiDocHandler)
  148. mux.Get(Config.sitePath+"API", http.RedirectHandler(Config.sitePath+"API/", 301))
  149. if Config.remoteUploads {
  150. mux.Get(Config.sitePath+"upload", uploadRemote)
  151. mux.Get(Config.sitePath+"upload/", uploadRemote)
  152. if Config.remoteAuthFile != "" {
  153. remoteAuthKeys = readAuthKeys(Config.remoteAuthFile)
  154. }
  155. }
  156. mux.Post(Config.sitePath+"upload", uploadPostHandler)
  157. mux.Post(Config.sitePath+"upload/", uploadPostHandler)
  158. mux.Put(Config.sitePath+"upload", uploadPutHandler)
  159. mux.Put(Config.sitePath+"upload/", uploadPutHandler)
  160. mux.Put(Config.sitePath+"upload/:name", uploadPutHandler)
  161. mux.Delete(Config.sitePath+":name", deleteHandler)
  162. mux.Get(Config.sitePath+"static/*", staticHandler)
  163. mux.Get(Config.sitePath+"favicon.ico", staticHandler)
  164. mux.Get(Config.sitePath+"robots.txt", staticHandler)
  165. mux.Get(nameRe, fileDisplayHandler)
  166. mux.Get(selifRe, fileServeHandler)
  167. mux.Get(selifIndexRe, unauthorizedHandler)
  168. mux.Get(torrentRe, fileTorrentHandler)
  169. mux.NotFound(notFoundHandler)
  170. return mux
  171. }
  172. func main() {
  173. flag.StringVar(&Config.bind, "bind", "127.0.0.1:8080",
  174. "host to bind to (default: 127.0.0.1:8080)")
  175. flag.StringVar(&Config.filesDir, "filespath", "files/",
  176. "path to files directory")
  177. flag.StringVar(&Config.metaDir, "metapath", "meta/",
  178. "path to metadata directory")
  179. flag.BoolVar(&Config.noLogs, "nologs", false,
  180. "remove stdout output for each request")
  181. flag.BoolVar(&Config.allowHotlink, "allowhotlink", false,
  182. "Allow hotlinking of files")
  183. flag.StringVar(&Config.siteName, "sitename", "",
  184. "name of the site")
  185. flag.StringVar(&Config.siteURL, "siteurl", "",
  186. "site base url (including trailing slash)")
  187. flag.StringVar(&Config.selifPath, "selifpath", "selif",
  188. "path relative to site base url where files are accessed directly")
  189. flag.Int64Var(&Config.maxSize, "maxsize", 4*1024*1024*1024,
  190. "maximum upload file size in bytes (default 4GB)")
  191. flag.Uint64Var(&Config.maxExpiry, "maxexpiry", 0,
  192. "maximum expiration time in seconds (default is 0, which is no expiry)")
  193. flag.StringVar(&Config.certFile, "certfile", "",
  194. "path to ssl certificate (for https)")
  195. flag.StringVar(&Config.keyFile, "keyfile", "",
  196. "path to ssl key (for https)")
  197. flag.BoolVar(&Config.realIp, "realip", false,
  198. "use X-Real-IP/X-Forwarded-For headers as original host")
  199. flag.BoolVar(&Config.fastcgi, "fastcgi", false,
  200. "serve through fastcgi")
  201. flag.BoolVar(&Config.remoteUploads, "remoteuploads", false,
  202. "enable remote uploads")
  203. flag.StringVar(&Config.authFile, "authfile", "",
  204. "path to a file containing newline-separated scrypted auth keys")
  205. flag.StringVar(&Config.remoteAuthFile, "remoteauthfile", "",
  206. "path to a file containing newline-separated scrypted auth keys for remote uploads")
  207. flag.StringVar(&Config.contentSecurityPolicy, "contentsecuritypolicy",
  208. "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; frame-ancestors 'self';",
  209. "value of default Content-Security-Policy header")
  210. flag.StringVar(&Config.fileContentSecurityPolicy, "filecontentsecuritypolicy",
  211. "default-src 'none'; img-src 'self'; object-src 'self'; media-src 'self'; style-src 'self' 'unsafe-inline'; frame-ancestors 'self';",
  212. "value of Content-Security-Policy header for file access")
  213. flag.StringVar(&Config.referrerPolicy, "referrerpolicy",
  214. "same-origin",
  215. "value of default Referrer-Policy header")
  216. flag.StringVar(&Config.fileReferrerPolicy, "filereferrerpolicy",
  217. "same-origin",
  218. "value of Referrer-Policy header for file access")
  219. flag.StringVar(&Config.xFrameOptions, "xframeoptions", "SAMEORIGIN",
  220. "value of X-Frame-Options header")
  221. flag.Var(&Config.addHeaders, "addheader",
  222. "Add an arbitrary header to the response. This option can be used multiple times.")
  223. flag.BoolVar(&Config.noDirectAgents, "nodirectagents", false,
  224. "disable serving files directly for wget/curl user agents")
  225. iniflags.Parse()
  226. mux := setup()
  227. if Config.fastcgi {
  228. listener, err := net.Listen("tcp", Config.bind)
  229. if err != nil {
  230. log.Fatal("Could not bind: ", err)
  231. }
  232. log.Printf("Serving over fastcgi, bound on %s", Config.bind)
  233. fcgi.Serve(listener, mux)
  234. } else if Config.certFile != "" {
  235. log.Printf("Serving over https, bound on %s", Config.bind)
  236. err := graceful.ListenAndServeTLS(Config.bind, Config.certFile, Config.keyFile, mux)
  237. if err != nil {
  238. log.Fatal(err)
  239. }
  240. } else {
  241. log.Printf("Serving over http, bound on %s", Config.bind)
  242. err := graceful.ListenAndServe(Config.bind, mux)
  243. if err != nil {
  244. log.Fatal(err)
  245. }
  246. }
  247. }