Пример #1
0
func nutsHandler(c appengine.Context, w http.ResponseWriter, r *http.Request) {
	d := make(ContentData)
	apiCall := r.Header.Get("Accept") == "application/json"

	// TODO: no need to load all, then render all - replace with chunking
	var nuts []gonuts.Nut
	var err error
	var title string
	vendor := r.URL.Query().Get(":vendor")
	q := r.URL.Query().Get("q")
	if vendor != "" {
		title = fmt.Sprintf("%s's Nuts", vendor)
		_, err = datastore.NewQuery("Nut").Filter("Vendor=", vendor).Order("Name").GetAll(c, &nuts)
	} else if q == "" {
		title = "All Nuts"
		_, err = datastore.NewQuery("Nut").Order("Vendor").Order("Name").GetAll(c, &nuts)
	} else {
		title = fmt.Sprintf("Search %q", q)
		res, err := gonuts.SearchIndex(c, q)
		gonuts.LogError(c, err)
		keys := make([]*datastore.Key, len(res))
		for i, pair := range res {
			keys[i] = gonuts.NutKey(c, pair[0], pair[1])
		}
		nuts = make([]gonuts.Nut, len(keys))
		err = datastore.GetMulti(c, keys, nuts)
	}
	gonuts.LogError(c, err)
	d["Nuts"] = nuts
	status := http.StatusOK
	if len(nuts) == 0 {
		status = http.StatusNotFound
	}

	if apiCall {
		d["Message"] = title
		ServeJSON(w, status, d)
		return
	}

	var content bytes.Buffer
	gonuts.PanicIfErr(Base.ExecuteTemplate(&content, "nuts.html", d))

	bd := BaseData{
		Tabtitle: title,
		Title:    title,
		Content:  template.HTML(content.String()),
	}

	w.WriteHeader(status)
	gonuts.PanicIfErr(Base.Execute(w, &bd))
}
Пример #2
0
func ahCronSearchHandler(c appengine.Context, w http.ResponseWriter, r *http.Request) {
	start := time.Now()
	d := make(ContentData)

	var nut gonuts.Nut
	for i := datastore.NewQuery("Nut").Run(c); ; {
		_, err := i.Next(&nut)
		if err == datastore.Done {
			break
		}
		if err == nil {
			err = gonuts.AddToSearchIndex(c, &nut)
		}
		if err != nil {
			gonuts.LogError(c, err)
			ServeJSONError(w, http.StatusInternalServerError, err, d)
			return
		}
	}

	m := fmt.Sprintf("Search index updated in %d seconds.", time.Since(start)/time.Second)
	c.Infof("%s", m)
	d["Message"] = m
	ServeJSON(w, http.StatusOK, d)
	return
}
Пример #3
0
func registerHandler(c appengine.Context, w http.ResponseWriter, r *http.Request) {
	u := gaeuser.Current(c)
	if u != nil && u.ID != "" {
		var err error
		vendor := r.FormValue("vendor")
		if !nutp.VendorRegexp.MatchString(vendor) {
			err = fmt.Errorf("Vendor name should match %s.", nutp.VendorRegexp.String())
			WriteError(w, http.StatusBadRequest, err)
			return
		}

		v := &gonuts.Vendor{Vendor: vendor}
		user := &gonuts.User{Id: u.ID, Email: u.Email, FederatedIdentity: u.FederatedIdentity}
		gonuts.PanicIfErr(user.GenerateToken())

		err = datastore.Get(c, gonuts.VendorKey(c, vendor), v)
		if err != datastore.ErrNoSuchEntity {
			if err == nil {
				err = fmt.Errorf("Vendor name %q is already registered.", vendor)
				WriteError(w, http.StatusForbidden, err)
				return
			}

			gonuts.LogError(c, err)
			WriteError(w, http.StatusInternalServerError, err)
			return
		}

		err = datastore.Get(c, gonuts.UserKey(c, u), user)
		if err != datastore.ErrNoSuchEntity {
			gonuts.LogError(c, err)
			WriteError(w, http.StatusInternalServerError, err)
			return
		}

		c.Infof("Adding user %s (%s) to vendor %s.", user.Id, user.Identifier(), v.Vendor)
		user.AddVendor(v)
		_, err = datastore.Put(c, gonuts.VendorKey(c, vendor), v)
		gonuts.PanicIfErr(err)
		_, err = datastore.Put(c, gonuts.UserKey(c, u), user)
		gonuts.PanicIfErr(err)
	}

	http.Redirect(w, r, "/-/me", http.StatusSeeOther)
}
Пример #4
0
func myHandler(c appengine.Context, w http.ResponseWriter, r *http.Request) {
	d := make(ContentData)
	u := gaeuser.Current(c)

	if u == nil || u.ID == "" {
		url, err := gaeuser.LoginURL(c, "/-/me")
		gonuts.LogError(c, err)
		d["LoginURL"] = url
		d["OpenIDURL"] = "/-/me/openid"
	} else {
		user := new(gonuts.User)
		err := datastore.Get(c, gonuts.UserKey(c, u), user)
		if err == nil {
			url, err := gaeuser.LogoutURL(c, "/")
			gonuts.LogError(c, err)
			d["LogoutURL"] = url
			d["Identifier"] = user.Identifier()
			d["Token"] = user.Token
			d["GenerateURL"] = "/-/me/generate"
			d["Vendors"] = user.Vendors
		} else if err == datastore.ErrNoSuchEntity {
			user = &gonuts.User{Id: u.ID, Email: u.Email, FederatedIdentity: u.FederatedIdentity}
			url, err := gaeuser.LogoutURL(c, "/-/me")
			gonuts.LogError(c, err)
			d["LogoutURL"] = url
			d["Identifier"] = user.Identifier()
			d["RegisterURL"] = "/-/me/register"
		} else {
			panic(err)
		}
	}

	var content bytes.Buffer
	gonuts.PanicIfErr(Base.ExecuteTemplate(&content, "me.html", d))

	bd := BaseData{
		Tabtitle: "Me",
		Title:    "Me",
		Content:  template.HTML(content.String()),
	}
	gonuts.PanicIfErr(Base.Execute(w, &bd))
}
Пример #5
0
func welcomeHandler(c appengine.Context, w http.ResponseWriter, r *http.Request) {
	d := make(ContentData)

	nuts, err := datastore.NewQuery("Version").Count(c)
	gonuts.LogError(c, err)
	d["VersionCount"] = nuts

	nuts, err = datastore.NewQuery("Nut").Count(c)
	gonuts.LogError(c, err)
	d["NutsCount"] = nuts

	users, err := datastore.NewQuery("User").Count(c)
	gonuts.LogError(c, err)
	d["UsersCount"] = users

	var content bytes.Buffer
	gonuts.PanicIfErr(Base.ExecuteTemplate(&content, "welcome.html", d))

	bd := BaseData{
		Content: template.HTML(content.String()),
	}
	gonuts.PanicIfErr(Base.Execute(w, &bd))
}
Пример #6
0
func generateHandler(c appengine.Context, w http.ResponseWriter, r *http.Request) {
	u := gaeuser.Current(c)
	if u != nil && u.ID != "" {
		key := gonuts.UserKey(c, u)
		user := gonuts.User{}
		err := datastore.Get(c, key, &user)
		if err == nil {
			gonuts.PanicIfErr(user.GenerateToken())
			_, err = datastore.Put(c, key, &user)
		}
		gonuts.LogError(c, err)
	}
	http.Redirect(w, r, "/-/me", http.StatusSeeOther)
}
Пример #7
0
func openIdHandler(c appengine.Context, w http.ResponseWriter, r *http.Request) {
	u := gaeuser.Current(c)
	if u != nil && u.ID != "" {
		err := fmt.Errorf("This page is not supposed to be accessible by logged-in users")
		WriteError(w, http.StatusForbidden, err)
		return
	}

	provider := r.FormValue("provider")
	if _, err := url.Parse(provider); err != nil {
		err := fmt.Errorf("OpenID provider name should be a valid url")
		WriteError(w, http.StatusBadRequest, err)
		return
	}

	url, err := gaeuser.LoginURLFederated(c, "/-/me", provider)
	if err != nil {
		gonuts.LogError(c, err)
		return
	}

	http.Redirect(w, r, url, http.StatusSeeOther)
}
Пример #8
0
func nutCreateHandler(c appengine.Context, w http.ResponseWriter, r *http.Request) {
	d := make(ContentData)
	ct := r.Header.Get("Content-Type")
	putNut := ct == "application/zip"

	if !putNut {
		err := fmt.Errorf(`Unexpected Content-Type %q, should be "application/zip".`, ct)
		ServeJSONError(w, http.StatusNotAcceptable, err, d)
		return
	}

	vendor := r.URL.Query().Get(":vendor")
	name := r.URL.Query().Get(":name")
	ver := r.URL.Query().Get(":version")

	if vendor == "" || !nutp.VendorRegexp.MatchString(vendor) || name == "" || (ver != "" && !nutp.VersionRegexp.MatchString(ver)) {
		err := fmt.Errorf("Invalid vendor %q, name %q or version %q.", vendor, name, ver)
		ServeJSONError(w, http.StatusBadRequest, err, d)
		return
	}

	// extract token from request
	token := r.URL.Query().Get("token")
	if token == "" {
		ServeJSONError(w, http.StatusForbidden, fmt.Errorf("Can't find 'token' in get parameters."), d)
		return
	}

	// find user by token
	q := datastore.NewQuery("User").KeysOnly().Filter("Token=", token)
	userKeys, err := q.Limit(2).GetAll(c, nil)
	if err != nil || len(userKeys) != 1 {
		if err == nil || err == datastore.ErrNoSuchEntity {
			err = fmt.Errorf("Can't find user with token %q.", token)
		}
		ServeJSONError(w, http.StatusForbidden, err, d)
		return
	}
	userID := userKeys[0].StringID()

	// user should belong to vendor
	v := gonuts.Vendor{}
	err = datastore.Get(c, gonuts.VendorKey(c, vendor), &v)
	if err == datastore.ErrNoSuchEntity {
		err = fmt.Errorf("Can't find vendor %q.", vendor)
		ServeJSONError(w, http.StatusNotFound, err, d)
		return
	}
	if err != nil {
		ServeJSONError(w, http.StatusInternalServerError, err, d)
		return
	}
	found := false
	for _, id := range v.UserStringID {
		if id == userID {
			found = true
			break
		}
	}
	if !found {
		err = fmt.Errorf("You don't have publish access to vendor %q.", vendor)
		ServeJSONError(w, http.StatusForbidden, err, d)
		return
	}

	// nut version should not exist
	nutKey := gonuts.NutKey(c, vendor, name)
	nut := gonuts.Nut{Vendor: vendor, Name: name}
	versionKey := gonuts.VersionKey(c, vendor, name, ver)
	version := gonuts.Version{Vendor: vendor, Name: name, Version: ver, CreatedAt: time.Now()}
	err = datastore.Get(c, versionKey, &version)
	if err != nil && err != datastore.ErrNoSuchEntity {
		ServeJSONError(w, http.StatusInternalServerError, err, d)
		return
	}
	if err == nil {
		ServeJSONError(w, http.StatusConflict, fmt.Errorf("Nut %s/%s version %s already exists.", vendor, name, ver), d)
		return
	}

	// read nut from request body
	nf := new(nutp.NutFile)
	b, err := ioutil.ReadAll(r.Body)
	if err == nil {
		_, err = nf.ReadFrom(bytes.NewReader(b))
	}
	if err != nil {
		ServeJSONError(w, http.StatusBadRequest, err, d)
		return
	}
	nut.Doc = nf.Doc
	version.Doc = nf.Doc
	version.Homepage = nf.Homepage
	version.VersionNum = nf.Version.Major*1000000 + nf.Version.Minor*1000 + nf.Version.Patch // for sorting

	// check vendor, name and version match
	if nf.Vendor != vendor || nf.Name != name || nf.Version.String() != ver {
		err = fmt.Errorf("Nut vendor %q, name %q and version %q from URL don't match found in body: %q %q %q.",
			vendor, name, ver, nf.Vendor, nf.Name, nf.Version.String())
		ServeJSONError(w, http.StatusBadRequest, err, d)
		return
	}

	// check nut
	errors := nf.Check()
	if len(errors) != 0 {
		err = fmt.Errorf("%s", strings.Join(errors, "\n"))
		ServeJSONError(w, http.StatusBadRequest, err, d)
		return
	}

	// store nut blob
	bw, err := blobstore.Create(c, ct)
	if err == nil {
		_, err = bw.Write(b)
		if err == nil {
			err = bw.Close()
		}
	}
	if err != nil {
		ServeJSONError(w, http.StatusInternalServerError, err, d)
		return
	}

	// store nut version
	blobKey, err := bw.Key()
	if err == nil {
		version.BlobKey = blobKey
		_, err = datastore.Put(c, versionKey, &version)
	}
	if err != nil {
		ServeJSONError(w, http.StatusInternalServerError, err, d)
		return
	}

	// store nut with new doc
	_, err = datastore.Put(c, nutKey, &nut)
	if err != nil {
		ServeJSONError(w, http.StatusInternalServerError, err, d)
		return
	}

	// update search index
	err = gonuts.AddToSearchIndex(c, &nut)
	gonuts.LogError(c, err)

	// done!
	d["Message"] = fmt.Sprintf("Nut %s/%s version %s published.", vendor, name, ver)
	ServeJSON(w, http.StatusCreated, d)
	return
}
Пример #9
0
func nutShowHandler(c appengine.Context, w http.ResponseWriter, r *http.Request) {
	d := make(ContentData)
	getNut := r.Header.Get("Accept") == "application/zip"

	vendor := r.URL.Query().Get(":vendor")
	name := r.URL.Query().Get(":name")
	ver := r.URL.Query().Get(":version")

	if vendor == "" || !nutp.VendorRegexp.MatchString(vendor) || name == "" || (ver != "" && !nutp.VersionRegexp.MatchString(ver)) {
		err := fmt.Errorf("Invalid vendor %q, name %q or version %q.", vendor, name, ver)
		ServeJSONError(w, http.StatusBadRequest, err, d)
		return
	}

	current := new(gonuts.Version)
	var err error
	q := datastore.NewQuery("Version").Filter("Vendor=", vendor).Filter("Name=", name)
	if ver == "" {
		_, err = q.Order("-VersionNum").Limit(1).Run(c).Next(current)
	} else {
		key := gonuts.VersionKey(c, vendor, name, ver)
		err = datastore.Get(c, key, current)
	}
	gonuts.LogError(c, err)

	var title string
	if current.BlobKey != "" {
		// send nut file and exit
		if getNut {
			current.Downloads++
			key := gonuts.VersionKey(c, current.Vendor, current.Name, current.Version)
			_, err := datastore.Put(c, key, current)
			gonuts.LogError(c, err)
			blobstore.Send(w, current.BlobKey)
			return
		}

		// find all versions
		var all []gonuts.Version
		_, err = q.Order("-CreatedAt").GetAll(c, &all)
		gonuts.LogError(c, err)

		// prepare data for template
		cm := make(map[string]interface{})
		cm["Vendor"] = current.Vendor
		cm["Name"] = current.Name
		cm["Version"] = current.Version
		cm["Doc"] = current.Doc
		cm["Homepage"] = current.Homepage
		d["Current"] = cm
		d["All"] = all
		title = fmt.Sprintf("%s/%s %s", current.Vendor, current.Name, current.Version)
	} else {
		w.WriteHeader(http.StatusNotFound)
		if getNut {
			return
		}
		title = fmt.Sprintf("Nut %s/%s version %s not found", vendor, name, ver)
	}

	var content bytes.Buffer
	gonuts.PanicIfErr(Base.ExecuteTemplate(&content, "nut.html", d))

	bd := BaseData{
		Tabtitle: title,
		Title:    title,
		Content:  template.HTML(content.String()),
	}

	gonuts.PanicIfErr(Base.Execute(w, &bd))
}
Пример #10
0
func debugPrepareTestHandler(c appengine.Context, w http.ResponseWriter, r *http.Request) {
	d := make(ContentData)

	// extract token from request
	token := r.URL.Query().Get("token")
	if token == "" {
		ServeJSONError(w, http.StatusForbidden, fmt.Errorf("Can't find 'token' in get parameters."), d)
		return
	}

	// find user by token
	q := datastore.NewQuery("User").Filter("Token=", token)
	var users []gonuts.User
	_, err := q.Limit(2).GetAll(c, &users)
	if err != nil || len(users) != 1 {
		if err == nil || err == datastore.ErrNoSuchEntity {
			err = fmt.Errorf("Can't find user with token %q.", token)
		}
		ServeJSONError(w, http.StatusForbidden, err, d)
		return
	}

	// user should be debug
	if !users[0].Debug {
		err = fmt.Errorf("User doesn't have debug access.")
		ServeJSONError(w, http.StatusForbidden, err, d)
		return
	}

	// remove versions
	for i := datastore.NewQuery("Version").Filter("Vendor=", "debug").KeysOnly().Run(c); ; {
		key, err := i.Next(nil)
		if err == datastore.Done {
			break
		}
		if err == nil {
			err = datastore.Delete(c, key)
		}
		if err != nil {
			gonuts.LogError(c, err)
			ServeJSONError(w, http.StatusInternalServerError, err, d)
			return
		}
	}

	// remove nuts
	var nut gonuts.Nut
	for i := datastore.NewQuery("Nut").Filter("Vendor=", "debug").Run(c); ; {
		key, err := i.Next(&nut)
		if err == datastore.Done {
			break
		}
		if err == nil {
			err = datastore.Delete(c, key)
		}
		if err != nil {
			gonuts.LogError(c, err)
			ServeJSONError(w, http.StatusInternalServerError, err, d)
			return
		}
		err = gonuts.RemoveFromSearchIndex(c, &nut)
		gonuts.LogError(c, err)
	}

	d["Message"] = "OK"
	ServeJSON(w, http.StatusOK, d)
	return
}