func init() { recordFormValidator.Field("start_at").Func(util.ValidateDateTimeString) recordFormValidator.Field("end_at").Func(util.ValidateDateTimeString) app.Api.Get("/records/", func(res *wcg.Response, req *wcg.Request) { ctx := gae.NewContext(req) d := NewRecordCacheDriver(app.Key, ctx, req.Logger) if list, err := d.GetRecords(req.Query("force") == "1"); err != nil { app.Api.InternalError(res, req, err) } else { res.WriteJson(list) } }, ) app.Api.Post("/records/", lib.Family.Required( func(res *wcg.Response, req *wcg.Request) { if err := recordFormValidator.Eval(req.HttpRequest().PostForm); err != nil { app.Api.BadRequest(res, req, err) return } ctx := gae.NewContext(req) d := NewRecordCacheDriver(app.Key, ctx, req.Logger) start, _ := util.ParseDateTime(req.Form("start_at")) end, _ := util.ParseDateTime(req.Form("end_at")) rec := tv.NewTvRecord( req.Form("title"), req.Form("category"), start, end, req.Form("cid"), req.Form("sid"), req.User.Id(), ) if err := d.Save(rec); err != nil { app.Api.InternalError(res, req, err) } else { app.Api.Created(res, req, fmt.Sprintf("/records/%s.json", rec.Key())) } }, )) app.Api.Delete("/records/:key.json", lib.Family.Required( func(res *wcg.Response, req *wcg.Request) { d := NewRecordCacheDriver(app.Key, gae.NewContext(req), req.Logger) if err := d.Delete(req.Param("key")); err != nil { app.Api.InternalError(res, req, err) return } else { app.Api.Ok(res, req) } }, )) }
func NewAppContextFromRequest(req *wcg.Request) *AppContext { return &AppContext{ GetCurrentApp(req), gae.NewContext(req), req.Logger, } }
func fetchBlog(res *wcg.Response, req *wcg.Request, includePosts bool) (*models.Blog, *supports.Page, error) { // default query for posts. id := req.Param("blog_id") driver := models.NewBlogDriver(gae.NewContext(req), req.Logger) blog, query, err := driver.PostQuery(id) if err != nil { if err == models.ErrBlogNotFound { res.WriteHeader(404) res.WriteString("Not found") res.End() return nil, nil, err } res.RenderInternalError(err.Error()) return nil, nil, err } if !includePosts { return blog, supports.EmptyPage, nil } query = query.Filter("IsNew =", false).Filter("IsDraft =", false).Order("-PostDate").Order("-UpdatedAt") per_page := wcg.ParseInt(req.Query("num"), AppConfig.DefaultPostsFetched, 0, AppConfig.MaxPostsFetched) current := wcg.ParseInt(req.Query("p"), 0, 0, wcg.ParseIntMax) page, err := supports.NewPage(current, per_page, query) if err != nil { res.RenderInternalError("Post pagination error: %v", err) return nil, nil, err } return blog, page, nil }
func fetchPostForUpdate(req *wcg.Request, res *wcg.Response, blog *models.Blog) (*models.Post, error) { driver := models.NewPostDriver(gae.NewContext(req), req.Logger) id := req.Param("post_id") blogId := req.Param("blog_id") post, err := driver.FindPostById(id, blogId) if err != nil { if err == models.ErrPostNotFound { res.WriteHeader(404) res.WriteString("Not found") res.End() return nil, err } res.RenderInternalError(err.Error()) return nil, err } if blog.Id != post.BlogId { res.WriteHeader(403) res.WriteString("You could not manage that blog.") res.End() } if post.OwnerId != req.User.Id() { res.WriteHeader(403) res.WriteString("You could not manage this post.") res.End() return nil, err } return post, err }
func fetchPost(res *wcg.Response, req *wcg.Request, blog *models.Blog) (*models.Post, error) { driver := models.NewPostDriver(gae.NewContext(req), req.Logger) id := req.Param("post_id") blogId := req.Param("blog_id") post, err := driver.FindPostById(id, blogId) if err != nil { if err == models.ErrPostNotFound { res.WriteHeader(404) res.WriteString("Not found") res.End() return nil, err } res.RenderInternalError(err.Error()) return nil, err } if post.PostState() != models.PostStatePublished { if post.OwnerId != req.User.Id() { res.WriteHeader(404) res.WriteString("Not found") res.End() return nil, err } } return post, err }
func getVersion(res *wcg.Response, req *wcg.Request) { stats, _ := runtime.Stats(gae.NewContext(req)) res.WriteJson(map[string]interface{}{ "version": lib.APP_COMMIT, "timestamp": lib.APP_TIMESTAMP, "stats": stats, }) }
func createPostHandler(res *wcg.Response, req *wcg.Request) { blog, _, err := fetchBlogForUpdate(res, req, false) if err != nil { return } post := models.NewPost(req.User.Id(), blog.Id) err = models.NewPostDriver(gae.NewContext(req), req.Logger).SavePost(post) if err != nil { res.RenderInternalError(err.Error()) return } res.Redirect(fmt.Sprintf("/admin/blogs/%s/%s/", blog.Id, post.Id), http.StatusSeeOther) }
func init() { app.Api.Get("/channels/", func(res *wcg.Response, req *wcg.Request) { if list, err := listTvChannels(res, req); err != nil { app.Api.InternalError(res, req, err) } else { res.WriteJson(list) } }, ) app.Api.Post("/channels/", lib.Admin.Required( func(res *wcg.Response, req *wcg.Request) { ctx := gae.NewContext(req) d := NewTvChannelDriver(app.Key, ctx, req.Logger) mc := memcache.NewDriver(ctx, req.Logger) if err := d.AddChannel(req.Form("cid"), req.Form("sid"), req.Form("name"), req.Form("iepg_station_id")); err != nil { lib.InternalError(res, req, err) } else { mc.Delete(MC_KEY_CHANNELS) id := fmt.Sprintf("%s/%s", req.Form("cid"), req.Form("sid")) app.Api.Created(res, req, id) } }, )) app.Api.Delete("/channels/:cid/:sid.json", lib.Admin.Required( func(res *wcg.Response, req *wcg.Request) { ctx := gae.NewContext(req) d := NewTvChannelDriver(app.Key, ctx, req.Logger) mc := memcache.NewDriver(ctx, req.Logger) if err := d.DelChannel(req.Param("cid"), req.Param("sid")); err != nil { lib.InternalError(res, req, err) } else { mc.Delete(MC_KEY_CHANNELS) app.Api.Ok(res, req) } }, )) }
func deleteBlogHandler(res *wcg.Response, req *wcg.Request) { driver := models.NewBlogDriver(gae.NewContext(req), req.Logger) err := driver.DeleteBlog(req.Param("blog_id"), req.User.Id()) if err != nil { if supports.IsValidationError(err) { res.WriteJsonWithStatus(400, nil, err) } else if err == models.ErrBlogNotOwned { res.WriteJsonWithStatus(403, nil, no_permission) } else if err == models.ErrBlogNotFound { res.WriteJsonWithStatus(404, nil, not_found) } else { res.RenderInternalError(err.Error()) } return } res.WriteJsonWithStatus(200, nil, ok) }
func TopHandler(res *Response, req *Request) { if !IsGuest(req.User) { driver := models.NewBlogDriver(gae.NewContext(req), req.Logger) blogs, err := driver.FindBlogsByOwner(req.User.Id()) if err != nil { res.RenderInternalError(err.Error()) return } if len(blogs) != 0 { res.SetLocal("blogs", blogs) } else { res.SetLocal("no_blogs", true) } } res.SetLocal("js", "/static/js/index.js") res.Templates("index.html", "header.html", "footer.html") }
func deletePostHandler(res *wcg.Response, req *wcg.Request) { blog, _, err := fetchBlogForUpdate(res, req, false) if err != nil { return } post, err := fetchPostForUpdate(req, res, blog) if err != nil { return } err = models.NewPostDriver(gae.NewContext(req), req.Logger).DeletePost(post) if err != nil { if supports.IsValidationError(err) { res.WriteJsonWithStatus(400, nil, err) } else { res.RenderInternalError(err.Error()) } return } res.WriteJsonWithStatus(200, nil, ok) }
func updatePostHandler(res *wcg.Response, req *wcg.Request) { blog, _, err := fetchBlogForUpdate(res, req, false) if err != nil { return } post, err := fetchPostForUpdate(req, res, blog) if err != nil { return } post.Title = req.Form("title") post.Content = req.Form("content") post.IsDraft = req.Form("is_draft") == "true" post.IsNew = false post.PostDate, err = time.Parse(wcg.FormDateFormat, req.Form("post_date")) post.Tags = strings.Split(req.Form("tags"), ",") for i, v := range post.Tags { post.Tags[i] = strings.TrimSpace(v) } if err != nil { res.WriteHeader(400) res.WriteString("Invalid date format.") res.End() } driver := models.NewPostDriver(gae.NewContext(req), req.Logger) if AppConfig.GithubMarkdown { driver.HttpClient = gae.NewHttpClient(req) } err = driver.SavePost(post) if err != nil { if supports.IsValidationError(err) { res.WriteJsonWithStatus(400, nil, err) } else { res.RenderInternalError(err.Error()) } return } req.Logger.Info("A post is created at %s.", post.Id) res.WriteJsonWithStatus(200, nil, ok) }
func createBlogHandler(res *wcg.Response, req *wcg.Request) { driver := models.NewBlogDriver(gae.NewContext(req), req.Logger) blog, err := driver.CreateBlog( req.Form("path"), req.Form("title"), req.Form("description"), req.User.Id(), ) if err != nil { if err == models.ErrBlogAlreadyExists { res.WriteJsonWithStatus(409, nil, already_taken) } else if supports.IsValidationError(err) { res.WriteJsonWithStatus(400, nil, err) } else { res.RenderInternalError(err.Error()) } return } res.WriteJsonWithStatus(201, map[string]string{ "location": wcg.AbsoluteUrl(req, "/"+blog.Id), }, ok) }
func listTvChannels(res *wcg.Response, req *wcg.Request) ([]*tv.TvChannel, error) { var list []*tv.TvChannel app := lib.GetCurrentApp(req) ctx := gae.NewContext(req) d := NewTvChannelDriver(app.Key, ctx, req.Logger) mc := memcache.NewDriver(ctx, req.Logger) err := mc.CachedObject(MC_KEY_CHANNELS, &list, func() (interface{}, error) { return d.AllAsList() }, req.Query("force") == "1") if err != nil { return nil, err } else { if len(list) == 0 { req.Logger.Warn("No channel is defined. Reset the configuraiton.") d.AddChannelList(defaultChannels) mc.Delete(MC_KEY_CHANNELS) mc.Set(MC_KEY_CHANNELS, defaultChannels) res.WriteJson(defaultChannels) return defaultChannels, nil } else { return list, nil } } }
func myblogRedirector(public bool) func(res *wcg.Response, req *wcg.Request) { return func(res *wcg.Response, req *wcg.Request) { if wcg.IsGuest(req.User) { res.Redirect("/", http.StatusSeeOther) } else { driver := models.NewBlogDriver(gae.NewContext(req), req.Logger) blogs, err := driver.FindBlogsByOwner(req.User.Id()) if err != nil { res.RenderInternalError(err.Error()) return } if len(blogs) > 0 { blog := blogs[0] if public { res.Redirect(fmt.Sprintf("/%s/", blog.Id), http.StatusSeeOther) } else { res.Redirect(fmt.Sprintf("/admin/blogs/%s/", blog.Id), http.StatusSeeOther) } } else { res.Redirect("/", http.StatusSeeOther) } } } }
func init() { app.Cron.Get( "Crawl keyword IEPGs", "every 3 hours", "/keywords/crawl/", lib.Admin.Required( func(res *wcg.Response, req *wcg.Request) { var channels []*tv.TvChannel var cfglist []*tv.CrawlerConfig ctx := gae.NewContext(req) d := NewCrawlerConfigDriver(app.Key, ctx, req.Logger) mc := memcache.NewDriver(ctx, req.Logger) err := mc.CachedObject(MC_KEY_CHANNELS, &channels, func() (interface{}, error) { return NewTvChannelDriver(app.Key, ctx, req.Logger).AllAsList() }, false) if err != nil { app.Api.InternalError(res, req, err) return } req.Logger.Info("Retrieving all cralwer configurations...") q := d.NewQuery() q.Order("-CreatedAt") if _, err := q.GetAll(&cfglist); err != nil { app.Api.InternalError(res, req, err) return } else if len(cfglist) == 0 { req.Logger.Info("No cralwer configuration is found.") res.WriteJson(map[string]int{ "updates": 0, }) return } else { req.Logger.Info("Found %d configs, start crawling", len(cfglist)) iepglist := make([]*tv.IEpg, 0) for i := range cfglist { if list, err := getIEpgListFromCrawlerConfig(res, req, cfglist[i], channels); err != nil { req.Logger.Warn("Failed to crawl %v: %v", cfglist[i], err) } else { iepglist = append(iepglist, list...) } } // TODO: Sync bulk update d := NewRecordCacheDriver(app.Key, ctx, req.Logger) if keys, err := d.IEpg.BulkUpdate(iepglist); err != nil { app.Api.InternalError(res, req, err) return } else { req.Logger.Debug("Updated %d iepg entries...", len(keys)) // invalidate cache after 10 seconds time.Sleep(10 * time.Second) d.GetRecords(true) res.WriteJson(map[string]int{ "updates": len(iepglist), }) return } } }, )) }
func init() { app.Api.Get("/keywords/", func(res *wcg.Response, req *wcg.Request) { var list []*tv.CrawlerConfig ctx := gae.NewContext(req) mc := memcache.NewDriver(ctx, req.Logger) err := mc.CachedObject(MC_KEY_KEYWORDS, &list, func() (interface{}, error) { var list []*tv.CrawlerConfig d := NewCrawlerConfigDriver(app.Key, ctx, req.Logger) q := d.NewQuery().Order("-CreatedAt") _, err := q.GetAll(&list) if list == nil { return make([]*tv.CrawlerConfig, 0), nil } else { return list, err } }, req.Query("force") == "1") if err != nil { lib.InternalError(res, req, err) } else { res.WriteJson(list) } }, ) app.Api.Get("/keywords/preview/:keyword.json", lib.Family.Required( func(res *wcg.Response, req *wcg.Request) { var channels []*tv.TvChannel ctx := gae.NewContext(req) mc := memcache.NewDriver(ctx, req.Logger) err := mc.CachedObject(MC_KEY_CHANNELS, &channels, func() (interface{}, error) { return NewTvChannelDriver(app.Key, ctx, req.Logger).AllAsList() }, false) keyword := req.Param("keyword") scope, _ := strconv.Atoi(req.Query("scope")) list, err := getIEpgListFromCrawlerConfig(res, req, &tv.CrawlerConfig{ Keyword: keyword, Scope: scope, Category: "dummy", }, channels) if err != nil { lib.InternalError(res, req, err) } else { res.WriteJson(map[string]interface{}{ "samples": list, "total": len(list), }) } }, )) app.Api.Post("/keywords/", lib.Family.Required( func(res *wcg.Response, req *wcg.Request) { ctx := gae.NewContext(req) mc := memcache.NewDriver(ctx, req.Logger) d := NewCrawlerConfigDriver(app.Key, ctx, req.Logger) scope, _ := strconv.Atoi(req.Form("scope")) cfg := &tv.CrawlerConfig{ Keyword: req.Form("keyword"), Category: req.Form("category"), Scope: scope, } if err := d.Add(cfg); err != nil { app.Api.InternalError(res, req, err) return } else { mc.Delete(MC_KEY_KEYWORDS) app.Api.Created(res, req, req.Form("keyword")) return } }, )) app.Api.Delete("/keywords/:keyword.json", lib.Family.Required( func(res *wcg.Response, req *wcg.Request) { ctx := gae.NewContext(req) d := NewCrawlerConfigDriver(app.Key, ctx, req.Logger) mc := memcache.NewDriver(ctx, req.Logger) keyword := req.Param("keyword") if err := d.Delete(keyword); err != nil { lib.InternalError(res, req, err) return } else { mc.Delete(MC_KEY_KEYWORDS) app.Api.Ok(res, req) return } }, )) }