<commit_before>// github.com/c9s/r3 binding for go
package r3

/*
#cgo LDFLAGS: -lr3

#include <r3/r3.h>
#include <stdlib.h>

void * getData(route *r) {
	return r->data;
}

void * getNodeData(node *n) {
	return n->data;
}

int getVarsLength(match_entry *e) {
	return e->vars->len;
}

int getRequestMethod(match_entry *e) {
	return e->request_method;
}

const char* getVar(match_entry *e, int i) {
	return e->vars->tokens[i];
}

node * treeMatchEntry(node* tree, match_entry* entry) {
	return r3_tree_matchl(tree, entry->path, entry->path_len, entry);
}
*/
import "C"
import "unsafe"
import (
	"errors"
)

var (
	MethodGet     Method = C.METHOD_GET
	MethodPost    Method = C.METHOD_POST
	MethodPut     Method = C.METHOD_PUT
	MethodDelete  Method = C.METHOD_DELETE
	MethodPatch   Method = C.METHOD_PATCH
	MethodHead    Method = C.METHOD_HEAD
	MethodOptions Method = C.METHOD_OPTIONS
	MethodAll     Method = C.METHOD_GET | C.METHOD_POST | C.METHOD_PUT | C.METHOD_DELETE | C.METHOD_PATCH | C.METHOD_HEAD | C.METHOD_OPTIONS
)

type Method int
type Node struct {
	node  *C.node
	datas []interface{}
}

type Data struct {
	Value interface{}
}

type Tree Node

func (n *Node) Data() interface{} {
	var p *Data
	p = (*Data)(C.getNodeData(n.node))
	return p.Value
}

// Create a new Tree
func NewTree(capacity int) *Tree {
	var n *C.node
	n = C.r3_tree_create(C.int(capacity))
	t := &Tree{
		node:  n,
		datas: make([]interface{}, 0, 10),
	}
	return t
}

// Insert route with method and arbitary data
func (n *Tree) InsertRoute(m Method, path string, data interface{}) {
	d := &Data{
		Value: data,
	}
	p := C.CString(path)
	/* hold reference to d so that it's not GCed */
	n.datas = append(n.datas, d)
	C.r3_tree_insert_routel(n.node, C.int(m), p, C.int(C.strlen(p)), unsafe.Pointer(d))
}

// Insert path
func (n *Tree) InsertPath(path string, data interface{}) {
	d := &Data{
		Value: data,
	}
	/* hold reference to d so that it's not GCed */
	n.datas = append(n.datas, d)
	p := C.CString(path)
	C.r3_tree_insert_pathl(n.node, p, C.int(C.strlen(p)), unsafe.Pointer(d))
}

// Compile the tree, returns error if any
func (n *Tree) Compile() error {
	var err C.int
	var errstr *C.char
	err = C.r3_tree_compile(n.node, &errstr)
	if err != 0 {
		defer C.free(unsafe.Pointer(errstr))
		return errors.New(C.GoString(errstr))
	}
	return nil
}

// Match a MatchEntry, returns a Route object if found, nil otherwise
func (n *Tree) MatchRoute(e *MatchEntry) *Route {
	r := C.r3_tree_match_route(n.node, e.entry)
	if r == nil {
		return nil
	}
	return &Route{
		route: r,
	}
}

// Match a MatchEntry, returns a Node object if found, nil otherwise
func (n *Tree) MatchNode(e *MatchEntry) *Node {
	ret := C.treeMatchEntry(n.node, e.entry)
	if ret == nil {
		return nil
	}
	return &Node{
		node: ret,
	}
}

// Free the memory of a tree
func (n *Tree) Free() {
	C.r3_tree_free(n.node)
	n.node = nil
	n.datas = nil
}

// Dump tree to stdout
func (n *Tree) Dump() {
	C.r3_tree_dump(n.node, 0)
}

type MatchEntry struct {
	entry *C.match_entry
}

// creates a new MatchEntry
func NewMatchEntry(path string) *MatchEntry {
	p := C.CString(path)
	e := C.match_entry_createl(p, C.int(C.strlen(p)))
	return &MatchEntry{
		entry: e,
	}
}

func (e *MatchEntry) RequestMethod() Method {
	return Method(C.getRequestMethod(e.entry))
}

// Set request method of the entry
func (e *MatchEntry) SetRequestMethod(m Method) {
	e.entry.request_method = C.int(m)
}

// Get tokens in the path
func (e *MatchEntry) Vars() []string {
	var length int = int(C.getVarsLength(e.entry))
	var tokens []string = make([]string, length)
	for i := 0; i < length; i++ {
		tokens[i] = C.GoString(C.getVar(e.entry, C.int(i)))
	}
	return tokens
}

// Free memory
func (e *MatchEntry) Free() {
	C.match_entry_free(e.entry)
	e.entry = nil
}

type Route struct {
	route *C.route
}

// Returns data payload of the route
func (r *Route) Data() interface{} {
	var p *Data
	p = (*Data)(C.getData(r.route))
	return p.Value
}

// Free memory
func (r *Route) Free() {
	C.r3_route_free(r.route)
	r.route = nil
}
<commit_msg>remove unused C header<commit_after>// github.com/c9s/r3 binding for go
package r3

/*
#cgo LDFLAGS: -lr3

#include <r3/r3.h>

void * getData(route *r) {
	return r->data;
}

void * getNodeData(node *n) {
	return n->data;
}

int getVarsLength(match_entry *e) {
	return e->vars->len;
}

int getRequestMethod(match_entry *e) {
	return e->request_method;
}

const char* getVar(match_entry *e, int i) {
	return e->vars->tokens[i];
}

node * treeMatchEntry(node* tree, match_entry* entry) {
	return r3_tree_matchl(tree, entry->path, entry->path_len, entry);
}
*/
import "C"
import "unsafe"
import (
	"errors"
)

var (
	MethodGet     Method = C.METHOD_GET
	MethodPost    Method = C.METHOD_POST
	MethodPut     Method = C.METHOD_PUT
	MethodDelete  Method = C.METHOD_DELETE
	MethodPatch   Method = C.METHOD_PATCH
	MethodHead    Method = C.METHOD_HEAD
	MethodOptions Method = C.METHOD_OPTIONS
	MethodAll     Method = C.METHOD_GET | C.METHOD_POST | C.METHOD_PUT | C.METHOD_DELETE | C.METHOD_PATCH | C.METHOD_HEAD | C.METHOD_OPTIONS
)

type Method int
type Node struct {
	node  *C.node
	datas []interface{}
}

type Data struct {
	Value interface{}
}

type Tree Node

func (n *Node) Data() interface{} {
	var p *Data
	p = (*Data)(C.getNodeData(n.node))
	return p.Value
}

// Create a new Tree
func NewTree(capacity int) *Tree {
	var n *C.node
	n = C.r3_tree_create(C.int(capacity))
	t := &Tree{
		node:  n,
		datas: make([]interface{}, 0, 10),
	}
	return t
}

// Insert route with method and arbitary data
func (n *Tree) InsertRoute(m Method, path string, data interface{}) {
	d := &Data{
		Value: data,
	}
	p := C.CString(path)
	/* hold reference to d so that it's not GCed */
	n.datas = append(n.datas, d)
	C.r3_tree_insert_routel(n.node, C.int(m), p, C.int(C.strlen(p)), unsafe.Pointer(d))
}

// Insert path
func (n *Tree) InsertPath(path string, data interface{}) {
	d := &Data{
		Value: data,
	}
	/* hold reference to d so that it's not GCed */
	n.datas = append(n.datas, d)
	p := C.CString(path)
	C.r3_tree_insert_pathl(n.node, p, C.int(C.strlen(p)), unsafe.Pointer(d))
}

// Compile the tree, returns error if any
func (n *Tree) Compile() error {
	var err C.int
	var errstr *C.char
	err = C.r3_tree_compile(n.node, &errstr)
	if err != 0 {
		defer C.free(unsafe.Pointer(errstr))
		return errors.New(C.GoString(errstr))
	}
	return nil
}

// Match a MatchEntry, returns a Route object if found, nil otherwise
func (n *Tree) MatchRoute(e *MatchEntry) *Route {
	r := C.r3_tree_match_route(n.node, e.entry)
	if r == nil {
		return nil
	}
	return &Route{
		route: r,
	}
}

// Match a MatchEntry, returns a Node object if found, nil otherwise
func (n *Tree) MatchNode(e *MatchEntry) *Node {
	ret := C.treeMatchEntry(n.node, e.entry)
	if ret == nil {
		return nil
	}
	return &Node{
		node: ret,
	}
}

// Free the memory of a tree
func (n *Tree) Free() {
	C.r3_tree_free(n.node)
	n.node = nil
	n.datas = nil
}

// Dump tree to stdout
func (n *Tree) Dump() {
	C.r3_tree_dump(n.node, 0)
}

type MatchEntry struct {
	entry *C.match_entry
}

// creates a new MatchEntry
func NewMatchEntry(path string) *MatchEntry {
	p := C.CString(path)
	e := C.match_entry_createl(p, C.int(C.strlen(p)))
	return &MatchEntry{
		entry: e,
	}
}

func (e *MatchEntry) RequestMethod() Method {
	return Method(C.getRequestMethod(e.entry))
}

// Set request method of the entry
func (e *MatchEntry) SetRequestMethod(m Method) {
	e.entry.request_method = C.int(m)
}

// Get tokens in the path
func (e *MatchEntry) Vars() []string {
	var length int = int(C.getVarsLength(e.entry))
	var tokens []string = make([]string, length)
	for i := 0; i < length; i++ {
		tokens[i] = C.GoString(C.getVar(e.entry, C.int(i)))
	}
	return tokens
}

// Free memory
func (e *MatchEntry) Free() {
	C.match_entry_free(e.entry)
	e.entry = nil
}

type Route struct {
	route *C.route
}

// Returns data payload of the route
func (r *Route) Data() interface{} {
	var p *Data
	p = (*Data)(C.getData(r.route))
	return p.Value
}

// Free memory
func (r *Route) Free() {
	C.r3_route_free(r.route)
	r.route = nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Rana Ian. All rights reserved.
// Use of this source code is governed by The MIT License
// found in the accompanying LICENSE file.

package ora

/*
#include <oci.h>
#include <stdlib.h>
*/
import "C"
import (
	"container/list"
)

// Tx represents an Oracle transaction associated with a session.
//
// Implements the driver.Tx interface.
type Tx struct {
	id   uint64
	ses  *Ses
	elem *list.Element
}

// checkIsOpen validates that the session is open.
func (tx *Tx) checkIsOpen() error {
	if tx.ses != nil {
		return errNewF("Tx is closed (id %v)", tx.id)
	}
	return nil
}

func (tx *Tx) close() {
	if tx.ses != nil {
		tx.ses.txs.Remove(tx.elem)
		tx.ses = nil
		tx.elem = nil
	}
}

// Commit commits the transaction.
//
// Commit is a member of the driver.Tx interface.
func (tx *Tx) Commit() (err error) {
	if tx.checkIsOpen(); err != nil {
		return err
	}
	defer tx.close()
	r := C.OCITransCommit(
		tx.ses.srv.ocisvcctx,  //OCISvcCtx    *svchp,
		tx.ses.srv.env.ocierr, //OCIError     *errhp,
		C.OCI_DEFAULT)         //ub4          flags );
	if r == C.OCI_ERROR {
		return tx.ses.srv.env.ociError()
	}
	return nil
}

// Rollback rolls back a transaction.
//
// Rollback is a member of the driver.Tx interface.
func (tx *Tx) Rollback() (err error) {
	if tx.checkIsOpen(); err != nil {
		return err
	}
	defer tx.close()
	r := C.OCITransRollback(
		tx.ses.srv.ocisvcctx,  //OCISvcCtx    *svchp,
		tx.ses.srv.env.ocierr, //OCIError     *errhp,
		C.OCI_DEFAULT)         //ub4          flags );
	if r == C.OCI_ERROR {
		return tx.ses.srv.env.ociError()
	}
	return nil
}
<commit_msg>return tx to pool<commit_after>// Copyright 2014 Rana Ian. All rights reserved.
// Use of this source code is governed by The MIT License
// found in the accompanying LICENSE file.

package ora

/*
#include <oci.h>
#include <stdlib.h>
*/
import "C"
import (
	"container/list"
)

// Tx represents an Oracle transaction associated with a session.
//
// Implements the driver.Tx interface.
type Tx struct {
	id   uint64
	ses  *Ses
	elem *list.Element
}

// checkIsOpen validates that the session is open.
func (tx *Tx) checkIsOpen() error {
	if tx.ses != nil {
		return errNewF("Tx is closed (id %v)", tx.id)
	}
	return nil
}

func (tx *Tx) close() {
	if tx.ses != nil {
		tx.ses.txs.Remove(tx.elem)
		tx.ses = nil
		tx.elem = nil
		_drv.txPool.Put(tx)
	}
}

// Commit commits the transaction.
//
// Commit is a member of the driver.Tx interface.
func (tx *Tx) Commit() (err error) {
	if tx.checkIsOpen(); err != nil {
		return err
	}
	defer tx.close()
	r := C.OCITransCommit(
		tx.ses.srv.ocisvcctx,  //OCISvcCtx    *svchp,
		tx.ses.srv.env.ocierr, //OCIError     *errhp,
		C.OCI_DEFAULT)         //ub4          flags );
	if r == C.OCI_ERROR {
		return tx.ses.srv.env.ociError()
	}
	return nil
}

// Rollback rolls back a transaction.
//
// Rollback is a member of the driver.Tx interface.
func (tx *Tx) Rollback() (err error) {
	if tx.checkIsOpen(); err != nil {
		return err
	}
	defer tx.close()
	r := C.OCITransRollback(
		tx.ses.srv.ocisvcctx,  //OCISvcCtx    *svchp,
		tx.ses.srv.env.ocierr, //OCIError     *errhp,
		C.OCI_DEFAULT)         //ub4          flags );
	if r == C.OCI_ERROR {
		return tx.ses.srv.env.ociError()
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import "github.com/nsf/termbox-go"

const MAX_CELL_WIDTH = 20

func writeString(x, y int, fg, bg termbox.Attribute, msg string) {
	for _, c := range msg {
		termbox.SetCell(x, y, c, fg, bg)
		x += 1
	}
}

func writeColumns(x, y int, fg, bg termbox.Attribute, cols []Column) {
	for i, col := range cols {
		if col.Collapsed {
			writeString(x, y, fg, bg, "…")
			x += 1
		} else {
			writeString(x, y, fg, bg, col.Name)

			if col.Width > MAX_CELL_WIDTH {
				x += MAX_CELL_WIDTH
			} else {
				x += col.Width
			}
		}

		if i < len(cols)-1 {
			writeString(x, y, fg, bg, " │ ")
			x += 3
		}
	}
}

func (ui UI) writeRow(x, y int, row []string) {
	const def = termbox.ColorDefault

	for i, col := range ui.data.Columns {
		if col.Collapsed {
			writeString(x, y, def, termbox.ColorWhite, "…")
			x += 1
		} else {
			writeString(x, y, def, def, row[i])
			if col.Width > MAX_CELL_WIDTH {
				x += MAX_CELL_WIDTH
			} else {
				x += col.Width
			}
		}

		if i < len(ui.data.Columns)-1 {
			writeString(x, y, termbox.ColorRed, def, " │ ")
			x += 3
		}
	}
}

type UI struct {
	data             TabularData
	offsetX, offsetY int
}

func NewUi(data TabularData) UI {
	return UI{
		data:    data,
		offsetX: 0,
		offsetY: 0,
	}
}

func (ui UI) Init() error {
	if err := termbox.Init(); err != nil {
		return err
	}

	termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse)

	return nil
}

func (ui UI) Loop() {
	defer termbox.Close()

	ui.repaint()

eventloop:
	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			if ev.Key == termbox.KeyEsc || ev.Ch == 'q' || ev.Key == termbox.KeyCtrlC {
				break eventloop
			} else if ev.Key == termbox.KeyArrowRight {
				ui.offsetX -= 5
				ui.repaint()
			} else if ev.Key == termbox.KeyArrowLeft {
				if ui.offsetX < 0 {
					ui.offsetX += 5
					ui.repaint()
				}
			} else if ev.Ch == 'w' {
				ui.data.Columns[0].Collapsed = !ui.data.Columns[0].Collapsed
				ui.repaint()
			}

		}
	}
}

func (ui UI) repaint() {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	_, height := termbox.Size()

	const coldef = termbox.ColorDefault

	writeColumns(ui.offsetX+0, 0, termbox.ColorWhite, termbox.ColorBlack|termbox.AttrBold, ui.data.Columns)

	for i := 0; i < height-1; i += 1 {
		if i < len(ui.data.Rows) {
			ui.writeRow(ui.offsetX+0, i+1, ui.data.Rows[i])
		} else {
			writeString(0, i+1, termbox.ColorWhite, termbox.ColorBlack|termbox.AttrBold, "~")
		}
	}

	termbox.Flush()
}
<commit_msg>Add filtering modes, more key bindings<commit_after>package main

import "strings"
import "github.com/nsf/termbox-go"

const MAX_CELL_WIDTH = 20

type inputMode int

const (
	ModeDefault = iota
	ModeFilter
	ModeColumnSelect
	ModeRowSelect
)

func writeString(x, y int, fg, bg termbox.Attribute, msg string) {
	for _, c := range msg {
		termbox.SetCell(x, y, c, fg, bg)
		x += 1
	}
}

func writeColumns(x, y int, fg, bg termbox.Attribute, cols []Column) {
	for i, col := range cols {
		if col.Collapsed {
			writeString(x, y, fg, bg, "…")
			x += 1
		} else {
			writeString(x, y, fg, bg, col.Name)

			if col.Width > MAX_CELL_WIDTH {
				x += MAX_CELL_WIDTH
			} else {
				x += col.Width
			}
		}

		if i < len(cols)-1 {
			writeString(x, y, fg, bg, " │ ")
			x += 3
		}
	}
}

func (ui UI) writeRow(x, y int, row []string) {
	const def = termbox.ColorDefault

	for i, col := range ui.data.Columns {
		if col.Collapsed {
			writeString(x, y, termbox.ColorWhite, def, "…")
			x += 1
		} else {
			writeString(x, y, def, def, row[i])
			if col.Width > MAX_CELL_WIDTH {
				writeString(x+MAX_CELL_WIDTH-1, y, def, def, "…")
				x += MAX_CELL_WIDTH
			} else {
				x += col.Width
			}
		}

		if i < len(ui.data.Columns)-1 {
			writeString(x, y, termbox.ColorRed, def, " │ ")
			x += 3
		}
	}
}

type UI struct {
	data             TabularData
	mode             inputMode
	rowIdx, colIdx   int // Selection control
	offsetX, offsetY int // Pan control
	filterString     string
}

func NewUi(data TabularData) UI {
	return UI{
		data:    data,
		offsetX: 0,
		offsetY: 0,
		mode:    ModeDefault,
	}
}

func (ui *UI) Init() error {
	if err := termbox.Init(); err != nil {
		return err
	}

	termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse)

	return nil
}

func (ui *UI) Loop() {
	defer termbox.Close()

	ui.repaint()

eventloop:
	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			if ev.Key == termbox.KeyCtrlC {
				break eventloop
			}

			switch ui.mode {
			case ModeFilter:
				ui.handleKeyFilter(ev)

			default:
				ui.handleKeyDefault(ev)
			}
		}

		ui.repaint()
	}
}

// Return indices of rows to display
func (ui *UI) filterRows(num int) []int {
	rows := make([]int, 0, num)

	if ui.mode != ModeFilter {
		for i := 0; i < num; i += 1 {
			if i+ui.offsetY >= len(ui.data.Rows) {
				break
			}
			rows = append(rows, i+ui.offsetY)
		}
	} else {
		for i := 0; i < num; i += 1 {
			if i+ui.offsetY >= len(ui.data.Rows) {
				break
			}

			for _, col := range ui.data.Rows[i+ui.offsetY] {
				if strings.Contains(col, ui.filterString) {
					rows = append(rows, i+ui.offsetY)
					break
				}
			}
		}
	}

	return rows
}

func (ui *UI) repaint() {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	_, height := termbox.Size()

	const coldef = termbox.ColorDefault

	writeColumns(ui.offsetX+0, 0, termbox.ColorWhite, termbox.ColorBlack|termbox.AttrBold, ui.data.Columns)

	rowIdx := ui.filterRows(height - 2)

	for i := 0; i < height-2; i += 1 {
		if i < len(rowIdx) {
			ui.writeRow(ui.offsetX+0, i+1, ui.data.Rows[rowIdx[i]])
		} else {
			writeString(0, i+1, termbox.ColorWhite|termbox.AttrBold, termbox.ColorBlack, "~")
		}
	}

	switch ui.mode {
	case ModeFilter:
		line := "FILTER (^g quit): " + ui.filterString
		writeString(0, height-1, termbox.ColorWhite|termbox.AttrBold, termbox.ColorDefault, line)
	default:
		writeString(0, height-1, termbox.ColorDefault, termbox.ColorDefault, ":")
	}

	termbox.Flush()
}

func (ui *UI) handleKeyFilter(ev termbox.Event) {
	// Ch == 0 implies this was a special key
	if ev.Ch == 0 && ev.Key != termbox.KeySpace {
		if ev.Key == termbox.KeyEsc || ev.Key == termbox.KeyCtrlG {
			ui.mode = ModeDefault
		} else if ev.Key == termbox.KeyDelete || ev.Key == termbox.KeyBackspace ||
			ev.Key == termbox.KeyBackspace2 {
			if sz := len(ui.filterString); sz > 0 {
				ui.filterString = ui.filterString[:sz-1]
			}
		} else {
			// Fallback to default handling for arrows etc
			ui.handleKeyDefault(ev)
		}
		return
	}

	if ev.Key == termbox.KeySpace {
		ui.filterString += " "
	} else {
		ui.filterString += string(ev.Ch)
	}

	ui.offsetY = 0
}

func (ui *UI) handleKeyDefault(ev termbox.Event) {
	switch {
	case ev.Key == termbox.KeyArrowRight:
		ui.offsetX -= 5
	case ev.Key == termbox.KeyArrowLeft:
		if ui.offsetX < 0 {
			ui.offsetX += 5
		}
	case ev.Key == termbox.KeyArrowUp:
		if ui.offsetY > 0 {
			ui.offsetY -= 1
		}
	case ev.Key == termbox.KeyArrowDown:
		ui.offsetY += 1
	case ev.Ch == '/':
		ui.mode = ModeFilter
		ui.filterString = ""
	case ev.Ch == 'w':
		ui.data.Columns[0].Collapsed = !ui.data.Columns[0].Collapsed
	case ui.mode == ModeDefault && ev.Ch == 'q':
		panic("TODO: real exit")
	}
}
<|endoftext|>


<commit_before>package parsesearch

import (
	"bytes"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"

	"github.com/alecthomas/template"
	"github.com/elazarl/go-bindata-assetfs"
	"github.com/sheki/parsesearch/static"
)

type UI struct {
	ClassName     string
	JavascriptKey string
	AppID         string

	http.Handler
}

func NewUI(appID, javascriptKey, className string) (*UI, error) {
	return &UI{
		AppID:         appID,
		JavascriptKey: javascriptKey,
		ClassName:     className,
		Handler: http.FileServer(&assetfs.AssetFS{
			Asset: static.Asset, AssetDir: static.AssetDir}),
	}, nil
}

func (ui *UI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	rec := httptest.NewRecorder()
	ui.Handler.ServeHTTP(rec, r)
	rendered, err := ui.render(rec.Body)
	if err != nil {
		log.Println("error rendering template:", err)
		io.Copy(w, rec.Body)
		return
	}
	w.WriteHeader(rec.Code)
	io.Copy(w, rendered)
}

func (ui *UI) render(r io.Reader) (io.Reader, error) {
	content, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}
	tmpl, err := template.New("").Parse(string(content))
	if err != nil {
		return nil, err
	}
	output := new(bytes.Buffer)
	if err := tmpl.Execute(output, ui); err != nil {
		return nil, err
	}
	return output, nil
}
<commit_msg>Simplify file serving<commit_after>package parsesearch

import (
	"bytes"
	"io"
	"io/ioutil"
	"log"
	"net/http"

	"text/template"

	"github.com/sheki/parsesearch/static"
)

type UI struct {
	ClassName     string
	JavascriptKey string
	AppID         string
}

func NewUI(appID, javascriptKey, className string) (*UI, error) {
	return &UI{
		AppID:         appID,
		JavascriptKey: javascriptKey,
		ClassName:     className,
	}, nil
}

func (ui *UI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	contents, err := static.Asset(r.URL.Path)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte("Not found."))
		return
	}
	rendered, err := ui.render(bytes.NewReader(contents))
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		log.Println("error rendering template:", err)
		return
	}
	io.Copy(w, rendered)
}

