<commit_before>package main

import (
	"bytes"
	"encoding/xml"
	"errors"
	"flag"
	"fmt"
	"io"
	"math"
	"net/http"
	"os/exec"
	"strconv"
	"strings"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
)

const (
	namespace = "passenger_nginx"
)

var (
	Version = "0.0.0"

	timeoutErr = errors.New("passenger-status command timed out")

	processIdentifiers = make(map[string]int)
)

// Exporter collects metrics from a passenger-nginx integration.
type Exporter struct {
	// binary file path for querying passenger state.
	cmd  string
	args []string

	// Passenger command timeout.
	timeout time.Duration

	// Passenger metrics.
	up                  *prometheus.Desc
	version             *prometheus.Desc
	toplevelQueue       *prometheus.Desc
	maxProcessCount     *prometheus.Desc
	currentProcessCount *prometheus.Desc
	appCount            *prometheus.Desc

	// App metrics.
	appQueue         *prometheus.Desc
	appProcsSpawning *prometheus.Desc

	// Process metrics.
	requestsProcessed *prometheus.Desc
	procUptime        *prometheus.Desc
	procMemory        *prometheus.Desc
}

func NewExporter(cmd string, timeout time.Duration) *Exporter {
	cmdComponents := strings.Split(cmd, " ")

	return &Exporter{
		cmd:     cmdComponents[0],
		args:    cmdComponents[1:],
		timeout: timeout,
		up: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "up"),
			"Could passenger status be queried.",
			nil,
			nil,
		),
		version: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "version"),
			"Version of passenger",
			[]string{"version"},
			nil,
		),
		toplevelQueue: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "top_level_queue"),
			"Number of requests in the top-level queue.",
			nil,
			nil,
		),
		maxProcessCount: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "max_processes"),
			"Configured maximum number of processes.",
			nil,
			nil,
		),
		currentProcessCount: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "current_processes"),
			"Current number of processes.",
			nil,
			nil,
		),
		appCount: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "app_count"),
			"Number of apps.",
			nil,
			nil,
		),
		appQueue: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "app_queue"),
			"Number of requests in app process queues.",
			[]string{"name"},
			nil,
		),
		appProcsSpawning: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "app_procs_spawning"),
			"Number of processes spawning.",
			[]string{"name"},
			nil,
		),
		requestsProcessed: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "requests_processed_total"),
			"Number of processes served by a process.",
			[]string{"name", "id"},
			nil,
		),
		procUptime: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "proc_uptime"),
			"Number of seconds since processor started.",
			[]string{"name", "id", "codeRevision"},
			nil,
		),
		procMemory: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "proc_memory"),
			"Memory consumed by a process",
			[]string{"name", "id"},
			nil,
		),
	}
}

// Collect fetches the statistics from the configured passenger frontend, and
// delivers them as Prometheus metrics. It implements prometheus.Collector.
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
	info, err := e.status()
	if err != nil {
		ch <- prometheus.MustNewConstMetric(e.up, prometheus.GaugeValue, 0)
		log.Errorf("failed to collect status from passenger: %s", err)
		return
	}
	ch <- prometheus.MustNewConstMetric(e.up, prometheus.GaugeValue, 1)

	ch <- prometheus.MustNewConstMetric(e.version, prometheus.GaugeValue, 1, info.PassengerVersion)

	ch <- prometheus.MustNewConstMetric(e.toplevelQueue, prometheus.GaugeValue, parseFloat(info.TopLevelRequestsInQueue))
	ch <- prometheus.MustNewConstMetric(e.maxProcessCount, prometheus.GaugeValue, parseFloat(info.MaxProcessCount))
	ch <- prometheus.MustNewConstMetric(e.currentProcessCount, prometheus.GaugeValue, parseFloat(info.CurrentProcessCount))
	ch <- prometheus.MustNewConstMetric(e.appCount, prometheus.GaugeValue, parseFloat(info.AppCount))

	for _, sg := range info.SuperGroups {
		ch <- prometheus.MustNewConstMetric(e.appQueue, prometheus.GaugeValue, parseFloat(sg.RequestsInQueue), sg.Name)
		ch <- prometheus.MustNewConstMetric(e.appProcsSpawning, prometheus.GaugeValue, parseFloat(sg.Group.ProcessesSpawning), sg.Name)

		// Update process identifiers map.
		processIdentifiers = updateProcesses(processIdentifiers, sg.Group.Processes)
		for _, proc := range sg.Group.Processes {
			if bucketID, ok := processIdentifiers[proc.PID]; ok {
				ch <- prometheus.MustNewConstMetric(e.procMemory, prometheus.GaugeValue, parseFloat(proc.RealMemory), sg.Name, strconv.Itoa(bucketID))
				ch <- prometheus.MustNewConstMetric(e.requestsProcessed, prometheus.CounterValue, parseFloat(proc.RequestsProcessed), sg.Name, strconv.Itoa(bucketID))

				if uptime, err := parsePassengerInterval(proc.Uptime); err == nil {
					ch <- prometheus.MustNewConstMetric(e.procUptime, prometheus.GaugeValue, float64(uptime),
						sg.Name, strconv.Itoa(bucketID), proc.CodeRevision,
					)
				}
			}
		}
	}

}

func (e *Exporter) status() (*Info, error) {
	var (
		out bytes.Buffer
		cmd = exec.Command(e.cmd, e.args...)
	)
	cmd.Stdout = &out

	err := cmd.Start()
	if err != nil {
		return nil, err
	}

	errc := make(chan error)
	go func(cmd *exec.Cmd, c chan<- error) {
		c <- cmd.Wait()
	}(cmd, errc)

	select {
	case err := <-errc:
		if err != nil {
			return nil, err
		}
	case <-time.After(e.timeout):
		return nil, timeoutErr
	}

	return parseOutput(&out)
}

func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
	ch <- e.up
	ch <- e.version
	ch <- e.toplevelQueue
	ch <- e.maxProcessCount
	ch <- e.currentProcessCount
	ch <- e.appCount
	ch <- e.appQueue
	ch <- e.appProcsSpawning
	ch <- e.requestsProcessed
	ch <- e.procUptime
	ch <- e.procMemory
}

func parseOutput(r io.Reader) (*Info, error) {
	var info Info
	err := xml.NewDecoder(r).Decode(&info)
	if err != nil {
		return nil, err
	}
	return &info, nil
}

func parseFloat(val string) float64 {
	v, err := strconv.ParseFloat(val, 64)
	if err != nil {
		log.Errorf("failed to parse %s: %v", val, err)
		v = math.NaN()
	}
	return v
}

// updateProcesses updates the global map from process id:exporter id. Process
// TTLs cause new processes to be created on a user-defined cycle. When a new
// process replaces an old process, the new process's statistics will be
// bucketed with those of the process it replaced.
// Processes are restarted at an offset, user-defined interval. The
// restarted process is appended to the end of the status output.  For
// maintaining consistent process identifiers between process starts,
// pids are mapped to an identifier based on process count. When a new
// process/pid appears, it is mapped to either the first empty place
// within the global map storing process identifiers, or mapped to
// pid:id pair in the map.
func updateProcesses(old map[string]int, processes []Process) map[string]int {
	var (
		updated = make(map[string]int)
		found   = make([]string, len(old))
		missing []string
	)

	for _, p := range processes {
		if id, ok := old[p.PID]; ok {
			found[id] = p.PID
			// id also serves as an index.
			// By putting the pid at a certain index, we can loop
			// through the array to find the values that are the 0
			// value (empty string).
			// If index i has the empty value, then it was never
			// updated, so we slot the first of the missingPIDs
			// into that position. Passenger-status orders output
			// by pid, increasing. We can then assume that
			// unclaimed pid positions map in order to the missing
			// pids.
		} else {
			missing = append(missing, p.PID)
		}
	}

	j := 0
	for i, pid := range found {
		if pid == "" {
			if j >= len(missing) {
				continue
			}
			pid = missing[j]
			j++
		}
		updated[pid] = i
	}

	// If the number of elements in missing iterated through is less
	// than len(missing), there are new elements to be added to the map.
	// Unused pids from the last collection are not copied from old to
	// updated, thereby cleaning the return value of unused PIDs.
	if j < len(missing) {
		count := len(found)
		for i, pid := range missing[j:] {
			updated[pid] = count + i
		}
	}

	return updated
}

// parsePassengerInterval formats and parses the default Passenger time output.
func parsePassengerInterval(val string) (time.Duration, error) {
	return time.ParseDuration(strings.Replace(val, " ", "", -1))
}

func main() {
	var (
		cmd           = flag.String("passenger.command", "passenger-status --show=xml", "Passenger command for querying passenger status.")
		timeout       = flag.Duration("passenger.command.timeout", 500*time.Millisecond, "Timeout for passenger.command.")
		metricsPath   = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
		listenAddress = flag.String("web.listen-address", ":9106", "Address to listen on for web interface and telemetry.")
	)
	flag.Parse()

	prometheus.MustRegister(prometheus.NewProcessCollectorPIDFn(
		func() (int, error) {
			var (
				out bytes.Buffer
				cmd = exec.Command("pidof", `"Passenger core"`)
			)
			cmd.Stdout = &out

			if err := cmd.Run(); err != nil {
				return 0, fmt.Errorf("error running pid command: %s", cmd.Args)
			}
			return strconv.Atoi(out.String())
		},
		namespace),
	)

	prometheus.MustRegister(NewExporter(*cmd, *timeout))

	http.Handle(*metricsPath, prometheus.Handler())

	log.Infof("starting passenger_exporter_nginx v%s at %s", Version, *listenAddress)
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}
<commit_msg>Use buffered errc<commit_after>package main

import (
	"bytes"
	"encoding/xml"
	"errors"
	"flag"
	"fmt"
	"io"
	"math"
	"net/http"
	"os/exec"
	"strconv"
	"strings"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
)

const (
	namespace = "passenger_nginx"
)

var (
	Version = "0.0.0"

	timeoutErr = errors.New("passenger-status command timed out")

	processIdentifiers = make(map[string]int)
)

// Exporter collects metrics from a passenger-nginx integration.
type Exporter struct {
	// binary file path for querying passenger state.
	cmd  string
	args []string

	// Passenger command timeout.
	timeout time.Duration

	// Passenger metrics.
	up                  *prometheus.Desc
	version             *prometheus.Desc
	toplevelQueue       *prometheus.Desc
	maxProcessCount     *prometheus.Desc
	currentProcessCount *prometheus.Desc
	appCount            *prometheus.Desc

	// App metrics.
	appQueue         *prometheus.Desc
	appProcsSpawning *prometheus.Desc

	// Process metrics.
	requestsProcessed *prometheus.Desc
	procUptime        *prometheus.Desc
	procMemory        *prometheus.Desc
}

func NewExporter(cmd string, timeout time.Duration) *Exporter {
	cmdComponents := strings.Split(cmd, " ")

	return &Exporter{
		cmd:     cmdComponents[0],
		args:    cmdComponents[1:],
		timeout: timeout,
		up: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "up"),
			"Could passenger status be queried.",
			nil,
			nil,
		),
		version: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "version"),
			"Version of passenger",
			[]string{"version"},
			nil,
		),
		toplevelQueue: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "top_level_queue"),
			"Number of requests in the top-level queue.",
			nil,
			nil,
		),
		maxProcessCount: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "max_processes"),
			"Configured maximum number of processes.",
			nil,
			nil,
		),
		currentProcessCount: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "current_processes"),
			"Current number of processes.",
			nil,
			nil,
		),
		appCount: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "app_count"),
			"Number of apps.",
			nil,
			nil,
		),
		appQueue: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "app_queue"),
			"Number of requests in app process queues.",
			[]string{"name"},
			nil,
		),
		appProcsSpawning: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "app_procs_spawning"),
			"Number of processes spawning.",
			[]string{"name"},
			nil,
		),
		requestsProcessed: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "requests_processed_total"),
			"Number of processes served by a process.",
			[]string{"name", "id"},
			nil,
		),
		procUptime: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "proc_uptime"),
			"Number of seconds since processor started.",
			[]string{"name", "id", "codeRevision"},
			nil,
		),
		procMemory: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, "", "proc_memory"),
			"Memory consumed by a process",
			[]string{"name", "id"},
			nil,
		),
	}
}

// Collect fetches the statistics from the configured passenger frontend, and
// delivers them as Prometheus metrics. It implements prometheus.Collector.
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
	info, err := e.status()
	if err != nil {
		ch <- prometheus.MustNewConstMetric(e.up, prometheus.GaugeValue, 0)
		log.Errorf("failed to collect status from passenger: %s", err)
		return
	}
	ch <- prometheus.MustNewConstMetric(e.up, prometheus.GaugeValue, 1)

	ch <- prometheus.MustNewConstMetric(e.version, prometheus.GaugeValue, 1, info.PassengerVersion)

	ch <- prometheus.MustNewConstMetric(e.toplevelQueue, prometheus.GaugeValue, parseFloat(info.TopLevelRequestsInQueue))
	ch <- prometheus.MustNewConstMetric(e.maxProcessCount, prometheus.GaugeValue, parseFloat(info.MaxProcessCount))
	ch <- prometheus.MustNewConstMetric(e.currentProcessCount, prometheus.GaugeValue, parseFloat(info.CurrentProcessCount))
	ch <- prometheus.MustNewConstMetric(e.appCount, prometheus.GaugeValue, parseFloat(info.AppCount))

	for _, sg := range info.SuperGroups {
		ch <- prometheus.MustNewConstMetric(e.appQueue, prometheus.GaugeValue, parseFloat(sg.RequestsInQueue), sg.Name)
		ch <- prometheus.MustNewConstMetric(e.appProcsSpawning, prometheus.GaugeValue, parseFloat(sg.Group.ProcessesSpawning), sg.Name)

		// Update process identifiers map.
		processIdentifiers = updateProcesses(processIdentifiers, sg.Group.Processes)
		for _, proc := range sg.Group.Processes {
			if bucketID, ok := processIdentifiers[proc.PID]; ok {
				ch <- prometheus.MustNewConstMetric(e.procMemory, prometheus.GaugeValue, parseFloat(proc.RealMemory), sg.Name, strconv.Itoa(bucketID))
				ch <- prometheus.MustNewConstMetric(e.requestsProcessed, prometheus.CounterValue, parseFloat(proc.RequestsProcessed), sg.Name, strconv.Itoa(bucketID))

				if uptime, err := parsePassengerInterval(proc.Uptime); err == nil {
					ch <- prometheus.MustNewConstMetric(e.procUptime, prometheus.GaugeValue, float64(uptime),
						sg.Name, strconv.Itoa(bucketID), proc.CodeRevision,
					)
				}
			}
		}
	}

}

