mirror of https://github.com/statping/statping
parent
1aef1948d4
commit
c20cde567e
|
@ -18,7 +18,7 @@ services:
|
|||
|
||||
env:
|
||||
global:
|
||||
- VERSION=0.28.3
|
||||
- VERSION=0.28.4
|
||||
- DB_HOST=localhost
|
||||
- DB_USER=travis
|
||||
- DB_PASS=
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
FROM alpine:latest
|
||||
|
||||
ENV VERSION=v0.28.3
|
||||
ENV VERSION=v0.28.4
|
||||
|
||||
RUN apk --no-cache add libstdc++ ca-certificates
|
||||
RUN wget -q https://github.com/hunterlong/statup/releases/download/$VERSION/statup-linux-alpine.tar.gz && \
|
||||
|
@ -20,9 +20,6 @@ RUN printf "#!/usr/bin/env sh\n\$1\n" > $CMD_FILE && \
|
|||
chmod +x $CMD_FILE
|
||||
|
||||
WORKDIR /app
|
||||
#COPY build/statup-linux-alpine /usr/local/bin/statup
|
||||
|
||||
VOLUME /app
|
||||
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT statup
|
3
cli.go
3
cli.go
|
@ -16,6 +16,8 @@ func CatchCLI(args []string) {
|
|||
core.CreateAllAssets()
|
||||
case "sass":
|
||||
core.CompileSASS()
|
||||
case "api":
|
||||
HelpEcho()
|
||||
case "export":
|
||||
var err error
|
||||
fmt.Printf("Statup v%v Exporting Static 'index.html' page...\n", VERSION)
|
||||
|
@ -84,6 +86,7 @@ func HelpEcho() {
|
|||
fmt.Println(" statup - Main command to run Statup server")
|
||||
fmt.Println(" statup version - Returns the current version of Statup")
|
||||
fmt.Println(" statup run - Check all service 1 time and then quit")
|
||||
fmt.Println(" statup assets - Export all assets used locally to be edited.")
|
||||
fmt.Println(" statup env - Show all environment variables being used for Statup")
|
||||
fmt.Println(" statup export - Exports the index page as a static HTML for pushing")
|
||||
fmt.Println(" to Github Pages or your own FTP server. Export will")
|
||||
|
|
|
@ -96,6 +96,10 @@ func CreateAllAssets() {
|
|||
CopyToPublic(JsBox, "js", "jquery-3.3.1.slim.min.js")
|
||||
CopyToPublic(JsBox, "js", "main.js")
|
||||
CopyToPublic(JsBox, "js", "setup.js")
|
||||
CopyToPublic(JsBox, "js", "setup.js")
|
||||
CopyToPublic(TmplBox, "", "robots.txt")
|
||||
CopyToPublic(TmplBox, "", "favicon.ico")
|
||||
|
||||
utils.Log(1, "Compiling CSS from SCSS style...")
|
||||
err := CompileSASS()
|
||||
if err != nil {
|
||||
|
|
|
@ -5,7 +5,9 @@ import (
|
|||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
@ -33,7 +35,28 @@ func (s *Service) CheckQueue() {
|
|||
time.Sleep(time.Duration(s.Interval) * time.Second)
|
||||
}
|
||||
|
||||
func (s *Service) DNSCheck() (float64, error) {
|
||||
t1 := time.Now()
|
||||
url, err := url.Parse(s.Domain)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
_, err = net.LookupIP(url.Host)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
t2 := time.Now()
|
||||
subTime := t2.Sub(t1).Seconds()
|
||||
return subTime, err
|
||||
}
|
||||
|
||||
func (s *Service) Check() *Service {
|
||||
dnsLookup, err := s.DNSCheck()
|
||||
if err != nil {
|
||||
s.Failure(fmt.Sprintf("Could not get IP address for domain %v, %v", s.Domain, err))
|
||||
return s
|
||||
}
|
||||
s.dnsLookup = dnsLookup
|
||||
t1 := time.Now()
|
||||
client := http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
|
|
20
core/core.go
20
core/core.go
|
@ -10,16 +10,16 @@ type PluginJSON types.PluginJSON
|
|||
type PluginRepos types.PluginRepos
|
||||
|
||||
type Core struct {
|
||||
Name string `db:"name"`
|
||||
Description string `db:"description"`
|
||||
Config string `db:"config"`
|
||||
ApiKey string `db:"api_key"`
|
||||
ApiSecret string `db:"api_secret"`
|
||||
Style string `db:"style"`
|
||||
Footer string `db:"footer"`
|
||||
Domain string `db:"domain"`
|
||||
Version string `db:"version"`
|
||||
Services []*Service
|
||||
Name string `db:"name" json:"name"`
|
||||
Description string `db:"description" json:"name"`
|
||||
Config string `db:"config" json:"-"`
|
||||
ApiKey string `db:"api_key" json:"-"`
|
||||
ApiSecret string `db:"api_secret" json:"-"`
|
||||
Style string `db:"style" json:"-"`
|
||||
Footer string `db:"footer" json:"-"`
|
||||
Domain string `db:"domain" json:"domain,omitempty"`
|
||||
Version string `db:"version" json:"version,omitempty"`
|
||||
Services []*Service `json:"services,omitempty"`
|
||||
Plugins []plugin.Info
|
||||
Repos []PluginJSON
|
||||
//PluginFields []PluginSelect
|
||||
|
|
|
@ -94,6 +94,10 @@ func (f *Failure) ParseError() string {
|
|||
if err {
|
||||
return fmt.Sprintf("SSL Certificate invalid")
|
||||
}
|
||||
err = strings.Contains(f.Issue, "Client.Timeout exceeded while awaiting headers")
|
||||
if err {
|
||||
return fmt.Sprintf("Connection Timed Out")
|
||||
}
|
||||
err = strings.Contains(f.Issue, "no such host")
|
||||
if err {
|
||||
return fmt.Sprintf("Domain is offline or not found")
|
||||
|
@ -106,5 +110,13 @@ func (f *Failure) ParseError() string {
|
|||
if err {
|
||||
return fmt.Sprintf("Connection Failed")
|
||||
}
|
||||
err = strings.Contains(f.Issue, "can't assign requested address")
|
||||
if err {
|
||||
return fmt.Sprintf("Unable to Request Address")
|
||||
}
|
||||
err = strings.Contains(f.Issue, "no route to host")
|
||||
if err {
|
||||
return fmt.Sprintf("Domain is offline or not found")
|
||||
}
|
||||
return f.Issue
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ type Service struct {
|
|||
LastResponse string
|
||||
LastStatusCode int
|
||||
LastOnline time.Time
|
||||
dnsLookup float64 `json:"dns_lookup_time"`
|
||||
}
|
||||
|
||||
func serviceCol() db.Collection {
|
||||
|
@ -194,7 +195,12 @@ func (u *Service) Delete() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (u *Service) Update() {
|
||||
func (u *Service) Update(s *Service) {
|
||||
res := serviceCol().Find("id", u.Id)
|
||||
err := res.Update(s)
|
||||
if err != nil {
|
||||
utils.Log(3, fmt.Sprintf("Failed to update service %v. %v", u.Name, err))
|
||||
}
|
||||
OnUpdateService(u)
|
||||
}
|
||||
|
||||
|
|
|
@ -9,10 +9,18 @@ import (
|
|||
)
|
||||
|
||||
func ApiIndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !isAPIAuthorized(r) {
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(core.CoreApp)
|
||||
}
|
||||
|
||||
func ApiCheckinHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !isAPIAuthorized(r) {
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
checkin := core.FindCheckin(vars["api"])
|
||||
checkin.Receivehit()
|
||||
|
@ -21,32 +29,63 @@ func ApiCheckinHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func ApiServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !isAPIAuthorized(r) {
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||
json.NewEncoder(w).Encode(service)
|
||||
}
|
||||
|
||||
func ApiServiceUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !isAPIAuthorized(r) {
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||
var s core.Service
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
decoder.Decode(&s)
|
||||
json.NewEncoder(w).Encode(service)
|
||||
service.Update(&s)
|
||||
json.NewEncoder(w).Encode(s)
|
||||
}
|
||||
|
||||
func ApiAllServicesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !isAPIAuthorized(r) {
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
services, _ := core.SelectAllServices()
|
||||
json.NewEncoder(w).Encode(services)
|
||||
}
|
||||
|
||||
func ApiUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !isAPIAuthorized(r) {
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
user, _ := core.SelectUser(utils.StringInt(vars["id"]))
|
||||
json.NewEncoder(w).Encode(user)
|
||||
}
|
||||
|
||||
func ApiAllUsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !isAPIAuthorized(r) {
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
users, _ := core.SelectAllUsers()
|
||||
json.NewEncoder(w).Encode(users)
|
||||
}
|
||||
|
||||
func isAPIAuthorized(r *http.Request) bool {
|
||||
if IsAuthenticated(r) {
|
||||
return true
|
||||
}
|
||||
if isAuthorized(r) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -14,8 +14,7 @@ type dashboard struct {
|
|||
}
|
||||
|
||||
func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||
auth := IsAuthenticated(r)
|
||||
if !auth {
|
||||
if !IsAuthenticated(r) {
|
||||
err := core.ErrorResponse{}
|
||||
ExecuteResponse(w, r, "login.html", err)
|
||||
} else {
|
||||
|
|
|
@ -1,28 +1,10 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/hunterlong/statup/core"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func RobotsTxtHandler(w http.ResponseWriter, r *http.Request) {
|
||||
robots := []byte(`User-agent: *
|
||||
Disallow: /login
|
||||
Disallow: /dashboard
|
||||
|
||||
Host: ` + core.CoreApp.Domain)
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(robots))
|
||||
}
|
||||
|
||||
func FavIconHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data, err := core.TmplBox.String("favicon.ico")
|
||||
if err != nil {
|
||||
utils.Log(2, err)
|
||||
}
|
||||
w.Header().Set("Content-Type", "image/x-icon")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(data))
|
||||
func Error404Handler(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
ExecuteResponse(w, r, "error_404.html", nil)
|
||||
}
|
||||
|
|
|
@ -8,24 +8,26 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
//
|
||||
// Use your Statup Secret API Key for Authentication
|
||||
//
|
||||
// scrape_configs:
|
||||
// - job_name: 'statup'
|
||||
// bearer_token: MY API SECRET HERE
|
||||
// static_configs:
|
||||
// - targets: ['statup:8080']
|
||||
//
|
||||
|
||||
func PrometheusHandler(w http.ResponseWriter, r *http.Request) {
|
||||
utils.Log(1, fmt.Sprintf("Prometheus /metrics Request From IP: %v\n", r.RemoteAddr))
|
||||
var token string
|
||||
tokens, ok := r.Header["Authorization"]
|
||||
if ok && len(tokens) >= 1 {
|
||||
token = tokens[0]
|
||||
token = strings.TrimPrefix(token, "Bearer ")
|
||||
}
|
||||
if token != core.CoreApp.ApiSecret {
|
||||
if !isAuthorized(r) {
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
metrics := []string{}
|
||||
system := fmt.Sprintf("statup_total_failures %v\n", core.CountFailures())
|
||||
system += fmt.Sprintf("statup_total_services %v", len(core.CoreApp.Services))
|
||||
metrics = append(metrics, system)
|
||||
|
||||
for _, v := range core.CoreApp.Services {
|
||||
online := 1
|
||||
if !v.Online {
|
||||
|
@ -42,3 +44,16 @@ func PrometheusHandler(w http.ResponseWriter, r *http.Request) {
|
|||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(output))
|
||||
}
|
||||
|
||||
func isAuthorized(r *http.Request) bool {
|
||||
var token string
|
||||
tokens, ok := r.Header["Authorization"]
|
||||
if ok && len(tokens) >= 1 {
|
||||
token = tokens[0]
|
||||
token = strings.TrimPrefix(token, "Bearer ")
|
||||
}
|
||||
if token == core.CoreApp.ApiSecret {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -10,17 +10,7 @@ import (
|
|||
func Router() *mux.Router {
|
||||
r := mux.NewRouter()
|
||||
r.Handle("/", http.HandlerFunc(IndexHandler))
|
||||
if core.UsingAssets {
|
||||
cssHandler := http.FileServer(http.Dir("./assets/css"))
|
||||
jsHandler := http.FileServer(http.Dir("./assets/js"))
|
||||
r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", cssHandler))
|
||||
r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", jsHandler))
|
||||
} else {
|
||||
r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(core.CssBox.HTTPBox())))
|
||||
r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(core.JsBox.HTTPBox())))
|
||||
}
|
||||
r.Handle("/robots.txt", http.HandlerFunc(RobotsTxtHandler)).Methods("GET")
|
||||
r.Handle("/favicon.ico", http.HandlerFunc(FavIconHandler)).Methods("GET")
|
||||
LocalizedAssets(r)
|
||||
r.Handle("/setup", http.HandlerFunc(SetupHandler)).Methods("GET")
|
||||
r.Handle("/setup", http.HandlerFunc(ProcessSetupHandler)).Methods("POST")
|
||||
r.Handle("/dashboard", http.HandlerFunc(DashboardHandler)).Methods("GET")
|
||||
|
@ -54,6 +44,25 @@ func Router() *mux.Router {
|
|||
r.Handle("/api/users", http.HandlerFunc(ApiAllUsersHandler))
|
||||
r.Handle("/api/users/{id}", http.HandlerFunc(ApiUserHandler))
|
||||
r.Handle("/metrics", http.HandlerFunc(PrometheusHandler))
|
||||
r.NotFoundHandler = http.HandlerFunc(Error404Handler)
|
||||
Store = sessions.NewCookieStore([]byte("secretinfo"))
|
||||
return r
|
||||
}
|
||||
|
||||
func LocalizedAssets(r *mux.Router) *mux.Router {
|
||||
if core.UsingAssets {
|
||||
cssHandler := http.FileServer(http.Dir("./assets/css"))
|
||||
jsHandler := http.FileServer(http.Dir("./assets/js"))
|
||||
indexHandler := http.FileServer(http.Dir("./assets/"))
|
||||
r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", cssHandler))
|
||||
r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", jsHandler))
|
||||
r.PathPrefix("/robots.txt").Handler(indexHandler)
|
||||
r.PathPrefix("/favicon.ico").Handler(indexHandler)
|
||||
} else {
|
||||
r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(core.CssBox.HTTPBox())))
|
||||
r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(core.JsBox.HTTPBox())))
|
||||
r.PathPrefix("/robots.txt").Handler(http.FileServer(core.TmplBox.HTTPBox()))
|
||||
r.PathPrefix("/favicon.ico").Handler(http.FileServer(core.TmplBox.HTTPBox()))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ func ServicesUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
interval, _ := strconv.Atoi(r.PostForm.Get("interval"))
|
||||
port, _ := strconv.Atoi(r.PostForm.Get("port"))
|
||||
checkType := r.PostForm.Get("check_type")
|
||||
service = &core.Service{
|
||||
serviceUpdate := &core.Service{
|
||||
Name: name,
|
||||
Domain: domain,
|
||||
Method: method,
|
||||
|
@ -115,7 +115,7 @@ func ServicesUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
Type: checkType,
|
||||
Port: port,
|
||||
}
|
||||
service.Update()
|
||||
service.Update(serviceUpdate)
|
||||
ExecuteResponse(w, r, "service.html", service)
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ HTML, BODY {
|
|||
font-size: 2.3rem;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
color: #47d337; }
|
||||
color: #4f4f4f; }
|
||||
|
||||
.stats_area {
|
||||
text-align: center;
|
||||
|
|
|
@ -8,7 +8,7 @@ $description-color: #939393;
|
|||
$service-background: #ffffff;
|
||||
$service-border: 1px solid rgba(0,0,0,.125);
|
||||
$service-title: #444444;
|
||||
$service-stats-color: #47d337;
|
||||
$service-stats-color: #4f4f4f;
|
||||
$service-description-color: #fff;
|
||||
$service-stats-size: 2.3rem;
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, maximum-scale=1.0, user-scalable=0">
|
||||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/css/base.css">
|
||||
|
||||
<title>Statup | Page Not Found</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div class="container col-md-7 col-sm-12 mt-md-5 bg-light">
|
||||
|
||||
<div class="col-12 mt-3">
|
||||
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Sorry, this page doesn't seem to exist.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{template "footer"}}
|
||||
|
||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -3,6 +3,6 @@
|
|||
{{ if .Core.Footer }}
|
||||
{{ safe .Core.Footer }}
|
||||
{{ end }}
|
||||
<a href="https://statup.io" target="_blank">Statup {{ VERSION }} made with ❤️</a> | <a href="/dashboard">Dashboard</a>
|
||||
<a href="https://github.com/hunterlong/statup" target="_blank">Statup {{ VERSION }} made with ❤️</a> | <a href="/dashboard">Dashboard</a>
|
||||
</div>
|
||||
{{ end }}
|
|
@ -1,35 +0,0 @@
|
|||
{{define "slack"}}
|
||||
|
||||
<form action="/plugins/save_slack" method="POST">
|
||||
<div class="form-group row">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="customCheck1">
|
||||
<label class="custom-control-label" for="customCheck1">Slack Integration Enabled</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="inputPassword3" class="col-sm-4 col-form-label">API Key</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="domain" class="form-control" id="inputPassword3">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="inputPassword3" class="col-sm-4 col-form-label">API Secret</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="expected" class="form-control" id="inputPassword3">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="inputPassword3" class="col-sm-4 col-form-label">Channel</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" name="expected_status" class="form-control" id="inputPassword3">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-10">
|
||||
<button type="submit" class="btn btn-success">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{{end}}
|
|
@ -0,0 +1,3 @@
|
|||
User-agent: *
|
||||
Disallow: /login
|
||||
Disallow: /dashboard
|
|
@ -23,11 +23,11 @@ type Hit struct {
|
|||
}
|
||||
|
||||
type Failure struct {
|
||||
Id int `db:"id,omitempty"`
|
||||
Issue string `db:"issue"`
|
||||
Method string `db:"method"`
|
||||
Service int64 `db:"service"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
Id int `db:"id,omitempty" json:"id"`
|
||||
Issue string `db:"issue" json:"issue"`
|
||||
Method string `db:"method" json:"method,omitempty"`
|
||||
Service int64 `db:"service" json:"service_id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
type Checkin struct {
|
||||
|
@ -43,17 +43,17 @@ type Checkin struct {
|
|||
type Communication struct {
|
||||
Id int64 `db:"id,omitempty" json:"id"`
|
||||
Method string `db:"method" json:"method"`
|
||||
Host string `db:"host" json:"host"`
|
||||
Port int `db:"port" json:"port"`
|
||||
Username string `db:"username" json:"user"`
|
||||
Host string `db:"host" json:"-"`
|
||||
Port int `db:"port" json:"-"`
|
||||
Username string `db:"username" json:"-"`
|
||||
Password string `db:"password" json:"-"`
|
||||
Var1 string `db:"var1" json:"var1"`
|
||||
Var2 string `db:"var2" json:"var2"`
|
||||
ApiKey string `db:"api_key" json:"api_key"`
|
||||
ApiSecret string `db:"api_secret" json:"api_secret"`
|
||||
Var1 string `db:"var1" json:"-"`
|
||||
Var2 string `db:"var2" json:"-"`
|
||||
ApiKey string `db:"api_key" json:"-"`
|
||||
ApiSecret string `db:"api_secret" json:"-"`
|
||||
Enabled bool `db:"enabled" json:"enabled"`
|
||||
Limits int64 `db:"limits" json:"limits"`
|
||||
Removable bool `db:"removable" json:"removable"`
|
||||
Limits int64 `db:"limits" json:"-"`
|
||||
Removable bool `db:"removable" json:"-"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue