mirror of https://github.com/statping/statping
Merge branch 'dev' into grpcCheckFixes
commit
09b554e5a1
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -1,3 +1,17 @@
|
|||
# 0.90.69 (09-18-2020)
|
||||
- Fixed issue with service view not loading. #808 #811 #800
|
||||
|
||||
# 0.90.68 (09-17-2020)
|
||||
- Added DB_DSN env for mysql, postgres or sqlite DSN database connection string
|
||||
- Added READ_ONLY env for a read only connection to the database
|
||||
- Added Custom OAuth OpenID toggle switch in settings (appends 'openid' in scope)
|
||||
- Fixed Custom OAuth response_type issue
|
||||
- Added Configs tab in Settings to edit the config.yml from frontend
|
||||
|
||||
# 0.90.67 (09-14-2020)
|
||||
- Modified core settings to update config.yml on save
|
||||
- Modified Theme Editor to restart the HTTP router on create/delete (fixing 404's)
|
||||
|
||||
# 0.90.66 (09-08-2020)
|
||||
- Added Import and Export views in Dashboard
|
||||
- Modified services list sparkline to use start/end of day timestamp
|
||||
|
|
18
cmd/cli.go
18
cmd/cli.go
|
@ -18,6 +18,10 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
importAll *bool
|
||||
)
|
||||
|
||||
func assetsCli() error {
|
||||
dir := utils.Directory
|
||||
if err := utils.InitLogs(); err != nil {
|
||||
|
@ -254,6 +258,9 @@ func importCli(args []string) error {
|
|||
if len(exportData.Messages) > 0 {
|
||||
log.Printf("Messages: %d\n", len(exportData.Messages))
|
||||
}
|
||||
if len(exportData.Incidents) > 0 {
|
||||
log.Printf("Incidents: %d\n", len(exportData.Incidents))
|
||||
}
|
||||
if len(exportData.Users) > 0 {
|
||||
log.Printf("Users: %d\n", len(exportData.Users))
|
||||
}
|
||||
|
@ -285,14 +292,14 @@ func importCli(args []string) error {
|
|||
if ask("Import Core settings?") {
|
||||
c := exportData.Core
|
||||
if err := c.Update(); err != nil {
|
||||
return err
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
for _, s := range exportData.Groups {
|
||||
if ask(fmt.Sprintf("Import Group '%s'?", s.Name)) {
|
||||
s.Id = 0
|
||||
if err := s.Create(); err != nil {
|
||||
return err
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -300,7 +307,7 @@ func importCli(args []string) error {
|
|||
if ask(fmt.Sprintf("Import Service '%s'?", s.Name)) {
|
||||
s.Id = 0
|
||||
if err := s.Create(); err != nil {
|
||||
return err
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -308,7 +315,7 @@ func importCli(args []string) error {
|
|||
if ask(fmt.Sprintf("Import Checkin '%s'?", s.Name)) {
|
||||
s.Id = 0
|
||||
if err := s.Create(); err != nil {
|
||||
return err
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -316,7 +323,7 @@ func importCli(args []string) error {
|
|||
if ask(fmt.Sprintf("Import Message '%s'?", s.Title)) {
|
||||
s.Id = 0
|
||||
if err := s.Create(); err != nil {
|
||||
return err
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -333,6 +340,7 @@ func importCli(args []string) error {
|
|||
}
|
||||
|
||||
func ask(format string) bool {
|
||||
|
||||
fmt.Printf(fmt.Sprintf(format + " [y/N]: "))
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
text, _ := reader.ReadString('\n')
|
||||
|
|
|
@ -169,10 +169,6 @@ func Available(db Database) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("service = ?", 1000)
|
||||
}
|
||||
|
||||
func (it *Db) MultipleSelects(args ...string) Database {
|
||||
joined := strings.Join(args, ", ")
|
||||
return it.Select(joined)
|
||||
|
@ -181,6 +177,7 @@ func (it *Db) MultipleSelects(args ...string) Database {
|
|||
type Db struct {
|
||||
Database *gorm.DB
|
||||
Type string
|
||||
ReadOnly bool
|
||||
}
|
||||
|
||||
// Openw is a drop-in replacement for Open()
|
||||
|
@ -223,6 +220,9 @@ func OpenTester() (Database, error) {
|
|||
default:
|
||||
dbString = fmt.Sprintf("file:%s?mode=memory&cache=shared", utils.RandomString(12))
|
||||
}
|
||||
if utils.Params.IsSet("DB_DSN") {
|
||||
dbString = utils.Params.GetString("DB_DSN")
|
||||
}
|
||||
newDb, err := Openw(testDB, dbString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -239,6 +239,7 @@ func Wrap(db *gorm.DB) Database {
|
|||
return &Db{
|
||||
Database: db,
|
||||
Type: db.Dialect().GetName(),
|
||||
ReadOnly: utils.Params.GetBool("READ_ONLY"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,14 +380,26 @@ func (it *Db) Related(value interface{}, foreignKeys ...string) Database {
|
|||
}
|
||||
|
||||
func (it *Db) FirstOrInit(out interface{}, where ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.FirstOrInit(out, where...))
|
||||
}
|
||||
|
||||
func (it *Db) FirstOrCreate(out interface{}, where ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.FirstOrCreate(out, where...))
|
||||
}
|
||||
|
||||
func (it *Db) Update(attrs ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.Update(attrs...))
|
||||
}
|
||||
|
||||
|
@ -395,22 +408,42 @@ func (it *Db) Updates(values interface{}, ignoreProtectedAttrs ...bool) Database
|
|||
}
|
||||
|
||||
func (it *Db) UpdateColumn(attrs ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.UpdateColumn(attrs...))
|
||||
}
|
||||
|
||||
func (it *Db) UpdateColumns(values interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.UpdateColumns(values))
|
||||
}
|
||||
|
||||
func (it *Db) Save(value interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.Save(value))
|
||||
}
|
||||
|
||||
func (it *Db) Create(value interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.Create(value))
|
||||
}
|
||||
|
||||
func (it *Db) Delete(value interface{}, where ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.Delete(value, where...))
|
||||
}
|
||||
|
||||
|
@ -435,14 +468,26 @@ func (it *Db) Debug() Database {
|
|||
}
|
||||
|
||||
func (it *Db) Begin() Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.Begin())
|
||||
}
|
||||
|
||||
func (it *Db) Commit() Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.Commit())
|
||||
}
|
||||
|
||||
func (it *Db) Rollback() Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.Rollback())
|
||||
}
|
||||
|
||||
|
@ -455,14 +500,26 @@ func (it *Db) RecordNotFound() bool {
|
|||
}
|
||||
|
||||
func (it *Db) CreateTable(values ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.CreateTable(values...))
|
||||
}
|
||||
|
||||
func (it *Db) DropTable(values ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.DropTable(values...))
|
||||
}
|
||||
|
||||
func (it *Db) DropTableIfExists(values ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.DropTableIfExists(values...))
|
||||
}
|
||||
|
||||
|
@ -471,26 +528,50 @@ func (it *Db) HasTable(value interface{}) bool {
|
|||
}
|
||||
|
||||
func (it *Db) AutoMigrate(values ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.AutoMigrate(values...))
|
||||
}
|
||||
|
||||
func (it *Db) ModifyColumn(column string, typ string) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.ModifyColumn(column, typ))
|
||||
}
|
||||
|
||||
func (it *Db) DropColumn(column string) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.DropColumn(column))
|
||||
}
|
||||
|
||||
func (it *Db) AddIndex(indexName string, columns ...string) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.AddIndex(indexName, columns...))
|
||||
}
|
||||
|
||||
func (it *Db) AddUniqueIndex(indexName string, columns ...string) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.AddUniqueIndex(indexName, columns...))
|
||||
}
|
||||
|
||||
func (it *Db) RemoveIndex(indexName string) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.RemoveIndex(indexName))
|
||||
}
|
||||
|
||||
|
@ -519,6 +600,10 @@ func (it *Db) SetJoinTableHandler(source interface{}, column string, handler gor
|
|||
}
|
||||
|
||||
func (it *Db) AddForeignKey(field string, dest string, onDelete string, onUpdate string) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.AddForeignKey(field, dest, onDelete, onUpdate))
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ var (
|
|||
|
||||
// Maintenance will automatically delete old records from 'failures' and 'hits'
|
||||
// this function is currently set to delete records 7+ days old every 60 minutes
|
||||
// env: REMOVE_AFTER - golang duration parsed time for deleting records older than REMOVE_AFTER duration from now
|
||||
// env: CLEANUP_INTERVAL - golang duration parsed time for checking old records routine
|
||||
func Maintenance() {
|
||||
dur := utils.Params.GetDuration("REMOVE_AFTER")
|
||||
interval := utils.Params.GetDuration("CLEANUP_INTERVAL")
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: statping
|
||||
spec:
|
||||
ports:
|
||||
- port: 8080
|
||||
selector:
|
||||
app: statping
|
||||
clusterIP: None
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: statping
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: statping
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: statping
|
||||
spec:
|
||||
containers:
|
||||
- image: statping/statping
|
||||
name: statping
|
||||
env:
|
||||
- name: ALLOW_REPORTS
|
||||
value: "true"
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: statping
|
||||
volumeMounts:
|
||||
- name: statping-storage
|
||||
mountPath: /app
|
||||
volumes:
|
||||
- name: statping-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: statping-claim
|
|
@ -7,8 +7,8 @@ const tokenKey = "statping_auth";
|
|||
|
||||
class Api {
|
||||
constructor() {
|
||||
this.version = "0.90.66";
|
||||
this.commit = "a3ce4124f654b13c7f2af88b8410f998bd57fcef";
|
||||
this.version = "0.90.67";
|
||||
this.commit = "7e121335791d2143a2eefd404dbcce83b8f46f61";
|
||||
}
|
||||
|
||||
async oauth() {
|
||||
|
@ -261,6 +261,14 @@ class Api {
|
|||
})
|
||||
}
|
||||
|
||||
async configs() {
|
||||
return axios.get('api/settings/configs').then(response => (response.data)) || []
|
||||
}
|
||||
|
||||
async configs_save(data) {
|
||||
return axios.post('api/settings/configs', data).then(response => (response.data)) || []
|
||||
}
|
||||
|
||||
token() {
|
||||
return $cookies.get(tokenKey);
|
||||
}
|
||||
|
|
|
@ -10,10 +10,6 @@ A {
|
|||
color: $text-color;
|
||||
}
|
||||
|
||||
A:HOVER {
|
||||
color: lighten($text-color, 12%) !important;
|
||||
}
|
||||
|
||||
.modal-backdrop {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -21,7 +17,7 @@ A:HOVER {
|
|||
z-index: 1040;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgb(0 0 0 / 50%);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.modal {
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
<template>
|
||||
<div>
|
||||
<h3>Configuration</h3>
|
||||
For security reasons, all database credentials cannot be editted from this page.
|
||||
|
||||
<codemirror v-show="loaded" v-model="configs" ref="configs" :options="cmOptions" class="mt-4 codemirrorInput"/>
|
||||
|
||||
<button @click.prevent="save" class="btn col-12 btn-primary mt-3">Save</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../../API";
|
||||
|
||||
import {codemirror} from 'vue-codemirror'
|
||||
import('codemirror/lib/codemirror.css')
|
||||
import('codemirror/mode/yaml/yaml.js')
|
||||
|
||||
export default {
|
||||
name: "Configs",
|
||||
components: {
|
||||
codemirror
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loaded: false,
|
||||
configs: null,
|
||||
cmOptions: {
|
||||
height: 700,
|
||||
tabSize: 4,
|
||||
lineNumbers: true,
|
||||
matchBrackets: true,
|
||||
mode: "text/x-yaml",
|
||||
line: true
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loaded = false
|
||||
this.update()
|
||||
this.loaded = true
|
||||
},
|
||||
watch: {
|
||||
"configs" () {
|
||||
this.$refs.configs.codemirror.refresh()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async update() {
|
||||
this.configs = await Api.configs()
|
||||
this.$refs.configs.codemirror.value = this.configs
|
||||
this.$refs.configs.codemirror.refresh()
|
||||
},
|
||||
async save() {
|
||||
try {
|
||||
await Api.configs_save(this.configs)
|
||||
} catch(e) {
|
||||
window.console.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -6,14 +6,16 @@
|
|||
</p>
|
||||
|
||||
<div v-if="!loaded" class="mt-4 row">
|
||||
<div class="col-6 custom-file">
|
||||
<input @change="onFileChange" type="file" class="custom-file-input" id="customFile" accept=".json,application/json">
|
||||
<div class="col-8 custom-file">
|
||||
<input @change="onFileChange" type="file" class="custom-file-input pointer" id="customFile" accept=".json,application/json">
|
||||
<label class="custom-file-label" for="customFile">Choose exported Statping JSON file</label>
|
||||
</div>
|
||||
<a class="ml-2 col-2 btn btn-light btn-outline-secondary" href="/api/settings/export">Export</a>
|
||||
<div class="col-4">
|
||||
<a class="btn btn-block btn-light btn-outline-secondary" href="/api/settings/export">Export</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loaded" class="col-12 mb-3">
|
||||
<div v-if="loaded" class="col-12 mb-4">
|
||||
<h3>Core Settings
|
||||
<span @click="file.core.enabled = !!file.core.enabled" class="switch switch-sm float-right">
|
||||
<input @change="update" v-model="file.core.enabled" type="checkbox" class="switch" :id="`switch-core`">
|
||||
|
@ -21,13 +23,13 @@
|
|||
</span>
|
||||
</h3>
|
||||
|
||||
<div class="row"><span class="col-4">Name</span><span class="col-8 text-right font-weight-bold">{{file.core.name}}</span></div>
|
||||
<div class="row"><span class="col-4">Description</span><span class="col-8 text-right font-weight-bold">{{file.core.description}}</span></div>
|
||||
<div class="row"><span class="col-4">Domain</span><span class="col-8 text-right font-weight-bold">{{file.core.domain}}</span></div>
|
||||
<div class="row mb-2"><span class="col-4">Name</span><span class="col-8 text-right font-weight-bold">{{file.core.name}}</span></div>
|
||||
<div class="row mb-2"><span class="col-4">Description</span><span class="col-8 text-right font-weight-bold">{{file.core.description}}</span></div>
|
||||
<div class="row mb-2"><span class="col-4">Domain</span><span class="col-8 text-right font-weight-bold">{{file.core.domain}}</span></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div v-if="loaded" class="col-12 mb-3">
|
||||
<div v-if="loaded" class="col-12 mb-4">
|
||||
<h3>Users
|
||||
<button @click.prevent="toggle_all(file.users)" class="btn btn-sm btn-outline-dark float-right mt-1">Select All</button>
|
||||
</h3>
|
||||
|
@ -51,7 +53,7 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div v-if="loaded" class="col-12 mb-3">
|
||||
<div v-if="loaded" class="col-12 mb-4">
|
||||
<h3>Checkins
|
||||
<button @click.prevent="toggle_all(file.checkins)" class="btn btn-sm btn-outline-dark float-right mt-1">Select All</button>
|
||||
</h3>
|
||||
|
@ -60,10 +62,10 @@
|
|||
</div>
|
||||
<div v-for="checkin in file.checkins" v-bind:key="checkin.id" class="row">
|
||||
<div class="col-4 font-weight-bold">
|
||||
{{checkin.id}}
|
||||
{{checkin.name}}
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{checkin.service_id}}
|
||||
Service #{{checkin.service_id}}
|
||||
</div>
|
||||
<div class="col-2 text-right">
|
||||
<span @click="checkin.enabled = !!checkin.enabled" class="switch switch-sm">
|
||||
|
@ -74,7 +76,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loaded" class="col-12 mb-3">
|
||||
<div v-if="loaded" class="col-12 mb-4">
|
||||
<h3>Services
|
||||
<button @click.prevent="toggle_all(file.services)" class="btn btn-sm btn-outline-dark float-right mt-1">Select All</button>
|
||||
</h3>
|
||||
|
@ -97,7 +99,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loaded" class="col-12 mb-3">
|
||||
<div v-if="loaded" class="col-12 mb-4">
|
||||
<h3>Groups
|
||||
<button @click.prevent="toggle_all(file.groups)" class="btn btn-sm btn-outline-dark float-right mt-1">Select All</button>
|
||||
</h3>
|
||||
|
@ -117,6 +119,26 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loaded" class="col-12 mb-4">
|
||||
<h3>Incidents
|
||||
<button @click.prevent="toggle_all(file.incidents)" class="btn btn-sm btn-outline-dark float-right mt-1">Select All</button>
|
||||
</h3>
|
||||
<div v-if="!file.incidents" class="alert alert-link">
|
||||
No Incidents in file
|
||||
</div>
|
||||
<div v-for="incident in file.incidents" v-bind:key="incident.id" class="row">
|
||||
<div class="col-4 font-weight-bold">
|
||||
{{incident.name}}
|
||||
</div>
|
||||
<div class="col-8 text-right">
|
||||
<span @click="incident.enabled = !!incident.enabled" class="switch switch-sm">
|
||||
<input @change="update" v-model="incident.enabled" type="checkbox" class="switch" :id="`switch-incident-${incident.id}`">
|
||||
<label :for="`switch-incident-${incident.id}`"></label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loaded" class="col-12 mb-3">
|
||||
<h3>Notifiers
|
||||
<button @click.prevent="toggle_all(file.notifiers)" class="btn btn-sm btn-outline-dark float-right mt-1">Select All</button>
|
||||
|
@ -141,7 +163,9 @@
|
|||
{{error}}
|
||||
</div>
|
||||
|
||||
<button v-if="loaded" @click.prevent="import_all" class="btn btn-success">Import</button>
|
||||
<div class="col-12">
|
||||
<button v-if="loaded" @click.prevent="import_all" class="btn btn-block btn-success">Import</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -95,17 +95,31 @@
|
|||
}
|
||||
this.loading = false
|
||||
},
|
||||
encode(val) {
|
||||
return encodeURI(val)
|
||||
},
|
||||
custom_scopes() {
|
||||
let scopes = []
|
||||
if (this.oauth.custom_open_id) {
|
||||
scopes.push("openid")
|
||||
}
|
||||
scopes.push(this.oauth.custom_scopes.split(","))
|
||||
if (scopes.length !== 0) {
|
||||
return "&scopes="+scopes.join(",")
|
||||
}
|
||||
return ""
|
||||
},
|
||||
GHlogin() {
|
||||
window.location = `https://github.com/login/oauth/authorize?client_id=${this.oauth.gh_client_id}&redirect_uri=${this.core.domain}/oauth/github&scope=user,repo`
|
||||
window.location = `https://github.com/login/oauth/authorize?client_id=${this.oauth.gh_client_id}&redirect_uri=${this.encode(this.core.domain+"/oauth/github")}&scope=user,repo`
|
||||
},
|
||||
Slacklogin() {
|
||||
window.location = `https://slack.com/oauth/authorize?client_id=${this.oauth.slack_client_id}&redirect_uri=${this.core.domain}/oauth/slack&scope=identity.basic`
|
||||
window.location = `https://slack.com/oauth/authorize?client_id=${this.oauth.slack_client_id}&redirect_uri=${this.encode(this.core.domain+"/oauth/slack")}&scope=identity.basic`
|
||||
},
|
||||
Googlelogin() {
|
||||
window.location = `https://accounts.google.com/signin/oauth?client_id=${this.oauth.google_client_id}&redirect_uri=${this.core.domain}/oauth/google&response_type=code&scope=https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email`
|
||||
window.location = `https://accounts.google.com/signin/oauth?client_id=${this.oauth.google_client_id}&redirect_uri=${this.encode(this.core.domain+"/oauth/google")}&response_type=code&scope=https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email`
|
||||
},
|
||||
Customlogin() {
|
||||
window.location = `${this.oauth.custom_endpoint_auth}?client_id=${this.oauth.custom_client_id}&redirect_uri=${this.core.domain}/oauth/custom${this.oauth.custom_scopes !== "" ? "&scope="+this.oauth.custom_scopes : "" }`
|
||||
window.location = `${this.oauth.custom_endpoint_auth}?client_id=${this.oauth.custom_client_id}&redirect_uri=${this.encode(this.core.domain+"/oauth/custom")}&response_type=code${this.custom_scopes()}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,8 +168,8 @@
|
|||
<label for="switch-custom-oauth" class="mb-0"> </label>
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body" :class="{'d-none': !expanded.custom}">
|
||||
<div class="form-group row mt-3">
|
||||
<div class="card-body" :class="{'d-none': !expanded.custom || !custom_enabled}">
|
||||
<div class="form-group row">
|
||||
<label for="custom_name" class="col-sm-4 col-form-label">Custom Name</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.custom_name" type="text" class="form-control" id="custom_name" required>
|
||||
|
@ -199,13 +199,24 @@
|
|||
<input v-model="oauth.custom_endpoint_token" type="text" class="form-control" id="custom_endpoint_token" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="custom_scopes" class="col-sm-4 col-form-label">Scopes</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.custom_scopes" type="text" class="form-control" id="custom_scopes">
|
||||
<small>Optional comma delimited list of oauth scopes</small>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="custom_scopes" class="col-sm-4 col-form-label">Scopes</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.custom_scopes" type="text" class="form-control" id="custom_scopes">
|
||||
<small>Optional comma delimited list of oauth scopes</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="custom_scopes" class="col-sm-4 col-form-label">Open ID</label>
|
||||
<div class="col-sm-8">
|
||||
<span @click="oauth.custom_open_id = !!oauth.custom_open_id" class="switch switch-rd-gr float-right">
|
||||
<input v-model="oauth.custom_open_id" type="checkbox" id="switch-custom-openid" :checked="oauth.custom_open_id">
|
||||
<label for="switch-custom-openid" class="mb-0"> </label>
|
||||
</span>
|
||||
<small>Enable if provider is OpenID</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="slack_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||
<div class="col-sm-8">
|
||||
|
@ -271,6 +282,7 @@
|
|||
custom_endpoint_auth: "",
|
||||
custom_endpoint_token: "",
|
||||
custom_scopes: "",
|
||||
custom_open_id: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -9,6 +9,7 @@ const chinese = {
|
|||
logout: "注销",
|
||||
online: "在线",
|
||||
offline: "离线",
|
||||
configs: "配置",
|
||||
username: "用户名",
|
||||
password: "密码",
|
||||
email: "电子邮件",
|
||||
|
@ -50,6 +51,11 @@ const chinese = {
|
|||
service_offline_time: "服务已脱机",
|
||||
days_ago: "天前",
|
||||
today: "今天",
|
||||
week: "周",
|
||||
month: "月份",
|
||||
day: "日",
|
||||
hour: "小时",
|
||||
minute: "分钟",
|
||||
failures_24_hours: "过去 24 小时失败",
|
||||
no_services: "您目前没有任何服务!",
|
||||
theme: "主题",
|
||||
|
@ -125,9 +131,12 @@ const chinese = {
|
|||
follow_redir: "跟随重定向",
|
||||
verify_ssl: "验证 SSL",
|
||||
tls_cert: "使用 TLS 证书",
|
||||
notification_opts: "通知选项",
|
||||
notifications_enable: "启用通知",
|
||||
notify_after: "故障后通知",
|
||||
notify_all: "通知所有更改"
|
||||
notify_all: "通知所有更改",
|
||||
service_update: "更新服务",
|
||||
service_create: "创建服务"
|
||||
}
|
||||
|
||||
export default chinese
|
|
@ -9,6 +9,7 @@ login,Login
|
|||
logout,Logout
|
||||
online,Online
|
||||
offline,Offline
|
||||
configs,Configuration
|
||||
username,Username
|
||||
password,Password
|
||||
email,Email
|
||||
|
@ -50,6 +51,11 @@ service_online_check,Online checked
|
|||
service_offline_time,Service has been offline for
|
||||
days_ago,Days Ago
|
||||
today,Today
|
||||
week,Week
|
||||
month,Month
|
||||
day,Day
|
||||
hour,Hour
|
||||
minute,Minute
|
||||
failures_24_hours,Failures last 24 hours
|
||||
no_services,You currently don't have any services!
|
||||
theme,Theme
|
||||
|
|
|
|
@ -9,6 +9,7 @@ const english = {
|
|||
logout: "Logout",
|
||||
online: "Online",
|
||||
offline: "Offline",
|
||||
configs: "Configuration",
|
||||
username: "Username",
|
||||
password: "Password",
|
||||
email: "Email",
|
||||
|
@ -50,6 +51,11 @@ const english = {
|
|||
service_offline_time: "Service has been offline for",
|
||||
days_ago: "Days Ago",
|
||||
today: "Today",
|
||||
week: "Week",
|
||||
month: "Month",
|
||||
day: "Day",
|
||||
hour: "Hour",
|
||||
minute: "Minute",
|
||||
failures_24_hours: "Failures last 24 hours",
|
||||
no_services: "You currently don't have any services!",
|
||||
theme: "Theme",
|
||||
|
@ -125,9 +131,12 @@ const english = {
|
|||
follow_redir: "Follow Redirects",
|
||||
verify_ssl: "Verify SSL",
|
||||
tls_cert: "Use TLS Cert",
|
||||
notification_opts: "Notification Options",
|
||||
notifications_enable: "Enable Notifications",
|
||||
notify_after: "Notify After Failures",
|
||||
notify_all: "Notify All Changes"
|
||||
notify_all: "Notify All Changes",
|
||||
service_update: "Update Service",
|
||||
service_create: "Create Service"
|
||||
}
|
||||
|
||||
export default english
|
|
@ -9,6 +9,7 @@ const french = {
|
|||
logout: "Déconnexion",
|
||||
online: "En ligne",
|
||||
offline: "Offline",
|
||||
configs: "Configuration",
|
||||
username: "Nom d'utilisateur",
|
||||
password: "mot de passe",
|
||||
email: "Email",
|
||||
|
@ -50,6 +51,11 @@ const french = {
|
|||
service_offline_time: "Le service a été hors ligne pour",
|
||||
days_ago: "Il y a jours",
|
||||
today: "Aujourd'hui",
|
||||
week: "Semaine",
|
||||
month: "Mois",
|
||||
day: "Jour",
|
||||
hour: "Heure",
|
||||
minute: "Minute",
|
||||
failures_24_hours: "Les échecs durent 24 heures",
|
||||
no_services: "Vous n'avez actuellement aucun service !",
|
||||
theme: "Thème",
|
||||
|
@ -125,9 +131,12 @@ const french = {
|
|||
follow_redir: "Suivre les redirections",
|
||||
verify_ssl: "Vérifier SSL",
|
||||
tls_cert: "Utiliser le certificat TLS",
|
||||
notification_opts: "Options de notification",
|
||||
notifications_enable: "Activer les notifications",
|
||||
notify_after: "Notification après les échecs",
|
||||
notify_all: "Notifier toutes les modifications"
|
||||
notify_all: "Notifier toutes les modifications",
|
||||
service_update: "Mise à jour Service",
|
||||
service_create: "Créer un service"
|
||||
}
|
||||
|
||||
export default french
|
|
@ -9,6 +9,7 @@ const german = {
|
|||
logout: "Abmelden",
|
||||
online: "Online",
|
||||
offline: "Offline",
|
||||
configs: "Konfiguration",
|
||||
username: "Benutzername",
|
||||
password: "Kennwort",
|
||||
email: "Mail",
|
||||
|
@ -50,6 +51,11 @@ const german = {
|
|||
service_offline_time: "Dienst war offline für",
|
||||
days_ago: "Vor Tagen",
|
||||
today: "Heute",
|
||||
week: "Woche",
|
||||
month: "Monat",
|
||||
day: "Tag",
|
||||
hour: "Stunde",
|
||||
minute: "Minute",
|
||||
failures_24_hours: "Ausfälle dauern 24 Stunden",
|
||||
no_services: "Sie haben derzeit keine Dienste!",
|
||||
theme: "Thema",
|
||||
|
@ -125,9 +131,12 @@ const german = {
|
|||
follow_redir: "Weiterleitungen folgen",
|
||||
verify_ssl: "SSL verifizieren",
|
||||
tls_cert: "TLS-Zertifikat verwenden",
|
||||
notification_opts: "Benachrichtigungsoptionen",
|
||||
notifications_enable: "Benachrichtigungen aktivieren",
|
||||
notify_after: "Benachrichtigung nach Fehlern",
|
||||
notify_all: "Alle Änderungen benachrichtigen"
|
||||
notify_all: "Alle Änderungen benachrichtigen",
|
||||
service_update: "Dienst aktualisieren",
|
||||
service_create: "Dienst erstellen"
|
||||
}
|
||||
|
||||
export default german
|
|
@ -9,6 +9,7 @@ const italian = {
|
|||
logout: "Disconnetti",
|
||||
online: "Online",
|
||||
offline: "Offline",
|
||||
configs: "Configurazione",
|
||||
username: "Nome utente",
|
||||
password: "Password",
|
||||
email: "E-mail",
|
||||
|
@ -50,6 +51,11 @@ const italian = {
|
|||
service_offline_time: "Il servizio è stato offline per",
|
||||
days_ago: "Giorni fa",
|
||||
today: "Oggi",
|
||||
week: "Settimana",
|
||||
month: "Mese",
|
||||
day: "Giorno",
|
||||
hour: "Ora",
|
||||
minute: "Minuti",
|
||||
failures_24_hours: "Errori nelle ultime 24 ore",
|
||||
no_services: "Al momento non hai alcun servizio!",
|
||||
theme: "Tema",
|
||||
|
@ -125,9 +131,12 @@ const italian = {
|
|||
follow_redir: "Segui i reindirizzamenti",
|
||||
verify_ssl: "Verifica SSL",
|
||||
tls_cert: "Usa certificato TLS",
|
||||
notification_opts: "Opzioni di notifica",
|
||||
notifications_enable: "Abilita notifiche",
|
||||
notify_after: "Notify dopo gli errori",
|
||||
notify_all: "Notifichi tutte le modifiche"
|
||||
notify_all: "Notifichi tutte le modifiche",
|
||||
service_update: "Servizio di aggiornamento",
|
||||
service_create: "Crea servizio"
|
||||
}
|
||||
|
||||
export default italian
|
|
@ -9,6 +9,7 @@ const japanese = {
|
|||
logout: "ログアウト",
|
||||
online: "オンライン",
|
||||
offline: "オフライン",
|
||||
configs: "構成",
|
||||
username: "ユーザ名",
|
||||
password: "パスワード",
|
||||
email: "Eメール",
|
||||
|
@ -50,6 +51,11 @@ const japanese = {
|
|||
service_offline_time: "のサービスがオフラインになりました",
|
||||
days_ago: "日前",
|
||||
today: "今日",
|
||||
week: "週数",
|
||||
month: "月",
|
||||
day: "日",
|
||||
hour: "アワー",
|
||||
minute: "分",
|
||||
failures_24_hours: "過去 24 時間の障害",
|
||||
no_services: "現在、サービスをお持ちになりません。",
|
||||
theme: "テーマ",
|
||||
|
@ -125,9 +131,12 @@ const japanese = {
|
|||
follow_redir: "リダイレクトに従う",
|
||||
verify_ssl: "SSL の確認",
|
||||
tls_cert: "TLS 証明書を使用",
|
||||
notification_opts: "通知オプション",
|
||||
notifications_enable: "通知を有効にする",
|
||||
notify_after: "障害発生後に通知する",
|
||||
notify_all: "すべての変更を通知"
|
||||
notify_all: "すべての変更を通知",
|
||||
service_update: "サービスの更新",
|
||||
service_create: "サービスの作成"
|
||||
}
|
||||
|
||||
export default japanese
|
|
@ -9,6 +9,7 @@ const korean = {
|
|||
logout: "로그아웃",
|
||||
online: "온라인",
|
||||
offline: "오프라인",
|
||||
configs: "구성",
|
||||
username: "사용자 이름",
|
||||
password: "비밀번호",
|
||||
email: "이메일",
|
||||
|
@ -50,6 +51,11 @@ const korean = {
|
|||
service_offline_time: "서비스가 에 대해 오프라인되었습니다.",
|
||||
days_ago: "일 전",
|
||||
today: "오늘",
|
||||
week: "주",
|
||||
month: "월",
|
||||
day: "일",
|
||||
hour: "시간",
|
||||
minute: "분",
|
||||
failures_24_hours: "지난 24시간 동안 장애 발생",
|
||||
no_services: "현재 서비스가 없습니다!",
|
||||
theme: "테마",
|
||||
|
@ -125,9 +131,12 @@ const korean = {
|
|||
follow_redir: "리다이렉트 따르기",
|
||||
verify_ssl: "SSL 확인",
|
||||
tls_cert: "TLS 인증서 사용",
|
||||
notification_opts: "알림 옵션",
|
||||
notifications_enable: "알림 활성화",
|
||||
notify_after: "실패 후 알림",
|
||||
notify_all: "모든 변경 사항 알림"
|
||||
notify_all: "모든 변경 사항 알림",
|
||||
service_update: "서비스 업데이트",
|
||||
service_create: "서비스 만들기"
|
||||
}
|
||||
|
||||
export default korean
|
|
@ -9,6 +9,7 @@ const russian = {
|
|||
logout: "Выход из системы",
|
||||
online: "Онлайн",
|
||||
offline: "Оффлайн",
|
||||
configs: "Конфигурация",
|
||||
username: "Имя пользователя",
|
||||
password: "Пароль",
|
||||
email: "Электронная почта",
|
||||
|
@ -50,6 +51,11 @@ const russian = {
|
|||
service_offline_time: "Служба была отключена для",
|
||||
days_ago: "Дней назад",
|
||||
today: "Сегодня",
|
||||
week: "Недели",
|
||||
month: "Месяц",
|
||||
day: "День",
|
||||
hour: "Час",
|
||||
minute: "Минута",
|
||||
failures_24_hours: "Сбои за 24 часа",
|
||||
no_services: "В настоящее время у вас нет услуг!",
|
||||
theme: "Тема",
|
||||
|
@ -125,9 +131,12 @@ const russian = {
|
|||
follow_redir: "Следуйте за перенаправленными",
|
||||
verify_ssl: "Проверить SSL",
|
||||
tls_cert: "Использовать сертификат TLS",
|
||||
notification_opts: "Параметры уведомления",
|
||||
notifications_enable: "Включить уведомления",
|
||||
notify_after: "Уведомлять после сбоев",
|
||||
notify_all: "Уведомлять обо всех изменениях"
|
||||
notify_all: "Уведомлять обо всех изменениях",
|
||||
service_update: "Обновить службу",
|
||||
service_create: "Создать службу"
|
||||
}
|
||||
|
||||
export default russian
|
|
@ -9,6 +9,7 @@ const spanish = {
|
|||
logout: "Cerrar sesión",
|
||||
online: "En línea",
|
||||
offline: "Offline",
|
||||
configs: "Configuración",
|
||||
username: "Nombre de usuario",
|
||||
password: "Contraseña",
|
||||
email: "Correo",
|
||||
|
@ -50,6 +51,11 @@ const spanish = {
|
|||
service_offline_time: "El servicio ha estado desconectado para",
|
||||
days_ago: "Hace días",
|
||||
today: "Hoy",
|
||||
week: "Semana",
|
||||
month: "Mes",
|
||||
day: "Día",
|
||||
hour: "Hora",
|
||||
minute: "Minuto",
|
||||
failures_24_hours: "Fallos de las últimas 24 horas",
|
||||
no_services: "¡Actualmente no tienes ningún servicio!",
|
||||
theme: "Tema",
|
||||
|
@ -125,9 +131,12 @@ const spanish = {
|
|||
follow_redir: "Seguir redirecciones",
|
||||
verify_ssl: "Verificar SSL",
|
||||
tls_cert: "Usar Cert TLS",
|
||||
notification_opts: "Opciones de notificación",
|
||||
notifications_enable: "Habilitar notificaciones",
|
||||
notify_after: "Notificar después de errores",
|
||||
notify_all: "Notificar todos los cambios"
|
||||
notify_all: "Notificar todos los cambios",
|
||||
service_update: "Servicio de actualización",
|
||||
service_create: "Crear servicio"
|
||||
}
|
||||
|
||||
export default spanish
|
|
@ -1545,8 +1545,8 @@ systemctl start statping
|
|||
<p>You can even run Statping on your Raspberry Pi by installing the precompiled binary from <a href="https://github.com/statping/statping/releases/latest" target="_blank">Latest Releases</a>. For the Raspberry Pi 3 you’ll want to download the <code>statping-linux-arm7.tar.gz</code> file. Be sure to change <code>VERSION</code> to the latest version in Releases, and include the ‘v’.</p>
|
||||
|
||||
<pre><code>VERSION=$(curl -sL "https://github.com/statping/statping/releases/latest" | grep -o 'tag/[v.0-9]*' | awk -F/ '{print $2}' | head -n1)
|
||||
wget https://github.com/statping/statping/releases/download/$VERSION/statping-linux-arm7.tar.gz
|
||||
tar -xvzf statping-linux-arm7.tar.gz
|
||||
wget https://github.com/statping/statping/releases/download/$VERSION/statping-linux-arm-7.tar.gz
|
||||
tar -xvzf statping-linux-arm-7.tar.gz
|
||||
chmod +x statping
|
||||
mv statping /usr/local/bin/statping
|
||||
|
||||
|
@ -2275,7 +2275,7 @@ OluFxewsEO0QNDrfFb+0gnjYlnGqOFcZjUMXbDdY5oLSPtXohynuTK1qyQ==
|
|||
</div>
|
||||
|
||||
<div class="text-center small text-dim" v-pre>
|
||||
Automatically generated from Statping's Wiki on 2020-09-09 01:24:21.649582 +0000 UTC
|
||||
Automatically generated from Statping's Wiki on 2020-09-15 19:09:14.703237 +0000 UTC
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<div class="container col-md-7 col-sm-12 mt-md-5">
|
||||
|
||||
<div v-if="!ready" class="row mt-5">
|
||||
<div class="col-12 text-center">
|
||||
<font-awesome-icon icon="circle-notch" size="3x" spin/>
|
||||
|
@ -10,7 +9,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="ready" class="col-12 mb-4">
|
||||
<div v-if="ready && service" class="col-12 mb-4">
|
||||
<span class="mt-3 mb-3 text-white d-md-none btn d-block d-md-none text-uppercase" :class="{'bg-success': service.online, 'bg-danger': !service.online}">
|
||||
{{service.online ? $t('online') : $t('offline')}}
|
||||
</span>
|
||||
|
@ -31,11 +30,11 @@
|
|||
<div class="card-body pb-4">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<flatPickr :disabled="!loaded" @on-change="reload" v-model="start_time" :config="{ wrap: true, allowInput: true, enableTime: true, dateFormat: 'Z', altInput: true, altFormat: 'Y-m-d h:i K', maxDate: new Date() }" type="text" class="form-control text-left" required />
|
||||
<flatPickr :disabled="!loaded" @on-change="reload" v-model="start_time" :config="{ wrap: true, allowInput: true, enableTime: true, dateFormat: 'Z', altInput: true, altFormat: 'Y-m-d h:i K', maxDate: this.endOf('today') }" type="text" class="form-control text-left" required />
|
||||
<small class="d-block">From {{this.format(new Date(start_time))}}</small>
|
||||
</div>
|
||||
<div class="col">
|
||||
<flatPickr :disabled="!loaded" @on-change="reload" v-model="end_time" :config="{ wrap: true, allowInput: true, enableTime: true, dateFormat: 'Z', altInput: true, altFormat: 'Y-m-d h:i K', maxDate: new Date() }" type="text" class="form-control text-left" required />
|
||||
<flatPickr :disabled="!loaded" @on-change="reload" v-model="end_time" :config="{ wrap: true, allowInput: true, enableTime: true, dateFormat: 'Z', altInput: true, altFormat: 'Y-m-d h:i K', maxDate: this.endOf('today') }" type="text" class="form-control text-left" required />
|
||||
<small class="d-block">To {{this.format(new Date(end_time))}}</small>
|
||||
</div>
|
||||
<div class="col">
|
||||
|
@ -55,6 +54,7 @@
|
|||
<small class="d-block d-md-none d-block">Increment Timeframe</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -99,10 +99,11 @@
|
|||
const ServiceHeatmap = () => import(/* webpackChunkName: "service" */ '@/components/Service/ServiceHeatmap')
|
||||
const ServiceTopStats = () => import(/* webpackChunkName: "service" */ '@/components/Service/ServiceTopStats')
|
||||
const AdvancedChart = () => import(/* webpackChunkName: "service" */ '@/components/Service/AdvancedChart')
|
||||
const FailuresBarChart = () => import(/* webpackChunkName: "service" */ '@/components/Service/FailuresBarChart')
|
||||
|
||||
import flatPickr from 'vue-flatpickr-component';
|
||||
import 'flatpickr/dist/flatpickr.css';
|
||||
import FailuresBarChart from "@/components/Service/FailuresBarChart";
|
||||
|
||||
const timeoptions = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' };
|
||||
|
||||
const axisOptions = {
|
||||
|
@ -149,13 +150,12 @@ export default {
|
|||
ready: false,
|
||||
group: "15m",
|
||||
data: null,
|
||||
service: null,
|
||||
uptime_data: null,
|
||||
loaded: false,
|
||||
messages: [],
|
||||
failures: [],
|
||||
start_time: this.nowSubtract(84600 * 30),
|
||||
end_time: this.nowSubtract(0),
|
||||
start_time: this.beginningOf('day', this.nowSubtract(259200 * 3)),
|
||||
end_time: this.endOf('today'),
|
||||
timedata: null,
|
||||
load_timedata: false,
|
||||
dailyRangeOpts: {
|
||||
|
@ -232,7 +232,7 @@ export default {
|
|||
window.console.log(event)
|
||||
},
|
||||
updated: (chartContext, config) => {
|
||||
window.console.log('updated')
|
||||
window.console.log('updated chart')
|
||||
},
|
||||
beforeZoom: (chartContext, { xaxis }) => {
|
||||
const start = (xaxis.min / 1000).toFixed(0)
|
||||
|
@ -368,6 +368,9 @@ export default {
|
|||
core () {
|
||||
return this.$store.getters.core
|
||||
},
|
||||
service() {
|
||||
return this.$store.getters.serviceByAll(this.$route.params.id)
|
||||
},
|
||||
params () {
|
||||
return {start: this.toUnix(new Date(this.start_time)), end: this.toUnix(new Date(this.end_time))}
|
||||
},
|
||||
|
@ -388,20 +391,25 @@ export default {
|
|||
this.fetchData()
|
||||
},
|
||||
mounted() {
|
||||
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
async fetchData () {
|
||||
fetchData () {
|
||||
if (!this.$route.params.id) {
|
||||
this.ready = false
|
||||
return
|
||||
}
|
||||
this.services = await Api.services()
|
||||
await this.$store.commit("setServices", this.services)
|
||||
|
||||
this.service = await Api.service(this.$route.params.id)
|
||||
await this.reload()
|
||||
this.reload()
|
||||
this.ready = true
|
||||
this.loaded = true
|
||||
},
|
||||
async reload() {
|
||||
if (!this.ready || !this.service) {
|
||||
return
|
||||
}
|
||||
await this.chartHits()
|
||||
await this.chartFailures()
|
||||
await this.fetchUptime()
|
||||
},
|
||||
async updated_chart(start, end) {
|
||||
this.loaded = false
|
||||
|
@ -409,20 +417,14 @@ export default {
|
|||
this.end_time = end
|
||||
this.loaded = true
|
||||
},
|
||||
async reload() {
|
||||
await this.chartHits()
|
||||
await this.chartFailures()
|
||||
await this.fetchUptime()
|
||||
this.loaded = true
|
||||
},
|
||||
async fetchUptime(service) {
|
||||
if (service) {
|
||||
return
|
||||
}
|
||||
async fetchUptime() {
|
||||
const uptime = await Api.service_uptime(this.service.id, this.params.start, this.params.end)
|
||||
this.uptime_data = this.parse_uptime(uptime)
|
||||
},
|
||||
parse_uptime(timedata) {
|
||||
if (!timedata.series) {
|
||||
return []
|
||||
}
|
||||
const data = timedata.series.filter((g) => g.online) || []
|
||||
const offData = timedata.series.filter((g) => !g.online) || []
|
||||
let arr = [];
|
||||
|
@ -456,18 +458,10 @@ export default {
|
|||
return this.isBetween(this.now(), message.start_on, message.start_on === message.end_on ? this.maxDate().toISOString() : message.end_on)
|
||||
},
|
||||
async chartHits(start=0, end=99999999999) {
|
||||
if (!this.service) {
|
||||
return
|
||||
}
|
||||
this.data = await Api.service_hits(this.service.id, this.params.start, this.params.end, this.group, false)
|
||||
this.ready = true
|
||||
},
|
||||
async chartFailures(start=0, end=99999999999) {
|
||||
if (!this.service) {
|
||||
return
|
||||
}
|
||||
this.failures_data = await Api.service_failures_data(this.service.id, this.params.start, this.params.end, this.group, true)
|
||||
this.ready = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,16 @@
|
|||
<div class="col-md-3 col-sm-12 mb-4 mb-md-0">
|
||||
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical">
|
||||
|
||||
<div v-if="version_below" class="alert small text-center mt-0 pt-0 pb-0">
|
||||
<div v-if="version_below" class="col-12 small text-center mt-0 pt-0 pb-0 mb-3">
|
||||
Update {{github.tag_name}} Available
|
||||
<a href="https://github.com/statping/statping/releases/latest" class="btn btn-sm text-success mt-2">Download</a>
|
||||
<a href="https://github.com/statping/statping/blob/master/CHANGELOG.md" class="btn btn-sm text-dim mt-2">Changelog</a>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<a href="https://github.com/statping/statping/releases/latest" class="btn btn-sm text-success mt-2">Download</a>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<a href="https://github.com/statping/statping/blob/master/CHANGELOG.md" class="btn btn-sm text-dim mt-2">Changelog</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h6 class="text-muted">{{ $t('main_settings') }}</h6>
|
||||
|
@ -27,6 +33,9 @@
|
|||
<a @click.prevent="changeTab" class="nav-link" v-bind:class="{active: liClass('v-pills-import-tab')}" id="v-pills-import-tab" data-toggle="pill" href="#v-pills-import" role="tab" aria-controls="v-pills-import" aria-selected="false">
|
||||
<font-awesome-icon icon="cloud-download-alt" class="mr-2"/> {{ $t('import') }}
|
||||
</a>
|
||||
<a @click.prevent="changeTab" class="nav-link" v-bind:class="{active: liClass('v-pills-configs-tab')}" id="v-pills-configs-tab" data-toggle="pill" href="#v-pills-configs" role="tab" aria-controls="v-pills-configs" aria-selected="false">
|
||||
<font-awesome-icon icon="cogs" class="mr-2"/> {{ $t('configs') }}
|
||||
</a>
|
||||
|
||||
<h6 class="mt-4 text-muted">{{$t('notifiers')}}</h6>
|
||||
|
||||
|
@ -105,9 +114,13 @@
|
|||
<Cache/>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" v-bind:class="{active: liClass('v-pills-oauth-tab'), show: liClass('v-pills-oauth-tab')}" id="v-pills-oauth" role="tabpanel" aria-labelledby="v-pills-oauth-tab">
|
||||
<OAuth/>
|
||||
</div>
|
||||
<div class="tab-pane fade" v-bind:class="{active: liClass('v-pills-oauth-tab'), show: liClass('v-pills-oauth-tab')}" id="v-pills-oauth" role="tabpanel" aria-labelledby="v-pills-oauth-tab">
|
||||
<OAuth/>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" v-bind:class="{active: liClass('v-pills-configs-tab'), show: liClass('v-pills-configs-tab')}" id="v-pills-configs" role="tabpanel" aria-labelledby="v-pills-configs-tab">
|
||||
<Configs/>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" v-bind:class="{active: liClass('v-pills-import-tab'), show: liClass('v-pills-import-tab')}" id="v-pills-import" role="tabpanel" aria-labelledby="v-pills-import-tab">
|
||||
<Importer/>
|
||||
|
@ -140,10 +153,12 @@
|
|||
const Cache = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Cache')
|
||||
const Importer = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Importer')
|
||||
const Variables = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Variables')
|
||||
const Configs = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Configs')
|
||||
|
||||
export default {
|
||||
name: 'Settings',
|
||||
components: {
|
||||
Configs,
|
||||
Importer,
|
||||
Variables,
|
||||
OAuth,
|
||||
|
|
|
@ -72,8 +72,8 @@ export default new Vuex.Store({
|
|||
return state.checkins.filter(c => c.service_id === id)
|
||||
},
|
||||
serviceByAll: (state) => (element) => {
|
||||
if (element % 1 === 0) {
|
||||
return state.services.find(s => s.id === element)
|
||||
if (!isNaN(parseFloat(element)) && isFinite(element)) {
|
||||
return state.services.find(s => s.id === parseInt(element))
|
||||
} else {
|
||||
return state.services.find(s => s.permalink === element)
|
||||
}
|
||||
|
|
4
go.mod
4
go.mod
|
@ -6,6 +6,7 @@ go 1.14
|
|||
require (
|
||||
github.com/GeertJohan/go.rice v1.0.0
|
||||
github.com/aws/aws-sdk-go v1.30.20
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/fatih/structs v1.1.0
|
||||
github.com/foomo/simplecert v1.7.5
|
||||
|
@ -17,12 +18,13 @@ require (
|
|||
github.com/jinzhu/gorm v1.9.12
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pquerna/cachecontrol v0.0.0-20200819021114-67c6ae64274f // indirect
|
||||
github.com/prometheus/client_golang v1.1.0
|
||||
github.com/sirupsen/logrus v1.5.0
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/spf13/viper v1.6.3
|
||||
github.com/statping/emails v1.0.0
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/t-tiger/gorm-bulk-insert/v2 v2.0.1
|
||||
github.com/tensorflow/tensorflow v2.3.0+incompatible // indirect
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||
|
|
6
go.sum
6
go.sum
|
@ -131,6 +131,8 @@ github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkE
|
|||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk=
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
|
@ -552,6 +554,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20200819021114-67c6ae64274f h1:JDEmUDtyiLMyMlFwiaDOv2hxUp35497fkwePcLeV7j4=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20200819021114-67c6ae64274f/go.mod h1:hoLfEwdY11HjRfKFH6KqnPsfxlo3BP6bJehpDv8t6sQ=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
|
||||
|
@ -652,6 +656,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
|||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/t-tiger/gorm-bulk-insert/v2 v2.0.1 h1:HGVkRrwDCbmSP6h1CoBDj6l/mhnvsP5JbYaQ4ss0R6o=
|
||||
|
@ -1047,6 +1052,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.0.1 h1:omJoilUzyrAp0xNoio88lGJCroGdIOen9hq2A/+3ifw=
|
||||
gorm.io/driver/mysql v1.0.1/go.mod h1:KtqSthtg55lFp3S5kUXqlGaelnWpKitn4k1xZTnoiPw=
|
||||
gorm.io/driver/postgres v1.0.0 h1:Yh4jyFQ0a7F+JPU0Gtiam/eKmpT/XFc1FKxotGqc6FM=
|
||||
|
|
|
@ -3,6 +3,7 @@ package handlers
|
|||
import (
|
||||
"fmt"
|
||||
"github.com/statping/statping/types/checkins"
|
||||
"github.com/statping/statping/types/configs"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/types/groups"
|
||||
|
@ -90,10 +91,22 @@ func apiCoreHandler(w http.ResponseWriter, r *http.Request) {
|
|||
if c.Domain != app.Domain {
|
||||
app.Domain = c.Domain
|
||||
}
|
||||
if c.Language != app.Language {
|
||||
app.Language = c.Language
|
||||
}
|
||||
utils.Params.Set("LANGUAGE", app.Language)
|
||||
app.UseCdn = null.NewNullBool(c.UseCdn.Bool)
|
||||
app.AllowReports = null.NewNullBool(c.AllowReports.Bool)
|
||||
utils.SentryInit(app.AllowReports.Bool)
|
||||
err = app.Update()
|
||||
if err := app.Update(); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
if err := configs.Save(); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
returnJson(core.App, w, r)
|
||||
}
|
||||
|
||||
|
|
|
@ -10,11 +10,13 @@ import (
|
|||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/types/groups"
|
||||
"github.com/statping/statping/types/incidents"
|
||||
"github.com/statping/statping/types/messages"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/services"
|
||||
"github.com/statping/statping/types/users"
|
||||
"github.com/statping/statping/utils"
|
||||
"gopkg.in/yaml.v2"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
@ -157,18 +159,21 @@ func apiThemeRemoveHandler(w http.ResponseWriter, r *http.Request) {
|
|||
if err := source.DeleteAllAssets(utils.Directory); err != nil {
|
||||
log.Errorln(fmt.Errorf("error deleting all assets %v", err))
|
||||
}
|
||||
resetRouter()
|
||||
sendJsonAction(utils.Directory+"/assets", "deleted", w, r)
|
||||
}
|
||||
|
||||
type ExportData struct {
|
||||
Config *configs.DbConfig `json:"config,omitempty"`
|
||||
Core *core.Core `json:"core"`
|
||||
Services []services.Service `json:"services"`
|
||||
Messages []*messages.Message `json:"messages"`
|
||||
Checkins []*checkins.Checkin `json:"checkins"`
|
||||
Users []*users.User `json:"users"`
|
||||
Groups []*groups.Group `json:"groups"`
|
||||
Notifiers []notifications.Notification `json:"notifiers"`
|
||||
Config *configs.DbConfig `json:"config,omitempty"`
|
||||
Core *core.Core `json:"core"`
|
||||
Services []services.Service `json:"services"`
|
||||
Messages []*messages.Message `json:"messages"`
|
||||
Incidents []*incidents.Incident `json:"incidents"`
|
||||
IncidentUpdates []*incidents.IncidentUpdate `json:"incident_updates"`
|
||||
Checkins []*checkins.Checkin `json:"checkins"`
|
||||
Users []*users.User `json:"users"`
|
||||
Groups []*groups.Group `json:"groups"`
|
||||
Notifiers []notifications.Notification `json:"notifiers"`
|
||||
}
|
||||
|
||||
func (e *ExportData) JSON() []byte {
|
||||
|
@ -260,6 +265,44 @@ func settingsImportHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sendJsonAction(exportData, "import", w, r)
|
||||
}
|
||||
|
||||
func configsSaveHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var cfg *configs.DbConfig
|
||||
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
oldCfg, err := configs.LoadConfigs(utils.Directory + "/configs.yml")
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
newCfg := cfg.Merge(oldCfg)
|
||||
if err := newCfg.Save(utils.Directory); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
sendJsonAction(newCfg.Clean(), "updated", w, r)
|
||||
}
|
||||
|
||||
func configsViewHandler(w http.ResponseWriter, r *http.Request) {
|
||||
db, err := configs.LoadConfigs(utils.Directory + "/configs.yml")
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
w.Write(db.Clean().ToYAML())
|
||||
}
|
||||
|
||||
func settingsExportHandler(w http.ResponseWriter, r *http.Request) {
|
||||
exported, err := ExportSettings()
|
||||
if err != nil {
|
||||
|
|
|
@ -11,9 +11,7 @@ import (
|
|||
func customOAuth(r *http.Request) (*oAuth, error) {
|
||||
auth := core.App.OAuth
|
||||
code := r.URL.Query().Get("code")
|
||||
|
||||
scopes := strings.Split(auth.CustomScopes, ",")
|
||||
|
||||
config := &oauth2.Config{
|
||||
ClientID: auth.CustomClientID,
|
||||
ClientSecret: auth.CustomClientSecret,
|
||||
|
|
|
@ -103,6 +103,8 @@ func Router() *mux.Router {
|
|||
api.Handle("/api/logs/last", authenticated(logsLineHandler, false)).Methods("GET")
|
||||
api.Handle("/api/settings/import", authenticated(settingsImportHandler, false)).Methods("POST")
|
||||
api.Handle("/api/settings/export", authenticated(settingsExportHandler, false)).Methods("GET")
|
||||
api.Handle("/api/settings/configs", authenticated(configsViewHandler, false)).Methods("GET")
|
||||
api.Handle("/api/settings/configs", authenticated(configsSaveHandler, false)).Methods("POST")
|
||||
|
||||
// API OAUTH Routes
|
||||
api.Handle("/api/oauth", scoped(apiOAuthHandler)).Methods("GET")
|
||||
|
|
|
@ -59,7 +59,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if err := configs.CreateAdminUser(confgs); err != nil {
|
||||
if err := configs.CreateAdminUser(); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ type discord struct {
|
|||
|
||||
var Discorder = &discord{¬ifications.Notification{
|
||||
Method: "discord",
|
||||
Title: "discord",
|
||||
Title: "Discord",
|
||||
Description: "Send notifications to your discord channel using discord webhooks. Insert your discord channel Webhook URL to receive notifications. Based on the <a href=\"https://discordapp.com/developers/docs/resources/Webhook\">discord webhooker API</a>.",
|
||||
Author: "Hunter Long",
|
||||
AuthorUrl: "https://github.com/hunterlong",
|
||||
|
|
|
@ -29,7 +29,7 @@ func (s *slack) Select() *notifications.Notification {
|
|||
|
||||
var slacker = &slack{¬ifications.Notification{
|
||||
Method: slackMethod,
|
||||
Title: "slack",
|
||||
Title: "Slack",
|
||||
Description: "Send notifications to your slack channel when a service is offline. Insert your Incoming webhook URL for your channel to receive notifications. Based on the <a href=\"https://api.slack.com/incoming-webhooks\">Slack API</a>.",
|
||||
Author: "Hunter Long",
|
||||
AuthorUrl: "https://github.com/hunterlong",
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// initModels sets the database for each Statping type packages
|
||||
func initModels(db database.Database) {
|
||||
core.SetDB(db)
|
||||
services.SetDB(db)
|
||||
|
@ -67,6 +68,10 @@ func Connect(configs *DbConfig, retry bool) error {
|
|||
log.Infoln(fmt.Sprintf("Database %s connection was successful.", configs.DbConn))
|
||||
}
|
||||
|
||||
if utils.Params.GetBool("READ_ONLY") {
|
||||
log.Warnln("Running in READ ONLY MODE")
|
||||
}
|
||||
|
||||
configs.Db = dbSession
|
||||
|
||||
initModels(configs.Db)
|
||||
|
@ -74,7 +79,9 @@ func Connect(configs *DbConfig, retry bool) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func CreateAdminUser(c *DbConfig) error {
|
||||
// CreateAdminUser will create the default admin user "admin", "admin", or use the
|
||||
// environment variables ADMIN_USER, ADMIN_PASSWORD, and ADMIN_EMAIL if set.
|
||||
func CreateAdminUser() error {
|
||||
adminUser := utils.Params.GetString("ADMIN_USER")
|
||||
adminPass := utils.Params.GetString("ADMIN_PASSWORD")
|
||||
adminEmail := utils.Params.GetString("ADMIN_EMAIL")
|
||||
|
|
|
@ -8,8 +8,9 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
var log = utils.Log
|
||||
var log = utils.Log.WithField("type", "configs")
|
||||
|
||||
// ConnectConfigs will connect to the database and save the config.yml file
|
||||
func ConnectConfigs(configs *DbConfig, retry bool) error {
|
||||
err := Connect(configs, retry)
|
||||
if err != nil {
|
||||
|
@ -21,6 +22,8 @@ func ConnectConfigs(configs *DbConfig, retry bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// findDbFile will attempt to find the "statping.db" database file in the current
|
||||
// working directory, or from STATPING_DIR env.
|
||||
func findDbFile(configs *DbConfig) (string, error) {
|
||||
location := utils.Directory + "/" + SqliteFilename
|
||||
if configs == nil {
|
||||
|
@ -37,6 +40,7 @@ func findDbFile(configs *DbConfig) (string, error) {
|
|||
return location, nil
|
||||
}
|
||||
|
||||
// findSQLin walks the current walking directory for statping.db
|
||||
func findSQLin(path string) (string, error) {
|
||||
filename := SqliteFilename
|
||||
var found []string
|
||||
|
|
|
@ -7,6 +7,37 @@ import (
|
|||
"os"
|
||||
)
|
||||
|
||||
func Save() error {
|
||||
p := utils.Params
|
||||
configs := &DbConfig{
|
||||
DbConn: p.GetString("DB_CONN"),
|
||||
DbHost: p.GetString("DB_HOST"),
|
||||
DbUser: p.GetString("DB_USER"),
|
||||
DbPass: p.GetString("DB_PASS"),
|
||||
DbData: p.GetString("DB_DATABASE"),
|
||||
DbPort: p.GetInt("DB_PORT"),
|
||||
Project: p.GetString("NAME"),
|
||||
Description: p.GetString("DESCRIPTION"),
|
||||
Domain: p.GetString("DOMAIN"),
|
||||
Email: p.GetString("EMAIL"),
|
||||
Username: p.GetString("ADMIN_USER"),
|
||||
Password: p.GetString("ADMIN_PASSWORD"),
|
||||
Location: utils.Directory,
|
||||
SqlFile: p.GetString("SQL_FILE"),
|
||||
Language: p.GetString("LANGUAGE"),
|
||||
AllowReports: p.GetBool("ALLOW_REPORTS"),
|
||||
LetsEncryptEnable: p.GetBool("LETSENCRYPT_ENABLE"),
|
||||
LetsEncryptHost: p.GetString("LETSENCRYPT_HOST"),
|
||||
LetsEncryptEmail: p.GetString("LETSENCRYPT_EMAIL"),
|
||||
ApiSecret: p.GetString("API_SECRET"),
|
||||
SampleData: p.GetBool("SAMPLE_DATA"),
|
||||
MaxOpenConnections: p.GetInt("MAX_OPEN_CONN"),
|
||||
MaxIdleConnections: p.GetInt("MAX_IDLE_CONN"),
|
||||
MaxLifeConnections: int(p.GetDuration("MAX_LIFE_CONN").Seconds()),
|
||||
}
|
||||
return configs.Save(utils.Directory)
|
||||
}
|
||||
|
||||
func LoadConfigs(cfgFile string) (*DbConfig, error) {
|
||||
writeAble, err := utils.DirWritable(utils.Directory)
|
||||
if err != nil {
|
||||
|
|
|
@ -18,6 +18,37 @@ func (d *DbConfig) Save(directory string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Merge will merge the database connection info into the input
|
||||
func (d *DbConfig) Merge(newCfg *DbConfig) *DbConfig {
|
||||
d.DbConn = newCfg.DbConn
|
||||
d.DbHost = newCfg.DbHost
|
||||
d.DbPort = newCfg.DbPort
|
||||
d.DbData = newCfg.DbData
|
||||
d.DbUser = newCfg.DbUser
|
||||
d.DbPass = newCfg.DbPass
|
||||
return d
|
||||
}
|
||||
|
||||
// Clean hides all sensitive database information for API requests
|
||||
func (d *DbConfig) Clean() *DbConfig {
|
||||
d.DbConn = ""
|
||||
d.DbHost = ""
|
||||
d.DbPort = 0
|
||||
d.DbData = ""
|
||||
d.DbUser = ""
|
||||
d.DbPass = ""
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *DbConfig) ToYAML() []byte {
|
||||
c, err := yaml.Marshal(d)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return nil
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (d *DbConfig) ConnectionString() string {
|
||||
var conn string
|
||||
postgresSSL := utils.Params.GetString("POSTGRES_SSLMODE")
|
||||
|
|
|
@ -38,7 +38,7 @@ func (d *DbConfig) ResetCore() error {
|
|||
if err := d.CreateDatabase(); err != nil {
|
||||
return errors.Wrap(err, "error creating database")
|
||||
}
|
||||
if err := CreateAdminUser(d); err != nil {
|
||||
if err := CreateAdminUser(); err != nil {
|
||||
return errors.Wrap(err, "error creating default admin user")
|
||||
}
|
||||
if utils.Params.GetBool("SAMPLE_DATA") {
|
||||
|
|
|
@ -6,15 +6,15 @@ const SqliteFilename = "statping.db"
|
|||
|
||||
// DbConfig struct is used for the Db connection and creates the 'config.yml' file
|
||||
type DbConfig struct {
|
||||
DbConn string `yaml:"connection" json:"connection"`
|
||||
DbHost string `yaml:"host" json:"-"`
|
||||
DbUser string `yaml:"user" json:"-"`
|
||||
DbPass string `yaml:"password" json:"-"`
|
||||
DbData string `yaml:"database" json:"-"`
|
||||
DbPort int `yaml:"port" json:"-"`
|
||||
ApiSecret string `yaml:"api_secret" json:"-"`
|
||||
Language string `yaml:"language" json:"language"`
|
||||
AllowReports bool `yaml:"allow_reports" json:"allow_reports"`
|
||||
DbConn string `yaml:"connection,omitempty" json:"connection"`
|
||||
DbHost string `yaml:"host,omitempty" json:"-"`
|
||||
DbUser string `yaml:"user,omitempty" json:"-"`
|
||||
DbPass string `yaml:"password,omitempty" json:"-"`
|
||||
DbData string `yaml:"database,omitempty" json:"-"`
|
||||
DbPort int `yaml:"port,omitempty" json:"-"`
|
||||
ApiSecret string `yaml:"api_secret,omitempty" json:"-"`
|
||||
Language string `yaml:"language,omitempty" json:"language"`
|
||||
AllowReports bool `yaml:"allow_reports,omitempty" json:"allow_reports"`
|
||||
Project string `yaml:"-" json:"-"`
|
||||
Description string `yaml:"-" json:"-"`
|
||||
Domain string `yaml:"-" json:"-"`
|
||||
|
@ -22,7 +22,7 @@ type DbConfig struct {
|
|||
Password string `yaml:"-" json:"-"`
|
||||
Email string `yaml:"-" json:"-"`
|
||||
Error error `yaml:"-" json:"-"`
|
||||
Location string `yaml:"location" json:"-"`
|
||||
Location string `yaml:"location,omitempty" json:"-"`
|
||||
SqlFile string `yaml:"sqlfile,omitempty" json:"-"`
|
||||
LetsEncryptHost string `yaml:"letsencrypt_host,omitempty" json:"letsencrypt_host"`
|
||||
LetsEncryptEmail string `yaml:"letsencrypt_email,omitempty" json:"letsencrypt_email"`
|
||||
|
@ -33,21 +33,21 @@ type DbConfig struct {
|
|||
DemoMode bool `yaml:"demo_mode" json:"demo_mode"`
|
||||
DisableLogs bool `yaml:"disable_logs" json:"disable_logs"`
|
||||
UseAssets bool `yaml:"use_assets" json:"use_assets"`
|
||||
BasePath string `yaml:"base_path" json:"base_path"`
|
||||
BasePath string `yaml:"base_path,omitempty" json:"base_path"`
|
||||
|
||||
AdminUser string `yaml:"admin_user" json:"admin_user"`
|
||||
AdminPassword string `yaml:"admin_password" json:"admin_password"`
|
||||
AdminEmail string `yaml:"admin_email" json:"admin_email"`
|
||||
AdminUser string `yaml:"admin_user,omitempty" json:"admin_user"`
|
||||
AdminPassword string `yaml:"admin_password,omitempty" json:"admin_password"`
|
||||
AdminEmail string `yaml:"admin_email,omitempty" json:"admin_email"`
|
||||
|
||||
MaxOpenConnections int `yaml:"db_open_connections" json:"db_open_connections"`
|
||||
MaxIdleConnections int `yaml:"db_idle_connections" json:"db_idle_connections"`
|
||||
MaxLifeConnections int `yaml:"db_max_life_connections" json:"db_max_life_connections"`
|
||||
MaxOpenConnections int `yaml:"db_open_connections,omitempty" json:"db_open_connections"`
|
||||
MaxIdleConnections int `yaml:"db_idle_connections,omitempty" json:"db_idle_connections"`
|
||||
MaxLifeConnections int `yaml:"db_max_life_connections,omitempty" json:"db_max_life_connections"`
|
||||
|
||||
SampleData bool `yaml:"sample_data" json:"sample_data"`
|
||||
UseCDN bool `yaml:"use_cdn" json:"use_cdn"`
|
||||
DisableColors bool `yaml:"disable_colors" json:"disable_colors"`
|
||||
|
||||
PostgresSSLMode string `yaml:"postgres_ssl" json:"postgres_ssl"`
|
||||
PostgresSSLMode string `yaml:"postgres_ssl,omitempty" json:"postgres_ssl"`
|
||||
|
||||
Db database.Database `yaml:"-" json:"-"`
|
||||
}
|
||||
|
|
|
@ -45,24 +45,25 @@ type Core struct {
|
|||
}
|
||||
|
||||
type OAuth struct {
|
||||
Providers string `gorm:"column:oauth_providers;" json:"oauth_providers"`
|
||||
GithubClientID string `gorm:"column:gh_client_id" json:"gh_client_id"`
|
||||
GithubClientSecret string `gorm:"column:gh_client_secret" json:"gh_client_secret" scope:"admin"`
|
||||
GithubUsers string `gorm:"column:gh_users" json:"gh_users" scope:"admin"`
|
||||
GithubOrgs string `gorm:"column:gh_orgs" json:"gh_orgs" scope:"admin"`
|
||||
GoogleClientID string `gorm:"column:google_client_id" json:"google_client_id"`
|
||||
GoogleClientSecret string `gorm:"column:google_client_secret" json:"google_client_secret" scope:"admin"`
|
||||
GoogleUsers string `gorm:"column:google_users" json:"google_users" scope:"admin"`
|
||||
SlackClientID string `gorm:"column:slack_client_id" json:"slack_client_id"`
|
||||
SlackClientSecret string `gorm:"column:slack_client_secret" json:"slack_client_secret" scope:"admin"`
|
||||
SlackTeam string `gorm:"column:slack_team" json:"slack_team" scope:"admin"`
|
||||
SlackUsers string `gorm:"column:slack_users" json:"slack_users" scope:"admin"`
|
||||
CustomName string `gorm:"column:custom_name" json:"custom_name"`
|
||||
CustomClientID string `gorm:"column:custom_client_id" json:"custom_client_id"`
|
||||
CustomClientSecret string `gorm:"column:custom_client_secret" json:"custom_client_secret" scope:"admin"`
|
||||
CustomEndpointAuth string `gorm:"column:custom_endpoint_auth" json:"custom_endpoint_auth"`
|
||||
CustomEndpointToken string `gorm:"column:custom_endpoint_token" json:"custom_endpoint_token" scope:"admin"`
|
||||
CustomScopes string `gorm:"column:custom_scopes" json:"custom_scopes"`
|
||||
Providers string `gorm:"column:oauth_providers;" json:"oauth_providers"`
|
||||
GithubClientID string `gorm:"column:gh_client_id" json:"gh_client_id"`
|
||||
GithubClientSecret string `gorm:"column:gh_client_secret" json:"gh_client_secret" scope:"admin"`
|
||||
GithubUsers string `gorm:"column:gh_users" json:"gh_users" scope:"admin"`
|
||||
GithubOrgs string `gorm:"column:gh_orgs" json:"gh_orgs" scope:"admin"`
|
||||
GoogleClientID string `gorm:"column:google_client_id" json:"google_client_id"`
|
||||
GoogleClientSecret string `gorm:"column:google_client_secret" json:"google_client_secret" scope:"admin"`
|
||||
GoogleUsers string `gorm:"column:google_users" json:"google_users" scope:"admin"`
|
||||
SlackClientID string `gorm:"column:slack_client_id" json:"slack_client_id"`
|
||||
SlackClientSecret string `gorm:"column:slack_client_secret" json:"slack_client_secret" scope:"admin"`
|
||||
SlackTeam string `gorm:"column:slack_team" json:"slack_team" scope:"admin"`
|
||||
SlackUsers string `gorm:"column:slack_users" json:"slack_users" scope:"admin"`
|
||||
CustomName string `gorm:"column:custom_name" json:"custom_name"`
|
||||
CustomClientID string `gorm:"column:custom_client_id" json:"custom_client_id"`
|
||||
CustomClientSecret string `gorm:"column:custom_client_secret" json:"custom_client_secret" scope:"admin"`
|
||||
CustomEndpointAuth string `gorm:"column:custom_endpoint_auth" json:"custom_endpoint_auth"`
|
||||
CustomEndpointToken string `gorm:"column:custom_endpoint_token" json:"custom_endpoint_token" scope:"admin"`
|
||||
CustomScopes string `gorm:"column:custom_scopes" json:"custom_scopes"`
|
||||
CustomIsOpenID null.NullBool `gorm:"column:custom_open_id" json:"custom_open_id"`
|
||||
}
|
||||
|
||||
// AllNotifiers contains all the Notifiers loaded
|
||||
|
|
|
@ -30,6 +30,7 @@ func InitEnvs() {
|
|||
Params.SetDefault("DEBUG", false)
|
||||
Params.SetDefault("DEMO_MODE", false)
|
||||
Params.SetDefault("DB_CONN", "")
|
||||
Params.SetDefault("DB_DSN", "")
|
||||
Params.SetDefault("DISABLE_LOGS", false)
|
||||
Params.SetDefault("USE_ASSETS", false)
|
||||
Params.SetDefault("BASE_PATH", "")
|
||||
|
@ -53,6 +54,7 @@ func InitEnvs() {
|
|||
Params.SetDefault("LETSENCRYPT_EMAIL", "")
|
||||
Params.SetDefault("LETSENCRYPT_LOCAL", false)
|
||||
Params.SetDefault("LETSENCRYPT_ENABLE", false)
|
||||
Params.SetDefault("READ_ONLY", false)
|
||||
Params.SetDefault("LOGS_MAX_COUNT", 5)
|
||||
Params.SetDefault("LOGS_MAX_AGE", 28)
|
||||
Params.SetDefault("LOGS_MAX_SIZE", 16)
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.90.66
|
||||
0.90.69
|
Loading…
Reference in New Issue