func (e *Exporter) status() (*Info, error) {
	var (
		out bytes.Buffer
		cmd = exec.Command(e.cmd, e.args...)
	)
	cmd.Stdout = &out

	err := cmd.Start()
	if err != nil {
		return nil, err
	}

	errc := make(chan error, 1)
	go func(cmd *exec.Cmd, c chan<- error) {
		c <- cmd.Wait()
	}(cmd, errc)

	select {
	case err := <-errc:
		if err != nil {
			return nil, err
		}
	case <-time.After(e.timeout):
		return nil, timeoutErr
	}

	return parseOutput(&out)
}

func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
	ch <- e.up
	ch <- e.version
	ch <- e.toplevelQueue
	ch <- e.maxProcessCount
	ch <- e.currentProcessCount
	ch <- e.appCount
	ch <- e.appQueue
	ch <- e.appProcsSpawning
	ch <- e.requestsProcessed
	ch <- e.procUptime
	ch <- e.procMemory
}

func parseOutput(r io.Reader) (*Info, error) {
	var info Info
	err := xml.NewDecoder(r).Decode(&info)
	if err != nil {
		return nil, err
	}
	return &info, nil
}

func parseFloat(val string) float64 {
	v, err := strconv.ParseFloat(val, 64)
	if err != nil {
		log.Errorf("failed to parse %s: %v", val, err)
		v = math.NaN()
	}
	return v
}

// updateProcesses updates the global map from process id:exporter id. Process
// TTLs cause new processes to be created on a user-defined cycle. When a new
// process replaces an old process, the new process's statistics will be
// bucketed with those of the process it replaced.
// Processes are restarted at an offset, user-defined interval. The
// restarted process is appended to the end of the status output.  For
// maintaining consistent process identifiers between process starts,
// pids are mapped to an identifier based on process count. When a new
// process/pid appears, it is mapped to either the first empty place
// within the global map storing process identifiers, or mapped to
// pid:id pair in the map.
func updateProcesses(old map[string]int, processes []Process) map[string]int {
	var (
		updated = make(map[string]int)
		found   = make([]string, len(old))
		missing []string
	)

	for _, p := range processes {
		if id, ok := old[p.PID]; ok {
			found[id] = p.PID
			// id also serves as an index.
			// By putting the pid at a certain index, we can loop
			// through the array to find the values that are the 0
			// value (empty string).
			// If index i has the empty value, then it was never
			// updated, so we slot the first of the missingPIDs
			// into that position. Passenger-status orders output
			// by pid, increasing. We can then assume that
			// unclaimed pid positions map in order to the missing
			// pids.
		} else {
			missing = append(missing, p.PID)
		}
	}

	j := 0
	for i, pid := range found {
		if pid == "" {
			if j >= len(missing) {
				continue
			}
			pid = missing[j]
			j++
		}
		updated[pid] = i
	}

	// If the number of elements in missing iterated through is less
	// than len(missing), there are new elements to be added to the map.
	// Unused pids from the last collection are not copied from old to
	// updated, thereby cleaning the return value of unused PIDs.
	if j < len(missing) {
		count := len(found)
		for i, pid := range missing[j:] {
			updated[pid] = count + i
		}
	}

	return updated
}

// parsePassengerInterval formats and parses the default Passenger time output.
func parsePassengerInterval(val string) (time.Duration, error) {
	return time.ParseDuration(strings.Replace(val, " ", "", -1))
}

func main() {
	var (
		cmd           = flag.String("passenger.command", "passenger-status --show=xml", "Passenger command for querying passenger status.")
		timeout       = flag.Duration("passenger.command.timeout", 500*time.Millisecond, "Timeout for passenger.command.")
		metricsPath   = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
		listenAddress = flag.String("web.listen-address", ":9106", "Address to listen on for web interface and telemetry.")
	)
	flag.Parse()

	prometheus.MustRegister(prometheus.NewProcessCollectorPIDFn(
		func() (int, error) {
			var (
				out bytes.Buffer
				cmd = exec.Command("pidof", `"Passenger core"`)
			)
			cmd.Stdout = &out

			if err := cmd.Run(); err != nil {
				return 0, fmt.Errorf("error running pid command: %s", cmd.Args)
			}
			return strconv.Atoi(out.String())
		},
		namespace),
	)

	prometheus.MustRegister(NewExporter(*cmd, *timeout))

	http.Handle(*metricsPath, prometheus.Handler())

	log.Infof("starting passenger_exporter_nginx v%s at %s", Version, *listenAddress)
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/printer"
	"go/token"
	"os"
	"strconv"
	"strings"

	"github.com/mailgun/godebug/Godeps/_workspace/src/golang.org/x/tools/go/ast/astutil"
	_ "github.com/mailgun/godebug/Godeps/_workspace/src/golang.org/x/tools/go/gcimporter"
	"github.com/mailgun/godebug/Godeps/_workspace/src/golang.org/x/tools/go/loader"
	"github.com/mailgun/godebug/Godeps/_workspace/src/golang.org/x/tools/go/types"
	_ "github.com/mailgun/godebug/lib" // so the library is also installed whenever this package is
)

var w = flag.Bool("w", false, "write result to (source) file instead of stdout")

// visitFn is a wrapper to make plain functions implement the ast.Visitor interface.
type visitFn func(ast.Node) ast.Visitor

// Visit is part of the ast.Visitor interface.
func (v visitFn) Visit(n ast.Node) ast.Visitor {
	return v(n)
}

var (
	defs map[*ast.Ident]types.Object
	fs   *token.FileSet
	pkg  *types.Package
)

func Usage() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "godebug <flags> <args>\n\n")
	fmt.Fprintf(os.Stderr, "flags:\n")
	flag.PrintDefaults()
	fmt.Fprint(os.Stderr, loader.FromArgsUsage)
	os.Exit(2)
}

func main() {
	flag.Parse()
	flag.Usage = Usage
	var conf loader.Config
	rest, err := conf.FromArgs(flag.Args(), false)
	if len(rest) > 0 {
		fmt.Fprintf(os.Stderr, "Unrecognized arguments:\n%v\n\n", strings.Join(rest, "\n"))
	}
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error identifying packages: %v\n\n", err)
	}
	if len(rest) > 0 || err != nil {
		flag.Usage()
	}
	prog, err := conf.Load()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error loading packages: %v\n\n", err)
		flag.Usage()
	}
	fs = prog.Fset
	for _, pkgInfo := range prog.InitialPackages() {
		defs = pkgInfo.Defs
		pkg = pkgInfo.Pkg
		for _, f := range pkgInfo.Files {
			ast.Walk(&visitor{context: f}, f)
			astutil.AddImport(fs, f, "github.com/mailgun/godebug/lib")
			cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8}
			out := os.Stdout
			if *w {
				file, err := os.Create(fs.Position(f.Pos()).Filename)
				if err != nil {
					fmt.Println(err)
					os.Exit(2)
				}
				defer file.Close()
				out = file
			}
			cfg.Fprint(out, fs, f)
		}
	}
}

func newCallStmt(selector, fnName string) *ast.ExprStmt {
	return &ast.ExprStmt{
		X: newCall(selector, fnName),
	}
}

func newCall(selector, fnName string) *ast.CallExpr {
	return &ast.CallExpr{Fun: newSel(selector, fnName)}
}

func newSel(selector, name string) *ast.SelectorExpr {
	return &ast.SelectorExpr{
		X:   ast.NewIdent(selector),
		Sel: ast.NewIdent(name),
	}
}

func getText(start, end token.Pos) (text string) {
	f, err := os.Open(fs.Position(start).Filename)
	if err != nil {
		return "<< Error reading source >>"
	}
	defer f.Close()
	startOffset, endOffset := fs.Position(start).Offset, fs.Position(end).Offset
	buf := make([]byte, 1+endOffset-startOffset)
	n, err := f.ReadAt(buf, int64(startOffset))
	text = string(buf[:n])
	if err != nil {
		text += "<< Error reading source >>"
	}
	return
}

func isNewIdent(ident *ast.Ident) bool {
	return ident.Name != "_" && defs[ident] != nil
}

// listNewIdentsFromDecl is for declarations using the keyword "var"
func listNewIdentsFromDecl(decl *ast.GenDecl) (idents []*ast.Ident) {
	if decl.Tok != token.VAR {
		return
	}
	for _, specs := range decl.Specs {
		for _, ident := range specs.(*ast.ValueSpec).Names {
			if isNewIdent(ident) {
				idents = append(idents, ident)
			}
		}
	}
	return
}

// listNewIdentsFromAssign is for short variable declarations
func listNewIdentsFromAssign(assign *ast.AssignStmt) (idents []*ast.Ident) {
	for _, expr := range assign.Lhs {
		if ident, ok := expr.(*ast.Ident); ok && isNewIdent(ident) {
			idents = append(idents, ident)
		}
	}
	return
}

func isSetTraceCall(node ast.Node) (b bool) {
	defer func() {
		if r := recover(); r != nil {
			b = false
		}
	}()
	sel := node.(*ast.ExprStmt).X.(*ast.CallExpr).Fun.(*ast.SelectorExpr)
	return sel.X.(*ast.Ident).Name == "godebug" && sel.Sel.Name == "SetTrace"
}

type visitor struct {
	context              ast.Node
	stmtBuf              []ast.Stmt
	scopeVar             string
	blockVars            []*ast.Ident
	createdExplicitScope bool
}

func (v *visitor) finalizeLoop(pos token.Pos, body *ast.BlockStmt) {
	if body == nil {
		return
	}
	text := getText(pos, body.Lbrace)
	call := newCall("godebug", "SLine")
	call.Args = append(call.Args, &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(text)})
	body.List = append(body.List, &ast.ExprStmt{X: call})
}

func ifElseCondWrap(cond ast.Expr, text string) ast.Expr {
	return &ast.CallExpr{
		Fun: &ast.FuncLit{
			Type: &ast.FuncType{
				Results: &ast.FieldList{
					List: []*ast.Field{{
						Type: ast.NewIdent("bool"),
					}},
				},
			},
			Body: &ast.BlockStmt{
				List: []ast.Stmt{
					&ast.ExprStmt{
						X: &ast.CallExpr{
							Fun: newSel("godebug", "ElseIfExpr"),
							Args: []ast.Expr{
								&ast.BasicLit{
									Kind:  token.STRING,
									Value: strconv.Quote(text),
								},
							},
						},
					},
					&ast.ReturnStmt{
						Results: []ast.Expr{cond},
					},
				},
			},
		},
	}
}

func ifElseInitWrap(vars []ast.Expr, vals []ast.Expr, text string) ast.Expr {
	results := &ast.FieldList{List: make([]*ast.Field, len(vars))}
	for i, expr := range vars {
		ident, ok := expr.(*ast.Ident)
		if !ok {
			panic(fmt.Sprintf("Unsupported type in if statement initializer: %T. Sorry! Let me (jeremy@mailgunhq.com) know about this and I'll fix it.", expr))
		}
		results.List[i] = &ast.Field{Type: ast.NewIdent(types.TypeString(pkg, defs[ident].Type()))}
	}
	return &ast.CallExpr{Fun: &ast.FuncLit{
		Type: &ast.FuncType{
			Results: results,
		},
		Body: &ast.BlockStmt{
			List: []ast.Stmt{
				&ast.ExprStmt{
					X: &ast.CallExpr{
						Fun: newSel("godebug", "ElseIfSimpleStmt"),
						Args: []ast.Expr{
							&ast.BasicLit{
								Kind:  token.STRING,
								Value: strconv.Quote(text),
							},
						},
					},
				},
				&ast.ReturnStmt{
					Results: vals,
				},
			},
		},
	}}
}

func (v *visitor) finalizeNode() {
	switch i := v.context.(type) {
	case *ast.FuncDecl:
		if i.Body == nil || (pkg.Name() == "main" && i.Name.Name == "main") {
			break
		}
		i.Body.List = append([]ast.Stmt{
			newCallStmt("godebug", "EnterFunc"),
			&ast.DeferStmt{
				Call: newCall("godebug", "ExitFunc"),
			},
		}, i.Body.List...)
	case *ast.BlockStmt:
		i.List = v.stmtBuf
	case *ast.IfStmt:
		if blk, ok := i.Else.(*ast.BlockStmt); ok {
			elseText := getText(i.Body.End()-1, blk.Lbrace)
			elseCall := newCall("godebug", "SLine")
			elseCall.Args = append(elseCall.Args, &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(elseText)})
			blk.List = append([]ast.Stmt{&ast.ExprStmt{X: elseCall}}, blk.List...)
		}
		if ifstmt, ok := i.Else.(*ast.IfStmt); ok {
			text := getText(i.Body.End()-1, ifstmt.Body.Pos())
			switch d := ifstmt.Init.(type) {
			case *ast.AssignStmt:
				d.Rhs = []ast.Expr{ifElseInitWrap(d.Lhs, d.Rhs, text)}
			case *ast.DeclStmt:
				spec := d.Decl.(*ast.GenDecl).Specs[0].(*ast.ValueSpec)
				exprs := make([]ast.Expr, len(spec.Names))
				for i := range exprs {
					exprs[i] = ast.Expr(spec.Names[i])
				}
				spec.Values = []ast.Expr{ifElseInitWrap(exprs, spec.Values, text)}
			// TODO: optimize nil case
			default:
			}
			ifstmt.Cond = ifElseCondWrap(ifstmt.Cond, text)
		}
	case *ast.RangeStmt:
		v.finalizeLoop(i.For, i.Body)
	case *ast.ForStmt:
		v.finalizeLoop(i.For, i.Body)
	case *ast.File:
		// Insert declaration of file-level godebug.Scope variable as first declaration in file.
		var newDecls []ast.Decl
		// But put it after any import declarations.
		for len(i.Decls) > 0 {
			if gd, ok := i.Decls[0].(*ast.GenDecl); !ok || gd.Tok != token.IMPORT {
				break
			}
			newDecls = append(newDecls, i.Decls[0])
			i.Decls = i.Decls[1:]
		}
		newDecls = append(newDecls, &ast.GenDecl{
			Tok: token.VAR,
			Specs: []ast.Spec{
				&ast.ValueSpec{
					Names:  []*ast.Ident{ast.NewIdent("main_goScope")},
					Values: []ast.Expr{newCall("godebug", "EnteringNewScope")},
				},
			},
		})
		i.Decls = append(newDecls, i.Decls...)
	}
}

func (v *visitor) Visit(node ast.Node) ast.Visitor {
	switch i := node.(type) {
	case nil:
		v.finalizeNode()
		return nil
	case *ast.FuncDecl:
		// Add Declare() call first thing in the function for any variables bound by the function signature.
		return &visitor{context: node, blockVars: getIdents(i.Recv, i.Type.Params, i.Type.Results), scopeVar: "main_goScope"}
	case *ast.BlockStmt:
		w := &visitor{context: node, stmtBuf: make([]ast.Stmt, 0, 3*len(i.List)), scopeVar: v.scopeVar}
		if len(v.blockVars) > 0 {
			w.createScope()
			w.stmtBuf = append(w.stmtBuf, newDeclareCall(w.scopeVar, v.blockVars))
		}
		return w
	}
	if v.stmtBuf == nil {
		return &visitor{context: node, scopeVar: v.scopeVar}
	}
	if !isSetTraceCall(node) {
		v.stmtBuf = append(v.stmtBuf, newCallStmt("godebug", "Line"))
	}
	var newIdents []*ast.Ident
	switch i := node.(type) {
	case *ast.DeclStmt:
		newIdents = listNewIdentsFromDecl(i.Decl.(*ast.GenDecl))
	case *ast.AssignStmt:
		newIdents = listNewIdentsFromAssign(i)
	}
	if stmt, ok := node.(ast.Stmt); ok {
		v.stmtBuf = append(v.stmtBuf, stmt)
	}
	if len(newIdents) > 0 {
		if !v.createdExplicitScope {
			v.createScope()
		}
		v.stmtBuf = append(v.stmtBuf, newDeclareCall("", newIdents))
	}
	return &visitor{context: node}
}

func getIdents(lists ...*ast.FieldList) (idents []*ast.Ident) {
	for _, l := range lists {
		if l == nil {
			continue
		}
		for _, fields := range l.List {
			for _, ident := range fields.Names {
				if ident.Name != "_" {
					idents = append(idents, ident)
				}
			}
		}
	}
	return
}

func newDeclareCall(scopeVar string, idents []*ast.Ident) ast.Stmt {
	if scopeVar == "" {
		scopeVar = "godebugScope"
	}
	expr := newCallStmt(scopeVar, "Declare")
	call := expr.X.(*ast.CallExpr)
	call.Args = make([]ast.Expr, 2*len(idents))
	for i, ident := range idents {
		call.Args[2*i] = &ast.BasicLit{
			Kind:  token.STRING,
			Value: strconv.Quote(ident.Name),
		}
		call.Args[2*i+1] = &ast.UnaryExpr{
			Op: token.AND,
			X:  ident,
		}
	}
	return expr
}

func (v *visitor) createScope() {
	name := "godebugScope"
	v.stmtBuf = append(v.stmtBuf, &ast.AssignStmt{
		Lhs: []ast.Expr{ast.NewIdent(name)},
		Tok: token.DEFINE,
		Rhs: []ast.Expr{newCall(v.scopeVar, "EnteringNewChildScope")},
	})
	v.stmtBuf = append(v.stmtBuf, &ast.DeferStmt{Call: newCall(name, "End")})
	v.scopeVar = name
	v.createdExplicitScope = true
}
<commit_msg>gen: fix scope creation bug<commit_after>package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/printer"
	"go/token"
	"os"
	"strconv"
	"strings"

	"github.com/mailgun/godebug/Godeps/_workspace/src/golang.org/x/tools/go/ast/astutil"
	_ "github.com/mailgun/godebug/Godeps/_workspace/src/golang.org/x/tools/go/gcimporter"
	"github.com/mailgun/godebug/Godeps/_workspace/src/golang.org/x/tools/go/loader"
	"github.com/mailgun/godebug/Godeps/_workspace/src/golang.org/x/tools/go/types"
	_ "github.com/mailgun/godebug/lib" // so the library is also installed whenever this package is
)

var w = flag.Bool("w", false, "write result to (source) file instead of stdout")

// visitFn is a wrapper to make plain functions implement the ast.Visitor interface.
type visitFn func(ast.Node) ast.Visitor

// Visit is part of the ast.Visitor interface.
func (v visitFn) Visit(n ast.Node) ast.Visitor {
	return v(n)
}

var (
	defs map[*ast.Ident]types.Object
	fs   *token.FileSet
	pkg  *types.Package
)

func Usage() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "godebug <flags> <args>\n\n")
	fmt.Fprintf(os.Stderr, "flags:\n")
	flag.PrintDefaults()
	fmt.Fprint(os.Stderr, loader.FromArgsUsage)
	os.Exit(2)
}

func main() {
	flag.Parse()
	flag.Usage = Usage
	var conf loader.Config
	rest, err := conf.FromArgs(flag.Args(), false)
	if len(rest) > 0 {
		fmt.Fprintf(os.Stderr, "Unrecognized arguments:\n%v\n\n", strings.Join(rest, "\n"))
	}
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error identifying packages: %v\n\n", err)
	}
	if len(rest) > 0 || err != nil {
		flag.Usage()
	}
	prog, err := conf.Load()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error loading packages: %v\n\n", err)
		flag.Usage()
	}
	fs = prog.Fset
	for _, pkgInfo := range prog.InitialPackages() {
		defs = pkgInfo.Defs
		pkg = pkgInfo.Pkg
		for _, f := range pkgInfo.Files {
			ast.Walk(&visitor{context: f}, f)
			astutil.AddImport(fs, f, "github.com/mailgun/godebug/lib")
			cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8}
			out := os.Stdout
			if *w {
				file, err := os.Create(fs.Position(f.Pos()).Filename)
				if err != nil {
					fmt.Println(err)
					os.Exit(2)
				}
				defer file.Close()
				out = file
			}
			cfg.Fprint(out, fs, f)
		}
	}
}

func newCallStmt(selector, fnName string) *ast.ExprStmt {
	return &ast.ExprStmt{
		X: newCall(selector, fnName),
	}
}

func newCall(selector, fnName string) *ast.CallExpr {
	return &ast.CallExpr{Fun: newSel(selector, fnName)}
}

func newSel(selector, name string) *ast.SelectorExpr {
	return &ast.SelectorExpr{
		X:   ast.NewIdent(selector),
		Sel: ast.NewIdent(name),
	}
}

func getText(start, end token.Pos) (text string) {
	f, err := os.Open(fs.Position(start).Filename)
	if err != nil {
		return "<< Error reading source >>"
	}
	defer f.Close()
	startOffset, endOffset := fs.Position(start).Offset, fs.Position(end).Offset
	buf := make([]byte, 1+endOffset-startOffset)
	n, err := f.ReadAt(buf, int64(startOffset))
	text = string(buf[:n])
	if err != nil {
		text += "<< Error reading source >>"
	}
	return
}

func isNewIdent(ident *ast.Ident) bool {
	return ident.Name != "_" && defs[ident] != nil
}

// listNewIdentsFromDecl is for declarations using the keyword "var"
func listNewIdentsFromDecl(decl *ast.GenDecl) (idents []*ast.Ident) {
	if decl.Tok != token.VAR {
		return
	}
	for _, specs := range decl.Specs {
		for _, ident := range specs.(*ast.ValueSpec).Names {
			if isNewIdent(ident) {
				idents = append(idents, ident)
			}
		}
	}
	return
}

// listNewIdentsFromAssign is for short variable declarations
func listNewIdentsFromAssign(assign *ast.AssignStmt) (idents []*ast.Ident) {
	for _, expr := range assign.Lhs {
		if ident, ok := expr.(*ast.Ident); ok && isNewIdent(ident) {
			idents = append(idents, ident)
		}
	}
	return
}

func isSetTraceCall(node ast.Node) (b bool) {
	defer func() {
		if r := recover(); r != nil {
			b = false
		}
	}()
	sel := node.(*ast.ExprStmt).X.(*ast.CallExpr).Fun.(*ast.SelectorExpr)
	return sel.X.(*ast.Ident).Name == "godebug" && sel.Sel.Name == "SetTrace"
}

type visitor struct {
	context              ast.Node
	stmtBuf              []ast.Stmt
	scopeVar             string
	blockVars            []*ast.Ident
	createdExplicitScope bool
}

func (v *visitor) finalizeLoop(pos token.Pos, body *ast.BlockStmt) {
	if body == nil {
		return
	}
	text := getText(pos, body.Lbrace)
	call := newCall("godebug", "SLine")
	call.Args = append(call.Args, &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(text)})
	body.List = append(body.List, &ast.ExprStmt{X: call})
}

func ifElseCondWrap(cond ast.Expr, text string) ast.Expr {
	return &ast.CallExpr{
		Fun: &ast.FuncLit{
			Type: &ast.FuncType{
				Results: &ast.FieldList{
					List: []*ast.Field{{
						Type: ast.NewIdent("bool"),
					}},
				},
			},
			Body: &ast.BlockStmt{
				List: []ast.Stmt{
					&ast.ExprStmt{
						X: &ast.CallExpr{
							Fun: newSel("godebug", "ElseIfExpr"),
							Args: []ast.Expr{
								&ast.BasicLit{
									Kind:  token.STRING,
									Value: strconv.Quote(text),
								},
							},
						},
					},
					&ast.ReturnStmt{
						Results: []ast.Expr{cond},
					},
				},
			},
		},
	}
}

func ifElseInitWrap(vars []ast.Expr, vals []ast.Expr, text string) ast.Expr {
	results := &ast.FieldList{List: make([]*ast.Field, len(vars))}
	for i, expr := range vars {
		ident, ok := expr.(*ast.Ident)
		if !ok {
			panic(fmt.Sprintf("Unsupported type in if statement initializer: %T. Sorry! Let me (jeremy@mailgunhq.com) know about this and I'll fix it.", expr))
		}
		results.List[i] = &ast.Field{Type: ast.NewIdent(types.TypeString(pkg, defs[ident].Type()))}
	}
	return &ast.CallExpr{Fun: &ast.FuncLit{
		Type: &ast.FuncType{
			Results: results,
		},
		Body: &ast.BlockStmt{
			List: []ast.Stmt{
				&ast.ExprStmt{
					X: &ast.CallExpr{
						Fun: newSel("godebug", "ElseIfSimpleStmt"),
						Args: []ast.Expr{
							&ast.BasicLit{
								Kind:  token.STRING,
								Value: strconv.Quote(text),
							},
						},
					},
				},
				&ast.ReturnStmt{
					Results: vals,
				},
			},
		},
	}}
}

func (v *visitor) finalizeNode() {
	switch i := v.context.(type) {
	case *ast.FuncDecl:
		if i.Body == nil || (pkg.Name() == "main" && i.Name.Name == "main") {
			break
		}
		i.Body.List = append([]ast.Stmt{
			newCallStmt("godebug", "EnterFunc"),
			&ast.DeferStmt{
				Call: newCall("godebug", "ExitFunc"),
			},
		}, i.Body.List...)
	case *ast.BlockStmt:
		i.List = v.stmtBuf
	case *ast.IfStmt:
		if blk, ok := i.Else.(*ast.BlockStmt); ok {
			elseText := getText(i.Body.End()-1, blk.Lbrace)
			elseCall := newCall("godebug", "SLine")
			elseCall.Args = append(elseCall.Args, &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(elseText)})
			blk.List = append([]ast.Stmt{&ast.ExprStmt{X: elseCall}}, blk.List...)
		}
		if ifstmt, ok := i.Else.(*ast.IfStmt); ok {
			text := getText(i.Body.End()-1, ifstmt.Body.Pos())
			switch d := ifstmt.Init.(type) {
			case *ast.AssignStmt:
				d.Rhs = []ast.Expr{ifElseInitWrap(d.Lhs, d.Rhs, text)}
			case *ast.DeclStmt:
				spec := d.Decl.(*ast.GenDecl).Specs[0].(*ast.ValueSpec)
				exprs := make([]ast.Expr, len(spec.Names))
				for i := range exprs {
					exprs[i] = ast.Expr(spec.Names[i])
				}
				spec.Values = []ast.Expr{ifElseInitWrap(exprs, spec.Values, text)}
			// TODO: optimize nil case
			default:
			}
			ifstmt.Cond = ifElseCondWrap(ifstmt.Cond, text)
		}
	case *ast.RangeStmt:
		v.finalizeLoop(i.For, i.Body)
	case *ast.ForStmt:
		v.finalizeLoop(i.For, i.Body)
	case *ast.File:
		// Insert declaration of file-level godebug.Scope variable as first declaration in file.
		var newDecls []ast.Decl
		// But put it after any import declarations.
		for len(i.Decls) > 0 {
			if gd, ok := i.Decls[0].(*ast.GenDecl); !ok || gd.Tok != token.IMPORT {
				break
			}
			newDecls = append(newDecls, i.Decls[0])
			i.Decls = i.Decls[1:]
		}
		newDecls = append(newDecls, &ast.GenDecl{
			Tok: token.VAR,
			Specs: []ast.Spec{
				&ast.ValueSpec{
					Names:  []*ast.Ident{ast.NewIdent("main_goScope")},
					Values: []ast.Expr{newCall("godebug", "EnteringNewScope")},
				},
			},
		})
		i.Decls = append(newDecls, i.Decls...)
	}
}

func (v *visitor) Visit(node ast.Node) ast.Visitor {
	switch i := node.(type) {
	case nil:
		v.finalizeNode()
		return nil
	case *ast.FuncDecl:
		// Add Declare() call first thing in the function for any variables bound by the function signature.
		return &visitor{context: node, blockVars: getIdents(i.Recv, i.Type.Params, i.Type.Results), scopeVar: "main_goScope"}
	case *ast.BlockStmt:
		w := &visitor{context: node, stmtBuf: make([]ast.Stmt, 0, 3*len(i.List)), scopeVar: v.scopeVar}
		if len(v.blockVars) > 0 {
			w.createScope()
			w.stmtBuf = append(w.stmtBuf, newDeclareCall(w.scopeVar, v.blockVars))
		}
		return w
	}
	if v.stmtBuf == nil {
		return &visitor{context: node, scopeVar: v.scopeVar}
	}
	if !isSetTraceCall(node) {
		v.stmtBuf = append(v.stmtBuf, newCallStmt("godebug", "Line"))
	}
	var newIdents []*ast.Ident
	switch i := node.(type) {
	case *ast.DeclStmt:
		newIdents = listNewIdentsFromDecl(i.Decl.(*ast.GenDecl))
	case *ast.AssignStmt:
		newIdents = listNewIdentsFromAssign(i)
	}
	if stmt, ok := node.(ast.Stmt); ok {
		v.stmtBuf = append(v.stmtBuf, stmt)
	}
	if len(newIdents) > 0 {
		if !v.createdExplicitScope {
			v.createScope()
		}
		v.stmtBuf = append(v.stmtBuf, newDeclareCall("", newIdents))
	}
	return &visitor{context: node}
}

func getIdents(lists ...*ast.FieldList) (idents []*ast.Ident) {
	for _, l := range lists {
		if l == nil {
			continue
		}
		for _, fields := range l.List {
			for _, ident := range fields.Names {
				if ident.Name != "_" {
					idents = append(idents, ident)
				}
			}
		}
	}
	return
}

func newDeclareCall(scopeVar string, idents []*ast.Ident) ast.Stmt {
	if scopeVar == "" {
		scopeVar = "godebugScope"
	}
	expr := newCallStmt(scopeVar, "Declare")
	call := expr.X.(*ast.CallExpr)
	call.Args = make([]ast.Expr, 2*len(idents))
	for i, ident := range idents {
		call.Args[2*i] = &ast.BasicLit{
			Kind:  token.STRING,
			Value: strconv.Quote(ident.Name),
		}
		call.Args[2*i+1] = &ast.UnaryExpr{
			Op: token.AND,
			X:  ident,
		}
	}
	return expr
}

func (v *visitor) createScope() {
	name := "godebugScope"
	if v.scopeVar == "" {
		v.scopeVar = name
	}
	v.stmtBuf = append(v.stmtBuf, &ast.AssignStmt{
		Lhs: []ast.Expr{ast.NewIdent(name)},
		Tok: token.DEFINE,
		Rhs: []ast.Expr{newCall(v.scopeVar, "EnteringNewChildScope")},
	})
	v.stmtBuf = append(v.stmtBuf, &ast.DeferStmt{Call: newCall(name, "End")})
	v.scopeVar = name
	v.createdExplicitScope = true
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"
	"os"

	"github.com/codegangsta/cli"
	"github.com/gin-gonic/gin"

	"github.com/deepakprakash/chattic/api/v1"
)

func setupMiddleware(e *gin.Engine) {
	// Setup all the global middleware that we need

	// Basic middlewares
	e.Use(gin.Logger())
	e.Use(gin.Recovery())
}

func setupRoutes(e *gin.Engine) {
	// Setup our routes and route specific middleware

	e.GET("/", homePage)

	// Group routes for v1 API
	apiV1 := e.Group("/api/v1")

	apiV1.GET("/profile", v1.GetProfile)
}

func main() {
	app := cli.NewApp()

	app.Name = "Chattic"
	app.Usage = "Standalone, self-hosted instant messaging."
	app.Version = "0.1exp"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "bind, b",
			Value: ":4000",
			Usage: "Host:Port address for the server binding. Eg: `:4000`, `localhost:4000`, `0.0.0.0:4000`",
		},
	}

	app.Action = func(c *cli.Context) {
		// Action when the app is run.

		// Init the minimal gin engine
		engine := gin.New()

		// Setup middleware
		setupMiddleware(engine)

		// Setup routes
		setupRoutes(engine)

		// Run the server
		if err := http.ListenAndServe(c.String("bind"), engine); err != nil {
			// Error starting server. Log and exit.
			log.Fatalf("Error starting server: %s", err)
		}
	}

	app.Run(os.Args)
}
<commit_msg>Use logrus for better logging<commit_after>package main

import (
	"net/http"
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/gin-gonic/gin"

	"github.com/deepakprakash/chattic/api/v1"
)

func setupMiddleware(e *gin.Engine) {
	// Setup all the global middleware that we need

	// Basic middlewares
	e.Use(gin.Logger())
	e.Use(gin.Recovery())
}

func setupRoutes(e *gin.Engine) {
	// Setup our routes and route specific middleware

	e.GET("/", homePage)

	// Group routes for v1 API
	apiV1 := e.Group("/api/v1")

	apiV1.GET("/profile", v1.GetProfile)
}

func main() {
	app := cli.NewApp()

	app.Name = "Chattic"
	app.Usage = "Standalone, self-hosted instant messaging."
	app.Version = "0.1exp"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "bind, b",
			Value: ":4000",
			Usage: "Host:Port address for the server binding. Eg: `:4000`, `localhost:4000`, `0.0.0.0:4000`",
		},
	}

	app.Action = func(c *cli.Context) {
		// Action when the app is run.

		// Init the minimal gin engine
		engine := gin.New()

		// Setup middleware
		setupMiddleware(engine)

		// Setup routes
		setupRoutes(engine)

		// Run the server
		if err := http.ListenAndServe(c.String("bind"), engine); err != nil {
			// Error starting server. Log and exit.
			log.WithFields(log.Fields{
				"error": err,
			}).Fatal("Failed to start server.")
		}
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"

	"github.com/giantswarm/microerror"
	"github.com/giantswarm/microkit/command"
	microserver "github.com/giantswarm/microkit/server"
	"github.com/giantswarm/micrologger"
	"github.com/spf13/viper"

	"github.com/giantswarm/azure-operator/flag"
	"github.com/giantswarm/azure-operator/server"
	"github.com/giantswarm/azure-operator/service"
)

const (
	notAvailable = "n/a"
)

var (
	description string     = "The azure-operator manages Kubernetes clusters on Azure."
	f           *flag.Flag = flag.New()
	gitCommit   string     = notAvailable
	name        string     = "azure-operator"
	source      string     = "https://github.com/giantswarm/azure-operator"
)

func main() {
	err := mainError()
	if err != nil {
		panic(fmt.Sprintf("%#v\n", err))
	}
}