func (ui *UI) render(r io.Reader) (io.Reader, error) {
	content, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}
	tmpl, err := template.New("").Parse(string(content))
	if err != nil {
		return nil, err
	}
	output := new(bytes.Buffer)
	if err := tmpl.Execute(output, ui); err != nil {
		return nil, err
	}
	return output, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"net/http"
	"strconv"
)

// Return the data in JSON format. This is the default return method.
func returnJson(obj interface{}, w http.ResponseWriter, h *http.Request) {
	// Don't cache json returns. This is to work around ie's weird caching behavior
	w.Header().Set("Cache-Control", "no-cache")
	// Set the content type to json
	w.Header().Set("Content-Type", "application/json")

	j, err := json.Marshal(obj)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	fmt.Fprint(w, string(j))
}

func defaultHandler(w http.ResponseWriter, h *http.Request) {
	fmt.Fprint(w, "cool, running")
}

// Return all memories
func allMemoryHandler(w http.ResponseWriter, h *http.Request) {
	returnJson(brain, w, h)
}

// Return a random memory
func randomMemoryHandler(w http.ResponseWriter, h *http.Request) {
	returnJson(brain, w, h)
}

// Return a specific memory
func getMemoryHandler(w http.ResponseWriter, h *http.Request) {
	vars := mux.Vars(h)
	id_string := vars["id"]

	id, err := strconv.Atoi(id_string)
	if err != nil {
		fmt.Fprint(w, "Could not parse Id")
	}

	for _, v := range brain.Memories {
		if v.Id == id {
			returnJson(v, w, h)
		}
	}
}

// Add a memory
func addMemoryHandler(w http.ResponseWriter, h *http.Request) {
	returnJson(brain, w, h)
}

// Change a memory
func changeMemoryHandler(w http.ResponseWriter, h *http.Request) {
	returnJson(brain, w, h)
}

// Remove a memory
func removeMemoryHandler(w http.ResponseWriter, h *http.Request) {
	returnJson(brain, w, h)
}
<commit_msg>Working on adding memories to ginsbrain<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"net/http"
	"strconv"
)

// Return the data in JSON format. This is the default return method.
func returnJson(obj interface{}, w http.ResponseWriter, h *http.Request) {
	// Don't cache json returns. This is to work around ie's weird caching behavior
	w.Header().Set("Cache-Control", "no-cache")
	// Set the content type to json
	w.Header().Set("Content-Type", "application/json")

	j, err := json.Marshal(obj)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	fmt.Fprint(w, string(j))
}

func defaultHandler(w http.ResponseWriter, h *http.Request) {
	fmt.Fprint(w, "cool, running")
}

// Return all memories
func allMemoryHandler(w http.ResponseWriter, h *http.Request) {
	returnJson(brain, w, h)
}

// Return a random memory
func randomMemoryHandler(w http.ResponseWriter, h *http.Request) {
	returnJson(brain, w, h)
}

// Return a specific memory
func getMemoryHandler(w http.ResponseWriter, h *http.Request) {
	vars := mux.Vars(h)
	id_string := vars["id"]

	id, err := strconv.Atoi(id_string)
	if err != nil {
		fmt.Fprint(w, "Could not parse Id")
	}

	for _, v := range brain.Memories {
		if v.Id == id {
			returnJson(v, w, h)
		}
	}
}

// Add a memory
func addMemoryHandler(w http.ResponseWriter, h *http.Request) {
	var m Memory
	b := json.NewDecoder(h.Body)
	b.Decode(&m)

	brain.Memories = append(brain.Memories, m)
	returnJson(m, w, h)
}

// Change a memory
func changeMemoryHandler(w http.ResponseWriter, h *http.Request) {
	returnJson(brain, w, h)
}

// Remove a memory
func removeMemoryHandler(w http.ResponseWriter, h *http.Request) {
	returnJson(brain, w, h)
}
<|endoftext|>


<commit_before>// Copyright 2015 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
	"github.com/prometheus/common/model"
	"github.com/prometheus/common/route"
	"github.com/prometheus/common/version"
	"github.com/satori/go.uuid"
	"golang.org/x/net/context"

	"github.com/prometheus/alertmanager/provider"
	"github.com/prometheus/alertmanager/types"
)

var (
	numReceivedAlerts = prometheus.NewCounterVec(prometheus.CounterOpts{
		Namespace: "alertmanager",
		Name:      "alerts_received_total",
		Help:      "The total number of received alerts.",
	}, []string{"status"})

	numInvalidAlerts = prometheus.NewCounter(prometheus.CounterOpts{
		Namespace: "alertmanager",
		Name:      "alerts_invalid_total",
		Help:      "The total number of received alerts that were invalid.",
	})
)

func init() {
	prometheus.Register(numReceivedAlerts)
	prometheus.Register(numInvalidAlerts)
}

// API provides registration of handlers for API routes.
type API struct {
	alerts         provider.Alerts
	silences       provider.Silences
	config         string
	resolveTimeout time.Duration
	uptime         time.Time

	groups func() AlertOverview

	// context is an indirection for testing.
	context func(r *http.Request) context.Context
	mtx     sync.RWMutex
}

// NewAPI returns a new API.
func NewAPI(alerts provider.Alerts, silences provider.Silences, gf func() AlertOverview) *API {
	return &API{
		context:  route.Context,
		alerts:   alerts,
		silences: silences,
		groups:   gf,
		uptime:   time.Now(),
	}
}

// Register registers the API handlers under their correct routes
// in the given router.
func (api *API) Register(r *route.Router) {
	ihf := prometheus.InstrumentHandlerFunc

	// Register legacy forwarder for alert pushing.
	r.Post("/alerts", ihf("legacy_add_alerts", api.legacyAddAlerts))

	// Register actual API.
	r = r.WithPrefix("/v1")

	r.Get("/status", ihf("status", api.status))
	r.Get("/alerts/groups", ihf("alert_groups", api.alertGroups))

	r.Get("/alerts", ihf("list_alerts", api.listAlerts))
	r.Post("/alerts", ihf("add_alerts", api.addAlerts))

	r.Get("/silences", ihf("list_silences", api.listSilences))
	r.Post("/silences", ihf("add_silence", api.addSilence))
	r.Get("/silence/:sid", ihf("get_silence", api.getSilence))
	r.Del("/silence/:sid", ihf("del_silence", api.delSilence))
}

// Update sets the configuration string to a new value.
func (api *API) Update(config string, resolveTimeout time.Duration) {
	api.mtx.Lock()
	defer api.mtx.Unlock()

	api.config = config
	api.resolveTimeout = resolveTimeout
}

type errorType string

const (
	errorNone     errorType = ""
	errorInternal           = "server_error"
	errorBadData            = "bad_data"
)

type apiError struct {
	typ errorType
	err error
}

func (e *apiError) Error() string {
	return fmt.Sprintf("%s: %s", e.typ, e.err)
}

func (api *API) status(w http.ResponseWriter, req *http.Request) {
	api.mtx.RLock()

	var status = struct {
		Config      string            `json:"config"`
		VersionInfo map[string]string `json:"versionInfo"`
		Uptime      time.Time         `json:"uptime"`
	}{
		Config: api.config,
		VersionInfo: map[string]string{
			"version":   version.Version,
			"revision":  version.Revision,
			"branch":    version.Branch,
			"buildUser": version.BuildUser,
			"buildDate": version.BuildDate,
			"goVersion": version.GoVersion,
		},
		Uptime: api.uptime,
	}

	api.mtx.RUnlock()

	respond(w, status)
}

func (api *API) alertGroups(w http.ResponseWriter, req *http.Request) {
	respond(w, api.groups())
}

func (api *API) listAlerts(w http.ResponseWriter, r *http.Request) {
	alerts := api.alerts.GetPending()
	defer alerts.Close()

	var (
		err error
		res []*types.Alert
	)
	// TODO(fabxc): enforce a sensible timeout.
	for a := range alerts.Next() {
		if err = alerts.Err(); err != nil {
			break
		}
		res = append(res, a)
	}

	if err != nil {
		respondError(w, apiError{
			typ: errorInternal,
			err: err,
		}, nil)
		return
	}
	respond(w, types.Alerts(res...))
}

func (api *API) legacyAddAlerts(w http.ResponseWriter, r *http.Request) {
	var legacyAlerts = []struct {
		Summary     model.LabelValue `json:"summary"`
		Description model.LabelValue `json:"description"`
		Runbook     model.LabelValue `json:"runbook"`
		Labels      model.LabelSet   `json:"labels"`
		Payload     model.LabelSet   `json:"payload"`
	}{}
	if err := receive(r, &legacyAlerts); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var alerts []*types.Alert
	for _, la := range legacyAlerts {
		a := &types.Alert{
			Alert: model.Alert{
				Labels:      la.Labels,
				Annotations: la.Payload,
			},
		}
		if a.Annotations == nil {
			a.Annotations = model.LabelSet{}
		}
		a.Annotations["summary"] = la.Summary
		a.Annotations["description"] = la.Description
		a.Annotations["runbook"] = la.Runbook

		alerts = append(alerts, a)
	}

	api.insertAlerts(w, r, alerts...)
}

func (api *API) addAlerts(w http.ResponseWriter, r *http.Request) {
	var alerts []*types.Alert
	if err := receive(r, &alerts); err != nil {
		respondError(w, apiError{
			typ: errorBadData,
			err: err,
		}, nil)
		return
	}

	api.insertAlerts(w, r, alerts...)
}

func (api *API) insertAlerts(w http.ResponseWriter, r *http.Request, alerts ...*types.Alert) {
	now := time.Now()

	for _, alert := range alerts {
		alert.UpdatedAt = now

		// Ensure StartsAt is set.
		if alert.StartsAt.IsZero() {
			alert.StartsAt = now
		}
		// If no end time is defined, set a timeout after which an alert
		// is marked resolved if it is not updated.
		if alert.EndsAt.IsZero() {
			alert.Timeout = true
			alert.EndsAt = now.Add(api.resolveTimeout)

			numReceivedAlerts.WithLabelValues("firing").Inc()
		} else {
			numReceivedAlerts.WithLabelValues("resolved").Inc()
		}
	}

	// Make a best effort to insert all alerts that are valid.
	var (
		validAlerts    = make([]*types.Alert, 0, len(alerts))
		validationErrs = &types.MultiError{}
	)
	for _, a := range alerts {
		if err := a.Validate(); err != nil {
			validationErrs.Add(err)
			numInvalidAlerts.Inc()
			continue
		}
		validAlerts = append(validAlerts, a)
	}
	if err := api.alerts.Put(validAlerts...); err != nil {
		respondError(w, apiError{
			typ: errorInternal,
			err: err,
		}, nil)
		return
	}

	if validationErrs.Len() > 0 {
		respondError(w, apiError{
			typ: errorBadData,
			err: validationErrs,
		}, nil)
		return
	}

	respond(w, nil)
}

func (api *API) addSilence(w http.ResponseWriter, r *http.Request) {
	var msil model.Silence
	if err := receive(r, &msil); err != nil {
		respondError(w, apiError{
			typ: errorBadData,
			err: err,
		}, nil)
		return
	}
	sil := types.NewSilence(&msil)

	// if sil.CreatedAt.IsZero() {
	//	sil.CreatedAt = time.Now()
	// }

	if err := sil.Validate(); err != nil {
		respondError(w, apiError{
			typ: errorBadData,
			err: err,
		}, nil)
		return
	}
	if err := sil.Init(); err != nil {
		respondError(w, apiError{
			typ: errorBadData,
			err: err,
		}, nil)
		return
	}

	sid, err := api.silences.Set(sil)
	if err != nil {
		respondError(w, apiError{
			typ: errorInternal,
			err: err,
		}, nil)
		return
	}

	respond(w, struct {
		SilenceID uuid.UUID `json:"silenceId"`
	}{
		SilenceID: sid,
	})
}

func (api *API) getSilence(w http.ResponseWriter, r *http.Request) {
	sids := route.Param(api.context(r), "sid")
	sid, err := uuid.FromString(sids)
	if err != nil {
		respondError(w, apiError{
			typ: errorBadData,
			err: err,
		}, nil)
		return
	}

	sil, err := api.silences.Get(sid)
	if err != nil {
		http.Error(w, fmt.Sprint("Error getting silence: ", err), http.StatusNotFound)
		return
	}

	respond(w, &sil)
}

