mirror of https://github.com/statping/statping
0.90.68 (#821)
- 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 frontendpull/822/head^2
parent
a216187055
commit
93d9dc9915
|
@ -1,3 +1,10 @@
|
|||
# 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)
|
||||
|
|
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;
|
||||
|
|
|
@ -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: "电子邮件",
|
||||
|
|
|
@ -9,6 +9,7 @@ login,Login
|
|||
logout,Logout
|
||||
online,Online
|
||||
offline,Offline
|
||||
configs,Configuration
|
||||
username,Username
|
||||
password,Password
|
||||
email,Email
|
||||
|
|
|
|
@ -9,6 +9,7 @@ const english = {
|
|||
logout: "Logout",
|
||||
online: "Online",
|
||||
offline: "Offline",
|
||||
configs: "Configuration",
|
||||
username: "Username",
|
||||
password: "Password",
|
||||
email: "Email",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -9,6 +9,7 @@ const german = {
|
|||
logout: "Abmelden",
|
||||
online: "Online",
|
||||
offline: "Offline",
|
||||
configs: "Konfiguration",
|
||||
username: "Benutzername",
|
||||
password: "Kennwort",
|
||||
email: "Mail",
|
||||
|
|
|
@ -9,6 +9,7 @@ const italian = {
|
|||
logout: "Disconnetti",
|
||||
online: "Online",
|
||||
offline: "Offline",
|
||||
configs: "Configurazione",
|
||||
username: "Nome utente",
|
||||
password: "Password",
|
||||
email: "E-mail",
|
||||
|
|
|
@ -9,6 +9,7 @@ const japanese = {
|
|||
logout: "ログアウト",
|
||||
online: "オンライン",
|
||||
offline: "オフライン",
|
||||
configs: "構成",
|
||||
username: "ユーザ名",
|
||||
password: "パスワード",
|
||||
email: "Eメール",
|
||||
|
|
|
@ -9,6 +9,7 @@ const korean = {
|
|||
logout: "로그아웃",
|
||||
online: "온라인",
|
||||
offline: "오프라인",
|
||||
configs: "구성",
|
||||
username: "사용자 이름",
|
||||
password: "비밀번호",
|
||||
email: "이메일",
|
||||
|
|
|
@ -9,6 +9,7 @@ const russian = {
|
|||
logout: "Выход из системы",
|
||||
online: "Онлайн",
|
||||
offline: "Оффлайн",
|
||||
configs: "Конфигурация",
|
||||
username: "Имя пользователя",
|
||||
password: "Пароль",
|
||||
email: "Электронная почта",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
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=
|
||||
|
|
|
@ -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"
|
||||
|
@ -162,14 +164,16 @@ func apiThemeRemoveHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -261,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
|
||||
|
|
|
@ -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.67
|
||||
0.90.68
|
Loading…
Reference in New Issue