func mainError() error {
	var err error

	ctx := context.Background()
	logger, err := micrologger.New(micrologger.Config{})

	// We define a server factory to create the custom server once all command
	// line flags are parsed and all microservice configuration is storted out.
	serverFactory := func(v *viper.Viper) microserver.Server {
		// Create a new custom service which implements business logic.
		var newService *service.Service
		{
			c := service.Config{
				Flag:   f,
				Logger: logger,
				Viper:  v,

				Description: description,
				GitCommit:   gitCommit,
				ProjectName: name,
				Source:      source,
			}

			newService, err = service.New(c)
			if err != nil {
				panic(fmt.Sprintf("%#v", microerror.Mask(err)))
			}

			go newService.Boot(ctx)
		}

		// Create a new custom server which bundles our endpoints.
		var newServer microserver.Server
		{
			c := server.Config{
				Logger:  logger,
				Service: newService,
				Viper:   v,

				ProjectName: name,
			}

			newServer, err = server.New(c)
			if err != nil {
				panic(fmt.Sprintf("%#v", microerror.Mask(err)))
			}
		}

		return newServer
	}

	// Create a new microkit command which manages our custom microservice.
	var newCommand command.Command
	{
		c := command.Config{
			Logger:        logger,
			ServerFactory: serverFactory,

			Description:    description,
			GitCommit:      gitCommit,
			Name:           name,
			Source:         source,
			VersionBundles: service.NewVersionBundles(),
		}

		newCommand, err = command.New(c)
		if err != nil {
			return microerror.Mask(err)
		}
	}

	var defaultTemplateVersion string
	{
		if gitCommit != notAvailable {
			defaultTemplateVersion = gitCommit
		}
	}

	daemonCommand := newCommand.DaemonCommand().CobraCommand()

	daemonCommand.PersistentFlags().String(f.Service.Azure.ClientID, "", "ID of the Active Directory Service Principal.")
	daemonCommand.PersistentFlags().String(f.Service.Azure.ClientSecret, "", "Secret of the Active Directory Service Principal.")
	daemonCommand.PersistentFlags().String(f.Service.Azure.SubscriptionID, "", "ID of the Azure Subscription.")
	daemonCommand.PersistentFlags().String(f.Service.Azure.TenantID, "", "ID of the Active Directory Tenant.")
	daemonCommand.PersistentFlags().String(f.Service.Azure.Template.URI.Version, defaultTemplateVersion, "URI version for ARM template links.")
	daemonCommand.PersistentFlags().String(f.Service.Installation.Name, "", "Installation name for tagging Azure resources.")
	daemonCommand.PersistentFlags().String(f.Service.Kubernetes.Address, "", "Address used to connect to Kubernetes. When empty in-cluster config is created.")
	daemonCommand.PersistentFlags().Bool(f.Service.Kubernetes.InCluster, true, "Whether to use the in-cluster config to authenticate with Kubernetes.")
	daemonCommand.PersistentFlags().String(f.Service.Kubernetes.TLS.CAFile, "", "Certificate authority file path to use to authenticate with Kubernetes.")
	daemonCommand.PersistentFlags().String(f.Service.Kubernetes.TLS.CrtFile, "", "Certificate file path to use to authenticate with Kubernetes.")
	daemonCommand.PersistentFlags().String(f.Service.Kubernetes.TLS.KeyFile, "", "Key file path to use to authenticate with Kubernetes.")

	newCommand.CobraCommand().Execute()

	return nil
}
<commit_msg>main: handle micrologger.New error (#153)<commit_after>package main

import (
	"context"
	"fmt"

	"github.com/giantswarm/microerror"
	"github.com/giantswarm/microkit/command"
	microserver "github.com/giantswarm/microkit/server"
	"github.com/giantswarm/micrologger"
	"github.com/spf13/viper"

	"github.com/giantswarm/azure-operator/flag"
	"github.com/giantswarm/azure-operator/server"
	"github.com/giantswarm/azure-operator/service"
)

const (
	notAvailable = "n/a"
)

var (
	description string     = "The azure-operator manages Kubernetes clusters on Azure."
	f           *flag.Flag = flag.New()
	gitCommit   string     = notAvailable
	name        string     = "azure-operator"
	source      string     = "https://github.com/giantswarm/azure-operator"
)

func main() {
	err := mainError()
	if err != nil {
		panic(fmt.Sprintf("%#v\n", err))
	}
}

func mainError() error {
	var err error

	ctx := context.Background()
	logger, err := micrologger.New(micrologger.Config{})
	if err != nil {
		return microerror.Mask(err)
	}

	// We define a server factory to create the custom server once all command
	// line flags are parsed and all microservice configuration is storted out.
	serverFactory := func(v *viper.Viper) microserver.Server {
		// Create a new custom service which implements business logic.
		var newService *service.Service
		{
			c := service.Config{
				Flag:   f,
				Logger: logger,
				Viper:  v,

				Description: description,
				GitCommit:   gitCommit,
				ProjectName: name,
				Source:      source,
			}

			newService, err = service.New(c)
			if err != nil {
				panic(fmt.Sprintf("%#v", microerror.Mask(err)))
			}

			go newService.Boot(ctx)
		}

		// Create a new custom server which bundles our endpoints.
		var newServer microserver.Server
		{
			c := server.Config{
				Logger:  logger,
				Service: newService,
				Viper:   v,

				ProjectName: name,
			}

			newServer, err = server.New(c)
			if err != nil {
				panic(fmt.Sprintf("%#v", microerror.Mask(err)))
			}
		}

		return newServer
	}

	// Create a new microkit command which manages our custom microservice.
	var newCommand command.Command
	{
		c := command.Config{
			Logger:        logger,
			ServerFactory: serverFactory,

			Description:    description,
			GitCommit:      gitCommit,
			Name:           name,
			Source:         source,
			VersionBundles: service.NewVersionBundles(),
		}

		newCommand, err = command.New(c)
		if err != nil {
			return microerror.Mask(err)
		}
	}

	var defaultTemplateVersion string
	{
		if gitCommit != notAvailable {
			defaultTemplateVersion = gitCommit
		}
	}

	daemonCommand := newCommand.DaemonCommand().CobraCommand()

	daemonCommand.PersistentFlags().String(f.Service.Azure.ClientID, "", "ID of the Active Directory Service Principal.")
	daemonCommand.PersistentFlags().String(f.Service.Azure.ClientSecret, "", "Secret of the Active Directory Service Principal.")
	daemonCommand.PersistentFlags().String(f.Service.Azure.SubscriptionID, "", "ID of the Azure Subscription.")
	daemonCommand.PersistentFlags().String(f.Service.Azure.TenantID, "", "ID of the Active Directory Tenant.")
	daemonCommand.PersistentFlags().String(f.Service.Azure.Template.URI.Version, defaultTemplateVersion, "URI version for ARM template links.")
	daemonCommand.PersistentFlags().String(f.Service.Installation.Name, "", "Installation name for tagging Azure resources.")
	daemonCommand.PersistentFlags().String(f.Service.Kubernetes.Address, "", "Address used to connect to Kubernetes. When empty in-cluster config is created.")
	daemonCommand.PersistentFlags().Bool(f.Service.Kubernetes.InCluster, true, "Whether to use the in-cluster config to authenticate with Kubernetes.")
	daemonCommand.PersistentFlags().String(f.Service.Kubernetes.TLS.CAFile, "", "Certificate authority file path to use to authenticate with Kubernetes.")
	daemonCommand.PersistentFlags().String(f.Service.Kubernetes.TLS.CrtFile, "", "Certificate file path to use to authenticate with Kubernetes.")
	daemonCommand.PersistentFlags().String(f.Service.Kubernetes.TLS.KeyFile, "", "Key file path to use to authenticate with Kubernetes.")

	newCommand.CobraCommand().Execute()

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"strconv"
	"time"

	"github.com/macarrie/flemzerd/configuration"
	log "github.com/macarrie/flemzerd/logging"
	"github.com/macarrie/flemzerd/retention"
	flag "github.com/ogier/pflag"

	provider "github.com/macarrie/flemzerd/providers"
	"github.com/macarrie/flemzerd/providers/impl/tvdb"

	indexer "github.com/macarrie/flemzerd/indexers"
	"github.com/macarrie/flemzerd/indexers/impl/torznab"

	notifier "github.com/macarrie/flemzerd/notifiers"
	"github.com/macarrie/flemzerd/notifiers/impl/desktop"
	"github.com/macarrie/flemzerd/notifiers/impl/pushbullet"

	downloader "github.com/macarrie/flemzerd/downloaders"
	"github.com/macarrie/flemzerd/downloaders/impl/transmission"

	. "github.com/macarrie/flemzerd/objects"
)

func initProviders() {
	log.Debug("Initializing Providers")

	var newProviders []provider.Provider
	for providerType, providerElt := range configuration.Config.Providers {
		switch providerType {
		case "tvdb":
			np, _ := impl.New(providerElt["apikey"])
			newProviders = append(newProviders, np)
		default:
			log.WithFields(log.Fields{
				"providerType": providerType,
			}).Warning("Unknown provider type")
		}

		if len(newProviders) != 0 {
			for _, newProvider := range newProviders {
				provider.AddProvider(newProvider)
				log.WithFields(log.Fields{
					"provider": providerType,
				}).Info("Provider added to list of providers")
			}
			newProviders = []provider.Provider{}
		}
	}
}

func initIndexers() {
	log.Debug("Initializing Indexers")

	var newIndexers []indexer.Indexer
	for indexerType, indexerList := range configuration.Config.Indexers {
		switch indexerType {
		case "torznab":
			for _, indexer := range indexerList {
				newIndexers = append(newIndexers, torznab.New(indexer["name"], indexer["url"], indexer["apikey"]))
			}
		default:
			log.WithFields(log.Fields{
				"indexerType": indexerType,
			}).Warning("Unknown indexer type")
		}

		if len(newIndexers) != 0 {
			for _, newIndexer := range newIndexers {
				indexer.AddIndexer(newIndexer)
				log.WithFields(log.Fields{
					"indexer": newIndexer.GetName(),
				}).Info("Indexer added to list of indexers")
			}
			newIndexers = []indexer.Indexer{}
		}
	}
}

func initDownloaders() {
	log.Debug("Initializing Downloaders")

	var newDownloaders []downloader.Downloader
	for name, downloaderObject := range configuration.Config.Downloaders {
		switch name {
		case "transmission":
			address := downloaderObject["address"]
			port, _ := strconv.Atoi(downloaderObject["port"])
			user, authNeeded := downloaderObject["user"]
			password := downloaderObject["password"]
			if !authNeeded {
				user = ""
				password = ""
			}

			transmissionDownloader := transmission.New(address, port, user, password)
			newDownloaders = append(newDownloaders, transmissionDownloader)
		default:
			log.WithFields(log.Fields{
				"downloaderType": name,
			}).Warning("Unknown downloader type")
		}

		if len(newDownloaders) != 0 {
			for _, newDownloader := range newDownloaders {
				newDownloader.Init()
				downloader.AddDownloader(newDownloader)
				log.WithFields(log.Fields{
					"downloader": name,
				}).Info("Downloader added to list of downloaders")
			}
			newDownloaders = []downloader.Downloader{}
		}
	}
}

func initNotifiers() {
	log.Debug("Initializing Notifiers")

	for name, notifierObject := range configuration.Config.Notifiers {
		switch name {
		case "pushbullet":
			pushbulletNotifier := pushbullet.New(map[string]string{"AccessToken": notifierObject["accesstoken"]})
			notifier.AddNotifier(pushbulletNotifier)

			log.WithFields(log.Fields{
				"notifier": name,
			}).Info("Notifier added to list of notifiers")

		case "desktop":
			desktopNotifier := desktop.New()
			notifier.AddNotifier(desktopNotifier)

			log.WithFields(log.Fields{
				"notifier": name,
			}).Info("Notifier added to list of notifiers")

		default:
			log.WithFields(log.Fields{
				"notifierType": name,
			}).Warning("Unknown notifier type")
		}
	}
}

func main() {
	debugMode := flag.BoolP("debug", "d", false, "Start in debug mode")
	configFilePath := flag.StringP("config", "c", "", "Configuration file path to use")

	flag.Parse()

	if *debugMode {
		log.Setup(true)
	} else {
		log.Setup(false)
	}

	if *configFilePath != "" {
		log.Info("Loading provided configuration file")
		configuration.UseFile(*configFilePath)
	}

	err := configuration.Load()
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Fatal("Cannot load configuration file")
	}

	err = configuration.Check()
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Fatal("Errors in configuration file")
	}

	initNotifiers()
	initProviders()
	initIndexers()
	initDownloaders()
	retention.Load()

	//	 Load configuration objects
	var showObjects []TvShow

	for _, show := range configuration.Config.Shows {
		showName := show
		show, err := provider.FindShow(show)
		if err != nil {
			log.WithFields(log.Fields{
				"error": err,
				"show":  showName,
			}).Warning("Unable to get show informations")
		} else {
			showObjects = append(showObjects, show)
		}
	}
	if len(showObjects) == 0 {
		log.Fatal("Impossible to get show informations for shows defined in configuration. Shutting down")
	}

	// TODO: Ticker interval is set in seconds for dev. Ticker interval must be changed from time.Second to time.Minute.
	loopTicker := time.NewTicker(time.Duration(configuration.Config.System.EpisodeCheckInterval) * time.Second)
	exit := make(chan bool)

	log.Debug("Starting polling loop")
	for {
		log.Debug("========== Polling loop start ==========")

		for _, show := range showObjects {
			recentEpisodes, err := provider.FindRecentlyAiredEpisodesForShow(show)
			if err != nil {
				log.WithFields(log.Fields{
					"error": err,
					"show":  show.Name,
				}).Warning("No recent episodes found")
				continue
			}

			for _, recentEpisode := range recentEpisodes {
				err := notifier.NotifyRecentEpisode(show, recentEpisode)
				if err != nil {
					log.Warning(err)
				}

				if retention.HasBeenDownloaded(recentEpisode) {
					log.WithFields(log.Fields{
						"show":   show.Name,
						"number": recentEpisode.Number,
						"season": recentEpisode.Season,
						"name":   recentEpisode.Name,
					}).Debug("Episode already downloaded, nothing to do")
					continue
				}

				if retention.IsDownloading(recentEpisode) {
					log.WithFields(log.Fields{
						"show":   show.Name,
						"number": recentEpisode.Number,
						"season": recentEpisode.Season,
						"name":   recentEpisode.Name,
					}).Debug("Episode already being downloaded, nothing to do")
					continue
				}

				torrentList, err := indexer.GetTorrentForEpisode(show.Name, recentEpisode.Season, recentEpisode.Number)
				if err != nil {
					log.Warning(err)
					continue
				}
				log.Debug("Torrents found: ", len(torrentList))

				// Send only download for first 10 torrents. If the first 10 don't work, the download is probably fucked, or another problem is happening and trying more torrents won't change anything
				var toDownload []Torrent
				if len(torrentList) < 10 {
					toDownload = torrentList
				} else {
					toDownload = torrentList[:10]
				}

				go downloader.Download(show, recentEpisode, toDownload)
			}
		}

		log.Debug("========== Polling loop end ==========\n")
		<-loopTicker.C
	}

	<-exit
}
<commit_msg>Removed exit channel<commit_after>package main

import (
	"strconv"
	"time"

	"github.com/macarrie/flemzerd/configuration"
	log "github.com/macarrie/flemzerd/logging"
	"github.com/macarrie/flemzerd/retention"
	flag "github.com/ogier/pflag"

	provider "github.com/macarrie/flemzerd/providers"
	"github.com/macarrie/flemzerd/providers/impl/tvdb"

	indexer "github.com/macarrie/flemzerd/indexers"
	"github.com/macarrie/flemzerd/indexers/impl/torznab"

	notifier "github.com/macarrie/flemzerd/notifiers"
	"github.com/macarrie/flemzerd/notifiers/impl/desktop"
	"github.com/macarrie/flemzerd/notifiers/impl/pushbullet"

	downloader "github.com/macarrie/flemzerd/downloaders"
	"github.com/macarrie/flemzerd/downloaders/impl/transmission"

	. "github.com/macarrie/flemzerd/objects"
)

func initProviders() {
	log.Debug("Initializing Providers")

	var newProviders []provider.Provider
	for providerType, providerElt := range configuration.Config.Providers {
		switch providerType {
		case "tvdb":
			np, _ := impl.New(providerElt["apikey"])
			newProviders = append(newProviders, np)
		default:
			log.WithFields(log.Fields{
				"providerType": providerType,
			}).Warning("Unknown provider type")
		}

		if len(newProviders) != 0 {
			for _, newProvider := range newProviders {
				provider.AddProvider(newProvider)
				log.WithFields(log.Fields{
					"provider": providerType,
				}).Info("Provider added to list of providers")
			}
			newProviders = []provider.Provider{}
		}
	}
}

func initIndexers() {
	log.Debug("Initializing Indexers")

	var newIndexers []indexer.Indexer
	for indexerType, indexerList := range configuration.Config.Indexers {
		switch indexerType {
		case "torznab":
			for _, indexer := range indexerList {
				newIndexers = append(newIndexers, torznab.New(indexer["name"], indexer["url"], indexer["apikey"]))
			}
		default:
			log.WithFields(log.Fields{
				"indexerType": indexerType,
			}).Warning("Unknown indexer type")
		}

		if len(newIndexers) != 0 {
			for _, newIndexer := range newIndexers {
				indexer.AddIndexer(newIndexer)
				log.WithFields(log.Fields{
					"indexer": newIndexer.GetName(),
				}).Info("Indexer added to list of indexers")
			}
			newIndexers = []indexer.Indexer{}
		}
	}
}

func initDownloaders() {
	log.Debug("Initializing Downloaders")

	var newDownloaders []downloader.Downloader
	for name, downloaderObject := range configuration.Config.Downloaders {
		switch name {
		case "transmission":
			address := downloaderObject["address"]
			port, _ := strconv.Atoi(downloaderObject["port"])
			user, authNeeded := downloaderObject["user"]
			password := downloaderObject["password"]
			if !authNeeded {
				user = ""
				password = ""
			}

			transmissionDownloader := transmission.New(address, port, user, password)
			newDownloaders = append(newDownloaders, transmissionDownloader)
		default:
			log.WithFields(log.Fields{
				"downloaderType": name,
			}).Warning("Unknown downloader type")
		}

		if len(newDownloaders) != 0 {
			for _, newDownloader := range newDownloaders {
				newDownloader.Init()
				downloader.AddDownloader(newDownloader)
				log.WithFields(log.Fields{
					"downloader": name,
				}).Info("Downloader added to list of downloaders")
			}
			newDownloaders = []downloader.Downloader{}
		}
	}
}