func (api *API) delSilence(w http.ResponseWriter, r *http.Request) {
	sids := route.Param(api.context(r), "sid")
	sid, err := uuid.FromString(sids)
	if err != nil {
		respondError(w, apiError{
			typ: errorBadData,
			err: err,
		}, nil)
		return
	}

	if err := api.silences.Del(sid); err != nil {
		respondError(w, apiError{
			typ: errorInternal,
			err: err,
		}, nil)
		return
	}
	respond(w, nil)
}

func (api *API) listSilences(w http.ResponseWriter, r *http.Request) {
	sils, err := api.silences.All()
	if err != nil {
		respondError(w, apiError{
			typ: errorInternal,
			err: err,
		}, nil)
		return
	}
	respond(w, sils)
}

type status string

const (
	statusSuccess status = "success"
	statusError          = "error"
)

type response struct {
	Status    status      `json:"status"`
	Data      interface{} `json:"data,omitempty"`
	ErrorType errorType   `json:"errorType,omitempty"`
	Error     string      `json:"error,omitempty"`
}

func respond(w http.ResponseWriter, data interface{}) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)

	b, err := json.Marshal(&response{
		Status: statusSuccess,
		Data:   data,
	})
	if err != nil {
		return
	}
	w.Write(b)
}

func respondError(w http.ResponseWriter, apiErr apiError, data interface{}) {
	w.Header().Set("Content-Type", "application/json")

	switch apiErr.typ {
	case errorBadData:
		w.WriteHeader(http.StatusBadRequest)
	case errorInternal:
		w.WriteHeader(http.StatusInternalServerError)
	default:
		panic(fmt.Sprintf("unknown error type %q", apiErr))
	}

	b, err := json.Marshal(&response{
		Status:    statusError,
		ErrorType: apiErr.typ,
		Error:     apiErr.err.Error(),
		Data:      data,
	})
	if err != nil {
		return
	}
	log.Errorf("api error: %v", apiErr)

	w.Write(b)
}

func receive(r *http.Request, v interface{}) error {
	dec := json.NewDecoder(r.Body)
	defer r.Body.Close()

	err := dec.Decode(v)
	if err != nil {
		log.Debugf("Decoding request failed: %v", err)
	}
	return err
}
<commit_msg>api: push silence validation into provider<commit_after>// Copyright 2015 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
	"github.com/prometheus/common/model"
	"github.com/prometheus/common/route"
	"github.com/prometheus/common/version"
	"github.com/satori/go.uuid"
	"golang.org/x/net/context"

	"github.com/prometheus/alertmanager/provider"
	"github.com/prometheus/alertmanager/types"
)

var (
	numReceivedAlerts = prometheus.NewCounterVec(prometheus.CounterOpts{
		Namespace: "alertmanager",
		Name:      "alerts_received_total",
		Help:      "The total number of received alerts.",
	}, []string{"status"})

	numInvalidAlerts = prometheus.NewCounter(prometheus.CounterOpts{
		Namespace: "alertmanager",
		Name:      "alerts_invalid_total",
		Help:      "The total number of received alerts that were invalid.",
	})
)

func init() {
	prometheus.Register(numReceivedAlerts)
	prometheus.Register(numInvalidAlerts)
}

// API provides registration of handlers for API routes.
type API struct {
	alerts         provider.Alerts
	silences       provider.Silences
	config         string
	resolveTimeout time.Duration
	uptime         time.Time

	groups func() AlertOverview

	// context is an indirection for testing.
	context func(r *http.Request) context.Context
	mtx     sync.RWMutex
}

// NewAPI returns a new API.
func NewAPI(alerts provider.Alerts, silences provider.Silences, gf func() AlertOverview) *API {
	return &API{
		context:  route.Context,
		alerts:   alerts,
		silences: silences,
		groups:   gf,
		uptime:   time.Now(),
	}
}

// Register registers the API handlers under their correct routes
// in the given router.
func (api *API) Register(r *route.Router) {
	ihf := prometheus.InstrumentHandlerFunc

	// Register legacy forwarder for alert pushing.
	r.Post("/alerts", ihf("legacy_add_alerts", api.legacyAddAlerts))

	// Register actual API.
	r = r.WithPrefix("/v1")

	r.Get("/status", ihf("status", api.status))
	r.Get("/alerts/groups", ihf("alert_groups", api.alertGroups))

	r.Get("/alerts", ihf("list_alerts", api.listAlerts))
	r.Post("/alerts", ihf("add_alerts", api.addAlerts))

	r.Get("/silences", ihf("list_silences", api.listSilences))
	r.Post("/silences", ihf("add_silence", api.addSilence))
	r.Get("/silence/:sid", ihf("get_silence", api.getSilence))
	r.Del("/silence/:sid", ihf("del_silence", api.delSilence))
}

// Update sets the configuration string to a new value.
func (api *API) Update(config string, resolveTimeout time.Duration) {
	api.mtx.Lock()
	defer api.mtx.Unlock()

	api.config = config
	api.resolveTimeout = resolveTimeout
}

type errorType string

const (
	errorNone     errorType = ""
	errorInternal           = "server_error"
	errorBadData            = "bad_data"
)

type apiError struct {
	typ errorType
	err error
}

func (e *apiError) Error() string {
	return fmt.Sprintf("%s: %s", e.typ, e.err)
}

func (api *API) status(w http.ResponseWriter, req *http.Request) {
	api.mtx.RLock()

	var status = struct {
		Config      string            `json:"config"`
		VersionInfo map[string]string `json:"versionInfo"`
		Uptime      time.Time         `json:"uptime"`
	}{
		Config: api.config,
		VersionInfo: map[string]string{
			"version":   version.Version,
			"revision":  version.Revision,
			"branch":    version.Branch,
			"buildUser": version.BuildUser,
			"buildDate": version.BuildDate,
			"goVersion": version.GoVersion,
		},
		Uptime: api.uptime,
	}

	api.mtx.RUnlock()

	respond(w, status)
}

func (api *API) alertGroups(w http.ResponseWriter, req *http.Request) {
	respond(w, api.groups())
}

func (api *API) listAlerts(w http.ResponseWriter, r *http.Request) {
	alerts := api.alerts.GetPending()
	defer alerts.Close()

	var (
		err error
		res []*types.Alert
	)
	// TODO(fabxc): enforce a sensible timeout.
	for a := range alerts.Next() {
		if err = alerts.Err(); err != nil {
			break
		}
		res = append(res, a)
	}

	if err != nil {
		respondError(w, apiError{
			typ: errorInternal,
			err: err,
		}, nil)
		return
	}
	respond(w, types.Alerts(res...))
}

func (api *API) legacyAddAlerts(w http.ResponseWriter, r *http.Request) {
	var legacyAlerts = []struct {
		Summary     model.LabelValue `json:"summary"`
		Description model.LabelValue `json:"description"`
		Runbook     model.LabelValue `json:"runbook"`
		Labels      model.LabelSet   `json:"labels"`
		Payload     model.LabelSet   `json:"payload"`
	}{}
	if err := receive(r, &legacyAlerts); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var alerts []*types.Alert
	for _, la := range legacyAlerts {
		a := &types.Alert{
			Alert: model.Alert{
				Labels:      la.Labels,
				Annotations: la.Payload,
			},
		}
		if a.Annotations == nil {
			a.Annotations = model.LabelSet{}
		}
		a.Annotations["summary"] = la.Summary
		a.Annotations["description"] = la.Description
		a.Annotations["runbook"] = la.Runbook

		alerts = append(alerts, a)
	}

	api.insertAlerts(w, r, alerts...)
}

func (api *API) addAlerts(w http.ResponseWriter, r *http.Request) {
	var alerts []*types.Alert
	if err := receive(r, &alerts); err != nil {
		respondError(w, apiError{
			typ: errorBadData,
			err: err,
		}, nil)
		return
	}

	api.insertAlerts(w, r, alerts...)
}

func (api *API) insertAlerts(w http.ResponseWriter, r *http.Request, alerts ...*types.Alert) {
	now := time.Now()

	for _, alert := range alerts {
		alert.UpdatedAt = now

		// Ensure StartsAt is set.
		if alert.StartsAt.IsZero() {
			alert.StartsAt = now
		}
		// If no end time is defined, set a timeout after which an alert
		// is marked resolved if it is not updated.
		if alert.EndsAt.IsZero() {
			alert.Timeout = true
			alert.EndsAt = now.Add(api.resolveTimeout)

			numReceivedAlerts.WithLabelValues("firing").Inc()
		} else {
			numReceivedAlerts.WithLabelValues("resolved").Inc()
		}
	}

	// Make a best effort to insert all alerts that are valid.
	var (
		validAlerts    = make([]*types.Alert, 0, len(alerts))
		validationErrs = &types.MultiError{}
	)
	for _, a := range alerts {
		if err := a.Validate(); err != nil {
			validationErrs.Add(err)
			numInvalidAlerts.Inc()
			continue
		}
		validAlerts = append(validAlerts, a)
	}
	if err := api.alerts.Put(validAlerts...); err != nil {
		respondError(w, apiError{
			typ: errorInternal,
			err: err,
		}, nil)
		return
	}

	if validationErrs.Len() > 0 {
		respondError(w, apiError{
			typ: errorBadData,
			err: validationErrs,
		}, nil)
		return
	}

	respond(w, nil)
}

func (api *API) addSilence(w http.ResponseWriter, r *http.Request) {
	var msil model.Silence
	if err := receive(r, &msil); err != nil {
		respondError(w, apiError{
			typ: errorBadData,
			err: err,
		}, nil)
		return
	}
	sil := types.NewSilence(&msil)

	if err := sil.Init(); err != nil {
		respondError(w, apiError{
			typ: errorBadData,
			err: err,
		}, nil)
		return
	}

	sid, err := api.silences.Set(sil)
	if err != nil {
		respondError(w, apiError{
			typ: errorInternal,
			err: err,
		}, nil)
		return
	}

	respond(w, struct {
		SilenceID uuid.UUID `json:"silenceId"`
	}{
		SilenceID: sid,
	})
}

func (api *API) getSilence(w http.ResponseWriter, r *http.Request) {
	sids := route.Param(api.context(r), "sid")
	sid, err := uuid.FromString(sids)
	if err != nil {
		respondError(w, apiError{
			typ: errorBadData,
			err: err,
		}, nil)
		return
	}

	sil, err := api.silences.Get(sid)
	if err != nil {
		http.Error(w, fmt.Sprint("Error getting silence: ", err), http.StatusNotFound)
		return
	}

	respond(w, &sil)
}

func (api *API) delSilence(w http.ResponseWriter, r *http.Request) {
	sids := route.Param(api.context(r), "sid")
	sid, err := uuid.FromString(sids)
	if err != nil {
		respondError(w, apiError{
			typ: errorBadData,
			err: err,
		}, nil)
		return
	}

	if err := api.silences.Del(sid); err != nil {
		respondError(w, apiError{
			typ: errorInternal,
			err: err,
		}, nil)
		return
	}
	respond(w, nil)
}

func (api *API) listSilences(w http.ResponseWriter, r *http.Request) {
	sils, err := api.silences.All()
	if err != nil {
		respondError(w, apiError{
			typ: errorInternal,
			err: err,
		}, nil)
		return
	}
	respond(w, sils)
}

type status string

const (
	statusSuccess status = "success"
	statusError          = "error"
)

type response struct {
	Status    status      `json:"status"`
	Data      interface{} `json:"data,omitempty"`
	ErrorType errorType   `json:"errorType,omitempty"`
	Error     string      `json:"error,omitempty"`
}

func respond(w http.ResponseWriter, data interface{}) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)

	b, err := json.Marshal(&response{
		Status: statusSuccess,
		Data:   data,
	})
	if err != nil {
		return
	}
	w.Write(b)
}

func respondError(w http.ResponseWriter, apiErr apiError, data interface{}) {
	w.Header().Set("Content-Type", "application/json")

	switch apiErr.typ {
	case errorBadData:
		w.WriteHeader(http.StatusBadRequest)
	case errorInternal:
		w.WriteHeader(http.StatusInternalServerError)
	default:
		panic(fmt.Sprintf("unknown error type %q", apiErr))
	}

	b, err := json.Marshal(&response{
		Status:    statusError,
		ErrorType: apiErr.typ,
		Error:     apiErr.err.Error(),
		Data:      data,
	})
	if err != nil {
		return
	}
	log.Errorf("api error: %s", apiErr)

	w.Write(b)
}

