コード例 #1
0
ファイル: feedreaderd.go プロジェクト: husio/apps
func main() {
	go func() {
		sigc := make(chan os.Signal)
		signal.Notify(sigc, syscall.SIGUSR1, syscall.SIGHUP)
		defer signal.Stop(sigc)

		log.Debug("updating feeds", "trigger", "init")
		feedreader.Update()

		for {
			select {
			case now := <-time.After(30 * time.Minute):
				log.Debug("updating feeds", "trigger", "clock", "time", now.String())
			case sig := <-sigc:
				log.Debug("updating feeds", "trigger", "signal", "signal", sig.String())
			}
			feedreader.Update()
		}
	}()

	rt := web.NewRouter(web.Routes{
		{"GET", `/`, feedreader.HandleListEntries},
		{"GET", `/sources`, feedreader.HandleListSources},
		{web.AnyMethod, `.*`, feedreader.Handle404},
	})
	if err := http.ListenAndServe(":8000", rt); err != nil {
		log.Error("HTTP server failed", "error", err.Error())
	}
}
コード例 #2
0
ファイル: handlers.go プロジェクト: husio/apps
func imageMeta(r io.ReadSeeker) (*Image, error) {
	conf, err := jpeg.DecodeConfig(r)
	if err != nil {
		return nil, fmt.Errorf("cannot decode JPEG: %s", err)
	}

	// compute image hash from image content
	oid := sha256.New()
	if _, err := io.Copy(oid, r); err != nil {
		return nil, fmt.Errorf("cannot compute SHA: %s", err)
	}
	img := Image{
		ImageID: encode(oid),
		Width:   conf.Width,
		Height:  conf.Height,
	}

	if _, err := r.Seek(0, os.SEEK_SET); err != nil {
		return nil, fmt.Errorf("cannot seek: %s", err)
	}
	if meta, err := exif.Decode(r); err != nil {
		log.Error("cannot extract EXIF metadata", "error", err.Error())
	} else {
		if orientation, err := meta.Get(exif.Orientation); err != nil {
			log.Debug("cannot extract image orientation",
				"decoder", "EXIF",
				"error", err.Error())
		} else {
			if o, err := orientation.Int(0); err != nil {
				log.Debug("cannot format orientation",
					"decoder", "EXIF",
					"error", err.Error())
			} else {
				img.Orientation = o
			}
		}
		if dt, err := meta.Get(exif.DateTimeOriginal); err != nil {
			log.Debug("cannot extract image datetime original",
				"decoder", "EXIF",
				"error", err.Error())
		} else {
			if raw, err := dt.StringVal(); err != nil {
				log.Debug("cannot format datetime original",
					"decoder", "EXIF",
					"error", err.Error())
			} else {
				img.Created, err = time.Parse("2006:01:02 15:04:05", raw)
				if err != nil {
					log.Debug("cannot parse datetime original",
						"decoder", "EXIF",
						"value", raw,
						"error", err.Error())
				}
			}
		}
	}

	return &img, nil
}
コード例 #3
0
ファイル: note.go プロジェクト: husio/apps
func handleGetNote(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	var note Note
	key := datastore.NewKey(ctx, "Note", web.Args(ctx).ByIndex(0), 0, nil)
	if err := datastore.Get(ctx, key, &note); err != nil {
		log.Debug("cannot get note",
			"noteId", web.Args(ctx).ByIndex(0),
			"error", err.Error())
		// XXX - what about not found?
		web.StdJSONResp(w, http.StatusInternalServerError)
		return
	}
	web.JSONResp(w, note, http.StatusOK)
}
コード例 #4
0
ファイル: main.go プロジェクト: husio/apps
func keyManager() (*keys.KeyManager, func()) {
	var m keys.KeyManager
	t := time.NewTicker(24 * time.Hour)

	go func() {
		for range t.C {
			if id, err := m.GenerateKey(24 * 7 * time.Hour); err != nil {
				log.Error("cannot generate key", "error", err.Error())
			} else {
				log.Debug("new key generated", "id", id)
			}
		}
	}()

	return &m, t.Stop
}
コード例 #5
0
ファイル: bb.go プロジェクト: husio/apps
func main() {
	conf := struct {
		HTTP     string
		Postgres string
	}{
		HTTP:     "localhost:8000",
		Postgres: "host=localhost port=5432 user=postgres dbname=bb sslmode=disable",
	}
	envconf.Must(envconf.LoadEnv(&conf))

	ctx := context.Background()

	ctx = auth.WithOAuth(ctx, map[string]*oauth2.Config{
		"google": &oauth2.Config{
			ClientID:     "352914691292-2h70272sb408r3vibe4jm4egote804ka.apps.googleusercontent.com",
			ClientSecret: "L_bgOHLCgNYL-3KG8a5u99mF",
			RedirectURL:  "http://bb.example.com:8000/login/success",
			Scopes: []string{
				"https://www.googleapis.com/auth/userinfo.profile",
				"https://www.googleapis.com/auth/userinfo.email",
			},
			Endpoint: oauth2google.Endpoint,
		},
	})

	ctx = cache.WithLocalCache(ctx, 1000)

	db, err := sql.Open("postgres", conf.Postgres)
	if err != nil {
		log.Fatal("cannot open database", "error", err.Error())
	}
	defer db.Close()
	ctx = pg.WithDB(ctx, db)
	go func() {
		if err := db.Ping(); err != nil {
			log.Error("cannot ping database", "error", err.Error())
		}
	}()

	app := bb.NewApp(ctx)
	log.Debug("running HTTP server", "address", conf.HTTP)
	if err := http.ListenAndServe(conf.HTTP, app); err != nil {
		log.Error("HTTP server error", "error", err.Error())
	}
}
コード例 #6
0
ファイル: gallery.go プロジェクト: husio/apps
func main() {
	ctx := context.Background()

	db, err := sql.Open("sqlite3", "gallery.sqlite3")
	if err != nil {
		log.Fatal("cannot open database", "error", err.Error())
	}
	if err := db.Ping(); err != nil {
		log.Fatal("cannot ping database", "error", err.Error())
	}
	ctx = sq.WithDB(ctx, db)

	ctx = gallery.WithFileStore(ctx, "/tmp/gallery")

	app := gallery.NewApplication(ctx)

	log.Debug("running HTTP server", "address", ":8000")
	if err := http.ListenAndServe(":8000", app); err != nil {
		log.Fatal("HTTP server error", "error", err.Error())
	}
}
コード例 #7
0
ファイル: note.go プロジェクト: husio/apps
func handleAddNote(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	var input struct {
		Content string    `json:"content"`
		Created time.Time `json:"created"`
	}

	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		web.JSONErr(w, err.Error(), http.StatusBadRequest)
		return
	}

	var errs []string
	if input.Content == "" {
		errs = append(errs, `"content" is required`)
	}
	if len(errs) != 0 {
		web.JSONErrs(w, errs, http.StatusBadRequest)
		return
	}

	if input.Created.IsZero() {
		input.Created = time.Now()
	}

	n := Note{
		NoteID:  generateId(),
		Content: input.Content,
		Created: input.Created,
	}

	key := datastore.NewKey(ctx, "Note", n.NoteID, 0, nil)
	_, err := datastore.Put(ctx, key, &n)
	if err != nil {
		log.Debug("cannot put note", "error", err.Error())
		web.StdJSONResp(w, http.StatusInternalServerError)
		return
	}

	web.JSONResp(w, &n, http.StatusCreated)
}
コード例 #8
0
ファイル: scrap.go プロジェクト: husio/apps
func crawl(rp *redis.Pool, urlStr string, stopw map[string]struct{}) []string {
	log.Debug("crawling started", "url", urlStr)
	defer log.Debug("crawling done", "url", urlStr)

	resp, err := httpcli.Get(urlStr)
	if err != nil {
		log.Error("cannot GET",
			"url", urlStr,
			"error", err.Error())
		return nil
	}
	defer resp.Body.Close()

	if ct := resp.Header.Get("Content-Type"); !isHtml(ct) {
		log.Debug("non HTML resource",
			"url", urlStr,
			"contentType", ct)
		return nil
	}

	body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 100000))
	if err != nil {
		log.Error("cannot read body",
			"url", urlStr,
			"error", err.Error())
		return nil
	}
	text := htmlToText(bytes.NewReader(body))
	key := fmt.Sprintf("article:%x", sha1.Sum(text))

	rc := rp.Get()
	defer rc.Close()

	if exists, err := redis.Bool(rc.Do("EXISTS", key)); err != nil {
		log.Error("cannot query database",
			"url", urlStr,
			"error", err.Error())
		return nil
	} else if exists {
		log.Debug("article already stored",
			"url", urlStr,
			"key", key)
		return nil
	}

	func() {
		// all redis update is done in single batch
		if err := rc.Send("MULTI"); err != nil {
			log.Error("cannot start MULTI", "error", err.Error())
			return
		}

		err = rc.Send("HMSET", key,
			"url", urlStr,
			"title", pageTitle(body),
			"crated", time.Now().Unix())
		if err != nil {
			log.Error("cannot write article data",
				"key", key,
				"url", urlStr,
				"error", err.Error())
			return
		}

		for w, n := range words(bytes.NewReader(text), stopw) {
			if len(w) < 3 {
				continue
			}
			if err := rc.Send("ZADD", "word:"+w, n, key); err != nil {
				log.Error("cannot write word count",
					"key", key,
					"url", urlStr,
					"word", w,
					"error", err.Error())
				return
			}
		}
		if _, err := rc.Do("EXEC"); err != nil {
			log.Error("cannot flush redis command",
				"key", key,
				"url", urlStr,
				"error", err.Error())
			return
		}
	}()

	return pageUrls(body)
}
コード例 #9
0
ファイル: handlers.go プロジェクト: husio/apps
func handleUploadImage(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	if err := r.ParseMultipartForm(10 * megabyte); err != nil {
		web.JSONResp(w, err.Error(), http.StatusBadRequest)
		return
	}

	var header *multipart.FileHeader
	for _, headers := range r.MultipartForm.File {
		for _, h := range headers {
			log.Debug("uploading file", "name", h.Filename)
			if header != nil {
				web.JSONErr(w, "cannot upload more than one time at once", http.StatusBadRequest)
				return
			}
			header = h
		}
	}
	if header == nil {
		web.JSONErr(w, "image file missing", http.StatusBadRequest)
		return
	}
	if !strings.HasSuffix(strings.ToLower(header.Filename), ".jpg") {
		// XXX this is not the best validation
		web.JSONErr(w, "only JPEG format is allowed", http.StatusBadRequest)
		return
	}

	fd, err := header.Open()
	if err != nil {
		log.Error("cannot open uploaded file",
			"name", header.Filename,
			"error", err.Error())
		web.StdJSONResp(w, http.StatusInternalServerError)
		return
	}
	defer fd.Close()

	image, err := imageMeta(fd)
	if err != nil {
		log.Error("cannot extract image metadata", "error", err.Error())
		web.StdJSONResp(w, http.StatusInternalServerError)
		return
	}

	// store image in database
	db := sq.DB(ctx)
	image, err = CreateImage(db, *image)
	switch err {
	case nil:
		// all good
	case sq.ErrConflict:
		// image already exists, nothing more to do here
		web.JSONResp(w, image, http.StatusOK)
		return
	default:
		log.Error("cannot create object", "error", err.Error())
		web.StdJSONResp(w, http.StatusInternalServerError)
		return
	}

	if _, err := fd.Seek(0, os.SEEK_SET); err != nil {
		log.Error("cannot seek image", "error", err.Error())
		web.StdJSONResp(w, http.StatusInternalServerError)
		return
	}

	fs := FileStore(ctx)
	if err := fs.Put(image, fd); err != nil {
		log.Error("cannot store image", "error", err.Error())
		web.StdJSONResp(w, http.StatusInternalServerError)
		return
	}
	log.Debug("image file created", "id", image.ImageID)

	web.JSONResp(w, image, http.StatusCreated)
}
コード例 #10
0
ファイル: handlers.go プロジェクト: husio/apps
func handleServeImage(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	img, err := ImageByID(sq.DB(ctx), web.Args(ctx).ByIndex(0))
	switch err {
	case nil:
		// all good
	case sq.ErrNotFound:
		web.StdJSONResp(w, http.StatusNotFound)
		return
	default:
		log.Error("cannot get object",
			"object", web.Args(ctx).ByIndex(0),
			"error", err.Error())
		web.StdJSONResp(w, http.StatusInternalServerError)
		return
	}

	if web.CheckLastModified(w, r, img.Created) {
		return
	}

	fs := FileStore(ctx)
	fd, err := fs.Read(img.Created.Year(), img.ImageID)
	if err != nil {
		log.Error("cannot read image file",
			"image", img.ImageID,
			"error", err.Error())
		web.StdJSONResp(w, http.StatusInternalServerError)
		return
	}
	defer fd.Close()

	w.Header().Set("X-Image-ID", img.ImageID)
	w.Header().Set("X-Image-Width", fmt.Sprint(img.Width))
	w.Header().Set("X-Image-Height", fmt.Sprint(img.Height))
	w.Header().Set("X-Image-Created", img.Created.Format(time.RFC3339))
	w.Header().Set("Content-Type", "image/jpeg")

	if r.URL.Query().Get("resize") == "" {
		io.Copy(w, fd)
		return
	}

	image, err := jpeg.Decode(fd)
	if err != nil {
		log.Error("cannot read image file",
			"image", img.ImageID,
			"error", err.Error())
		web.StdJSONResp(w, http.StatusInternalServerError)
		return
	}
	var width, height int
	if _, err := fmt.Sscanf(r.URL.Query().Get("resize"), "%dx%d", &width, &height); err != nil {
		log.Error("cannot resize image",
			"image", img.ImageID,
			"error", err.Error())
	} else {
		switch img.Orientation {
		case 1:
			// all good
		case 3:
			image = imaging.Rotate180(image)
		case 8:
			image = imaging.Rotate90(image)
		case 6:
			image = imaging.Rotate270(image)
		default:
			log.Debug("unknown image orientation",
				"decoder", "EXIF",
				"image", img.ImageID,
				"value", fmt.Sprint(img.Orientation))
		}
		image = imaging.Fill(image, width, height, imaging.Center, imaging.Linear)
	}
	imaging.Encode(w, image, imaging.JPEG)
}