func initNotifiers() {
	log.Debug("Initializing Notifiers")

	for name, notifierObject := range configuration.Config.Notifiers {
		switch name {
		case "pushbullet":
			pushbulletNotifier := pushbullet.New(map[string]string{"AccessToken": notifierObject["accesstoken"]})
			notifier.AddNotifier(pushbulletNotifier)

			log.WithFields(log.Fields{
				"notifier": name,
			}).Info("Notifier added to list of notifiers")

		case "desktop":
			desktopNotifier := desktop.New()
			notifier.AddNotifier(desktopNotifier)

			log.WithFields(log.Fields{
				"notifier": name,
			}).Info("Notifier added to list of notifiers")

		default:
			log.WithFields(log.Fields{
				"notifierType": name,
			}).Warning("Unknown notifier type")
		}
	}
}

func main() {
	debugMode := flag.BoolP("debug", "d", false, "Start in debug mode")
	configFilePath := flag.StringP("config", "c", "", "Configuration file path to use")

	flag.Parse()

	if *debugMode {
		log.Setup(true)
	} else {
		log.Setup(false)
	}

	if *configFilePath != "" {
		log.Info("Loading provided configuration file")
		configuration.UseFile(*configFilePath)
	}

	err := configuration.Load()
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Fatal("Cannot load configuration file")
	}

	err = configuration.Check()
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Fatal("Errors in configuration file")
	}

	initNotifiers()
	initProviders()
	initIndexers()
	initDownloaders()
	retention.Load()

	//	 Load configuration objects
	var showObjects []TvShow

	for _, show := range configuration.Config.Shows {
		showName := show
		show, err := provider.FindShow(show)
		if err != nil {
			log.WithFields(log.Fields{
				"error": err,
				"show":  showName,
			}).Warning("Unable to get show informations")
		} else {
			showObjects = append(showObjects, show)
		}
	}
	if len(showObjects) == 0 {
		log.Fatal("Impossible to get show informations for shows defined in configuration. Shutting down")
	}

	// TODO: Ticker interval is set in seconds for dev. Ticker interval must be changed from time.Second to time.Minute.
	loopTicker := time.NewTicker(time.Duration(configuration.Config.System.EpisodeCheckInterval) * time.Second)

	log.Debug("Starting polling loop")
	for {
		log.Debug("========== Polling loop start ==========")

		for _, show := range showObjects {
			recentEpisodes, err := provider.FindRecentlyAiredEpisodesForShow(show)
			if err != nil {
				log.WithFields(log.Fields{
					"error": err,
					"show":  show.Name,
				}).Warning("No recent episodes found")
				continue
			}

			for _, recentEpisode := range recentEpisodes {
				err := notifier.NotifyRecentEpisode(show, recentEpisode)
				if err != nil {
					log.Warning(err)
				}

				if retention.HasBeenDownloaded(recentEpisode) {
					log.WithFields(log.Fields{
						"show":   show.Name,
						"number": recentEpisode.Number,
						"season": recentEpisode.Season,
						"name":   recentEpisode.Name,
					}).Debug("Episode already downloaded, nothing to do")
					continue
				}

				if retention.IsDownloading(recentEpisode) {
					log.WithFields(log.Fields{
						"show":   show.Name,
						"number": recentEpisode.Number,
						"season": recentEpisode.Season,
						"name":   recentEpisode.Name,
					}).Debug("Episode already being downloaded, nothing to do")
					continue
				}

				torrentList, err := indexer.GetTorrentForEpisode(show.Name, recentEpisode.Season, recentEpisode.Number)
				if err != nil {
					log.Warning(err)
					continue
				}
				log.Debug("Torrents found: ", len(torrentList))

				// Send only download for first 10 torrents. If the first 10 don't work, the download is probably fucked, or another problem is happening and trying more torrents won't change anything
				var toDownload []Torrent
				if len(torrentList) < 10 {
					toDownload = torrentList
				} else {
					toDownload = torrentList[:10]
				}

				go downloader.Download(show, recentEpisode, toDownload)
			}
		}

		log.Debug("========== Polling loop end ==========\n")
		<-loopTicker.C
	}
}
<|endoftext|>


<commit_before>package main

import (
    "net/http"
    "fmt"
)

func HelloWorldHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello world")
}

func main() {
    http.HandleFunc("/import", ImportHandler)
    http.HandleFunc("/", HelloWorldHandler)

    fmt.Println("Connected to mongodb, wait for connections on :8080")
    http.ListenAndServe(":8080", nil)
}
<commit_msg>Teste deploy<commit_after>package main

import (
    "net/http"
    "fmt"
)

func HelloWorldHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Version 1.1.2")
}

func main() {
    http.HandleFunc("/import", ImportHandler)
    http.HandleFunc("/", HelloWorldHandler)

    fmt.Println("Connected to mongodb, wait for connections on :8080")
    http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>package main

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

	"github.com/ant0ine/go-json-rest/rest"
	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
	"github.com/mattes/migrate/migrate"
	"github.com/spf13/viper"
)
import _ "github.com/mattes/migrate/driver/postgres"

// UserJSON is used for empty requests
type UserJSON struct {
	ID           int `json:"id"`
	DialogsCount int `json:"dialogs_count"`
}

// DialogJSON used for index action of API
type DialogJSON struct {
	ID                int       `json:"id"`
	Name              string    `json:"name"`
	CreatedAt         time.Time `json:"created_at"`
	UpdatedAt         time.Time `json:"updated_at"`
	UserIds           string    `json:"user_ids"`
	LastMessage       string    `json:"last_message"`
	LastMessageID     int       `json:"last_message_id"`
	LastMessageUserID int       `json:"last_message_user_id"`
	LastSeenMessageID int       `json:"last_seen_message_id"`
}

// DialogCreateJSON is used for dialogs creation
type DialogCreateJSON struct {
	Name    string `json:"name"`
	UserIds []int  `json:"user_ids"`
	Message string `json:"message"`
}

// Dialog is used to save dialogs into DB via GORM
type Dialog struct {
	ID            int       `json:"id"`
	Name          string    `json:"name"`
	LastMessageID int       `json:"last_message_id"`
	CreatedAt     time.Time `json:"created_at"`
	UpdatedAt     time.Time `json:"updated_at"`
}

// DialogShowJSON is used to form json
type DialogShowJSON struct {
	ID       int           `json:"id"`
	Name     string        `json:"name"`
	Messages []MessageJSON `json:"messages"`
}

// MessageJSON is used to response message in JSON format
type MessageJSON struct {
	ID        int       `json:"id"`
	Text      string    `json:"text"`
	CreatedAt time.Time `json:"created_at"`
	UserID    int       `json:"user_id"`
}

// Message is used to put messages in DB via GORM
type Message struct {
	ID       int    `json:"id"`
	Text     string `json:"text"`
	UserID   int    `json:"user_id"`
	DialogID int    `json:"dialog_id"`
}

func main() {
	readConfig()
	applyMigrations()
	i := Impl{}
	i.connectToDb()
	i.startChat()
}

func readConfig() {
	viper.SetEnvPrefix("chat")
	viper.SetDefault("database_url", "postgres:///chat_development?sslmode=disable")
	viper.SetDefault("bind_address", "localhost:8080")
	viper.AutomaticEnv()
	viper.BindEnv("database_url")
	viper.BindEnv("bind_address")
}

func applyMigrations() {
	allErrors, ok := migrate.UpSync(viper.GetString("database_url"), "./migrations")
	fmt.Println("Database: ", viper.GetString("database_url"))
	if !ok {
		fmt.Println("Migratinos failed")
		fmt.Println("driver: ", viper.GetString("database_url"))
		fmt.Println("Errors: ", allErrors)
	}
}

func (i *Impl) connectToDb() {
	var err error
	i.DB, err = gorm.Open("postgres", viper.GetString("database_url"))
	if err != nil {
		log.Fatalf("Got error when connect database, the error is '%v'", err)
	}
	i.DB.LogMode(true)
}

// Impl used to provide handler to DB
type Impl struct {
	DB gorm.DB
}

func (i *Impl) startChat() {
	api := rest.NewApi()
	api.Use(rest.DefaultDevStack...)
	router, err := rest.MakeRouter(
		rest.Get("/", Index),
		rest.Get("/users/:user_id.json", i.UserShow),
		rest.Get("/users/:user_id/dialogs.json", i.DialogIndex),
		rest.Get("/users/:user_id/dialogs/:dialog_id/messages.json", i.MessageIndex),
		rest.Get("/users/:user_id/dialogs/:dialog_id.json", i.DialogShow),
		rest.Post("/users/:user_id/dialogs.json", i.DialogCreate),
		rest.Post("/users/:user_id/dialogs/:dialog_id/messages.json", i.MessageCreate),
	)
	if err != nil {
		log.Fatal(err)
	}
	api.SetApp(router)
	fmt.Println("address: ", viper.GetString("bind_address"))
	log.Fatal(http.ListenAndServe(viper.GetString("bind_address"), api.MakeHandler()))
}

// Index is status function
func Index(w rest.ResponseWriter, r *rest.Request) {
	w.WriteJson("Welcome!\n")
}

// DialogIndex is used to get dialogs index
func (i *Impl) DialogIndex(w rest.ResponseWriter, r *rest.Request) {
	userID := r.PathParam("user_id")
	page, err := strconv.Atoi(r.FormValue("page"))
	offset := 0
	if err == nil {
		offset = (page - 1) * 10
	}

	dialogs := []DialogJSON{}
	i.DB.Raw(`
    SELECT c.*, array_agg(du.user_id) AS user_ids
    FROM
      (SELECT
        dialogs.id AS id,
        dialogs.name AS name,
        dialogs.created_at AS created_at,
        dialogs.updated_at AS updated_at,
        dialogs.last_message_id AS last_message_id,
        messages.text AS last_message,
        messages.user_id AS last_message_user_id,
    	  dialog_users.last_seen_message_id AS last_seen_message_id
      FROM dialogs
      JOIN messages ON messages.id = dialogs.last_message_id
      JOIN dialog_users ON dialog_users.dialog_id = dialogs.id
      WHERE dialog_users.user_id = ?
      ORDER BY dialogs.last_message_id DESC
      ) c
    JOIN dialog_users du ON c.id = du.dialog_id
    GROUP BY
      c.id,
      c.name,
      c.created_at,
      c.updated_at,
      c.last_message_id,
      c.last_message,
      c.last_message_user_id,
      c.last_seen_message_id
		ORDER BY c.last_message_id DESC
    LIMIT 10
    OFFSET ?
  `, userID, offset).Find(&dialogs)
	w.WriteJson(&dialogs)
}

// DialogShow is used to show dialog for RAILS
func (i *Impl) DialogShow(w rest.ResponseWriter, r *rest.Request) {
	userID := r.PathParam("user_id")
	dialogID, _ := strconv.Atoi(r.PathParam("dialog_id"))

	dialog := DialogJSON{}
	i.DB.Raw(`
    SELECT c.*, array_agg(du.user_id) AS user_ids
    FROM
      (SELECT
        dialogs.id AS id,
        dialogs.name AS name,
        dialogs.created_at AS created_at,
        dialogs.updated_at AS updated_at,
        dialogs.last_message_id AS last_message_id,
        messages.text AS last_message,
        messages.user_id AS last_message_user_id,
    	  dialog_users.last_seen_message_id AS last_seen_message_id
      FROM dialogs
      JOIN messages ON messages.id = dialogs.last_message_id
      JOIN dialog_users ON dialog_users.dialog_id = dialogs.id
      WHERE dialog_users.user_id = ?
      ORDER BY dialogs.last_message_id DESC
      ) c
    JOIN dialog_users du ON c.id = du.dialog_id
    WHERE c.id = ?
    GROUP BY
      c.id,
      c.name,
      c.created_at,
      c.updated_at,
      c.last_message_id,
      c.last_message,
      c.last_message_user_id,
      c.last_seen_message_id
  `, userID, dialogID).Find(&dialog)

	lastMessageID := 0
	i.DB.Raw("SELECT last_message_id FROM dialogs WHERE id = ?", dialogID).Row().Scan(&lastMessageID)
	i.DB.Exec("UPDATE dialog_users SET last_seen_message_id = ? WHERE dialog_id = ? AND user_id = ?", lastMessageID, dialogID, userID)

	w.WriteJson(&dialog)
}

// MessageIndex is used to show dialog messages
func (i *Impl) MessageIndex(w rest.ResponseWriter, r *rest.Request) {
	userID := r.PathParam("user_id")
	dialogID, _ := strconv.Atoi(r.PathParam("dialog_id"))
	page, err := strconv.Atoi(r.FormValue("page"))
	offset := 0
	if err == nil {
		offset = (page - 1) * 10
	}
	messages := []MessageJSON{}
	i.DB.Raw(`
    SELECT * FROM messages
    WHERE messages.dialog_id = ?
    ORDER BY messages.id DESC
    LIMIT 10
    OFFSET ?
  `, dialogID, offset).Find(&messages)

	lastMessageID := 0
	i.DB.Raw("SELECT last_message_id FROM dialogs WHERE id = ?", dialogID).Row().Scan(&lastMessageID)
	i.DB.Exec("UPDATE dialog_users SET last_seen_message_id = ? WHERE dialog_id = ? AND user_id = ?", lastMessageID, dialogID, userID)

	w.WriteJson(&messages)
}