func receive(r *http.Request, v interface{}) error {
	dec := json.NewDecoder(r.Body)
	defer r.Body.Close()

	err := dec.Decode(v)
	if err != nil {
		log.Debugf("Decoding request failed: %v", err)
	}
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/Gujarats/API-Golang/config"
	"github.com/Gujarats/API-Golang/controller/driver"
	"github.com/Gujarats/API-Golang/database"

	driverModel "github.com/Gujarats/API-Golang/model/driver"
	driverInterface "github.com/Gujarats/API-Golang/model/driver/interface"

	"github.com/Gujarats/API-Golang/util/logger"

	mgo "gopkg.in/mgo.v2"
	redis "gopkg.in/redis.v5"
)

var cfg config.Config

func init() {
	logger.InitLogger("App :: ", "./logs/", "App.txt")
}

func main() {
	//getting list of all the connection.
	listConnection := database.SystemConnection()

	//getting redis connection convert it from interface to *redisClient.
	redisConn := listConnection["redis"].(*redis.Client)

	// get postgre connection.
	mongoConn := listConnection["mongodb"].(*mgo.Session)

	//pass connection to model to model
	//reviewData := &reviewModel.ReviewData{}
	//reviewData.GetConn(postgreConn)

	// set driver interface
	var driverInterface driverInterface.DriverInterfacce
	driverData := &driverModel.DriverData{}
	driverInterface = driverData

	// pass database connections to model
	driverInterface.GetConn(mongoConn, redisConn)

	// driver router
	http.Handle("/driver/find", driver.FindDriver(driverInterface))
	http.Handle("/driver/update", driver.UpdateDriver(driverInterface))

	port := ":8080"
	fmt.Println("App Started on port = ", port)
	err := http.ListenAndServe(port, nil)
	if err != nil {
		log.Panic("App Started Failed = ", err.Error())
	}

}
<commit_msg>fix build for app.go<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/Gujarats/API-Golang/config"
	"github.com/Gujarats/API-Golang/database"

	driverModel "github.com/Gujarats/API-Golang/model/driver"
	driverInterface "github.com/Gujarats/API-Golang/model/driver/interface"

	cityModel "github.com/Gujarats/API-Golang/model/city"
	"github.com/Gujarats/API-Golang/model/city/interface"

	driverController "github.com/Gujarats/API-Golang/controller/driver"

	"github.com/Gujarats/API-Golang/util/logger"

	mgo "gopkg.in/mgo.v2"
	redis "gopkg.in/redis.v5"
)

var cfg config.Config

func init() {
	logger.InitLogger("App :: ", "./logs/", "App.txt")
}

func main() {
	//getting list of all the connection.
	listConnection := database.SystemConnection()

	//getting redis connection convert it from interface to *redisClient.
	redisConn := listConnection["redis"].(*redis.Client)

	// get postgre connection.
	mongoConn := listConnection["mongodb"].(*mgo.Session)

	//pass connection to model to model
	//reviewData := &reviewModel.ReviewData{}
	//reviewData.GetConn(postgreConn)

	// set driver interface
	var driver driverInterface.DriverInterfacce
	driverData := &driverModel.DriverData{}
	driver = driverData

	// set city interface
	var city cityInterface.CityInterfacce
	cityData := &cityModel.City{}
	city = cityData

	// pass database connections to model
	driver.GetConn(mongoConn, redisConn)
	city.GetConn(mongoConn, redisConn)

	// driver router
	http.Handle("/driver/find", driverController.FindDriver(driver, city))
	http.Handle("/driver/update", driverController.UpdateDriver(driver))

	port := ":8080"
	fmt.Println("App Started on port = ", port)
	err := http.ListenAndServe(port, nil)
	if err != nil {
		log.Panic("App Started Failed = ", err.Error())
	}

}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"text/tabwriter"
	"text/template"
	"time"
)

// App is the main structure of a cli application. It is recomended that
// and app be created with the cli.NewApp() function
type App struct {
	// The name of the program. Defaults to os.Args[0]
	Name string
	// Description of the program.
	Usage string
	// Version of the program
	Version string
	// List of commands to execute
	Commands []Command
	// List of flags to parse
	Flags []Flag
	// Boolean to enable bash completion commands
	EnableBashCompletion bool
	// Boolean to hide built-in help command
	HideHelp bool
	// Boolean to hide built-in version flag
	HideVersion bool
	// An action to execute when the bash-completion flag is set
	BashComplete func(context *Context)
	// An action to execute before any subcommands are run, but after the context is ready
	// If a non-nil error is returned, no subcommands are run
	Before func(context *Context) error
	// An action to execute after any subcommands are run, but after the subcommand has finished
	// It is run regardless of Because() result
	After func(context *Context) error
	// The action to execute when no subcommands are specified
	Action func(context *Context)
	// Execute this function if the proper command cannot be found
	CommandNotFound func(context *Context, command string)
	// Compilation date
	Compiled time.Time
	// Author
	Author string
	// Author e-mail
	Email string
	// Writer writer to write output to
	Writer io.Writer
}

// Tries to find out when this binary was compiled.
// Returns the current time if it fails to find it.
func compileTime() time.Time {
	info, err := os.Stat(os.Args[0])
	if err != nil {
		return time.Now()
	}
	return info.ModTime()
}

// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action.
func NewApp() *App {
	return &App{
		Name:         os.Args[0],
		Usage:        "A new cli application",
		Version:      "0.0.0",
		BashComplete: DefaultAppComplete,
		Action:       helpCommand.Action,
		Compiled:     compileTime(),
		Author:       "Author",
		Email:        "unknown@email",
		Writer:       os.Stdout,
	}
}

// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination
func (a *App) Run(arguments []string) error {
	var err error
	if HelpPrinter == nil {
		defer func() {
			HelpPrinter = nil
		}()

		HelpPrinter = func(templ string, data interface{}) {
			w := tabwriter.NewWriter(a.Writer, 0, 8, 1, '\t', 0)
			t := template.Must(template.New("help").Parse(templ))
			err := t.Execute(w, data)
			if err != nil {
				panic(err)
			}
			w.Flush()
		}
	}

	// append help to commands
	if a.Command(helpCommand.Name) == nil && !a.HideHelp {
		a.Commands = append(a.Commands, helpCommand)
		if (HelpFlag != BoolFlag{}) {
			a.appendFlag(HelpFlag)
		}
	}

	//append version/help flags
	if a.EnableBashCompletion {
		a.appendFlag(BashCompletionFlag)
	}

	if !a.HideVersion {
		a.appendFlag(VersionFlag)
	}

	// parse flags
	set := flagSet(a.Name, a.Flags)
	set.SetOutput(ioutil.Discard)
	err = set.Parse(arguments[1:])
	nerr := normalizeFlags(a.Flags, set)
	if nerr != nil {
		fmt.Fprintln(a.Writer, nerr)
		context := NewContext(a, set, set)
		ShowAppHelp(context)
		fmt.Fprintln(a.Writer)
		return nerr
	}
	context := NewContext(a, set, set)

	if err != nil {
		fmt.Fprintf(a.Writer, "Incorrect Usage.\n\n")
		ShowAppHelp(context)
		fmt.Fprintln(a.Writer)
		return err
	}

	if checkCompletions(context) {
		return nil
	}

	if checkHelp(context) {
		return nil
	}

	if checkVersion(context) {
		return nil
	}

	if a.After != nil {
		defer func() {
			// err is always nil here.
			// There is a check to see if it is non-nil
			// just few lines before.
			err = a.After(context)
		}()
	}

	if a.Before != nil {
		err := a.Before(context)
		if err != nil {
			return err
		}
	}

	args := context.Args()
	if args.Present() {
		name := args.First()
		c := a.Command(name)
		if c != nil {
			return c.Run(context)
		}
	}

	// Run default Action
	a.Action(context)
	return nil
}

// Another entry point to the cli app, takes care of passing arguments and error handling
func (a *App) RunAndExitOnError() {
	if err := a.Run(os.Args); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags
func (a *App) RunAsSubcommand(ctx *Context) error {
	var err error
	// append help to commands
	if len(a.Commands) > 0 {
		if a.Command(helpCommand.Name) == nil && !a.HideHelp {
			a.Commands = append(a.Commands, helpCommand)
			if (HelpFlag != BoolFlag{}) {
				a.appendFlag(HelpFlag)
			}
		}
	}

	// append flags
	if a.EnableBashCompletion {
		a.appendFlag(BashCompletionFlag)
	}

	// parse flags
	set := flagSet(a.Name, a.Flags)
	set.SetOutput(ioutil.Discard)
	err = set.Parse(ctx.Args().Tail())
	nerr := normalizeFlags(a.Flags, set)
	context := NewContext(a, set, ctx.globalSet)

	if nerr != nil {
		fmt.Fprintln(a.Writer, nerr)
		if len(a.Commands) > 0 {
			ShowSubcommandHelp(context)
		} else {
			ShowCommandHelp(ctx, context.Args().First())
		}
		fmt.Fprintln(a.Writer)
		return nerr
	}

	if err != nil {
		fmt.Fprintf(a.Writer, "Incorrect Usage.\n\n")
		ShowSubcommandHelp(context)
		return err
	}

	if checkCompletions(context) {
		return nil
	}

	if len(a.Commands) > 0 {
		if checkSubcommandHelp(context) {
			return nil
		}
	} else {
		if checkCommandHelp(ctx, context.Args().First()) {
			return nil
		}
	}

	if a.After != nil {
		defer func() {
			// err is always nil here.
			// There is a check to see if it is non-nil
			// just few lines before.
			err = a.After(context)
		}()
	}

	if a.Before != nil {
		err := a.Before(context)
		if err != nil {
			return err
		}
	}

	args := context.Args()
	if args.Present() {
		name := args.First()
		c := a.Command(name)
		if c != nil {
			return c.Run(context)
		}
	}

	// Run default Action
	if len(a.Commands) > 0 {
		a.Action(context)
	} else {
		a.Action(ctx)
	}

	return nil
}

// Returns the named command on App. Returns nil if the command does not exist
func (a *App) Command(name string) *Command {
	for _, c := range a.Commands {
		if c.HasName(name) {
			return &c
		}
	}

	return nil
}

func (a *App) hasFlag(flag Flag) bool {
	for _, f := range a.Flags {
		if flag == f {
			return true
		}
	}

	return false
}

func (a *App) appendFlag(flag Flag) {
	if !a.hasFlag(flag) {
		a.Flags = append(a.Flags, flag)
	}
}
<commit_msg>After handler brought up to speed.<commit_after>package cli

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"text/tabwriter"
	"text/template"
	"time"
)

// App is the main structure of a cli application. It is recomended that
// and app be created with the cli.NewApp() function
type App struct {
	// The name of the program. Defaults to os.Args[0]
	Name string
	// Description of the program.
	Usage string
	// Version of the program
	Version string
	// List of commands to execute
	Commands []Command
	// List of flags to parse
	Flags []Flag
	// Boolean to enable bash completion commands
	EnableBashCompletion bool
	// Boolean to hide built-in help command
	HideHelp bool
	// Boolean to hide built-in version flag
	HideVersion bool
	// An action to execute when the bash-completion flag is set
	BashComplete func(context *Context)
	// An action to execute before any subcommands are run, but after the context is ready
	// If a non-nil error is returned, no subcommands are run
	Before func(context *Context) error
	// An action to execute after any subcommands are run, but after the subcommand has finished
	// It is run regardless of Because() result
	After func(context *Context) error
	// The action to execute when no subcommands are specified
	Action func(context *Context)
	// Execute this function if the proper command cannot be found
	CommandNotFound func(context *Context, command string)
	// Compilation date
	Compiled time.Time
	// Author
	Author string
	// Author e-mail
	Email string
	// Writer writer to write output to
	Writer io.Writer
}

// Tries to find out when this binary was compiled.
// Returns the current time if it fails to find it.
func compileTime() time.Time {
	info, err := os.Stat(os.Args[0])
	if err != nil {
		return time.Now()
	}
	return info.ModTime()
}

// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action.
func NewApp() *App {
	return &App{
		Name:         os.Args[0],
		Usage:        "A new cli application",
		Version:      "0.0.0",
		BashComplete: DefaultAppComplete,
		Action:       helpCommand.Action,
		Compiled:     compileTime(),
		Author:       "Author",
		Email:        "unknown@email",
		Writer:       os.Stdout,
	}
}

// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination
func (a *App) Run(arguments []string) (err error) {
	if HelpPrinter == nil {
		defer func() {
			HelpPrinter = nil
		}()

		HelpPrinter = func(templ string, data interface{}) {
			w := tabwriter.NewWriter(a.Writer, 0, 8, 1, '\t', 0)
			t := template.Must(template.New("help").Parse(templ))
			err := t.Execute(w, data)
			if err != nil {
				panic(err)
			}
			w.Flush()
		}
	}

	// append help to commands
	if a.Command(helpCommand.Name) == nil && !a.HideHelp {
		a.Commands = append(a.Commands, helpCommand)
		if (HelpFlag != BoolFlag{}) {
			a.appendFlag(HelpFlag)
		}
	}

	//append version/help flags
	if a.EnableBashCompletion {
		a.appendFlag(BashCompletionFlag)
	}

	if !a.HideVersion {
		a.appendFlag(VersionFlag)
	}

	// parse flags
	set := flagSet(a.Name, a.Flags)
	set.SetOutput(ioutil.Discard)
	err = set.Parse(arguments[1:])
	nerr := normalizeFlags(a.Flags, set)
	if nerr != nil {
		fmt.Fprintln(a.Writer, nerr)
		context := NewContext(a, set, set)
		ShowAppHelp(context)
		fmt.Fprintln(a.Writer)
		return nerr
	}
	context := NewContext(a, set, set)

	if err != nil {
		fmt.Fprintf(a.Writer, "Incorrect Usage.\n\n")
		ShowAppHelp(context)
		fmt.Fprintln(a.Writer)
		return err
	}

	if checkCompletions(context) {
		return nil
	}

	if checkHelp(context) {
		return nil
	}

	if checkVersion(context) {
		return nil
	}

	if a.After != nil {
		defer func() {
			// err is always nil here.
			// There is a check to see if it is non-nil
			// just few lines before.
			err = a.After(context)
		}()
	}

	if a.Before != nil {
		err := a.Before(context)
		if err != nil {
			return err
		}
	}

	args := context.Args()
	if args.Present() {
		name := args.First()
		c := a.Command(name)
		if c != nil {
			return c.Run(context)
		}
	}

	// Run default Action
	a.Action(context)
	return nil
}

// Another entry point to the cli app, takes care of passing arguments and error handling
func (a *App) RunAndExitOnError() {
	if err := a.Run(os.Args); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
	// append help to commands
	if len(a.Commands) > 0 {
		if a.Command(helpCommand.Name) == nil && !a.HideHelp {
			a.Commands = append(a.Commands, helpCommand)
			if (HelpFlag != BoolFlag{}) {
				a.appendFlag(HelpFlag)
			}
		}
	}

	// append flags
	if a.EnableBashCompletion {
		a.appendFlag(BashCompletionFlag)
	}

	// parse flags
	set := flagSet(a.Name, a.Flags)
	set.SetOutput(ioutil.Discard)
	err = set.Parse(ctx.Args().Tail())
	nerr := normalizeFlags(a.Flags, set)
	context := NewContext(a, set, ctx.globalSet)

	if nerr != nil {
		fmt.Fprintln(a.Writer, nerr)
		if len(a.Commands) > 0 {
			ShowSubcommandHelp(context)
		} else {
			ShowCommandHelp(ctx, context.Args().First())
		}
		fmt.Fprintln(a.Writer)
		return nerr
	}

	if err != nil {
		fmt.Fprintf(a.Writer, "Incorrect Usage.\n\n")
		ShowSubcommandHelp(context)
		return err
	}

	if checkCompletions(context) {
		return nil
	}

	if len(a.Commands) > 0 {
		if checkSubcommandHelp(context) {
			return nil
		}
	} else {
		if checkCommandHelp(ctx, context.Args().First()) {
			return nil
		}
	}

	if a.After != nil {
		defer func() {
			// err is always nil here.
			// There is a check to see if it is non-nil
			// just few lines before.
			err = a.After(context)
		}()
	}

	if a.Before != nil {
		err := a.Before(context)
		if err != nil {
			return err
		}
	}

	args := context.Args()
	if args.Present() {
		name := args.First()
		c := a.Command(name)
		if c != nil {
			return c.Run(context)
		}
	}

	// Run default Action
	if len(a.Commands) > 0 {
		a.Action(context)
	} else {
		a.Action(ctx)
	}

	return nil
}

// Returns the named command on App. Returns nil if the command does not exist
func (a *App) Command(name string) *Command {
	for _, c := range a.Commands {
		if c.HasName(name) {
			return &c
		}
	}

	return nil
}

func (a *App) hasFlag(flag Flag) bool {
	for _, f := range a.Flags {
		if flag == f {
			return true
		}
	}

	return false
}

