func (r Registry) Main(ctx *Context, args []string) { opts := optparse.Parser("Usage: bolt <command-1> <command-2> ... [options]\n", "bolt 0.1") opts.String([]string{"--boltfile"}, "", "use the Boltfile at the given path", "PATH") opts.String([]string{"--gen"}, "", "save the generated executable at the given path", "PATH") opts.Bool([]string{"--recompile"}, false, "recompile the generated executable instead of using cached builds") noConsoleLog := opts.BoolConfig("no-console-log", false, "disable logging to the console [false]") completions := append(ctx.completions, r.Keys()...) opts.Completer = optparse.ListCompleter(completions...) opts.Parse(args) if len(args) == 1 { listing := r.Listing() fmt.Print(listing) runtime.Exit(0) } if !*noConsoleLog { log.AddConsoleLogger() } buf := &bytes.Buffer{} yaml.NormaliseID(buf, args[1]) cmd := buf.String() if task, ok := r[cmd]; ok { task.Func.Call([]reflect.Value{reflect.ValueOf(ctx)}) } else { fmt.Printf("Task not found:\n\n\t%s\n\n", args[1]) runtime.Exit(1) } }
func main() { opts := optparse.New("Usage: dynamodb-marshal file1.go [file2.go ...]", "dynamodb-marshal 0.0.1") force := opts.Bool([]string{"-f", "--force"}, "overwrite existing marshal files") os.Args[0] = "dynamodb-marshal" files := opts.Parse(os.Args) if len(files) == 0 { opts.PrintUsage() runtime.Exit(0) } log.AddConsoleLogger() for _, file := range files { path, err := filepath.Abs(file) if err != nil { runtime.StandardError(err) } parseFile(path, *force) } log.Wait() }
func main() { // Define the options for the command line and config file options parser. opts := optparse.New( "Usage: planfile <config.yaml> [options]\n", "planfile 0.0.1") cookieKeyFile := opts.StringConfig("cookie-key-file", "cookie.key", "the file containing the key to sign cookie values [cookie.key]") gaHost := opts.StringConfig("ga-host", "", "the google analytics hostname to use") gaID := opts.StringConfig("ga-id", "", "the google analytics id to use") httpAddr := opts.StringConfig("http-addr", ":8888", "the address to bind the http server [:8888]") oauthID := opts.Required().StringConfig("oauth-id", "", "the oauth client id for github") oauthSecret := opts.Required().StringConfig("oauth-secret", "", "the oauth client secret for github") redirectURL := opts.StringConfig("redirect-url", "/.oauth", "the redirect url for handling oauth [/.oauth]") repository := opts.Required().StringConfig("repository", "", "the username/repository on github") secureMode := opts.BoolConfig("secure-mode", "enable hsts and secure cookies [false]") title := opts.StringConfig("title", "Planfile", "the title for the web app [Planfile]") refreshKey := opts.StringConfig("refresh-key", "", "key for anonymously calling refresh at /.refresh?key=<refresh-key>") refreshOpt := opts.IntConfig("refresh-interval", 1, "the number of through-the-web edits before a full refresh [1]") debug, instanceDirectory, _, logPath, _ = runtime.DefaultOpts("planfile", opts, os.Args, true) service := &oauth.OAuthService{ ClientID: *oauthID, ClientSecret: *oauthSecret, Scope: "public_repo", AuthURL: "https://github.com/login/oauth/authorize", TokenURL: "https://github.com/login/oauth/access_token", RedirectURL: *redirectURL, AcceptHeader: "application/json", } assets := map[string]string{} json.Unmarshal(readFile("assets.json"), &assets) setupPygments() mutex := sync.RWMutex{} repo := &Repo{Path: *repository} err := repo.Load(callGithubAnon) if err != nil { runtime.Exit(1) } repo.Title = *title repo.Updated = time.Now().UTC() repoJSON, err := json.Marshal(repo) if err != nil { runtime.StandardError(err) } refreshCount := 0 refreshInterval := *refreshOpt refreshKeySet := *refreshKey != "" refreshKeyBytes := []byte(*refreshKey) secret := readFile(*cookieKeyFile) newContext := func(w http.ResponseWriter, r *http.Request) *Context { return &Context{ r: r, w: w, secret: secret, secure: *secureMode, } } register := func(path string, handler func(*Context), usegzip ...bool) { gzippable := len(usegzip) > 0 && usegzip[0] http.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { log.Info("serving %s", r.URL) w.Header().Set("Content-Type", "text/html; charset=utf-8") if gzippable && httputil.Parse(r, "Accept-Encoding").Accepts("gzip") { buf := &bytes.Buffer{} enc := gzip.NewWriter(buf) handler(newContext(GzipWriter{enc, w}, r)) enc.Close() w.Header().Set("Content-Encoding", "gzip") w.Header().Set("Content-Length", strconv.Itoa(buf.Len())) buf.WriteTo(w) } else { handler(newContext(w, r)) } }) } anon := []byte(", null, null, '', false") authFalse := []byte("', false") authTrue := []byte("', true") header := []byte(`<!doctype html> <meta charset=utf-8> <title>` + html.EscapeString(*title) + `</title> <link href="//fonts.googleapis.com/css?family=Abel|Coustard:400" rel=stylesheet> <link href=/.static/` + assets["planfile.css"] + ` rel=stylesheet> <body><script>DATA = ['` + *gaHost + `', '` + *gaID + `', `) footer := []byte(`];</script> <script src=/.static/` + assets["planfile.js"] + `></script> <noscript>Sorry, your browser needs <a href=http://enable-javascript.com>JavaScript enabled</a>.</noscript> `) register("/", func(ctx *Context) { mutex.RLock() defer mutex.RUnlock() ctx.Write(header) ctx.Write(repoJSON) avatar := ctx.GetCookie("avatar") user := ctx.GetCookie("user") if avatar != "" && user != "" { ctx.Write([]byte(", '" + user + "', '" + avatar + "', '" + ctx.GetCookie("xsrf"))) if ctx.IsAuthorised(repo) { ctx.Write(authTrue) } else { ctx.Write(authFalse) } } else { ctx.Write(anon) } ctx.Write(footer) }, true) register("/.api", func(ctx *Context) { mutex.RLock() defer mutex.RUnlock() if cb := ctx.FormValue("callback"); cb != "" { ctx.Write([]byte(cb)) ctx.Write([]byte{'('}) ctx.Write(repoJSON) ctx.Write([]byte{')', ';'}) } else { ctx.Write(repoJSON) } }, true) register("/.login", func(ctx *Context) { b := make([]byte, 20) if n, err := rand.Read(b); err != nil || n != 20 { ctx.Error("Couldn't access cryptographic device", err) return } s := hex.EncodeToString(b) ctx.SetCookie("xsrf", s) ctx.Redirect(service.AuthCodeURL(s)) }) register("/.logout", func(ctx *Context) { ctx.ExpireCookie("auth") ctx.ExpireCookie("avatar") ctx.ExpireCookie("token") ctx.ExpireCookie("user") ctx.ExpireCookie("xsrf") ctx.Redirect("/") }) notAuthorised := []byte("ERROR: Not Authorised!") savedHeader := []byte(`<!doctype html> <meta charset=utf-8> <title>` + html.EscapeString(*title) + `</title> <body><script>SAVED="`) savedFooter := []byte(`"</script><script src=/.static/` + assets["planfile.js"] + `></script>`) exportRepo := func(ctx *Context) bool { repo.Updated = time.Now().UTC() repoJSON, err = json.Marshal(repo) if err != nil { ctx.Error("Couldn't encode repo data during refresh", err) return false } return true } refresh := func(ctx *Context) { err := repo.Load(ctx.CreateCallGithub()) if err != nil { log.Error("couldn't rebuild planfile info: %s", err) ctx.Write([]byte("ERROR: " + err.Error())) return } exportRepo(ctx) } saveItem := func(ctx *Context, update bool) { mutex.Lock() defer mutex.Unlock() if !ctx.IsAuthorised(repo) { ctx.Write(notAuthorised) return } if !isEqual([]byte(ctx.FormValue("xsrf")), []byte(ctx.GetCookie("xsrf"))) { ctx.Write(notAuthorised) return } callGithub := ctx.CreateCallGithub() err := repo.UpdateInfo(callGithub) if err != nil { ctx.Error("Couldn't update repo info", err) return } var id, path, message string if update { id = ctx.FormValue("id") path = ctx.FormValue("path") } else { baseID := ctx.FormValue("id") id = baseID count := 0 for repo.Exists(id + ".md") { count += 1 id = fmt.Sprintf("%s%d", baseID, count) } path = id + ".md" } content := strings.Replace(ctx.FormValue("content"), "\r\n", "\n", -1) tags := ctx.FormValue("tags") title := ctx.FormValue("title") redir := "/" if ctx.FormValue("summary") == "yes" { if id != "/" { content = fmt.Sprintf(`--- title: %s --- %s`, title, content) if strings.HasPrefix(id, "summary.") { redir = "/" + id[8:] } else { // Shouldn't ever happen. But just in case... redir = "/" + id } } } else { redir = "/.item." + id content = fmt.Sprintf(`--- id: %s tags: %s title: %s --- %s`, id, tags, title, content) } if title == "" { title = id } if update { message = "update: " + title + "." } else { message = "add: " + title + "." } log.Info("SAVE PATH: %q for %q", path, title) err = repo.Modify(ctx, path, content, message) if err != nil { if update { ctx.Error("<a href='/.refresh'>Try refreshing.</a> Couldn't update item", err) } else { ctx.Error("<a href='/.refresh'>Try refreshing.</a> Couldn't save new item", err) } return } refreshCount++ if refreshCount%refreshInterval == 0 { refresh(ctx) } else { repo.AddPlanfile(path, []byte(content), callGithub) if !exportRepo(ctx) { return } } ctx.Write(savedHeader) ctx.Write([]byte(html.EscapeString(redir))) ctx.Write(savedFooter) } register("/.modify", func(ctx *Context) { saveItem(ctx, true) }) register("/.new", func(ctx *Context) { saveItem(ctx, false) }) register("/.oauth", func(ctx *Context) { s := ctx.FormValue("state") if s == "" { ctx.Redirect("/.login") return } if !isEqual([]byte(s), []byte(ctx.GetCookie("xsrf"))) { ctx.ExpireCookie("xsrf") ctx.Redirect("/.login") return } t := &oauth.Transport{OAuthService: service} tok, err := t.ExchangeAuthorizationCode(ctx.FormValue("code")) if err != nil { ctx.Error("Auth Exchange Error", err) return } jtok, err := json.Marshal(tok) if err != nil { ctx.Error("Couldn't encode token", err) return } ctx.SetCookie("token", hex.EncodeToString(jtok)) ctx.token = tok user := &User{} err = ctx.Call("/user", user, nil, false) if err != nil { ctx.Error("Couldn't load user info", err) return } ctx.SetCookie("avatar", user.AvatarURL) ctx.SetCookie("user", user.Login) ctx.Redirect("/") }) register("/.preview", func(ctx *Context) { rendered, err := renderMarkdown([]byte(ctx.FormValue("content"))) if err != nil { ctx.Error("Couldn't render Markdown", err) return } ctx.Write(rendered) }, true) register("/.refresh", func(ctx *Context) { if !ctx.IsAuthorised(repo) { if !(refreshKeySet && isEqual(refreshKeyBytes, []byte(ctx.FormValue("key")))) { ctx.Write(notAuthorised) return } } mutex.Lock() defer mutex.Unlock() refresh(ctx) ctx.Redirect("/") }) mimetypes := map[string]string{ "css": "text/css", "gif": "image/gif", "ico": "image/x-icon", "jpeg": "image/jpeg", "jpg": "image/jpeg", "js": "text/javascript", "png": "image/png", "swf": "application/x-shockwave-flash", "txt": "text/plain", } registerStatic := func(filepath, urlpath string) { _, ext := rsplit(filepath, ".") ctype, ok := mimetypes[ext] if !ok { ctype = "application/octet-stream" } if debug { register(urlpath, func(ctx *Context) { ctx.SetHeader("Content-Type", ctype) ctx.Write(readFile(filepath)) }, strings.HasPrefix(ctype, "text/")) } else { content := readFile(filepath) register(urlpath, func(ctx *Context) { ctx.SetHeader("Cache-Control", "public, max-age=86400") ctx.SetHeader("Content-Type", ctype) ctx.Write(content) }, strings.HasPrefix(ctype, "text/")) } } for _, path := range assets { registerStatic(filepath.Join(instanceDirectory, "static", path), "/.static/"+path) } wwwPath := filepath.Join(instanceDirectory, "www") if files, err := ioutil.ReadDir(wwwPath); err == nil { for _, file := range files { if !file.IsDir() { registerStatic(filepath.Join(wwwPath, file.Name()), "/"+file.Name()) } } } log.Info("Listening on %s", *httpAddr) server := &http.Server{ Addr: *httpAddr, ReadTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second, } err = server.ListenAndServe() if err != nil { runtime.Error("couldn't bind to tcp socket: %s", err) } }