// UserShow returns number of unread dialogs
func (i *Impl) UserShow(w rest.ResponseWriter, r *rest.Request) {
	userID, _ := strconv.Atoi(r.PathParam("user_id"))
	user := UserJSON{}
	user.ID = userID
	dialogsCount := 0
	i.DB.Raw(`
		SELECT COUNT(dialogs.id)
		FROM dialogs, dialog_users
		WHERE
	  dialogs.last_message_id > dialog_users.last_seen_message_id AND
	  dialog_users.user_id = ? AND
	  dialog_users.dialog_id = dialogs.id
	`, user.ID).Row().Scan(&dialogsCount)
	user.DialogsCount = dialogsCount
	w.WriteJson(&user)
}

// DialogCreate is used to create dialog and message
func (i *Impl) DialogCreate(w rest.ResponseWriter, r *rest.Request) {
	userID, _ := strconv.Atoi(r.PathParam("user_id"))
	dialogJSON := DialogCreateJSON{}
	if err := r.DecodeJsonPayload(&dialogJSON); err != nil {
		fmt.Println("error decoding json: ", err)
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	dialog := Dialog{}
	dialog.Name = dialogJSON.Name

	if err := i.DB.Save(&dialog).Error; err != nil {
		fmt.Println("error saving message: ", err)
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	message := Message{}
	message.DialogID = dialog.ID
	message.Text = dialogJSON.Message
	message.UserID = userID

	if err := i.DB.Save(&message).Error; err != nil {
		fmt.Println("error saving message: ", err)
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	for _, element := range dialogJSON.UserIds {
		i.DB.Exec("INSERT INTO dialog_users (dialog_id, user_id, last_seen_message_id) VALUES (?, ?, 0)", dialog.ID, element)
	}

	i.DB.Exec("UPDATE dialogs SET last_message_id = ? WHERE id = ?", message.ID, dialog.ID)

	dialog.LastMessageID = message.ID

	fmt.Println("dialog json: ", dialog.ID)

	w.WriteJson(&dialog)
}

// MessageCreate creates message for dialog
func (i *Impl) MessageCreate(w rest.ResponseWriter, r *rest.Request) {
	userID, _ := strconv.Atoi(r.PathParam("user_id"))
	dialogID, _ := strconv.Atoi(r.PathParam("dialog_id"))
	message := Message{}
	if err := r.DecodeJsonPayload(&message); err != nil {
		fmt.Println("error decoding json: ", err)
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	message.DialogID = dialogID
	message.UserID = userID
	if err := i.DB.Save(&message).Error; err != nil {
		fmt.Println("error saving message: ", err)
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	i.DB.Exec("UPDATE dialogs SET last_message_id = ? WHERE dialogs.id = ?", message.ID, message.DialogID)
	i.DB.Exec("UPDATE dialog_users SET last_seen_message_id = ? WHERE dialog_id = ? AND user_id = ?", message.ID, message.DialogID, message.UserID)

	w.WriteJson(&message)
}
<commit_msg>Set updated_at for dialog on message create<commit_after>package main

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

	"github.com/ant0ine/go-json-rest/rest"
	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
	"github.com/mattes/migrate/migrate"
	"github.com/spf13/viper"
)
import _ "github.com/mattes/migrate/driver/postgres"

// UserJSON is used for empty requests
type UserJSON struct {
	ID           int `json:"id"`
	DialogsCount int `json:"dialogs_count"`
}

// DialogJSON used for index action of API
type DialogJSON struct {
	ID                int       `json:"id"`
	Name              string    `json:"name"`
	CreatedAt         time.Time `json:"created_at"`
	UpdatedAt         time.Time `json:"updated_at"`
	UserIds           string    `json:"user_ids"`
	LastMessage       string    `json:"last_message"`
	LastMessageID     int       `json:"last_message_id"`
	LastMessageUserID int       `json:"last_message_user_id"`
	LastSeenMessageID int       `json:"last_seen_message_id"`
}

// DialogCreateJSON is used for dialogs creation
type DialogCreateJSON struct {
	Name    string `json:"name"`
	UserIds []int  `json:"user_ids"`
	Message string `json:"message"`
}

// Dialog is used to save dialogs into DB via GORM
type Dialog struct {
	ID            int       `json:"id"`
	Name          string    `json:"name"`
	LastMessageID int       `json:"last_message_id"`
	CreatedAt     time.Time `json:"created_at"`
	UpdatedAt     time.Time `json:"updated_at"`
}

// DialogShowJSON is used to form json
type DialogShowJSON struct {
	ID       int           `json:"id"`
	Name     string        `json:"name"`
	Messages []MessageJSON `json:"messages"`
}

// MessageJSON is used to response message in JSON format
type MessageJSON struct {
	ID        int       `json:"id"`
	Text      string    `json:"text"`
	CreatedAt time.Time `json:"created_at"`
	UserID    int       `json:"user_id"`
}

// Message is used to put messages in DB via GORM
type Message struct {
	ID       int    `json:"id"`
	Text     string `json:"text"`
	UserID   int    `json:"user_id"`
	DialogID int    `json:"dialog_id"`
}

func main() {
	readConfig()
	applyMigrations()
	i := Impl{}
	i.connectToDb()
	i.startChat()
}

func readConfig() {
	viper.SetEnvPrefix("chat")
	viper.SetDefault("database_url", "postgres:///chat_development?sslmode=disable")
	viper.SetDefault("bind_address", "localhost:8080")
	viper.AutomaticEnv()
	viper.BindEnv("database_url")
	viper.BindEnv("bind_address")
}

func applyMigrations() {
	allErrors, ok := migrate.UpSync(viper.GetString("database_url"), "./migrations")
	fmt.Println("Database: ", viper.GetString("database_url"))
	if !ok {
		fmt.Println("Migratinos failed")
		fmt.Println("driver: ", viper.GetString("database_url"))
		fmt.Println("Errors: ", allErrors)
	}
}

func (i *Impl) connectToDb() {
	var err error
	i.DB, err = gorm.Open("postgres", viper.GetString("database_url"))
	if err != nil {
		log.Fatalf("Got error when connect database, the error is '%v'", err)
	}
	i.DB.LogMode(true)
}

// Impl used to provide handler to DB
type Impl struct {
	DB gorm.DB
}

func (i *Impl) startChat() {
	api := rest.NewApi()
	api.Use(rest.DefaultDevStack...)
	router, err := rest.MakeRouter(
		rest.Get("/", Index),
		rest.Get("/users/:user_id.json", i.UserShow),
		rest.Get("/users/:user_id/dialogs.json", i.DialogIndex),
		rest.Get("/users/:user_id/dialogs/:dialog_id/messages.json", i.MessageIndex),
		rest.Get("/users/:user_id/dialogs/:dialog_id.json", i.DialogShow),
		rest.Post("/users/:user_id/dialogs.json", i.DialogCreate),
		rest.Post("/users/:user_id/dialogs/:dialog_id/messages.json", i.MessageCreate),
	)
	if err != nil {
		log.Fatal(err)
	}
	api.SetApp(router)
	fmt.Println("address: ", viper.GetString("bind_address"))
	log.Fatal(http.ListenAndServe(viper.GetString("bind_address"), api.MakeHandler()))
}

// Index is status function
func Index(w rest.ResponseWriter, r *rest.Request) {
	w.WriteJson("Welcome!\n")
}

// DialogIndex is used to get dialogs index
func (i *Impl) DialogIndex(w rest.ResponseWriter, r *rest.Request) {
	userID := r.PathParam("user_id")
	page, err := strconv.Atoi(r.FormValue("page"))
	offset := 0
	if err == nil {
		offset = (page - 1) * 10
	}

	dialogs := []DialogJSON{}
	i.DB.Raw(`
    SELECT c.*, array_agg(du.user_id) AS user_ids
    FROM
      (SELECT
        dialogs.id AS id,
        dialogs.name AS name,
        dialogs.created_at AS created_at,
        dialogs.updated_at AS updated_at,
        dialogs.last_message_id AS last_message_id,
        messages.text AS last_message,
        messages.user_id AS last_message_user_id,
    	  dialog_users.last_seen_message_id AS last_seen_message_id
      FROM dialogs
      JOIN messages ON messages.id = dialogs.last_message_id
      JOIN dialog_users ON dialog_users.dialog_id = dialogs.id
      WHERE dialog_users.user_id = ?
      ORDER BY dialogs.last_message_id DESC
      ) c
    JOIN dialog_users du ON c.id = du.dialog_id
    GROUP BY
      c.id,
      c.name,
      c.created_at,
      c.updated_at,
      c.last_message_id,
      c.last_message,
      c.last_message_user_id,
      c.last_seen_message_id
		ORDER BY c.last_message_id DESC
    LIMIT 10
    OFFSET ?
  `, userID, offset).Find(&dialogs)
	w.WriteJson(&dialogs)
}

// DialogShow is used to show dialog for RAILS
func (i *Impl) DialogShow(w rest.ResponseWriter, r *rest.Request) {
	userID := r.PathParam("user_id")
	dialogID, _ := strconv.Atoi(r.PathParam("dialog_id"))

	dialog := DialogJSON{}
	i.DB.Raw(`
    SELECT c.*, array_agg(du.user_id) AS user_ids
    FROM
      (SELECT
        dialogs.id AS id,
        dialogs.name AS name,
        dialogs.created_at AS created_at,
        dialogs.updated_at AS updated_at,
        dialogs.last_message_id AS last_message_id,
        messages.text AS last_message,
        messages.user_id AS last_message_user_id,
    	  dialog_users.last_seen_message_id AS last_seen_message_id
      FROM dialogs
      JOIN messages ON messages.id = dialogs.last_message_id
      JOIN dialog_users ON dialog_users.dialog_id = dialogs.id
      WHERE dialog_users.user_id = ?
      ORDER BY dialogs.last_message_id DESC
      ) c
    JOIN dialog_users du ON c.id = du.dialog_id
    WHERE c.id = ?
    GROUP BY
      c.id,
      c.name,
      c.created_at,
      c.updated_at,
      c.last_message_id,
      c.last_message,
      c.last_message_user_id,
      c.last_seen_message_id
  `, userID, dialogID).Find(&dialog)

	lastMessageID := 0
	i.DB.Raw("SELECT last_message_id FROM dialogs WHERE id = ?", dialogID).Row().Scan(&lastMessageID)
	i.DB.Exec("UPDATE dialog_users SET last_seen_message_id = ? WHERE dialog_id = ? AND user_id = ?", lastMessageID, dialogID, userID)

	w.WriteJson(&dialog)
}

// MessageIndex is used to show dialog messages
func (i *Impl) MessageIndex(w rest.ResponseWriter, r *rest.Request) {
	userID := r.PathParam("user_id")
	dialogID, _ := strconv.Atoi(r.PathParam("dialog_id"))
	page, err := strconv.Atoi(r.FormValue("page"))
	offset := 0
	if err == nil {
		offset = (page - 1) * 10
	}
	messages := []MessageJSON{}
	i.DB.Raw(`
    SELECT * FROM messages
    WHERE messages.dialog_id = ?
    ORDER BY messages.id DESC
    LIMIT 10
    OFFSET ?
  `, dialogID, offset).Find(&messages)

	lastMessageID := 0
	i.DB.Raw("SELECT last_message_id FROM dialogs WHERE id = ?", dialogID).Row().Scan(&lastMessageID)
	i.DB.Exec("UPDATE dialog_users SET last_seen_message_id = ? WHERE dialog_id = ? AND user_id = ?", lastMessageID, dialogID, userID)

	w.WriteJson(&messages)
}

// UserShow returns number of unread dialogs
func (i *Impl) UserShow(w rest.ResponseWriter, r *rest.Request) {
	userID, _ := strconv.Atoi(r.PathParam("user_id"))
	user := UserJSON{}
	user.ID = userID
	dialogsCount := 0
	i.DB.Raw(`
		SELECT COUNT(dialogs.id)
		FROM dialogs, dialog_users
		WHERE
	  dialogs.last_message_id > dialog_users.last_seen_message_id AND
	  dialog_users.user_id = ? AND
	  dialog_users.dialog_id = dialogs.id
	`, user.ID).Row().Scan(&dialogsCount)
	user.DialogsCount = dialogsCount
	w.WriteJson(&user)
}

// DialogCreate is used to create dialog and message
func (i *Impl) DialogCreate(w rest.ResponseWriter, r *rest.Request) {
	userID, _ := strconv.Atoi(r.PathParam("user_id"))
	dialogJSON := DialogCreateJSON{}
	if err := r.DecodeJsonPayload(&dialogJSON); err != nil {
		fmt.Println("error decoding json: ", err)
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	dialog := Dialog{}
	dialog.Name = dialogJSON.Name

	if err := i.DB.Save(&dialog).Error; err != nil {
		fmt.Println("error saving message: ", err)
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	message := Message{}
	message.DialogID = dialog.ID
	message.Text = dialogJSON.Message
	message.UserID = userID

	if err := i.DB.Save(&message).Error; err != nil {
		fmt.Println("error saving message: ", err)
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	for _, element := range dialogJSON.UserIds {
		i.DB.Exec("INSERT INTO dialog_users (dialog_id, user_id, last_seen_message_id) VALUES (?, ?, 0)", dialog.ID, element)
	}

	i.DB.Exec("UPDATE dialogs SET last_message_id = ? WHERE id = ?", message.ID, dialog.ID)

	dialog.LastMessageID = message.ID

	fmt.Println("dialog json: ", dialog.ID)

	w.WriteJson(&dialog)
}

// MessageCreate creates message for dialog
func (i *Impl) MessageCreate(w rest.ResponseWriter, r *rest.Request) {
	userID, _ := strconv.Atoi(r.PathParam("user_id"))
	dialogID, _ := strconv.Atoi(r.PathParam("dialog_id"))
	message := Message{}
	if err := r.DecodeJsonPayload(&message); err != nil {
		fmt.Println("error decoding json: ", err)
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	message.DialogID = dialogID
	message.UserID = userID
	if err := i.DB.Save(&message).Error; err != nil {
		fmt.Println("error saving message: ", err)
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	i.DB.Exec("UPDATE dialogs SET last_message_id = ? AND updated_at = now() WHERE dialogs.id = ?", message.ID, message.DialogID)
	i.DB.Exec("UPDATE dialog_users SET last_seen_message_id = ? WHERE dialog_id = ? AND user_id = ?", message.ID, message.DialogID, message.UserID)

	w.WriteJson(&message)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"path/filepath"

	"github.com/go-chi/chi"
	"github.com/qor/admin"
	"github.com/qor/middlewares"
	"github.com/qor/publish2"
	"github.com/qor/qor"
	"github.com/qor/qor-example/app/home"
	"github.com/qor/qor-example/config"
	"github.com/qor/qor-example/config/admin/bindatafs"
	"github.com/qor/qor-example/config/application"
	"github.com/qor/qor-example/config/auth"
	"github.com/qor/qor-example/config/db"
	_ "github.com/qor/qor-example/config/db/migrations"
	"github.com/qor/qor/utils"
	"github.com/qor/wildcard_router"
)

func main() {
	cmdLine := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
	compileTemplate := cmdLine.Bool("compile-templates", false, "Compile Templates")
	cmdLine.Parse(os.Args[1:])

	var (
		Router      = chi.NewRouter()
		Admin       = admin.New(&qor.Config{DB: db.DB.Set(publish2.VisibleMode, publish2.ModeOff).Set(publish2.ScheduleMode, publish2.ModeOff)})
		Application = application.New(&application.Config{
			Router: Router,
			Admin:  Admin,
			DB:     db.DB,
		})
	)

	Application.Use(home.New(&home.Config{}))

	rootMux := http.NewServeMux()
	rootMux.Handle("/auth/", auth.Auth.NewServeMux())
	rootMux.Handle("/system/", utils.FileServer(http.Dir(filepath.Join(config.Root, "public"))))
	assetFS := bindatafs.AssetFS.FileServer(http.Dir("public"), "javascripts", "stylesheets", "images", "dist", "fonts", "vendors")
	for _, path := range []string{"javascripts", "stylesheets", "images", "dist", "fonts", "vendors"} {
		rootMux.Handle(fmt.Sprintf("/%s/", path), assetFS)
	}

	wildcardRouter := wildcard_router.New()
	wildcardRouter.MountTo("/", rootMux)
	wildcardRouter.AddHandler(Router)

	if *compileTemplate {
		bindatafs.AssetFS.Compile()
	} else {
		fmt.Printf("Listening on: %v\n", config.Config.Port)
		if err := http.ListenAndServe(fmt.Sprintf(":%d", config.Config.Port), middlewares.Apply(wildcardRouter)); err != nil {
			panic(err)
		}
	}
}
<commit_msg>Fix serve static files<commit_after>package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"path/filepath"

	"github.com/go-chi/chi"
	"github.com/go-chi/chi/middleware"
	"github.com/qor/admin"
	"github.com/qor/middlewares"
	"github.com/qor/publish2"
	"github.com/qor/qor"
	"github.com/qor/qor-example/app/home"
	"github.com/qor/qor-example/config"
	"github.com/qor/qor-example/config/admin/bindatafs"
	"github.com/qor/qor-example/config/application"
	"github.com/qor/qor-example/config/auth"
	"github.com/qor/qor-example/config/db"
	_ "github.com/qor/qor-example/config/db/migrations"
	"github.com/qor/qor/utils"
	"github.com/qor/wildcard_router"
)

func main() {
	cmdLine := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
	compileTemplate := cmdLine.Bool("compile-templates", false, "Compile Templates")
	cmdLine.Parse(os.Args[1:])

	var (
		Router      = chi.NewRouter()
		Admin       = admin.New(&qor.Config{DB: db.DB.Set(publish2.VisibleMode, publish2.ModeOff).Set(publish2.ScheduleMode, publish2.ModeOff)})
		Application = application.New(&application.Config{
			Router: Router,
			Admin:  Admin,
			DB:     db.DB,
		})
	)

	Router.Use(middleware.RealIP)
	Router.Use(middleware.Logger)
	Router.Use(middleware.Recoverer)

	Application.Use(home.New(&home.Config{}))

	rootMux := http.NewServeMux()
	rootMux.Handle("/auth/", auth.Auth.NewServeMux())
	rootMux.Handle("/system/", utils.FileServer(http.Dir(filepath.Join(config.Root, "public"))))
	assetFS := bindatafs.AssetFS.FileServer(http.Dir("public"), "javascripts", "stylesheets", "images", "dist", "fonts", "vendors")
	for _, path := range []string{"javascripts", "stylesheets", "images", "dist", "fonts", "vendors"} {
		rootMux.Handle(fmt.Sprintf("/%s/", path), assetFS)
	}

	wildcardRouter := wildcard_router.New()
	wildcardRouter.AddHandler(Router)
	wildcardRouter.MountTo("/", rootMux)

	if *compileTemplate {
		bindatafs.AssetFS.Compile()
	} else {
		fmt.Printf("Listening on: %v\n", config.Config.Port)
		if err := http.ListenAndServe(fmt.Sprintf(":%d", config.Config.Port), middlewares.Apply(rootMux)); err != nil {
			panic(err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/csv"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"strings"
	"time"

	"github.com/fsnotify/fsnotify"
	"github.com/spf13/viper"
)

type Program struct {
	URL      string
	Duration time.Duration
}

// Loads a list of programs.
// A program consists of a list things to display on the rotator along
// with a number of seconds to display each one before moving on.
func loadProgramList(filename string) []Program {

	fmt.Printf("Loading programs from %s\n", filename)

	var list []Program

	bytes, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}
	webpages := string(bytes)

	r := csv.NewReader(strings.NewReader(webpages))

	for {
		var p Program
		record, err := r.Read()
		if err == io.EOF {
			fmt.Printf("Finished loading programs.\n\n")
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		p.URL = record[0]
		p.Duration, err = time.ParseDuration(record[1])
		fmt.Printf("  Loaded program %.50s to show for %s.\n", p.URL, p.Duration)
		list = append(list, p)
	}

	return list
}

func runProgram(program Program, abort chan struct{}) {

	// Does this leak goroutines over time because they are created more than they are consumed?
	// This will need to be revisited when a more elaborate API and/or console UI
	go func() {
		os.Stdin.Read(make([]byte, 1)) // read a single byte
		fmt.Println(" >> Got keyboard input, that means you want to move to the next program.  Can do! << \n")
		abort <- struct{}{}
	}()

	ip := viper.Get("IP")
	port := viper.GetInt("PORT")

	constr := fmt.Sprintf("%s:%d", ip, port)

	conn, err := net.Dial("tcp", constr)
	if err != nil {
		fmt.Printf("Error making network connection to: %s\n", constr)
		fmt.Println("Pausing for 30s")
		time.Sleep(30 * time.Second) // wait 30 seconds to slow retries
		return
	}

	fmt.Printf("Running program for %s:  %s\n", program.Duration, program.URL)
	fmt.Fprintf(conn, "window.location='%s'\n", program.URL)
	status, err := bufio.NewReader(conn).ReadString('\n')
	fmt.Printf("  %s", status)

	select {
	case <-time.After(program.Duration):
		// Do nothing.
	case <-abort:
		fmt.Println("Current program aborted")
		return
	}
}

// InitializeConfig loads our configuration using Viper package.
func InitializeConfig(abort chan<- struct{}) {

	viper.SetConfigType("yaml")
	// Set config file
	viper.SetConfigName("config")

	// Add config path
	//	viper.AddConfigPath("$HOME/.gorotator")
	viper.AddConfigPath(".")

	// Read in the config
	err := viper.ReadInConfig() // Find and read the config file
	if err != nil {             // Handle errors reading the config file
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}

	// Load default settings
	viper.SetDefault("debug", false)

	viper.SetEnvPrefix("gorotator") // will be uppercased automatically
	viper.BindEnv("debug")
	viper.BindEnv("ip")
	viper.BindEnv("port")

	// Do some flag handling and any complicated config logic
	if !viper.IsSet("ip") || !viper.IsSet("port") {
		fmt.Println("Configuration error.  Both IP and PORT must be set via either config or environment.")
		os.Exit(1)
	}

	viper.WatchConfig()
	viper.OnConfigChange(func(e fsnotify.Event) {
		fmt.Println("\nConfig file changed:", e.Name)
		abort <- struct{}{}
		fmt.Println("Content will change immediately.\n")
	})

}

func main() {

	// Control channel to stop running programs immediately
	abort := make(chan struct{})

	InitializeConfig(abort)

	// Load and run the acctive program_file indefinately
	for {
		// We pull filename inside the loop because the
		// configuration can change while our program is running.
		filename := viper.GetString("program_file")
		pl := loadProgramList(filename)

		for _, p := range pl {
			runProgram(p, abort)
		}

		fmt.Printf("\nLooping back to play program list from beginning\n\n")
	}
}
<commit_msg>Have main loop and webserver working concurrently<commit_after>package main

import (
	"bufio"
	"encoding/csv"
	"fmt"
	"html"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/fsnotify/fsnotify"
	"github.com/spf13/viper"
)

type Program struct {
	URL      string
	Duration time.Duration
}

// Loads a list of programs.
// A program consists of a list things to display on the rotator along
// with a number of seconds to display each one before moving on.
func loadProgramList(filename string) []Program {

	fmt.Printf("Loading programs from %s\n", filename)

	var list []Program

	bytes, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}
	webpages := string(bytes)

	r := csv.NewReader(strings.NewReader(webpages))

	for {
		var p Program
		record, err := r.Read()
		if err == io.EOF {
			fmt.Printf("Finished loading programs.\n\n")
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		p.URL = record[0]
		p.Duration, err = time.ParseDuration(record[1])
		fmt.Printf("  Loaded program %.50s to show for %s.\n", p.URL, p.Duration)
		list = append(list, p)
	}

	return list
}

func runProgram(program Program, abort chan struct{}) {

	// Does this leak goroutines over time because they are created more than they are consumed?
	// This will need to be revisited when a more elaborate API and/or console UI
	go func() {
		os.Stdin.Read(make([]byte, 1)) // read a single byte
		fmt.Printf(" >> Got keyboard input, that means you want to move to the next program.  Can do! << \n\n")
		abort <- struct{}{}
	}()

	ip := viper.Get("IP")
	port := viper.GetInt("PORT")

	constr := fmt.Sprintf("%s:%d", ip, port)

	conn, err := net.Dial("tcp", constr)
	if err != nil {
		fmt.Printf("Error making network connection to: %s\n", constr)
		fmt.Println("Pausing for 30s")
		time.Sleep(30 * time.Second) // wait 30 seconds to slow retries
		return
	}

	fmt.Printf("Running program for %s:  %s\n", program.Duration, program.URL)
	fmt.Fprintf(conn, "window.location='%s'\n", program.URL)
	status, err := bufio.NewReader(conn).ReadString('\n')
	fmt.Printf("  %s", status)

	select {
	case <-time.After(program.Duration):
		// Do nothing.
	case <-abort:
		fmt.Println("Current program aborted")
		return
	}
}

// InitializeConfig loads our configuration using Viper package.
func InitializeConfig(abort chan<- struct{}) {

	viper.SetConfigType("yaml")
	// Set config file
	viper.SetConfigName("config")

	// Add config path
	//	viper.AddConfigPath("$HOME/.gorotator")
	viper.AddConfigPath(".")

	// Read in the config
	err := viper.ReadInConfig() // Find and read the config file
	if err != nil {             // Handle errors reading the config file
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}

	// Load default settings
	viper.SetDefault("debug", false)

	viper.SetEnvPrefix("gorotator") // will be uppercased automatically
	viper.BindEnv("debug")
	viper.BindEnv("ip")
	viper.BindEnv("port")

	// Do some flag handling and any complicated config logic
	if !viper.IsSet("ip") || !viper.IsSet("port") {
		fmt.Println("Configuration error.  Both IP and PORT must be set via either config or environment.")
		os.Exit(1)
	}

	viper.WatchConfig()
	viper.OnConfigChange(func(e fsnotify.Event) {
		fmt.Println("\nConfig file changed:", e.Name)
		abort <- struct{}{}
		fmt.Printf("Content will change immediately.\n\n")
	})

}

func LoadAndRunLoop(abort chan struct{}) {

	// Load and run the acctive program_file indefinately
	for {
		// We pull filename inside the loop because the
		// configuration can change while our program is running.
		filename := viper.GetString("program_file")
		pl := loadProgramList(filename)

		for _, p := range pl {
			runProgram(p, abort)
		}

		fmt.Printf("\nLooping back to play program list from beginning\n\n")
	}

}

func main() {

	// Control channel to stop running programs immediately
	abort := make(chan struct{})

	InitializeConfig(abort)

	go LoadAndRunLoop(abort)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
	})

	go log.Fatal(http.ListenAndServe(":8080", nil))

}
<|endoftext|>