func (a *App) appendFlag(flag Flag) {
	if !a.hasFlag(flag) {
		a.Flags = append(a.Flags, flag)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/jinzhu/configor"
	"os"
)

type ip2ProxyConfig struct {
	Enabled  bool `default:false`
	Token    string
	Filename string
}

// Config - all configuration for tool defined here
var Config = struct {
	LogLevel  int    `default:0`
	OutputDir string `default:"output"`
	TOR       struct {
		Enabled bool `default:false`
	}
	IP2Proxy struct {
		Lite      ip2ProxyConfig
		Pro       ip2ProxyConfig
		PrintType bool `default:false`
	}
	MaxMind struct {
		Enabled   bool   `default:false`
		IPVer     int    `default:4`
		Lang      string `default:"ru"`
		TZNames   bool   `default:false`
		Include   string
		Exclude   string
		NoBase64  bool `default:false`
		NoCountry bool `default:false`
	}
	IPGeobase struct {
		Enabled bool `default:false`
	}
}{}

func main() {
	configFile := flag.String("c", "", "Read config from file")
	quiet := flag.Bool("q", false, "Be quiet - skip [OK]")
	veryQuiet := flag.Bool("qq", false, "Be very quiet - show only errors")
	version := flag.Bool("version", false, "Print version information and exit")

	flag.StringVar(&Config.OutputDir, "output", "output", "output directory for files")
	flag.BoolVar(&Config.IPGeobase.Enabled, "ipgeobase", false, "enable ipgeobase generation")
	flag.BoolVar(&Config.TOR.Enabled, "tor", false, "enable tor generation")
	flag.BoolVar(&Config.IP2Proxy.Lite.Enabled, "ip2proxy", false, "enable ip2proxy PX4-LITE generation")
	flag.BoolVar(&Config.IP2Proxy.Pro.Enabled, "ip2proxy-pro", false, "enable ip2proxy PX4 generation")
	flag.StringVar(&Config.IP2Proxy.Lite.Token, "ip2proxy-token", "", "Get token here https://lite.ip2location.com/file-download")
	flag.StringVar(&Config.IP2Proxy.Pro.Token, "ip2proxy-pro-token", "", "ip2proxy download token")
	flag.StringVar(&Config.IP2Proxy.Lite.Filename, "ip2proxy-lite-filename", "", "Filename of already downloaded ip2proxy-lite db")
	flag.StringVar(&Config.IP2Proxy.Pro.Filename, "ip2proxy-pro-filename", "", "Filename of already downloaded ip2proxy db")
	flag.BoolVar(&Config.IP2Proxy.PrintType, "ip2proxy-print-type", false, "Print proxy type in map, instead of `1`")
	flag.BoolVar(&Config.MaxMind.Enabled, "maxmind", false, "enable maxmind generation")
	flag.IntVar(&Config.MaxMind.IPVer, "ipver", 4, "MaxMind ip version (4 or 6)")
	flag.StringVar(&Config.MaxMind.Lang, "lang", "ru", "MaxMind city name language")
	flag.BoolVar(&Config.MaxMind.TZNames, "tznames", false, "MaxMind TZ in names format (for example `Europe/Moscow`)")
	flag.StringVar(&Config.MaxMind.Include, "include", "", "MaxMind output filter: only these countries")
	flag.StringVar(&Config.MaxMind.Exclude, "exclude", "", "MaxMind output filter: except these countries")
	flag.BoolVar(&Config.MaxMind.NoBase64, "nobase64", false, "MaxMind Cities as-is (without base64 encode). DO NOT USE IT IF YOU NOT SURE ABOUT MaxMind encoding")
	flag.BoolVar(&Config.MaxMind.NoCountry, "nocountry", false, "do not add maxmind country maps")
	flag.Parse()
	if *version {
		printMessage("ip2geo", "version "+VERSION, "OK")
		return
	}
	configor.Load(&Config, *configFile)
	if *quiet {
		Config.LogLevel = 1
	}
	if *veryQuiet {
		Config.LogLevel = 2
	}
	if !(Config.IPGeobase.Enabled || Config.TOR.Enabled || Config.MaxMind.Enabled || Config.IP2Proxy.Lite.Enabled || Config.IP2Proxy.Pro.Enabled) {
		// By default, generate all maps
		Config.IPGeobase.Enabled = true
		Config.TOR.Enabled = true
		Config.MaxMind.Enabled = true
		Config.IP2Proxy.Lite.Enabled = Config.IP2Proxy.Lite.Token != "" || Config.IP2Proxy.Lite.Filename != ""
		Config.IP2Proxy.Pro.Enabled = Config.IP2Proxy.Pro.Token != "" || Config.IP2Proxy.Pro.Filename != ""
	}

	os.MkdirAll(Config.OutputDir, 0755)
	if Config.LogLevel < 2 {
		printMessage(" ", "Use output directory", Config.OutputDir)
	}
	goroutinesCount := 0
	errorChannel := make(chan Error)
	if Config.IPGeobase.Enabled {
		goroutinesCount++
		i := IPGeobase{
			OutputDir:  Config.OutputDir,
			ErrorsChan: errorChannel,
		}
		go Generate(&i)
	}

	if Config.TOR.Enabled {
		goroutinesCount++
		t := Tor{
			OutputDir:  Config.OutputDir,
			ErrorsChan: errorChannel,
		}
		go t.Generate()
	}

	if Config.MaxMind.Enabled {
		goroutinesCount++
		m := MaxMind{
			OutputDir:  Config.OutputDir,
			ErrorsChan: errorChannel,
			lang:       Config.MaxMind.Lang,
			ipver:      Config.MaxMind.IPVer,
			tzNames:    Config.MaxMind.TZNames,
			include:    Config.MaxMind.Include,
			exclude:    Config.MaxMind.Exclude,
			noBase64:   Config.MaxMind.NoBase64,
			noCountry:  Config.MaxMind.NoCountry,
		}
		go Generate(&m)
	}

	for t, ip2ProxyType := range [2]ip2ProxyConfig{Config.IP2Proxy.Lite, Config.IP2Proxy.Pro} {
		if ip2ProxyType.Enabled {
			goroutinesCount++
			var name string
			if t == 0 {
				name = "ip2proxyLite"
			} else {
				name = "ip2proxyPro"

			}
			o := ip2proxy{
				Name:       name,
				Token:      ip2ProxyType.Token,
				Filename:   ip2ProxyType.Filename,
				ErrorsChan: errorChannel,
				OutputDir:  Config.OutputDir,
				PrintType:  Config.IP2Proxy.PrintType,
			}
			go o.Get()
		}
	}

	for i := 0; i < goroutinesCount; i++ {
		err := <-errorChannel
		if err.err != nil {
			printMessage(err.Module, err.Action+": "+err.err.Error(), "FAIL")
			os.Exit(1)
		}
	}
	if Config.LogLevel < 1 {
		printMessage(" ", "Generation done", "OK")
	}
}
<commit_msg>Fix go vet<commit_after>package main

import (
	"flag"
	"github.com/jinzhu/configor"
	"os"
)

type ip2ProxyConfig struct {
	Enabled  bool `default:"false"`
	Token    string
	Filename string
}

// Config - all configuration for tool defined here
var Config = struct {
	LogLevel  int    `default:"0"`
	OutputDir string `default:"output"`
	TOR       struct {
		Enabled bool `default:"false"`
	}
	IP2Proxy struct {
		Lite      ip2ProxyConfig
		Pro       ip2ProxyConfig
		PrintType bool `default:"false"`
	}
	MaxMind struct {
		Enabled   bool   `default:"false"`
		IPVer     int    `default:"4"`
		Lang      string `default:"ru"`
		TZNames   bool   `default:"false"`
		Include   string
		Exclude   string
		NoBase64  bool `default:"false"`
		NoCountry bool `default:"false"`
	}
	IPGeobase struct {
		Enabled bool `default:"false"`
	}
}{}

func main() {
	configFile := flag.String("c", "", "Read config from file")
	quiet := flag.Bool("q", false, "Be quiet - skip [OK]")
	veryQuiet := flag.Bool("qq", false, "Be very quiet - show only errors")
	version := flag.Bool("version", false, "Print version information and exit")

	flag.StringVar(&Config.OutputDir, "output", "output", "output directory for files")
	flag.BoolVar(&Config.IPGeobase.Enabled, "ipgeobase", false, "enable ipgeobase generation")
	flag.BoolVar(&Config.TOR.Enabled, "tor", false, "enable tor generation")
	flag.BoolVar(&Config.IP2Proxy.Lite.Enabled, "ip2proxy", false, "enable ip2proxy PX4-LITE generation")
	flag.BoolVar(&Config.IP2Proxy.Pro.Enabled, "ip2proxy-pro", false, "enable ip2proxy PX4 generation")
	flag.StringVar(&Config.IP2Proxy.Lite.Token, "ip2proxy-token", "", "Get token here https://lite.ip2location.com/file-download")
	flag.StringVar(&Config.IP2Proxy.Pro.Token, "ip2proxy-pro-token", "", "ip2proxy download token")
	flag.StringVar(&Config.IP2Proxy.Lite.Filename, "ip2proxy-lite-filename", "", "Filename of already downloaded ip2proxy-lite db")
	flag.StringVar(&Config.IP2Proxy.Pro.Filename, "ip2proxy-pro-filename", "", "Filename of already downloaded ip2proxy db")
	flag.BoolVar(&Config.IP2Proxy.PrintType, "ip2proxy-print-type", false, "Print proxy type in map, instead of `1`")
	flag.BoolVar(&Config.MaxMind.Enabled, "maxmind", false, "enable maxmind generation")
	flag.IntVar(&Config.MaxMind.IPVer, "ipver", 4, "MaxMind ip version (4 or 6)")
	flag.StringVar(&Config.MaxMind.Lang, "lang", "ru", "MaxMind city name language")
	flag.BoolVar(&Config.MaxMind.TZNames, "tznames", false, "MaxMind TZ in names format (for example `Europe/Moscow`)")
	flag.StringVar(&Config.MaxMind.Include, "include", "", "MaxMind output filter: only these countries")
	flag.StringVar(&Config.MaxMind.Exclude, "exclude", "", "MaxMind output filter: except these countries")
	flag.BoolVar(&Config.MaxMind.NoBase64, "nobase64", false, "MaxMind Cities as-is (without base64 encode). DO NOT USE IT IF YOU NOT SURE ABOUT MaxMind encoding")
	flag.BoolVar(&Config.MaxMind.NoCountry, "nocountry", false, "do not add maxmind country maps")
	flag.Parse()
	if *version {
		printMessage("ip2geo", "version "+VERSION, "OK")
		return
	}
	configor.Load(&Config, *configFile)
	if *quiet {
		Config.LogLevel = 1
	}
	if *veryQuiet {
		Config.LogLevel = 2
	}
	if !(Config.IPGeobase.Enabled || Config.TOR.Enabled || Config.MaxMind.Enabled || Config.IP2Proxy.Lite.Enabled || Config.IP2Proxy.Pro.Enabled) {
		// By default, generate all maps
		Config.IPGeobase.Enabled = true
		Config.TOR.Enabled = true
		Config.MaxMind.Enabled = true
		Config.IP2Proxy.Lite.Enabled = Config.IP2Proxy.Lite.Token != "" || Config.IP2Proxy.Lite.Filename != ""
		Config.IP2Proxy.Pro.Enabled = Config.IP2Proxy.Pro.Token != "" || Config.IP2Proxy.Pro.Filename != ""
	}

	os.MkdirAll(Config.OutputDir, 0755)
	if Config.LogLevel < 2 {
		printMessage(" ", "Use output directory", Config.OutputDir)
	}
	goroutinesCount := 0
	errorChannel := make(chan Error)
	if Config.IPGeobase.Enabled {
		goroutinesCount++
		i := IPGeobase{
			OutputDir:  Config.OutputDir,
			ErrorsChan: errorChannel,
		}
		go Generate(&i)
	}

	if Config.TOR.Enabled {
		goroutinesCount++
		t := Tor{
			OutputDir:  Config.OutputDir,
			ErrorsChan: errorChannel,
		}
		go t.Generate()
	}

	if Config.MaxMind.Enabled {
		goroutinesCount++
		m := MaxMind{
			OutputDir:  Config.OutputDir,
			ErrorsChan: errorChannel,
			lang:       Config.MaxMind.Lang,
			ipver:      Config.MaxMind.IPVer,
			tzNames:    Config.MaxMind.TZNames,
			include:    Config.MaxMind.Include,
			exclude:    Config.MaxMind.Exclude,
			noBase64:   Config.MaxMind.NoBase64,
			noCountry:  Config.MaxMind.NoCountry,
		}
		go Generate(&m)
	}

	for t, ip2ProxyType := range [2]ip2ProxyConfig{Config.IP2Proxy.Lite, Config.IP2Proxy.Pro} {
		if ip2ProxyType.Enabled {
			goroutinesCount++
			var name string
			if t == 0 {
				name = "ip2proxyLite"
			} else {
				name = "ip2proxyPro"

			}
			o := ip2proxy{
				Name:       name,
				Token:      ip2ProxyType.Token,
				Filename:   ip2ProxyType.Filename,
				ErrorsChan: errorChannel,
				OutputDir:  Config.OutputDir,
				PrintType:  Config.IP2Proxy.PrintType,
			}
			go o.Get()
		}
	}

	for i := 0; i < goroutinesCount; i++ {
		err := <-errorChannel
		if err.err != nil {
			printMessage(err.Module, err.Action+": "+err.err.Error(), "FAIL")
			os.Exit(1)
		}
	}
	if Config.LogLevel < 1 {
		printMessage(" ", "Generation done", "OK")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"os"

	"fmt"
	"strconv"
	"time"

	"github.com/Financial-Times/base-ft-rw-app-go/baseftrwapp"
	"github.com/Financial-Times/go-fthealth/v1a"
	"github.com/Financial-Times/http-handlers-go/httphandlers"
	"github.com/Financial-Times/public-people-api/people"
	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"github.com/jawher/mow.cli"
	"github.com/jmcvetta/neoism"
	"github.com/rcrowley/go-metrics"
)

func main() {
	log.Infof("Application starting with args %s", os.Args)
	app := cli.App("public-people-api-neo4j", "A public RESTful API for accessing People in neo4j")
	neoURL := app.StringOpt("neo-url", "http://localhost:7474/db/data", "neo4j endpoint URL")
	// neoURL := app.StringOpt("neo-url", "http://ftper60304-law1a-eu-t:8080/db/data", "neo4j endpoint URL")
	port := app.StringOpt("port", "8080", "Port to listen on")
	env := app.StringOpt("env", "local", "environment this app is running in")
	graphiteTCPAddress := app.StringOpt("graphiteTCPAddress", "",
		"Graphite TCP address, e.g. graphite.ft.com:2003. Leave as default if you do NOT want to output to graphite (e.g. if running locally)")
	graphitePrefix := app.StringOpt("graphitePrefix", "",
		"Prefix to use. Should start with content, include the environment, and the host name. e.g. content.test.public.people.api.ftaps59382-law1a-eu-t")
	logMetrics := app.BoolOpt("logMetrics", false, "Whether to log metrics. Set to true if running locally and you want metrics output")
	cacheDuration := app.StringOpt("cache-duration", "1h", "Duration Get requests should be cached for. e.g. 2h45m would set the max-age value to '7440' seconds")

	app.Action = func() {
		baseftrwapp.OutputMetricsIfRequired(*graphiteTCPAddress, *graphitePrefix, *logMetrics)

		if *env != "local" {
			f, err := os.OpenFile("/var/log/apps/public-people-api-go-app.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0755)
			if err == nil {
				log.SetOutput(f)
				log.SetFormatter(&log.TextFormatter{})
			} else {
				log.Fatalf("Failed to initialise log file, %v", err)
			}

			defer f.Close()
		}

		log.Infof("public-people-api will listen on port: %s, connecting to: %s", *port, *neoURL)
		runServer(*neoURL, *port, *cacheDuration, *env)
	}
	log.SetLevel(log.InfoLevel)
	log.Infof("Application started with args %s", os.Args)
	app.Run(os.Args)
}

func runServer(neoURL string, port string, cacheDuration string, env string) {

	if duration, durationErr := time.ParseDuration(cacheDuration); durationErr != nil {
		log.Fatalf("Failed to parse cache duration string, %v", durationErr)
	} else {
		people.CacheControlHeader = fmt.Sprintf("max-age=%s, public", strconv.FormatFloat(duration.Seconds(), 'f', 0, 64))
	}

	db, err := neoism.Connect(neoURL)
	db.Session.Client = &http.Client{Transport: &http.Transport{MaxIdleConnsPerHost: 100}}
	if err != nil {
		log.Fatalf("Error connecting to neo4j %s", err)
	}

	people.PeopleDriver = people.NewCypherDriver(db, env)

	servicesRouter := mux.NewRouter()

	// Healthchecks and standards first
	servicesRouter.HandleFunc("/__health", v1a.Handler("PeopleReadWriteNeo4j Healthchecks",
		"Checks for accessing neo4j", people.HealthCheck()))
	servicesRouter.HandleFunc("/ping", people.Ping)
	servicesRouter.HandleFunc("/__ping", people.Ping)

	// Then API specific ones:
	servicesRouter.HandleFunc("/people/{uuid}", people.GetPerson).Methods("GET")
	servicesRouter.HandleFunc("/people/{uuid}", people.MethodNotAllowedHandler)

	var monitoringRouter http.Handler = servicesRouter
	monitoringRouter = httphandlers.TransactionAwareRequestLoggingHandler(log.StandardLogger(), monitoringRouter)
	monitoringRouter = httphandlers.HTTPMetricsHandler(metrics.DefaultRegistry, monitoringRouter)

	// The following endpoints should not be monitored or logged (varnish calls one of these every second, depending on config)
	// The top one of these build info endpoints feels more correct, but the lower one matches what we have in Dropwizard,
	// so it's what apps expect currently same as ping, the content of build-info needs more definition
	http.HandleFunc("/__build-info", people.BuildInfoHandler)
	http.HandleFunc("/build-info", people.BuildInfoHandler)
	http.HandleFunc("/__gtg", people.GoodToGo)
	http.Handle("/", monitoringRouter)

	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatalf("Unable to start server: %v", err)
	}
}
<commit_msg>RDisabled colour in logs<commit_after>package main

import (
	"net/http"
	"os"

	"fmt"
	"strconv"
	"time"

	"github.com/Financial-Times/base-ft-rw-app-go/baseftrwapp"
	"github.com/Financial-Times/go-fthealth/v1a"
	"github.com/Financial-Times/http-handlers-go/httphandlers"
	"github.com/Financial-Times/public-people-api/people"
	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"github.com/jawher/mow.cli"
	"github.com/jmcvetta/neoism"
	"github.com/rcrowley/go-metrics"
)

func main() {
	log.Infof("Application starting with args %s", os.Args)
	app := cli.App("public-people-api-neo4j", "A public RESTful API for accessing People in neo4j")
	neoURL := app.StringOpt("neo-url", "http://localhost:7474/db/data", "neo4j endpoint URL")
	// neoURL := app.StringOpt("neo-url", "http://ftper60304-law1a-eu-t:8080/db/data", "neo4j endpoint URL")
	port := app.StringOpt("port", "8080", "Port to listen on")
	env := app.StringOpt("env", "local", "environment this app is running in")
	graphiteTCPAddress := app.StringOpt("graphiteTCPAddress", "",
		"Graphite TCP address, e.g. graphite.ft.com:2003. Leave as default if you do NOT want to output to graphite (e.g. if running locally)")
	graphitePrefix := app.StringOpt("graphitePrefix", "",
		"Prefix to use. Should start with content, include the environment, and the host name. e.g. content.test.public.people.api.ftaps59382-law1a-eu-t")
	logMetrics := app.BoolOpt("logMetrics", false, "Whether to log metrics. Set to true if running locally and you want metrics output")
	cacheDuration := app.StringOpt("cache-duration", "1h", "Duration Get requests should be cached for. e.g. 2h45m would set the max-age value to '7440' seconds")

	app.Action = func() {
		baseftrwapp.OutputMetricsIfRequired(*graphiteTCPAddress, *graphitePrefix, *logMetrics)

		if *env != "local" {
			f, err := os.OpenFile("/var/log/apps/public-people-api-go-app.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0755)
			if err == nil {
				log.SetOutput(f)
				log.SetFormatter(&log.TextFormatter{DisableColors: true})
			} else {
				log.Fatalf("Failed to initialise log file, %v", err)
			}

			defer f.Close()
		}

		log.Infof("public-people-api will listen on port: %s, connecting to: %s", *port, *neoURL)
		runServer(*neoURL, *port, *cacheDuration, *env)
	}
	log.SetLevel(log.InfoLevel)
	log.Infof("Application started with args %s", os.Args)
	app.Run(os.Args)
}

func runServer(neoURL string, port string, cacheDuration string, env string) {

	if duration, durationErr := time.ParseDuration(cacheDuration); durationErr != nil {
		log.Fatalf("Failed to parse cache duration string, %v", durationErr)
	} else {
		people.CacheControlHeader = fmt.Sprintf("max-age=%s, public", strconv.FormatFloat(duration.Seconds(), 'f', 0, 64))
	}

	db, err := neoism.Connect(neoURL)
	db.Session.Client = &http.Client{Transport: &http.Transport{MaxIdleConnsPerHost: 100}}
	if err != nil {
		log.Fatalf("Error connecting to neo4j %s", err)
	}

	people.PeopleDriver = people.NewCypherDriver(db, env)

	servicesRouter := mux.NewRouter()

	// Healthchecks and standards first
	servicesRouter.HandleFunc("/__health", v1a.Handler("PeopleReadWriteNeo4j Healthchecks",
		"Checks for accessing neo4j", people.HealthCheck()))
	servicesRouter.HandleFunc("/ping", people.Ping)
	servicesRouter.HandleFunc("/__ping", people.Ping)

	// Then API specific ones:
	servicesRouter.HandleFunc("/people/{uuid}", people.GetPerson).Methods("GET")
	servicesRouter.HandleFunc("/people/{uuid}", people.MethodNotAllowedHandler)

	var monitoringRouter http.Handler = servicesRouter
	monitoringRouter = httphandlers.TransactionAwareRequestLoggingHandler(log.StandardLogger(), monitoringRouter)
	monitoringRouter = httphandlers.HTTPMetricsHandler(metrics.DefaultRegistry, monitoringRouter)

	// The following endpoints should not be monitored or logged (varnish calls one of these every second, depending on config)
	// The top one of these build info endpoints feels more correct, but the lower one matches what we have in Dropwizard,
	// so it's what apps expect currently same as ping, the content of build-info needs more definition
	http.HandleFunc("/__build-info", people.BuildInfoHandler)
	http.HandleFunc("/build-info", people.BuildInfoHandler)
	http.HandleFunc("/__gtg", people.GoodToGo)
	http.Handle("/", monitoringRouter)

	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatalf("Unable to start server: %v", err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package web

import (
	"errors"
	"net/http"
	"path"
	"path/filepath"
	"strings"

	"github.com/issue9/logs"
	"github.com/issue9/middleware/compress"
	"github.com/issue9/mux"
	charset "golang.org/x/text/encoding"

	"github.com/issue9/web/context"
	"github.com/issue9/web/encoding"
	"github.com/issue9/web/internal/config"
	"github.com/issue9/web/internal/server"
)

const (
	logsFilename   = "logs.xml" // 日志配置文件的文件名。
	configFilename = "web.yaml" // 配置文件的文件名。
)

type app struct {
	configDir string
	config    *config.Config
	modules   []*Module

	middleware Middleware // 应用于全局所有路由项的中间件
	mux        *mux.Mux
	router     *mux.Prefix

	// 根据配置文件，获取相应的输出编码和字符集。
	outputEncoding encoding.MarshalFunc
	outputCharset  charset.Encoding
}

func newApp(configDir string, m Middleware) (*app, error) {
	dir, err := filepath.Abs(configDir)
	if err != nil {
		return nil, err
	}

	app := &app{
		configDir:  dir,
		middleware: m,
		modules:    make([]*Module, 0, 100),
	}

	if err := logs.InitFromXMLFile(app.File(logsFilename)); err != nil {
		return nil, err
	}

	if err = app.loadConfig(); err != nil {
		return nil, err
	}

	return app, nil
}

func (app *app) loadConfig() error {
	conf, err := config.Load(app.File(configFilename))
	if err != nil {
		return err
	}

	app.config = conf
	app.mux = mux.New(conf.DisableOptions, false, nil, nil)
	app.router = app.mux.Prefix(conf.Root)

	app.outputEncoding = encoding.Marshal(conf.OutputEncoding)
	if app.outputEncoding == nil {
		return errors.New("未找到 outputEncoding")
	}

	app.outputCharset = encoding.Charset(conf.OutputCharset)
	if app.outputCharset == nil {
		return errors.New("未找到 outputCharset")
	}

	return nil
}

// File 获取配置目录下的文件名
func (app *app) File(path ...string) string {
	paths := make([]string, 0, len(path)+1)
	paths = append(paths, app.configDir)
	return filepath.Join(append(paths, path...)...)
}

// Run 加载各个模块的数据，运行路由，执行监听程序。
//
// 必须得保证在调用 Run() 时，logs 包的所有功能是可用的，
// 之后的好多操作，都会将日志输出 logs 中的相关通道中。
func (app *app) Run() error {
	if err := app.initDependency(); err != nil {
		return err
	}

	// 静态文件路由，在其它路由构建之前调用
	for url, dir := range app.config.Static {
		pattern := path.Join(app.config.Root, url+"{path}")
		fs := http.FileServer(http.Dir(dir))
		app.router.Get(pattern, http.StripPrefix(url, compress.New(fs, logs.ERROR())))
	}

	if app.middleware != nil {
		return server.Listen(app.middleware(app.mux), app.config)
	}
	return server.Listen(app.mux, app.config)
}

// URL 构建一条基于 app.url 的完整 URL
func (app *app) URL(path string) string {
	if len(path) == 0 {
		return app.config.URL
	}

	if path[0] != '/' {
		path = "/" + path
	}
	return app.config.URL + path
}

// NewContext 根据当前配置，生成 context.Context 对象，若是出错则返回 nil
//
// encodingName 指定出输出时的编码方式，此编码名称必须已经通过 AddMarshal 添加；
// charsetName 指定输出时的字符集，此字符集名称必须已经通过 AddCharset 添加；
// strict 若为 true，则会验证用户的 Accept 报头是否接受 encodingName 编码。
// 输入时的编码与字符集信息从报头 Content-Type 中获取，若未指定字符集，则默认为 utf-8
func (app *app) NewContext(w http.ResponseWriter, r *http.Request) *context.Context {
	encName, charsetName := encoding.ParseContentType(r.Header.Get("Content-Type"))

	unmarshal := encoding.Unmarshal(encName)
	if unmarshal == nil {
		context.RenderStatus(w, http.StatusUnsupportedMediaType)
		return nil
	}

	inputCharset := encoding.Charset(charsetName)
	if inputCharset == nil {
		context.RenderStatus(w, http.StatusUnsupportedMediaType)
		return nil
	}

	if app.config.Strict {
		accept := r.Header.Get("Accept")
		if !strings.Contains(accept, app.config.OutputEncoding) && !strings.Contains(accept, "*/*") {
			context.RenderStatus(w, http.StatusNotAcceptable)
			return nil
		}
	}

	return &context.Context{
		Response:           w,
		Request:            r,
		OutputEncoding:     app.outputEncoding,
		OutputEncodingName: app.config.OutputEncoding,
		InputEncoding:      unmarshal,
		InputCharset:       inputCharset,
		OutputCharset:      app.outputCharset,
		OutputCharsetName:  app.config.OutputCharset,
	}
}
<commit_msg>添加对 accept-charset 报头的检测<commit_after>// Copyright 2017 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package web

import (
	"errors"
	"net/http"
	"path"
	"path/filepath"
	"strings"

	"github.com/issue9/logs"
	"github.com/issue9/middleware/compress"
	"github.com/issue9/mux"
	charset "golang.org/x/text/encoding"

	"github.com/issue9/web/context"
	"github.com/issue9/web/encoding"
	"github.com/issue9/web/internal/config"
	"github.com/issue9/web/internal/server"
)

const (
	logsFilename   = "logs.xml" // 日志配置文件的文件名。
	configFilename = "web.yaml" // 配置文件的文件名。
)

type app struct {
	configDir string
	config    *config.Config
	modules   []*Module

	middleware Middleware // 应用于全局所有路由项的中间件
	mux        *mux.Mux
	router     *mux.Prefix

	// 根据配置文件，获取相应的输出编码和字符集。
	outputEncoding encoding.MarshalFunc
	outputCharset  charset.Encoding
}

func newApp(configDir string, m Middleware) (*app, error) {
	dir, err := filepath.Abs(configDir)
	if err != nil {
		return nil, err
	}

	app := &app{
		configDir:  dir,
		middleware: m,
		modules:    make([]*Module, 0, 100),
	}

	if err := logs.InitFromXMLFile(app.File(logsFilename)); err != nil {
		return nil, err
	}

	if err = app.loadConfig(); err != nil {
		return nil, err
	}

	return app, nil
}

func (app *app) loadConfig() error {
	conf, err := config.Load(app.File(configFilename))
	if err != nil {
		return err
	}

	app.config = conf
	app.mux = mux.New(conf.DisableOptions, false, nil, nil)
	app.router = app.mux.Prefix(conf.Root)

	app.outputEncoding = encoding.Marshal(conf.OutputEncoding)
	if app.outputEncoding == nil {
		return errors.New("未找到 outputEncoding")
	}

	app.outputCharset = encoding.Charset(conf.OutputCharset)
	if app.outputCharset == nil {
		return errors.New("未找到 outputCharset")
	}

	return nil
}

// File 获取配置目录下的文件名
func (app *app) File(path ...string) string {
	paths := make([]string, 0, len(path)+1)
	paths = append(paths, app.configDir)
	return filepath.Join(append(paths, path...)...)
}

// Run 加载各个模块的数据，运行路由，执行监听程序。
//
// 必须得保证在调用 Run() 时，logs 包的所有功能是可用的，
// 之后的好多操作，都会将日志输出 logs 中的相关通道中。
func (app *app) Run() error {
	if err := app.initDependency(); err != nil {
		return err
	}

	// 静态文件路由，在其它路由构建之前调用
	for url, dir := range app.config.Static {
		pattern := path.Join(app.config.Root, url+"{path}")
		fs := http.FileServer(http.Dir(dir))
		app.router.Get(pattern, http.StripPrefix(url, compress.New(fs, logs.ERROR())))
	}

	if app.middleware != nil {
		return server.Listen(app.middleware(app.mux), app.config)
	}
	return server.Listen(app.mux, app.config)
}

// URL 构建一条基于 app.url 的完整 URL
func (app *app) URL(path string) string {
	if len(path) == 0 {
		return app.config.URL
	}

	if path[0] != '/' {
		path = "/" + path
	}
	return app.config.URL + path
}

// NewContext 根据当前配置，生成 context.Context 对象，若是出错则返回 nil
//
// encodingName 指定出输出时的编码方式，此编码名称必须已经通过 AddMarshal 添加；
// charsetName 指定输出时的字符集，此字符集名称必须已经通过 AddCharset 添加；
// strict 若为 true，则会验证用户的 Accept 报头是否接受 encodingName 编码。
// 输入时的编码与字符集信息从报头 Content-Type 中获取，若未指定字符集，则默认为 utf-8
func (app *app) NewContext(w http.ResponseWriter, r *http.Request) *context.Context {
	encName, charsetName := encoding.ParseContentType(r.Header.Get("Content-Type"))

	unmarshal := encoding.Unmarshal(encName)
	if unmarshal == nil {
		context.RenderStatus(w, http.StatusUnsupportedMediaType)
		return nil
	}

	inputCharset := encoding.Charset(charsetName)
	if inputCharset == nil {
		context.RenderStatus(w, http.StatusUnsupportedMediaType)
		return nil
	}

	if app.config.Strict {
		accept := r.Header.Get("Accept")
		if !strings.Contains(accept, app.config.OutputEncoding) && !strings.Contains(accept, "*/*") {
			context.RenderStatus(w, http.StatusNotAcceptable)
			return nil
		}

		acceptCharset := r.Header.Get("Accept-Charset")
		if !strings.Contains(acceptCharset, app.config.OutputCharset) && !strings.Contains(acceptCharset, "*") {
			context.RenderStatus(w, http.StatusNotAcceptable)
			return nil
		}
	}

	return &context.Context{
		Response:           w,
		Request:            r,
		OutputEncoding:     app.outputEncoding,
		OutputEncodingName: app.config.OutputEncoding,
		InputEncoding:      unmarshal,
		InputCharset:       inputCharset,
		OutputCharset:      app.outputCharset,
		OutputCharsetName:  app.config.OutputCharset,
	}
}
<|endoftext|>