Compare commits
No commits in common. "master" and "go-rewrite" have entirely different histories.
master
...
go-rewrite
34
.drone.yml
@ -1,34 +0,0 @@
|
||||
platform: linux/arm
|
||||
|
||||
workspace:
|
||||
base: /home/builder/package
|
||||
path: /
|
||||
|
||||
clone:
|
||||
default:
|
||||
image: plugins/git:linux-arm
|
||||
depth: 50
|
||||
|
||||
pipeline:
|
||||
build:
|
||||
image: openpdu/builder:3.7
|
||||
environment:
|
||||
- REPODEST=/home/builder/package
|
||||
secrets:
|
||||
- source: abuild_ssh_key
|
||||
target: RSA_PRIVATE_KEY
|
||||
- source: abuild_ssh_key_name
|
||||
target: RSA_PRIVATE_KEY_NAME
|
||||
|
||||
gitea_release:
|
||||
image: plugins/gitea-release:linux-arm
|
||||
files: builder/armhf/*.apk
|
||||
checksum:
|
||||
- md5
|
||||
- sha1
|
||||
- sha256
|
||||
- sha512
|
||||
secrets: [ gitea_token ]
|
||||
base_url: https://git.asperti.com
|
||||
when:
|
||||
event: tag
|
13
.editorconfig
Normal file
@ -0,0 +1,13 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
|
||||
[*.html]
|
||||
indent_style = space
|
||||
indent_size = 2
|
41
.gitignore
vendored
@ -1,3 +1,38 @@
|
||||
packages/
|
||||
*.apk
|
||||
.on-save.json
|
||||
openpdu.yaml
|
||||
*.sh
|
||||
|
||||
### Go ###
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
src/__debug_bin
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
### Go Patch ###
|
||||
/vendor/
|
||||
/Godeps/
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
*.code-workspace
|
||||
|
||||
### VisualStudioCode Patch ###
|
||||
# Ignore all local history of files
|
||||
.history
|
||||
.ionide
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,go
|
||||
|
16
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Package",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"program": "${workspaceFolder}/src",
|
||||
"cwd": "${workspaceFolder}/src"
|
||||
}
|
||||
]
|
||||
}
|
29
APKBUILD
@ -1,29 +0,0 @@
|
||||
# Contributor: Paolo Asperti <paolo@asperti.com>
|
||||
# Maintainer: Paolo Asperti <paolo@asperti.com>
|
||||
pkgname=openpdu
|
||||
pkgver=0.2.0
|
||||
pkgrel=1
|
||||
pkgdesc="OpenPDU project - main binary"
|
||||
url="https://git.asperti.com/OpenPDU/openpdu"
|
||||
arch="noarch"
|
||||
license="GPL2"
|
||||
depends="python py-argh apk-cron py-bottle i2c-tools"
|
||||
makedepends=""
|
||||
install="openpdu.post-install"
|
||||
subpackages=""
|
||||
source=""
|
||||
options="!check"
|
||||
|
||||
build() {
|
||||
:
|
||||
}
|
||||
|
||||
package() {
|
||||
mkdir -p "$pkgdir"
|
||||
install -Dm644 etc/openpdu/boards.conf "$pkgdir"/etc/openpdu/boards.conf
|
||||
install -Dm644 etc/openpdu/outlets.conf "$pkgdir"/etc/openpdu/outlets.conf
|
||||
install -Dm755 etc/local.d/openpdu.start "$pkgdir"/etc/local.d/openpdu.start
|
||||
|
||||
install -Dm755 openpdu "$pkgdir"/usr/bin/openpdu
|
||||
install -Dm755 openpdud "$pkgdir"/usr/bin/openpdud
|
||||
}
|
23
README.md
@ -1,23 +1,14 @@
|
||||
# The OpenPDU project
|
||||
|
||||
This repo contains the source code for the main binary of the OpenPDU project.
|
||||
|
||||
OpenPDU is an OpenHardware and OpenSource project for a network-connected PDU.
|
||||
The goal is to build a modular system than can be used to retrofit an existing 19" rack PDU to a real networked PDU. A secondary goal is the use of commonly available hardware, where possible, at least for the main controller.
|
||||
|
||||
## Hardware
|
||||
The first iteration will cover the scenario of a retrofit of an existing (rack) power strip. In any case we'll keep separated the system into the following modules:
|
||||
We want to build the best firmware possible for a smart PDU, giving you the possibility to customize your own. You can build a custom PDU or retrofit an existing one. You can control as many outlet as you want, and manage a shared UPS.
|
||||
|
||||
- DC power supply
|
||||
- controller board
|
||||
- power board
|
||||
## development tips
|
||||
|
||||
The power supply will power the controller and the power board, taking the power from the existing power switch (it's uncommon to have a rack power strip without a power button; in this case it's required to add a power button).
|
||||
### start a local mqtt container
|
||||
|
||||
The controller board will be a commonly available one (let's say a Raspberry PI 2/3, Orange Pi, ...). The requirements are:
|
||||
|
||||
- capable of running linux
|
||||
- at least 256mb of RAM
|
||||
- ethernet port
|
||||
- GPIO/i2c port
|
||||
- (40pin connector? some orangepis have only 26)
|
||||
```bash
|
||||
docker run --rm -d --name mqtt -p 1883:1883 docker.asperti.com/paspo/mqtt
|
||||
docker logs -f mqtt
|
||||
```
|
||||
|
@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
/usr/bin/openpdu initboards
|
||||
/usr/bin/openpdu initoutlets
|
@ -1,30 +0,0 @@
|
||||
[DEFAULT]
|
||||
|
||||
|
||||
# EXAMPLE GPIO BOARD
|
||||
# [board0]
|
||||
# channel0 = 1
|
||||
# channel1 = 2
|
||||
# channel2 = 3
|
||||
# channel3 = 7
|
||||
# channel4 = 8
|
||||
# channel5 = 9
|
||||
# channel6 = 11
|
||||
# channel7 = 12
|
||||
# type = gpio-out
|
||||
# channels = 8
|
||||
|
||||
|
||||
# EXAMPLE I2C BOARD
|
||||
# [board1]
|
||||
# type = i2c-out
|
||||
# address = 0x27
|
||||
# channels = 8
|
||||
# bus = 0
|
||||
|
||||
# EXAMPLE I2C CURRENT BOARD
|
||||
# [board2]
|
||||
# type = i2c-current
|
||||
# address = 0x48
|
||||
# channels = 4
|
||||
# bus = 0
|
@ -1,31 +0,0 @@
|
||||
[DEFAULT]
|
||||
description = Generic outlet
|
||||
startpower = 1
|
||||
|
||||
# [outlet0]
|
||||
# board = 0
|
||||
# channel = 0
|
||||
# description = Router
|
||||
|
||||
# [outlet1]
|
||||
# board = 0
|
||||
# channel = 1
|
||||
# description = NAS
|
||||
|
||||
# [outlet2]
|
||||
# board = 1
|
||||
# channel = 0
|
||||
# description = Switch
|
||||
|
||||
# [outlet3]
|
||||
# board=1
|
||||
# channel=1
|
||||
# currentboard=2
|
||||
# currentboardchannel=1
|
||||
# description = MailServer
|
||||
|
||||
# [outlet4]
|
||||
# board=1
|
||||
# channel=2
|
||||
# description = Extra Fan
|
||||
# startpower = 0
|
363
openpdu
@ -1,363 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argh
|
||||
from argh import arg
|
||||
from subprocess import call
|
||||
import re
|
||||
import glob
|
||||
import time
|
||||
import ConfigParser
|
||||
import json as JSON
|
||||
|
||||
VERSION = '0.1.2'
|
||||
boardsDefaults = {'inverted':False}
|
||||
outletsDefaults = {'description': 'Generic outlet','startpower':False}
|
||||
_boards = []
|
||||
_outlets = []
|
||||
|
||||
|
||||
def version():
|
||||
'show program version and quit'
|
||||
return 'OpenPDU version %s - Copyright (C) 2018 by Paolo Asperti.' % VERSION
|
||||
|
||||
@arg('-j', '--json', help="output in json")
|
||||
def boards(json=False):
|
||||
'dump boards configuration'
|
||||
if json:
|
||||
b = [board.toJSON() for board in _boards]
|
||||
return JSON.dumps({'boards':b})
|
||||
else:
|
||||
b = ''
|
||||
for board in _boards:
|
||||
b += board.toSTR()
|
||||
return '\nBoards:\n' + b
|
||||
|
||||
|
||||
def initboards():
|
||||
'initialize boards'
|
||||
for board in _boards:
|
||||
board.init()
|
||||
return
|
||||
|
||||
def initoutlets():
|
||||
'initialize outlets'
|
||||
for outlet in _outlets:
|
||||
outlet.init()
|
||||
return
|
||||
|
||||
@arg('-j', '--json', help="output in json")
|
||||
def outlets(json=False):
|
||||
'dump outlets configuration'
|
||||
if json:
|
||||
o = [outlet.toJSON() for outlet in _outlets]
|
||||
return JSON.dumps({'outlets':o})
|
||||
else:
|
||||
o = ''
|
||||
if len(_outlets)>0:
|
||||
for outlet in _outlets:
|
||||
o += outlet.toSTR()
|
||||
return '\nOutlets:\n' + o
|
||||
|
||||
|
||||
@arg("outlet", help="outlet number")
|
||||
@arg("onoff", help="1=on [everything else]=off")
|
||||
@arg('-j', '--json', help="output in json")
|
||||
def setpower(outlet, onoff, json=False):
|
||||
'enable or disable power to an outlet'
|
||||
outlet=int(outlet)
|
||||
o = [o for o in _outlets if o.outletnum==outlet]
|
||||
if len(o) != 1:
|
||||
msg = 'wrong outlet number: %s' % str(outlet)
|
||||
print JSON.dumps({'message':msg}) if json else msg
|
||||
sys.exit(1)
|
||||
theOutlet = o[0]
|
||||
status = (onoff == '1')
|
||||
out = theOutlet.setpower(status)
|
||||
if out is None:
|
||||
msg = "Cannot set power status for outlet %s" % outlet
|
||||
return JSON.dumps({'message':msg,'outlet':outlet}) if json else msg
|
||||
else:
|
||||
pwrstr = 'on' if out==1 else 'off'
|
||||
msg = "Outlet #%s powered %s" % (outlet, pwrstr)
|
||||
return JSON.dumps({'powerstatus':out==1,'outlet':outlet}) if json else msg
|
||||
|
||||
|
||||
@arg("outlet", help="outlet number")
|
||||
@arg('-j', '--json', help="output in json")
|
||||
def getpower(outlet, json=False):
|
||||
'get outlet power status'
|
||||
outlet=int(outlet)
|
||||
o = [o for o in _outlets if o.outletnum==outlet]
|
||||
if len(o) != 1:
|
||||
msg = 'wrong outlet number: %s' % str(outlet)
|
||||
print JSON.dumps({'message':msg}) if json else msg
|
||||
sys.exit(1)
|
||||
theOutlet = o[0]
|
||||
out = theOutlet.getpower()
|
||||
pwrstr = 'on' if out else 'off'
|
||||
msg = "Outlet #%s powered %s" % (outlet, pwrstr)
|
||||
return JSON.dumps({'powerstatus':out,'outlet':outlet}) if json else msg
|
||||
|
||||
|
||||
# class Abstract1( object ):
|
||||
# """Some description that tells you it's abstract,
|
||||
# often listing the methods you're expected to supply."""
|
||||
# def aMethod( self ):
|
||||
# raise NotImplementedError( "Should have implemented this" )
|
||||
#
|
||||
# class Cipo(Abstract1):
|
||||
# pass
|
||||
|
||||
|
||||
class BoardDummy(object):
|
||||
channels = []
|
||||
|
||||
def __init__(self, boardnum, channels=None, filename=None):
|
||||
self.boardnum = boardnum
|
||||
if channels is None:
|
||||
self.channels = 1
|
||||
else:
|
||||
self.channels = int(channels)
|
||||
self.parser = ConfigParser.SafeConfigParser()
|
||||
self.filename = filename
|
||||
if os.path.isfile(filename) and os.access(filename, os.R_OK):
|
||||
pass
|
||||
else:
|
||||
with open(self.filename, 'wb') as theFile:
|
||||
self.parser.add_section('STATUS')
|
||||
for c in range(0,self.channels):
|
||||
self.parser.set('STATUS', 'channel%s' % c, '0')
|
||||
self.parser.write(theFile)
|
||||
|
||||
def toJSON(self):
|
||||
return {'boardnum':self.boardnum,'type':'dummy','channels':self.channels}
|
||||
|
||||
def toSTR(self):
|
||||
return ' Board %s\n Type: dummy\n Channels: %s\n\n' % (self.boardnum,self.channels)
|
||||
|
||||
def setpower(self, channel, power):
|
||||
self.parser.read(self.filename)
|
||||
p = '1' if power else '0'
|
||||
s = self.parser.set('STATUS', 'channel%s' % channel, p)
|
||||
with open(self.filename, 'wb') as theFile:
|
||||
return self.parser.write(theFile)
|
||||
return False
|
||||
|
||||
def getpower(self, channel):
|
||||
self.parser.read(self.filename)
|
||||
s = self.parser.get('STATUS', 'channel%s' % channel)
|
||||
return int(s)==1
|
||||
|
||||
def init(self):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# MCP23008
|
||||
class BoardI2COut(object):
|
||||
data = 0
|
||||
next_refresh = 0
|
||||
lifetime_sec = 2
|
||||
|
||||
def __init__(self, boardnum, channels=None, address=None, bus=None, inverted=False):
|
||||
self.boardnum = boardnum
|
||||
if channels is None:
|
||||
self.channels = 0
|
||||
else:
|
||||
self.channels = channels
|
||||
if address is None:
|
||||
self.address = 0x20
|
||||
else:
|
||||
self.address = address
|
||||
if bus is None:
|
||||
self.bus = 1
|
||||
else:
|
||||
self.bus = bus
|
||||
self.inverted = inverted
|
||||
if not glob.glob('/dev/i2c*'):
|
||||
raise OSError('Cannot access I2C. Please ensure I2C is enabled')
|
||||
|
||||
def toJSON(self):
|
||||
return {'boardnum':self.boardnum,'type':'i2c-out','channels':self.channels,'address':self.address}
|
||||
|
||||
def toSTR(self):
|
||||
return ' Board %s\n Type: i2c-out\n Channels: %s\n Address: %s\n\n' % (self.boardnum,self.channels,self.address)
|
||||
|
||||
def setpower(self, channel, power):
|
||||
old_data = data = self.getdata()
|
||||
mask = 1 << channel
|
||||
data &= ~mask
|
||||
if self.inverted:
|
||||
power = not power
|
||||
if power:
|
||||
data |= mask
|
||||
if old_data != data:
|
||||
self.next_refresh = 0
|
||||
return os.popen("/usr/sbin/i2cset -y %s %s 0x09 0x%s" % (self.bus, self.address, format(data, '02x'))).read()
|
||||
|
||||
def getpower(self, channel):
|
||||
data = self.getdata()
|
||||
d = ( data >> channel ) & 1
|
||||
c = 0 if self.inverted else 1
|
||||
return d == c
|
||||
|
||||
def getdata(self):
|
||||
now = time.time()
|
||||
if now > self.next_refresh:
|
||||
self.data = int(os.popen("/usr/sbin/i2cget -y %s %s 0x0A" % (self.bus, self.address)).read(),0)
|
||||
self.next_refresh = now + self.lifetime_sec
|
||||
return self.data
|
||||
|
||||
def init(self):
|
||||
call(["/usr/sbin/i2cset", "-y", self.bus, self.address, "0x00", "0x00"])
|
||||
|
||||
|
||||
class BoardGpioOut(object):
|
||||
gpios = []
|
||||
|
||||
def __init__(self, boardnum, channels=None, gpios=None, inverted=False):
|
||||
self.boardnum = boardnum
|
||||
if channels is None:
|
||||
self.channels = 0
|
||||
else:
|
||||
self.channels = int(channels)
|
||||
self.inverted = inverted
|
||||
if not isinstance(gpios, list):
|
||||
print 'No gpios specified for gpio board %s' % self.boardnum
|
||||
if len(gpios) != self.channels:
|
||||
print 'Wrong number of gpios specified for gpio board %s' % self.boardnum
|
||||
self.gpios = [int(gpio) for gpio in gpios]
|
||||
|
||||
def toJSON(self):
|
||||
return {'boardnum':self.boardnum,'type':'gpio-out','channels':self.channels}
|
||||
|
||||
def toSTR(self):
|
||||
return ' Board %s\n Type: gpio-out\n Channels: %s\n\n' % (self.boardnum,self.channels)
|
||||
|
||||
def setpower(self, channel, power):
|
||||
io = self.gpios[channel]
|
||||
fn = '/sys/class/gpio/gpio%s/value' % io
|
||||
f = open(fn,'w')
|
||||
if self.inverted:
|
||||
power = not power
|
||||
out = '1' if power else '0'
|
||||
return f.write(out)
|
||||
|
||||
def getpower(self, channel):
|
||||
io = self.gpios[channel]
|
||||
fn = '/sys/class/gpio/gpio%s/value' % io
|
||||
f = open(fn,'r')
|
||||
e = f.read()
|
||||
power = int('0'+e) == 1
|
||||
if self.inverted:
|
||||
power = not power
|
||||
return power
|
||||
|
||||
def init(self):
|
||||
for gpio in self.gpios:
|
||||
if not os.path.isdir('/sys/class/gpio/gpio%s/' % gpio):
|
||||
open('/sys/class/gpio/export','w').write(str(gpio))
|
||||
fn = '/sys/class/gpio/gpio%s/direction' % gpio
|
||||
open(fn,'w').write('out')
|
||||
return
|
||||
|
||||
|
||||
class Outlet(object):
|
||||
def __init__(self, outletnum, boardnum, channel, startpower=False):
|
||||
self.outletnum = int(outletnum)
|
||||
b = [b for b in _boards if b.boardnum==int(boardnum)]
|
||||
self.board = b[0]
|
||||
self.channel = int(channel)
|
||||
self.description = 'Outlet # %s' % self.outletnum
|
||||
self.startpower = startpower
|
||||
|
||||
def init(self):
|
||||
self.setpower(self.startpower)
|
||||
return
|
||||
|
||||
def setpower(self, power):
|
||||
self.board.setpower(self.channel,power)
|
||||
if self.board.getpower(self.channel) == power:
|
||||
return power
|
||||
else:
|
||||
return None
|
||||
|
||||
def getpower(self):
|
||||
return self.board.getpower(self.channel)
|
||||
|
||||
def toSTR(self):
|
||||
status = self.board.getpower(self.channel)
|
||||
return ' Outlet %s\n Description: %s\n Board #: %s\n Channel: %s\n Power Status:%s\n\n' % (self.outletnum,self.description,self.board.boardnum,self.channel,status)
|
||||
|
||||
def toJSON(self):
|
||||
status = self.board.getpower(self.channel)
|
||||
return {'outlet':self.outletnum,'description':self.description,'board':self.board.boardnum,'channel':self.channel,'powerstatus':status}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
boardsConfigParser = ConfigParser.SafeConfigParser(defaults=boardsDefaults)
|
||||
boardsConfigFile = '/etc/openpdu/boards.conf'
|
||||
if os.path.isfile(boardsConfigFile) and os.access(boardsConfigFile, os.R_OK):
|
||||
boardsConfigParser.read(boardsConfigFile)
|
||||
|
||||
for s in boardsConfigParser.sections():
|
||||
if re.match('^board.*',s):
|
||||
bType = boardsConfigParser.get(s, 'type')
|
||||
num = int(re.sub(r'^board','',s))
|
||||
inverted = int('0' + boardsConfigParser.get(s, 'inverted')) == 1
|
||||
channels = int(boardsConfigParser.get(s, 'channels'))
|
||||
if bType=='gpio-out':
|
||||
gpios = []
|
||||
for g in range(0,channels):
|
||||
gpios.append(int(boardsConfigParser.get(s, 'channel%s' % g)))
|
||||
b = BoardGpioOut(boardnum=num, channels=channels, gpios=gpios, inverted=inverted)
|
||||
_boards.append(b)
|
||||
elif bType=='i2c-out':
|
||||
address = boardsConfigParser.get(s, 'address')
|
||||
bus = boardsConfigParser.get(s, 'bus')
|
||||
b = BoardI2COut(boardnum=num, channels=channels, address=address, bus=bus, inverted=inverted)
|
||||
_boards.append(b)
|
||||
elif bType=='dummy':
|
||||
filename = boardsConfigParser.get(s, 'filename')
|
||||
b = BoardDummy(boardnum=num, channels=channels, filename=filename)
|
||||
_boards.append(b)
|
||||
|
||||
outletsConfigParser = ConfigParser.SafeConfigParser(defaults=outletsDefaults)
|
||||
outletsConfig = '/etc/openpdu/outlets.conf'
|
||||
if os.path.isfile(outletsConfig) and os.access(outletsConfig, os.R_OK):
|
||||
outletsConfigParser.read(outletsConfig)
|
||||
for s in outletsConfigParser.sections():
|
||||
if re.match('^outlet.*',s):
|
||||
num = int(re.sub(r'^outlet','',s))
|
||||
description = outletsConfigParser.get(s, 'description')
|
||||
boardnum = outletsConfigParser.get(s, 'board')
|
||||
channel = outletsConfigParser.get(s, 'channel')
|
||||
startpower = int('0' + outletsConfigParser.get(s, 'startpower')) == 1
|
||||
o = Outlet(outletnum=num, boardnum=boardnum, channel=channel, startpower=startpower)
|
||||
o.description = description
|
||||
_outlets.append(o)
|
||||
|
||||
|
||||
|
||||
parser = argh.ArghParser()
|
||||
parser.add_commands([setpower, getpower, outlets, boards, initboards, version, initoutlets])
|
||||
|
||||
# dispatching:
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser.dispatch()
|
@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
exit 0
|
60
openpdu_example.yaml
Normal file
@ -0,0 +1,60 @@
|
||||
display:
|
||||
h: 32
|
||||
rotated: false
|
||||
swaptopbottom: false
|
||||
type: ssd1306
|
||||
w: 128
|
||||
system:
|
||||
hostname: openpdu-rack1
|
||||
|
||||
mqtt:
|
||||
cliendid: openpdu-main
|
||||
host: 192.168.1.230
|
||||
password: ""
|
||||
port: "1883"
|
||||
prefix: openpdu-main
|
||||
schema: tcp
|
||||
username: ""
|
||||
|
||||
ups:
|
||||
host: 192.168.1.132
|
||||
name: ups
|
||||
password: slave
|
||||
username: monuser
|
||||
boards:
|
||||
2306c7db-9d2e-4f55-8944-6a5a8b75a4a3:
|
||||
type: dummy
|
||||
inverted: false
|
||||
channelCount: 4
|
||||
name: The Dummy Board
|
||||
channels:
|
||||
4976c508-1f63-4894-a859-cc5bd555b3c1:
|
||||
num: 0
|
||||
name: autonomo
|
||||
onboot: off
|
||||
c0a4d271-2b75-41c0-8b05-86c5ca0b3e94:
|
||||
num: 1
|
||||
name: fenomeno
|
||||
onboot: off
|
||||
4d0b384f-6bcb-4eca-a803-ebea90c2a434:
|
||||
num: 2
|
||||
name: ecomeno
|
||||
onboot: off
|
||||
acb5e9d5-959c-4427-a850-17c262b96c16:
|
||||
num: 3
|
||||
name: ripopolo
|
||||
onboot: off
|
||||
outlets:
|
||||
0fca047e-0e7f-4cee-83a8-2086ee306faf:
|
||||
num: 0
|
||||
channelID: 4976c508-1f63-4894-a859-cc5bd555b3c1
|
||||
6b36abdc-dc8d-424a-997d-ffb59642c749:
|
||||
num: 1
|
||||
channelID: 4d0b384f-6bcb-4eca-a803-ebea90c2a434
|
||||
e14f53ba-8b53-4327-a1c7-10cf148d6c42:
|
||||
num: 2
|
||||
channelID: c0a4d271-2b75-41c0-8b05-86c5ca0b3e94
|
||||
1e66a9b5-54fe-4e0e-b34f-4d96b58f2111:
|
||||
num: 3
|
||||
channelID: acb5e9d5-959c-4427-a850-17c262b96c16
|
||||
|
475
openpdud
@ -1,475 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argh
|
||||
#from argh import arg
|
||||
import re
|
||||
import glob
|
||||
import time
|
||||
import ConfigParser
|
||||
import json as JSON
|
||||
from bottle import route, run, template
|
||||
import smbus2
|
||||
#import math
|
||||
from numpy import mean, sqrt, square
|
||||
|
||||
|
||||
|
||||
VERSION = '0.1.2'
|
||||
boardsDefaults = {'inverted':False}
|
||||
outletsDefaults = {'description': 'Generic outlet','startpower':False}
|
||||
_boards = []
|
||||
_outlets = []
|
||||
|
||||
|
||||
|
||||
|
||||
@route('/version')
|
||||
def index():
|
||||
return VERSION
|
||||
|
||||
@route('/boards')
|
||||
def index():
|
||||
b = [board.toJSON() for board in _boards]
|
||||
return JSON.dumps({'boards':b})
|
||||
|
||||
@route('/initboards')
|
||||
def index():
|
||||
for board in _boards:
|
||||
board.init()
|
||||
return
|
||||
|
||||
@route('/initoutlets')
|
||||
def index():
|
||||
for outlet in _outlets:
|
||||
outlet.init()
|
||||
return
|
||||
|
||||
@route('/outlets')
|
||||
def index():
|
||||
o = [outlet.toJSON() for outlet in _outlets]
|
||||
return JSON.dumps({'outlets':o})
|
||||
|
||||
@route('/outlet/<outlet>/power/<onoff>')
|
||||
def index(outlet, onoff):
|
||||
outlet=int(outlet)
|
||||
o = [o for o in _outlets if o.outletnum==outlet]
|
||||
if len(o) != 1:
|
||||
msg = 'wrong outlet number: %s' % str(outlet)
|
||||
return JSON.dumps({'message':msg})
|
||||
theOutlet = o[0]
|
||||
status = (onoff == 'on')
|
||||
out = theOutlet.setpower(status)
|
||||
if out is None:
|
||||
msg = "Cannot set power status for outlet %s" % outlet
|
||||
return JSON.dumps({'message':msg,'outlet':outlet})
|
||||
else:
|
||||
pwrstr = 'on' if out==1 else 'off'
|
||||
msg = "Outlet #%s powered %s" % (outlet, pwrstr)
|
||||
return JSON.dumps({'powerstatus':out==1,'outlet':outlet})
|
||||
|
||||
@route('/outlet/<outlet>/power')
|
||||
def index(outlet):
|
||||
outlet=int(outlet)
|
||||
o = [o for o in _outlets if o.outletnum==outlet]
|
||||
if len(o) != 1:
|
||||
msg = 'wrong outlet number: %s' % str(outlet)
|
||||
return JSON.dumps({'message':msg})
|
||||
theOutlet = o[0]
|
||||
out = theOutlet.getpower()
|
||||
return JSON.dumps({'powerstatus':out,'outlet':outlet})
|
||||
|
||||
@route('/outlet/<outlet>/current')
|
||||
def index(outlet):
|
||||
outlet=int(outlet)
|
||||
o = [o for o in _outlets if o.outletnum==outlet]
|
||||
if len(o) != 1:
|
||||
msg = 'wrong outlet number: %s' % str(outlet)
|
||||
return JSON.dumps({'message':msg})
|
||||
theOutlet = o[0]
|
||||
out = theOutlet.getcurrent()
|
||||
return JSON.dumps({'current':out,'outlet':outlet})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class BoardDummy(object):
|
||||
channels = []
|
||||
|
||||
def __init__(self, boardnum, channels=None, filename=None):
|
||||
self.boardnum = boardnum
|
||||
if channels is None:
|
||||
self.channels = 1
|
||||
else:
|
||||
self.channels = int(channels)
|
||||
self.parser = ConfigParser.SafeConfigParser()
|
||||
self.filename = filename
|
||||
if os.path.isfile(filename) and os.access(filename, os.R_OK):
|
||||
pass
|
||||
else:
|
||||
with open(self.filename, 'wb') as theFile:
|
||||
self.parser.add_section('STATUS')
|
||||
for c in range(0,self.channels):
|
||||
self.parser.set('STATUS', 'channel%s' % c, '0')
|
||||
self.parser.write(theFile)
|
||||
|
||||
def toJSON(self):
|
||||
return {'boardnum':self.boardnum,'type':'dummy','channels':self.channels}
|
||||
|
||||
def setpower(self, channel, power):
|
||||
self.parser.read(self.filename)
|
||||
p = '1' if power else '0'
|
||||
s = self.parser.set('STATUS', 'channel%s' % channel, p)
|
||||
with open(self.filename, 'wb') as theFile:
|
||||
return self.parser.write(theFile)
|
||||
return False
|
||||
|
||||
def getpower(self, channel):
|
||||
self.parser.read(self.filename)
|
||||
s = self.parser.get('STATUS', 'channel%s' % channel)
|
||||
return int(s)==1
|
||||
|
||||
def init(self):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# MCP23008
|
||||
class BoardI2COut(object):
|
||||
data = 0
|
||||
next_refresh = 0
|
||||
lifetime_sec = 2
|
||||
|
||||
def __init__(self, boardnum, channels=None, address=None, bus=None, inverted=False):
|
||||
self.boardnum = boardnum
|
||||
if channels is None:
|
||||
self.channels = 0
|
||||
else:
|
||||
self.channels = channels
|
||||
if address is None:
|
||||
self.address = 0x20
|
||||
else:
|
||||
self.address = int(address, 16)
|
||||
if bus is None:
|
||||
self.bus = 1
|
||||
else:
|
||||
self.bus = bus
|
||||
self.inverted = inverted
|
||||
if not glob.glob('/dev/i2c*'):
|
||||
raise OSError('Cannot access I2C. Please ensure I2C is enabled')
|
||||
self._bus = smbus2.SMBus(self.bus)
|
||||
|
||||
def toJSON(self):
|
||||
return {'boardnum':self.boardnum,'type':'i2c-out','channels':self.channels,'address':self.address}
|
||||
|
||||
def setpower(self, channel, power):
|
||||
old_data = data = self.getdata()
|
||||
mask = 1 << channel
|
||||
data &= ~mask
|
||||
if self.inverted:
|
||||
power = not power
|
||||
if power:
|
||||
data |= mask
|
||||
if old_data != data:
|
||||
self.next_refresh = 0
|
||||
return self._bus.write_byte_data(self.address, 0x09,data)
|
||||
|
||||
def getpower(self, channel):
|
||||
data = self.getdata()
|
||||
d = ( data >> channel ) & 1
|
||||
c = 0 if self.inverted else 1
|
||||
return d == c
|
||||
|
||||
def getdata(self):
|
||||
now = time.time()
|
||||
if now > self.next_refresh:
|
||||
self.data = self._bus.read_byte_data(self.address, 0x0A)
|
||||
self.next_refresh = now + self.lifetime_sec
|
||||
return self.data
|
||||
|
||||
def init(self):
|
||||
return self._bus.write_byte_data(self.address, 0x00, 0x00)
|
||||
|
||||
|
||||
class BoardGpioOut(object):
|
||||
gpios = []
|
||||
|
||||
def __init__(self, boardnum, channels=None, gpios=None, inverted=False):
|
||||
self.boardnum = boardnum
|
||||
if channels is None:
|
||||
self.channels = 0
|
||||
else:
|
||||
self.channels = int(channels)
|
||||
self.inverted = inverted
|
||||
if not isinstance(gpios, list):
|
||||
print 'No gpios specified for gpio board %s' % self.boardnum
|
||||
if len(gpios) != self.channels:
|
||||
print 'Wrong number of gpios specified for gpio board %s' % self.boardnum
|
||||
self.gpios = [int(gpio) for gpio in gpios]
|
||||
|
||||
def toJSON(self):
|
||||
return {'boardnum':self.boardnum,'type':'gpio-out','channels':self.channels,'inverted':self.inverted}
|
||||
|
||||
def setpower(self, channel, power):
|
||||
io = self.gpios[channel]
|
||||
fn = '/sys/class/gpio/gpio%s/value' % io
|
||||
f = open(fn,'w')
|
||||
if self.inverted:
|
||||
power = not power
|
||||
out = '1' if power else '0'
|
||||
return f.write(out)
|
||||
|
||||
def getpower(self, channel):
|
||||
io = self.gpios[channel]
|
||||
fn = '/sys/class/gpio/gpio%s/value' % io
|
||||
f = open(fn,'r')
|
||||
e = f.read()
|
||||
power = int('0'+e) == 1
|
||||
if self.inverted:
|
||||
power = not power
|
||||
return power
|
||||
|
||||
def init(self):
|
||||
for gpio in self.gpios:
|
||||
if not os.path.isdir('/sys/class/gpio/gpio%s/' % gpio):
|
||||
open('/sys/class/gpio/export','w').write(str(gpio))
|
||||
fn = '/sys/class/gpio/gpio%s/direction' % gpio
|
||||
open(fn,'w').write('out')
|
||||
return
|
||||
|
||||
|
||||
class Outlet(object):
|
||||
def __init__(self, outletnum, boardnum, channel, startpower=False):
|
||||
self.outletnum = int(outletnum)
|
||||
b = [b for b in _boards if b.boardnum==int(boardnum)]
|
||||
self.board = b[0]
|
||||
self.channel = int(channel)
|
||||
self.description = 'Outlet # %s' % self.outletnum
|
||||
self.startpower = startpower
|
||||
self.currentboard = None
|
||||
self.currentboardchannel = None
|
||||
|
||||
def init(self):
|
||||
self.setpower(self.startpower)
|
||||
return
|
||||
|
||||
def setpower(self, power):
|
||||
self.board.setpower(self.channel,power)
|
||||
if self.board.getpower(self.channel) == power:
|
||||
return power
|
||||
else:
|
||||
return None
|
||||
|
||||
def getpower(self):
|
||||
return self.board.getpower(self.channel)
|
||||
|
||||
def getcurrent(self):
|
||||
if self.currentboard is None:
|
||||
return 0
|
||||
return self.currentboard.readcurrent(self.currentboardchannel)
|
||||
|
||||
def setcurrentboard(self, boardnum, channel):
|
||||
b = [b for b in _boards if b.boardnum==int(boardnum)]
|
||||
self.currentboard = b[0]
|
||||
self.currentboardchannel = channel
|
||||
|
||||
|
||||
def toJSON(self):
|
||||
status = self.board.getpower(self.channel)
|
||||
return {'outlet':self.outletnum,'description':self.description,'board':self.board.boardnum,'channel':self.channel,'powerstatus':status}
|
||||
|
||||
|
||||
|
||||
|
||||
class BoardI2Ccurrent(object):
|
||||
data = 0
|
||||
next_refresh = 0
|
||||
lifetime_sec = 2
|
||||
|
||||
def __init__(self, boardnum, channels=None, address=None, bus=None):
|
||||
self.boardnum = boardnum
|
||||
if channels is None:
|
||||
self.channels = 0
|
||||
else:
|
||||
self.channels = channels
|
||||
if address is None:
|
||||
self.address = 0x48
|
||||
else:
|
||||
self.address = int(address, 16)
|
||||
if bus is None:
|
||||
self.bus = 1
|
||||
else:
|
||||
self.bus = bus
|
||||
if not glob.glob('/dev/i2c*'):
|
||||
raise OSError('Cannot access I2C. Please ensure I2C is enabled')
|
||||
self._bus = smbus2.SMBus(self.bus)
|
||||
self.my_zero = 19920
|
||||
self.num_samples = 100
|
||||
|
||||
def toJSON(self):
|
||||
return {'boardnum':self.boardnum,'type':'i2c-current','channels':self.channels,'address':self.address}
|
||||
|
||||
def _readvalue(self, channel):
|
||||
# 0 = no effect / 1 = start a single conversion
|
||||
cfg_OS = 1
|
||||
|
||||
# multiplexer configurations:
|
||||
cfg_chan = 0b100
|
||||
if channel == 0:
|
||||
# AIN0 -> GND
|
||||
cfg_chan = 0b100
|
||||
elif channel == 1:
|
||||
# AIN1 -> GND
|
||||
cfg_chan = 0b101
|
||||
elif channel == 2:
|
||||
# AIN2 -> GND
|
||||
cfg_chan = 0b110
|
||||
elif channel == 3:
|
||||
# AIN3 -> GND
|
||||
cfg_chan = 0b111
|
||||
# unused multiplexer configurations:
|
||||
# 0b000 AIN0 -> AIN1
|
||||
# 0b001 AIN0 -> AIN3
|
||||
# 0b010 AIN1 -> AIN3
|
||||
# 0b011 AIN2 -> AIN3
|
||||
|
||||
# gain amplifier configuration
|
||||
# 000 = +- 6.144 V
|
||||
# 001 = +- 4,096 V
|
||||
# 010 = +- 2,048 V
|
||||
# 011 = +- 1,024 V
|
||||
# 100 = +- 0,512 V
|
||||
# 101 = +- 0,256 V
|
||||
# 110 = +- 0,256 V
|
||||
# 111 = +- 0,256 V
|
||||
cfg_amplifier = 0b000
|
||||
|
||||
# 0 = continuous conversion / 1 = single-shot or power-down
|
||||
cfg_mode = 1
|
||||
|
||||
# data rate
|
||||
# 000 = 128 sps
|
||||
# 001 = 250 sps
|
||||
# 010 = 490 sps
|
||||
# 011 = 920 sps
|
||||
# 100 = 1600 sps
|
||||
# 101 = 2400 sps
|
||||
# 110 = 3300 sps
|
||||
# 111 = 3300 sps
|
||||
cfg_sps = 0b110
|
||||
|
||||
# 0 = traditional comparator / 1 = window comparator
|
||||
cfg_comp = 0
|
||||
|
||||
# 0 = comparator active low / 1 = comparator active high
|
||||
cfg_comp_pol = 0
|
||||
|
||||
# 0 = comparator non-latching / 1 = comparator latching
|
||||
cfg_comp_latch = 0
|
||||
|
||||
# comparator queue and disable
|
||||
# 00 = assert after one conversion
|
||||
# 01 = assert after two conversions
|
||||
# 10 = assert after three conversions
|
||||
# 11 = disable comparator
|
||||
cfg_comp_queue = 0b11
|
||||
|
||||
config = cfg_sps << 13 | cfg_comp << 12 | cfg_comp_pol << 11 | cfg_comp_latch << 10 | cfg_comp_queue << 8 | cfg_OS << 7 | cfg_chan << 4 | cfg_amplifier << 1 | cfg_mode
|
||||
|
||||
self._bus.write_word_data(self.address, 0x01, config)
|
||||
while True:
|
||||
d1 = self._bus.read_word_data(self.address, 0x01)
|
||||
if d1 == config:
|
||||
break
|
||||
d1 = self._bus.read_word_data(self.address, 0x00)
|
||||
msb = d1 & 0x00FF
|
||||
lsb = d1 & 0xFF00
|
||||
val = msb << 8 | lsb >> 8
|
||||
return (val - self.my_zero) >> 4
|
||||
|
||||
def readcurrent(self, channel):
|
||||
samples = []
|
||||
for i in range(1,self.num_samples):
|
||||
v = self._readvalue(channel)
|
||||
samples.append(v)
|
||||
rms = sqrt(mean(square(samples)))
|
||||
return rms
|
||||
|
||||
|
||||
|
||||
|
||||
boardsConfigParser = ConfigParser.SafeConfigParser(defaults=boardsDefaults)
|
||||
boardsConfigFile = '/etc/openpdu/boards.conf'
|
||||
if os.path.isfile(boardsConfigFile) and os.access(boardsConfigFile, os.R_OK):
|
||||
boardsConfigParser.read(boardsConfigFile)
|
||||
|
||||
for s in boardsConfigParser.sections():
|
||||
if re.match('^board.*',s):
|
||||
bType = boardsConfigParser.get(s, 'type')
|
||||
num = int(re.sub(r'^board','',s))
|
||||
inverted = int('0' + boardsConfigParser.get(s, 'inverted')) == 1
|
||||
channels = int(boardsConfigParser.get(s, 'channels'))
|
||||
if bType=='gpio-out':
|
||||
gpios = []
|
||||
for g in range(0,channels):
|
||||
gpios.append(int(boardsConfigParser.get(s, 'channel%s' % g)))
|
||||
b = BoardGpioOut(boardnum=num, channels=channels, gpios=gpios, inverted=inverted)
|
||||
_boards.append(b)
|
||||
elif bType=='i2c-out':
|
||||
address = boardsConfigParser.get(s, 'address')
|
||||
bus = boardsConfigParser.get(s, 'bus')
|
||||
b = BoardI2COut(boardnum=num, channels=channels, address=address, bus=bus, inverted=inverted)
|
||||
_boards.append(b)
|
||||
elif bType=='i2c-current':
|
||||
address = boardsConfigParser.get(s, 'address')
|
||||
bus = boardsConfigParser.get(s, 'bus')
|
||||
b = BoardI2Ccurrent(boardnum=num, channels=channels, address=address, bus=bus)
|
||||
_boards.append(b)
|
||||
elif bType=='dummy':
|
||||
filename = boardsConfigParser.get(s, 'filename')
|
||||
b = BoardDummy(boardnum=num, channels=channels, filename=filename)
|
||||
_boards.append(b)
|
||||
|
||||
outletsConfigParser = ConfigParser.SafeConfigParser(defaults=outletsDefaults)
|
||||
outletsConfig = '/etc/openpdu/outlets.conf'
|
||||
if os.path.isfile(outletsConfig) and os.access(outletsConfig, os.R_OK):
|
||||
outletsConfigParser.read(outletsConfig)
|
||||
for s in outletsConfigParser.sections():
|
||||
if re.match('^outlet.*',s):
|
||||
num = int(re.sub(r'^outlet','',s))
|
||||
description = outletsConfigParser.get(s, 'description')
|
||||
boardnum = outletsConfigParser.get(s, 'board')
|
||||
channel = outletsConfigParser.get(s, 'channel')
|
||||
startpower = int('0' + outletsConfigParser.get(s, 'startpower')) == 1
|
||||
o = Outlet(outletnum=num, boardnum=boardnum, channel=channel, startpower=startpower)
|
||||
o.description = description
|
||||
if outletsConfigParser.has_option(s,'currentboard') and outletsConfigParser.has_option(s,'currentboardchannel'):
|
||||
currentboard = outletsConfigParser.get(s, 'currentboard')
|
||||
currentboardchannel = int(outletsConfigParser.get(s, 'currentboardchannel'))
|
||||
o.setcurrentboard(currentboard, currentboardchannel)
|
||||
_outlets.append(o)
|
||||
|
||||
|
||||
|
||||
for sec in range(1,100):
|
||||
for outlet in range(0,4):
|
||||
o = [o for o in _outlets if o.outletnum==outlet]
|
||||
if len(o) != 1:
|
||||
msg = 'wrong outlet number: %s' % str(outlet)
|
||||
print JSON.dumps({'message':msg})
|
||||
theOutlet = o[0]
|
||||
out = theOutlet.getcurrent()
|
||||
print JSON.dumps({'current':out,'outlet':outlet})
|
||||
print "---"
|
||||
#run(host='0.0.0.0', port=5000)
|
||||
|
||||
|
58
src/board/board.go
Normal file
@ -0,0 +1,58 @@
|
||||
package board
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
MQTT "github.com/eclipse/paho.mqtt.golang"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var boardCreatorFunctions = make(map[string]BoardCreatorFunction)
|
||||
|
||||
var AllChannels = make(map[string]Channel)
|
||||
|
||||
type BoardCreatorFunction func(*viper.Viper, string) *Board
|
||||
type onChannelUpdateFunction func(oldValue bool, c Channel)
|
||||
|
||||
type Board interface {
|
||||
Initialize()
|
||||
Dump()
|
||||
Channel(uint64) Channel
|
||||
}
|
||||
|
||||
type Channel interface {
|
||||
Toggle() (bool, error)
|
||||
On() error
|
||||
Off() error
|
||||
ToString() string
|
||||
Status() bool
|
||||
Parent() Board
|
||||
Dump()
|
||||
Name() string
|
||||
OnBoot() string
|
||||
SetOnBoot(string)
|
||||
SetMQTTStateTopic(string)
|
||||
SetMQTTCommandTopic(string)
|
||||
MQTTStateTopic() string
|
||||
MQTTCommandTopic() string
|
||||
MQTTHandler(MQTT.Client, MQTT.Message)
|
||||
AddOnChannelUpdateFunction(string, onChannelUpdateFunction)
|
||||
}
|
||||
|
||||
func RegisterBoardType(name string, fun BoardCreatorFunction) {
|
||||
boardCreatorFunctions[name] = fun
|
||||
}
|
||||
|
||||
func CreateBoard(cfg *viper.Viper, key string) (Board, error) {
|
||||
var createNewBoard BoardCreatorFunction
|
||||
var ok bool
|
||||
|
||||
boardType := cfg.GetString("type")
|
||||
createNewBoard, ok = boardCreatorFunctions[boardType]
|
||||
|
||||
if !ok {
|
||||
return nil, errors.New("unknown board type")
|
||||
}
|
||||
|
||||
return *createNewBoard(cfg, key), nil
|
||||
}
|
228
src/board/board_dummy.go
Normal file
@ -0,0 +1,228 @@
|
||||
package board
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
PahoMQTT "github.com/eclipse/paho.mqtt.golang"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type DummyChannel struct {
|
||||
ID string
|
||||
Num uint
|
||||
name string
|
||||
mqttStateTopic string
|
||||
mqttCommandTopic string
|
||||
value bool
|
||||
onboot string
|
||||
parent *DummyBoard
|
||||
onChannelUpdateFunctions map[string]onChannelUpdateFunction
|
||||
}
|
||||
|
||||
type DummyBoard struct {
|
||||
ID string
|
||||
Name string
|
||||
ChannelCount uint
|
||||
channels []*DummyChannel
|
||||
}
|
||||
|
||||
func newDummyChannel(v *viper.Viper, channelID string) DummyChannel {
|
||||
v.SetDefault("name", "unknown")
|
||||
v.SetDefault("lastValue", false)
|
||||
v.SetDefault("onboot", "off")
|
||||
v.SetDefault("mqtt.statetopic", v.GetString("name"))
|
||||
|
||||
value := false
|
||||
switch v.GetString("onboot") {
|
||||
case "on":
|
||||
value = true
|
||||
case "last":
|
||||
value = v.GetBool("lastValue")
|
||||
}
|
||||
// newUUID := UUID.New().String()
|
||||
// v.SetDefault("id", newUUID)
|
||||
return DummyChannel{
|
||||
ID: channelID,
|
||||
Num: v.GetUint("num"),
|
||||
name: v.GetString("name"),
|
||||
mqttStateTopic: v.GetString("mqtt.statetopic"),
|
||||
mqttCommandTopic: v.GetString("mqtt.commandtopic"),
|
||||
value: value,
|
||||
onboot: v.GetString("onboot"),
|
||||
}
|
||||
}
|
||||
|
||||
func newDummyBoard(v *viper.Viper, id string) *Board {
|
||||
var b *DummyBoard
|
||||
|
||||
v.SetDefault("name", "board "+id)
|
||||
v.SetDefault("type", "dummy")
|
||||
v.SetDefault("channelCount", 0)
|
||||
v.SetDefault("channels", "")
|
||||
|
||||
if v.GetInt("channelCount") > 0 {
|
||||
for i := 0; i < v.GetInt("channelCount"); i++ {
|
||||
v.SetDefault("channels."+fmt.Sprint(i)+".num", i)
|
||||
}
|
||||
}
|
||||
|
||||
b = &DummyBoard{
|
||||
ID: id,
|
||||
Name: v.GetString("name"),
|
||||
ChannelCount: v.GetUint("channelCount"),
|
||||
}
|
||||
|
||||
channels := make([]*DummyChannel, v.GetInt("channelCount"))
|
||||
if v.GetInt("channelCount") > 0 {
|
||||
channelsConfig := v.Sub("channels")
|
||||
if channelsConfig != nil {
|
||||
for channelid1 := range channelsConfig.AllSettings() {
|
||||
channelid := strings.ToLower(channelid1)
|
||||
channelConfig := channelsConfig.Sub(channelid)
|
||||
c := newDummyChannel(channelConfig, channelid)
|
||||
c.parent = b
|
||||
if c.Num >= v.GetUint("channelCount") {
|
||||
continue
|
||||
}
|
||||
channels[c.Num] = &c
|
||||
AllChannels[c.ID] = &c
|
||||
}
|
||||
}
|
||||
}
|
||||
b.channels = channels
|
||||
var b1 Board = b
|
||||
return &b1
|
||||
}
|
||||
|
||||
func (c *DummyChannel) Toggle() (bool, error) {
|
||||
c.value = !c.value
|
||||
c.SaveLastState()
|
||||
for f := range c.onChannelUpdateFunctions {
|
||||
c.onChannelUpdateFunctions[f](!c.value, c)
|
||||
}
|
||||
return c.value, nil
|
||||
}
|
||||
|
||||
func (c *DummyChannel) On() error {
|
||||
oldval := c.value
|
||||
c.value = true
|
||||
c.SaveLastState()
|
||||
for f := range c.onChannelUpdateFunctions {
|
||||
c.onChannelUpdateFunctions[f](oldval, c)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *DummyChannel) Off() error {
|
||||
oldval := c.value
|
||||
c.value = true
|
||||
c.SaveLastState()
|
||||
for f := range c.onChannelUpdateFunctions {
|
||||
c.onChannelUpdateFunctions[f](oldval, c)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *DummyChannel) ToString() string {
|
||||
if !c.value {
|
||||
return "off"
|
||||
}
|
||||
return "on"
|
||||
}
|
||||
|
||||
func (c *DummyChannel) SaveLastState() {
|
||||
if c.onboot == "last" {
|
||||
s := fmt.Sprintf("boards.%s.channels.%s.lastvalue", c.parent.ID, c.ID)
|
||||
viper.Set(s, c.value)
|
||||
viper.WriteConfig()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DummyChannel) Status() bool {
|
||||
return c.value
|
||||
}
|
||||
|
||||
func (c *DummyChannel) Parent() Board {
|
||||
return c.parent
|
||||
}
|
||||
|
||||
func (c *DummyChannel) Dump() {
|
||||
log.Printf(" Channel %d (on boot: %s): %s \n", c.Num, c.onboot, c.name)
|
||||
}
|
||||
|
||||
func (b DummyBoard) Dump() {
|
||||
log.Printf("Board '%s' (id: %s): %d channels\n", b.Name, b.ID, b.ChannelCount)
|
||||
for c := range b.channels {
|
||||
b.channels[c].Dump()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *DummyBoard) Initialize() {
|
||||
return
|
||||
}
|
||||
|
||||
func (b *DummyBoard) Channel(num uint64) Channel {
|
||||
return b.channels[num]
|
||||
}
|
||||
|
||||
func (c *DummyChannel) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
func (c *DummyChannel) OnBoot() string {
|
||||
return c.onboot
|
||||
}
|
||||
|
||||
func (c *DummyChannel) SetOnBoot(str string) {
|
||||
c.onboot = str
|
||||
s := fmt.Sprintf("boards.%s.channels.%s.onboot", c.parent.ID, c.ID)
|
||||
viper.Set(s, str)
|
||||
}
|
||||
|
||||
func (c *DummyChannel) SetMQTTStateTopic(str string) {
|
||||
c.mqttStateTopic = str
|
||||
s := fmt.Sprintf("boards.%s.channels.%s.mqtt.statetopic", c.parent.ID, c.ID)
|
||||
viper.Set(s, str)
|
||||
}
|
||||
|
||||
func (c *DummyChannel) SetMQTTCommandTopic(str string) {
|
||||
c.mqttCommandTopic = str
|
||||
s := fmt.Sprintf("boards.%s.channels.%s.mqtt.commandtopic", c.parent.ID, c.ID)
|
||||
viper.Set(s, str)
|
||||
}
|
||||
|
||||
func (c *DummyChannel) MQTTHandler(client PahoMQTT.Client, msg PahoMQTT.Message) {
|
||||
switch string(msg.Payload()) {
|
||||
case "on":
|
||||
if !c.value {
|
||||
c.value = true
|
||||
c.SaveLastState()
|
||||
}
|
||||
case "off":
|
||||
if c.value {
|
||||
c.value = false
|
||||
c.SaveLastState()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DummyChannel) MQTTStateTopic() string {
|
||||
return c.mqttStateTopic
|
||||
}
|
||||
|
||||
func (c *DummyChannel) MQTTCommandTopic() string {
|
||||
return c.mqttCommandTopic
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBoardType("dummy", newDummyBoard)
|
||||
}
|
||||
|
||||
func (c *DummyChannel) AddOnChannelUpdateFunction(name string, f onChannelUpdateFunction) {
|
||||
if c.onChannelUpdateFunctions == nil {
|
||||
c.onChannelUpdateFunctions = make(map[string]onChannelUpdateFunction)
|
||||
}
|
||||
c.onChannelUpdateFunctions[name] = f
|
||||
}
|
391
src/board/board_i2c.go
Normal file
@ -0,0 +1,391 @@
|
||||
package board
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.openpdu.org/OpenPDU/openpdu/config"
|
||||
"git.openpdu.org/OpenPDU/openpdu/i2c"
|
||||
"git.openpdu.org/OpenPDU/openpdu/syslog"
|
||||
PahoMQTT "github.com/eclipse/paho.mqtt.golang"
|
||||
"github.com/spf13/viper"
|
||||
I2C "periph.io/x/conn/v3/i2c"
|
||||
)
|
||||
|
||||
type I2CChannel struct {
|
||||
ID string
|
||||
Num uint
|
||||
name string
|
||||
mqttStateTopic string
|
||||
mqttCommandTopic string
|
||||
onboot string
|
||||
parent *I2CBoard
|
||||
onChannelUpdateFunctions map[string]onChannelUpdateFunction
|
||||
}
|
||||
|
||||
type I2CBoard struct {
|
||||
ID string
|
||||
Name string
|
||||
ChannelCount uint
|
||||
channels []*I2CChannel
|
||||
i2cDevice *I2C.Dev
|
||||
inverted bool
|
||||
i2cDataA []byte
|
||||
i2cDataB []byte
|
||||
lastUpdate time.Time
|
||||
}
|
||||
|
||||
func newI2CChannel(v *viper.Viper, channelID string) I2CChannel {
|
||||
v.SetDefault("name", "unknown")
|
||||
v.SetDefault("lastValue", false)
|
||||
v.SetDefault("onboot", "off")
|
||||
v.SetDefault("mqtt.statetopic", v.GetString("name"))
|
||||
|
||||
return I2CChannel{
|
||||
ID: channelID,
|
||||
Num: v.GetUint("num"),
|
||||
name: v.GetString("name"),
|
||||
mqttStateTopic: v.GetString("mqtt.statetopic"),
|
||||
mqttCommandTopic: v.GetString("mqtt.commandtopic"),
|
||||
onboot: v.GetString("onboot"),
|
||||
}
|
||||
}
|
||||
|
||||
func newI2CBoard(v *viper.Viper, id string) *Board {
|
||||
var b *I2CBoard
|
||||
|
||||
v.SetDefault("name", "board "+id)
|
||||
v.SetDefault("type", "i2c")
|
||||
v.SetDefault("channelCount", 0)
|
||||
v.SetDefault("channels", "")
|
||||
v.SetDefault("inverted", true)
|
||||
|
||||
if v.GetInt("channelCount") > 0 {
|
||||
for i := 0; i < v.GetInt("channelCount"); i++ {
|
||||
v.SetDefault("channels."+fmt.Sprint(i)+".num", i)
|
||||
}
|
||||
}
|
||||
|
||||
b = &I2CBoard{
|
||||
ID: id,
|
||||
Name: v.GetString("name"),
|
||||
ChannelCount: v.GetUint("channelCount"),
|
||||
inverted: v.GetBool("inverted"),
|
||||
}
|
||||
|
||||
channels := make([]*I2CChannel, v.GetInt("channelCount"))
|
||||
if v.GetInt("channelCount") > 0 {
|
||||
channelsConfig := v.Sub("channels")
|
||||
if channelsConfig != nil {
|
||||
for channelid1 := range channelsConfig.AllSettings() {
|
||||
channelid := strings.ToLower(channelid1)
|
||||
channelConfig := channelsConfig.Sub(channelid)
|
||||
c := newI2CChannel(channelConfig, channelid)
|
||||
c.parent = b
|
||||
if c.Num >= v.GetUint("channelCount") {
|
||||
continue
|
||||
}
|
||||
channels[c.Num] = &c
|
||||
AllChannels[c.ID] = &c
|
||||
}
|
||||
}
|
||||
}
|
||||
b.channels = channels
|
||||
syslog.Info("Created new I2C board")
|
||||
var b1 Board = b
|
||||
return &b1
|
||||
}
|
||||
|
||||
func (c *I2CChannel) Toggle() (bool, error) {
|
||||
oldval := c.Status()
|
||||
c.setToggle()
|
||||
c.parent.WriteStatus()
|
||||
c.SaveLastState()
|
||||
for f := range c.onChannelUpdateFunctions {
|
||||
c.onChannelUpdateFunctions[f](oldval, c)
|
||||
}
|
||||
return c.Status(), nil
|
||||
}
|
||||
|
||||
func (c *I2CChannel) setToggle() {
|
||||
var data *[]byte
|
||||
var mask byte
|
||||
|
||||
if c.Num < 8 {
|
||||
data = &c.parent.i2cDataA
|
||||
} else {
|
||||
data = &c.parent.i2cDataB
|
||||
}
|
||||
mask = 1 << (c.Num % 8)
|
||||
(*data)[0] ^= mask
|
||||
}
|
||||
|
||||
func (c *I2CChannel) On() error {
|
||||
oldval := c.Status()
|
||||
c.SetOn()
|
||||
c.parent.WriteStatus()
|
||||
c.SaveLastState()
|
||||
for f := range c.onChannelUpdateFunctions {
|
||||
c.onChannelUpdateFunctions[f](oldval, c)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *I2CChannel) SetOn() {
|
||||
var data *[]byte
|
||||
var mask byte
|
||||
|
||||
if c.Num < 8 {
|
||||
data = &c.parent.i2cDataA
|
||||
} else {
|
||||
data = &c.parent.i2cDataB
|
||||
}
|
||||
mask = 1 << (c.Num % 8)
|
||||
(*data)[0] |= mask
|
||||
}
|
||||
|
||||
func (c *I2CChannel) Off() error {
|
||||
oldval := c.Status()
|
||||
c.SetOff()
|
||||
c.parent.WriteStatus()
|
||||
c.SaveLastState()
|
||||
for f := range c.onChannelUpdateFunctions {
|
||||
c.onChannelUpdateFunctions[f](oldval, c)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *I2CChannel) SetOff() {
|
||||
var data *[]byte
|
||||
var mask byte
|
||||
|
||||
if c.Num < 8 {
|
||||
data = &c.parent.i2cDataA
|
||||
} else {
|
||||
data = &c.parent.i2cDataB
|
||||
}
|
||||
mask = (1 << (c.Num % 8)) ^ 0xFF
|
||||
(*data)[0] &= mask
|
||||
}
|
||||
|
||||
func (c *I2CChannel) ToString() string {
|
||||
if !c.Status() {
|
||||
return "off"
|
||||
}
|
||||
return "on"
|
||||
}
|
||||
|
||||
func (c *I2CChannel) SaveLastState() {
|
||||
if c.onboot == "last" {
|
||||
s := fmt.Sprintf("boards.%s.channels.%s.lastvalue", c.parent.ID, c.ID)
|
||||
config.Set(s, c.Status())
|
||||
config.WriteConfig()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *I2CChannel) Status() bool {
|
||||
var data *[]byte
|
||||
c.parent.ReadStatus()
|
||||
if c.Num < 8 {
|
||||
data = &c.parent.i2cDataA
|
||||
} else {
|
||||
data = &c.parent.i2cDataB
|
||||
}
|
||||
value := ((*data)[0] >> (c.Num % 8) & 1) == 1
|
||||
return value
|
||||
}
|
||||
|
||||
func (c *I2CChannel) Parent() Board {
|
||||
return c.parent
|
||||
}
|
||||
|
||||
func (c *I2CChannel) Dump() {
|
||||
log.Printf(" Channel %d (on boot: %s): %s \n", c.Num, c.onboot, c.name)
|
||||
}
|
||||
|
||||
func (b I2CBoard) Dump() {
|
||||
log.Printf("Board '%s' (id: %s): %d channels\n", b.Name, b.ID, b.ChannelCount)
|
||||
for c := range b.channels {
|
||||
b.channels[c].Dump()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *I2CBoard) ReadStatus() {
|
||||
now := time.Now()
|
||||
diff := now.Sub(b.lastUpdate)
|
||||
ns := diff.Nanoseconds()
|
||||
if ns < 10*1000000 { // 10ms
|
||||
// syslog.Debug(fmt.Sprintf("I2C update skipped because less than 10ms passed (%v)", ns))
|
||||
return
|
||||
}
|
||||
|
||||
replyA := [1]byte{}
|
||||
replyB := [1]byte{}
|
||||
b.i2cDevice.Tx([]byte{0x12}, replyA[:])
|
||||
b.i2cDevice.Tx([]byte{0x13}, replyB[:])
|
||||
|
||||
if b.inverted {
|
||||
replyA[0] ^= 0xFF
|
||||
replyB[0] ^= 0xFF
|
||||
}
|
||||
|
||||
b.i2cDataA[0] = replyA[0]
|
||||
b.i2cDataB[0] = replyB[0]
|
||||
b.lastUpdate = time.Now()
|
||||
}
|
||||
|
||||
func (b *I2CBoard) WriteStatus() {
|
||||
// var dA, dB, oA, oB []byte
|
||||
var dA, dB []byte
|
||||
|
||||
dA = []byte{0x12, 0x00}
|
||||
dB = []byte{0x13, 0x00}
|
||||
|
||||
dA[1] = b.i2cDataA[0]
|
||||
|
||||
dB[1] = b.i2cDataB[0]
|
||||
if b.inverted {
|
||||
dA[1] ^= 0xff
|
||||
dB[1] ^= 0xff
|
||||
}
|
||||
|
||||
if _, err := b.i2cDevice.Write(dA); err != nil {
|
||||
syslog.Err(err.Error())
|
||||
}
|
||||
if _, err := b.i2cDevice.Write(dB); err != nil {
|
||||
syslog.Err(err.Error())
|
||||
}
|
||||
|
||||
b.lastUpdate = time.Now()
|
||||
}
|
||||
|
||||
func (b *I2CBoard) Initialize() {
|
||||
syslog.Info("Initializing I2C board")
|
||||
|
||||
for {
|
||||
if i2c.I2Cbus == nil {
|
||||
syslog.Warning("i2cboard: i2cbus not found")
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* MCP23017 - can work in 8bit mode or 16bit mode, you have to set IOCON.BANK=1 (8bit) or =0 (16bit default)
|
||||
* 16 bit mode:
|
||||
* 0x00 IO direction bank A (1=input 0=output)
|
||||
* 0x01 IO direction bank B (1=input 0=output)
|
||||
* 0x12 GPIO A
|
||||
* 0x13 GPIO B
|
||||
*
|
||||
* 8 bit mode
|
||||
* 0x00 IO direction (1=input 0=output)
|
||||
* 0x09 GPIO
|
||||
*/
|
||||
|
||||
mydevice := &I2C.Dev{Addr: 0x27, Bus: i2c.I2Cbus}
|
||||
|
||||
// set all output
|
||||
write := []byte{0x0, 0x0}
|
||||
if _, err := mydevice.Write(write); err != nil {
|
||||
syslog.Err(err.Error())
|
||||
}
|
||||
write = []byte{0x01, 0x0}
|
||||
if _, err := mydevice.Write(write); err != nil {
|
||||
syslog.Err(err.Error())
|
||||
}
|
||||
|
||||
b.i2cDevice = mydevice
|
||||
b.i2cDataA = make([]byte, 1)
|
||||
b.i2cDataB = make([]byte, 1)
|
||||
syslog.Info("I2C board: reading initial status")
|
||||
b.ReadStatus()
|
||||
|
||||
for i := range b.channels {
|
||||
c := b.channels[i]
|
||||
switch c.onboot {
|
||||
case "on":
|
||||
c.SetOn()
|
||||
case "off":
|
||||
c.SetOff()
|
||||
case "last":
|
||||
s := fmt.Sprintf("boards.%s.channels.%s.lastvalue", c.parent.ID, c.ID)
|
||||
|
||||
switch config.GetBool(s) {
|
||||
case true:
|
||||
c.SetOn()
|
||||
case false:
|
||||
c.SetOff()
|
||||
}
|
||||
}
|
||||
syslog.Info(fmt.Sprintf("Channel %d status: %v", i, c.Status()))
|
||||
}
|
||||
|
||||
b.WriteStatus()
|
||||
}
|
||||
|
||||
func (b *I2CBoard) Channel(num uint64) Channel {
|
||||
return b.channels[num]
|
||||
}
|
||||
|
||||
func (c *I2CChannel) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
func (c *I2CChannel) OnBoot() string {
|
||||
return c.onboot
|
||||
}
|
||||
|
||||
func (c *I2CChannel) SetOnBoot(str string) {
|
||||
c.onboot = str
|
||||
s := fmt.Sprintf("boards.%s.channels.%s.onboot", c.parent.ID, c.ID)
|
||||
config.Set(s, str)
|
||||
}
|
||||
|
||||
func (c *I2CChannel) SetMQTTStateTopic(str string) {
|
||||
c.mqttStateTopic = str
|
||||
s := fmt.Sprintf("boards.%s.channels.%s.mqtt.statetopic", c.parent.ID, c.ID)
|
||||
config.Set(s, str)
|
||||
}
|
||||
|
||||
func (c *I2CChannel) SetMQTTCommandTopic(str string) {
|
||||
c.mqttCommandTopic = str
|
||||
s := fmt.Sprintf("boards.%s.channels.%s.mqtt.commandtopic", c.parent.ID, c.ID)
|
||||
config.Set(s, str)
|
||||
}
|
||||
|
||||
func (c *I2CChannel) MQTTHandler(client PahoMQTT.Client, msg PahoMQTT.Message) {
|
||||
switch string(msg.Payload()) {
|
||||
case "on":
|
||||
if !c.Status() {
|
||||
c.On()
|
||||
}
|
||||
case "off":
|
||||
if c.Status() {
|
||||
c.Off()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *I2CChannel) MQTTStateTopic() string {
|
||||
return c.mqttStateTopic
|
||||
}
|
||||
|
||||
func (c *I2CChannel) MQTTCommandTopic() string {
|
||||
return c.mqttCommandTopic
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBoardType("i2c", newI2CBoard)
|
||||
}
|
||||
|
||||
func (c *I2CChannel) AddOnChannelUpdateFunction(name string, f onChannelUpdateFunction) {
|
||||
if c.onChannelUpdateFunctions == nil {
|
||||
c.onChannelUpdateFunctions = make(map[string]onChannelUpdateFunction)
|
||||
}
|
||||
c.onChannelUpdateFunctions[name] = f
|
||||
}
|
347
src/board/board_mqtt.go
Normal file
@ -0,0 +1,347 @@
|
||||
package board
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.openpdu.org/OpenPDU/openpdu/config"
|
||||
"git.openpdu.org/OpenPDU/openpdu/syslog"
|
||||
PahoMQTT "github.com/eclipse/paho.mqtt.golang"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type MQTTChannel struct {
|
||||
ID string
|
||||
Num uint
|
||||
name string
|
||||
mqttStateTopic string
|
||||
mqttCommandTopic string
|
||||
value bool
|
||||
onboot string
|
||||
parent *MQTTBoard
|
||||
MQTTRemoteStateTopic string
|
||||
MQTTRemoteCommandTopic string
|
||||
MQTTRemotePayloadOn string
|
||||
MQTTRemotePayloadOff string
|
||||
onChannelUpdateFunctions map[string]onChannelUpdateFunction
|
||||
}
|
||||
|
||||
type MQTTBoard struct {
|
||||
ID string
|
||||
Name string
|
||||
ChannelCount uint
|
||||
channels []*MQTTChannel
|
||||
MQTTRemoteSchema string
|
||||
MQTTRemoteHost string
|
||||
MQTTRemotePort string
|
||||
MQTTRemoteUsername string
|
||||
MQTTRemotePassword string
|
||||
MQTTClient PahoMQTT.Client
|
||||
}
|
||||
|
||||
func newMQTTChannel(v *viper.Viper, channelID string) MQTTChannel {
|
||||
v.SetDefault("name", "unknown")
|
||||
v.SetDefault("lastValue", false)
|
||||
v.SetDefault("onboot", "off")
|
||||
v.SetDefault("mqtt.statetopic", v.GetString("name"))
|
||||
v.SetDefault("mqtt.commandtopic", v.GetString("name"))
|
||||
v.SetDefault("mqttremote.statetopic", v.GetString("name"))
|
||||
v.SetDefault("mqttremote.commandtopic", v.GetString("name"))
|
||||
v.SetDefault("mqttremote.payloadon", "on")
|
||||
v.SetDefault("mqttremote.payloadoff", "off")
|
||||
|
||||
value := false
|
||||
switch v.GetString("onboot") {
|
||||
case "on":
|
||||
value = true
|
||||
case "last":
|
||||
value = v.GetBool("lastValue")
|
||||
}
|
||||
// newUUID := UUID.New().String()
|
||||
// v.SetDefault("id", newUUID)
|
||||
return MQTTChannel{
|
||||
ID: channelID,
|
||||
Num: v.GetUint("num"),
|
||||
name: v.GetString("name"),
|
||||
mqttStateTopic: v.GetString("mqtt.statetopic"),
|
||||
mqttCommandTopic: v.GetString("mqtt.commandtopic"),
|
||||
value: value,
|
||||
onboot: v.GetString("onboot"),
|
||||
MQTTRemoteStateTopic: v.GetString("mqttremote.statetopic"),
|
||||
MQTTRemoteCommandTopic: v.GetString("mqttremote.commandtopic"),
|
||||
MQTTRemotePayloadOn: v.GetString("mqttremote.payloadon"),
|
||||
MQTTRemotePayloadOff: v.GetString("mqttremote.payloadoff"),
|
||||
}
|
||||
}
|
||||
|
||||
func newMQTTBoard(v *viper.Viper, id string) *Board {
|
||||
var b MQTTBoard
|
||||
|
||||
v.SetDefault("name", "board "+id)
|
||||
v.SetDefault("type", "mqtt")
|
||||
v.SetDefault("channelCount", 0)
|
||||
v.SetDefault("channels", "")
|
||||
v.SetDefault("MQTTRemote.Schema", "tcp")
|
||||
v.SetDefault("MQTTRemote.Host", "")
|
||||
v.SetDefault("MQTTRemote.Port", "1883")
|
||||
v.SetDefault("MQTTRemote.Username", "")
|
||||
v.SetDefault("MQTTRemote.Password", "")
|
||||
|
||||
if v.GetInt("channelCount") > 0 {
|
||||
for i := 0; i < v.GetInt("channelCount"); i++ {
|
||||
v.SetDefault("channels."+fmt.Sprint(i)+".num", i)
|
||||
}
|
||||
}
|
||||
|
||||
b = MQTTBoard{
|
||||
ID: id,
|
||||
Name: v.GetString("name"),
|
||||
ChannelCount: v.GetUint("channelCount"),
|
||||
MQTTRemoteSchema: v.GetString("MQTTRemote.Schema"),
|
||||
MQTTRemoteHost: v.GetString("MQTTRemote.Host"),
|
||||
MQTTRemotePort: v.GetString("MQTTRemote.Port"),
|
||||
MQTTRemoteUsername: v.GetString("MQTTRemote.Username"),
|
||||
MQTTRemotePassword: v.GetString("MQTTRemote.Password"),
|
||||
}
|
||||
|
||||
channels := make([]*MQTTChannel, v.GetInt("channelCount"))
|
||||
if v.GetInt("channelCount") > 0 {
|
||||
channelsConfig := v.Sub("channels")
|
||||
if channelsConfig != nil {
|
||||
for channelid1 := range channelsConfig.AllSettings() {
|
||||
channelid := strings.ToLower(channelid1)
|
||||
channelConfig := channelsConfig.Sub(channelid)
|
||||
c := newMQTTChannel(channelConfig, channelid)
|
||||
c.parent = &b
|
||||
if c.Num >= v.GetUint("channelCount") {
|
||||
continue
|
||||
}
|
||||
channels[c.Num] = &c
|
||||
AllChannels[c.ID] = &c
|
||||
}
|
||||
}
|
||||
}
|
||||
b.channels = channels
|
||||
|
||||
var b1 Board = &b
|
||||
return &b1
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBoardType("mqtt", newMQTTBoard)
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) Toggle() (bool, error) {
|
||||
c.value = !c.value
|
||||
c.SaveLastState()
|
||||
for f := range c.onChannelUpdateFunctions {
|
||||
c.onChannelUpdateFunctions[f](!c.value, c)
|
||||
}
|
||||
c.UpdateRemoteMQTT()
|
||||
return c.value, nil
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) On() error {
|
||||
oldval := c.value
|
||||
c.value = true
|
||||
c.SaveLastState()
|
||||
for f := range c.onChannelUpdateFunctions {
|
||||
c.onChannelUpdateFunctions[f](oldval, c)
|
||||
}
|
||||
c.UpdateRemoteMQTT()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) Off() error {
|
||||
oldval := c.value
|
||||
c.value = false
|
||||
c.SaveLastState()
|
||||
for f := range c.onChannelUpdateFunctions {
|
||||
c.onChannelUpdateFunctions[f](oldval, c)
|
||||
}
|
||||
c.UpdateRemoteMQTT()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) ToString() string {
|
||||
if !c.value {
|
||||
return "off"
|
||||
}
|
||||
return "on"
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) SaveLastState() {
|
||||
if c.onboot == "last" {
|
||||
s := fmt.Sprintf("boards.%s.channels.%s.lastvalue", c.parent.ID, c.ID)
|
||||
config.Set(s, c.value)
|
||||
config.WriteConfig()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) UpdateRemoteMQTT() {
|
||||
connected := c.parent.MQTTClient.IsConnected()
|
||||
if connected {
|
||||
v := c.MQTTRemotePayloadOff
|
||||
if c.value {
|
||||
v = c.MQTTRemotePayloadOn
|
||||
}
|
||||
c.parent.MQTTClient.Publish(c.MQTTRemoteCommandTopic, 0, false, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) Status() bool {
|
||||
return c.value
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) Parent() Board {
|
||||
return c.parent
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) Dump() {
|
||||
log.Printf(" Channel %d (on boot: %s): %s \n", c.Num, c.onboot, c.name)
|
||||
}
|
||||
|
||||
func (b MQTTBoard) Dump() {
|
||||
log.Printf("Board '%s' (id: %s): %d channels\n", b.Name, b.ID, b.ChannelCount)
|
||||
for c := range b.channels {
|
||||
b.channels[c].Dump()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *MQTTBoard) Initialize() {
|
||||
uri := b.MQTTRemoteSchema + "://" + b.MQTTRemoteHost + ":" + b.MQTTRemotePort
|
||||
opts := PahoMQTT.NewClientOptions().AddBroker(uri)
|
||||
|
||||
opts.SetClientID(b.ID)
|
||||
|
||||
if b.MQTTRemoteUsername != "" {
|
||||
opts.SetUsername(b.MQTTRemoteUsername)
|
||||
}
|
||||
|
||||
if b.MQTTRemotePassword != "" {
|
||||
opts.SetPassword(b.MQTTRemotePassword)
|
||||
}
|
||||
|
||||
opts.SetAutoReconnect(true)
|
||||
opts.SetConnectRetryInterval(5 * time.Second)
|
||||
opts.SetConnectTimeout(5 * time.Second)
|
||||
|
||||
opts.SetConnectionLostHandler(func(c PahoMQTT.Client, err error) {
|
||||
syslog.Err("mqtt connection lost error: " + err.Error())
|
||||
})
|
||||
opts.SetReconnectingHandler(func(c PahoMQTT.Client, options *PahoMQTT.ClientOptions) {
|
||||
syslog.Notice("mqtt reconnecting")
|
||||
})
|
||||
opts.SetOnConnectHandler(func(c PahoMQTT.Client) {
|
||||
syslog.Notice("mqtt connected")
|
||||
})
|
||||
|
||||
b.MQTTClient = PahoMQTT.NewClient(opts)
|
||||
|
||||
for {
|
||||
token := b.MQTTClient.Connect()
|
||||
token.Wait()
|
||||
if b.MQTTClient.IsConnected() {
|
||||
break
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
for i := range b.channels {
|
||||
topic := b.channels[i].MQTTRemoteStateTopic
|
||||
if topic == "" {
|
||||
continue
|
||||
}
|
||||
b.MQTTClient.Subscribe(topic, 1, b.channels[i].MQTTRemoteHandler)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) MQTTRemoteHandler(client PahoMQTT.Client, msg PahoMQTT.Message) {
|
||||
switch string(msg.Payload()) {
|
||||
case c.MQTTRemotePayloadOn:
|
||||
if !c.value {
|
||||
c.value = true
|
||||
c.SaveLastState()
|
||||
for f := range c.onChannelUpdateFunctions {
|
||||
c.onChannelUpdateFunctions[f](false, c)
|
||||
}
|
||||
}
|
||||
case c.MQTTRemotePayloadOff:
|
||||
if c.value {
|
||||
c.value = false
|
||||
c.SaveLastState()
|
||||
for f := range c.onChannelUpdateFunctions {
|
||||
c.onChannelUpdateFunctions[f](true, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) MQTTHandler(client PahoMQTT.Client, msg PahoMQTT.Message) {
|
||||
syslog.Debug(fmt.Sprintf("Board '%s', channel '%s', received MQTT message %s",
|
||||
c.parent.Name,
|
||||
c.name,
|
||||
string(msg.Payload()),
|
||||
))
|
||||
switch string(msg.Payload()) {
|
||||
case "on":
|
||||
if !c.value {
|
||||
c.value = true
|
||||
c.SaveLastState()
|
||||
c.UpdateRemoteMQTT()
|
||||
}
|
||||
case "off":
|
||||
if c.value {
|
||||
c.value = false
|
||||
c.SaveLastState()
|
||||
c.UpdateRemoteMQTT()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *MQTTBoard) Channel(num uint64) Channel {
|
||||
return b.channels[num]
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) OnBoot() string {
|
||||
return c.onboot
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) SetOnBoot(str string) {
|
||||
c.onboot = str
|
||||
s := fmt.Sprintf("boards.%s.channels.%s.onboot", c.parent.ID, c.ID)
|
||||
config.Set(s, str)
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) SetMQTTStateTopic(str string) {
|
||||
c.mqttStateTopic = str
|
||||
s := fmt.Sprintf("boards.%s.channels.%s.mqtt.statetopic", c.parent.ID, c.ID)
|
||||
config.Set(s, str)
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) SetMQTTCommandTopic(str string) {
|
||||
c.mqttCommandTopic = str
|
||||
s := fmt.Sprintf("boards.%s.channels.%s.mqtt.commandtopic", c.parent.ID, c.ID)
|
||||
config.Set(s, str)
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) MQTTStateTopic() string {
|
||||
return c.mqttStateTopic
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) MQTTCommandTopic() string {
|
||||
return c.mqttCommandTopic
|
||||
}
|
||||
|
||||
func (c *MQTTChannel) AddOnChannelUpdateFunction(name string, f onChannelUpdateFunction) {
|
||||
if c.onChannelUpdateFunctions == nil {
|
||||
c.onChannelUpdateFunctions = make(map[string]onChannelUpdateFunction)
|
||||
}
|
||||
c.onChannelUpdateFunctions[name] = f
|
||||
}
|
62
src/boards.go
Normal file
@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.openpdu.org/OpenPDU/openpdu/board"
|
||||
"git.openpdu.org/OpenPDU/openpdu/mqtt"
|
||||
"git.openpdu.org/OpenPDU/openpdu/syslog"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var boards []*board.Board
|
||||
|
||||
func CreateBoards() {
|
||||
|
||||
// AllChannels = make(map[string]Channel)
|
||||
|
||||
boardsConfig := viper.Sub("boards")
|
||||
if boardsConfig == nil {
|
||||
syslog.Warning("No board configured")
|
||||
return
|
||||
}
|
||||
|
||||
for key := range boardsConfig.AllSettings() {
|
||||
boardConfig := boardsConfig.Sub(key)
|
||||
boardConfig.SetDefault("type", "dummy")
|
||||
|
||||
b, err := board.CreateBoard(boardConfig, key)
|
||||
if err == nil {
|
||||
boards = append(boards, &b)
|
||||
}
|
||||
}
|
||||
|
||||
for i := range board.AllChannels {
|
||||
board.AllChannels[i].AddOnChannelUpdateFunction(
|
||||
"mqtt-publish",
|
||||
func(oldValue bool, c board.Channel) {
|
||||
mqtt.Publish(c.MQTTStateTopic(), c.ToString())
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: init boards array?
|
||||
|
||||
// outlets = make([]*Outlet, len(allChannels))
|
||||
|
||||
// // dumpa tutto
|
||||
// for b := range boards {
|
||||
// boards[b].Dump()
|
||||
// }
|
||||
|
||||
// // dumpa tutto
|
||||
// for o := range outlets {
|
||||
// outlet.Outlets[o].Dump()
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
func InitBoards() {
|
||||
var b board.Board
|
||||
for i := range boards {
|
||||
b = *boards[i]
|
||||
go b.Initialize()
|
||||
}
|
||||
}
|
57
src/config/config.go
Normal file
@ -0,0 +1,57 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func init() {
|
||||
viper.SetEnvPrefix("OPENPDU_")
|
||||
viper.AllowEmptyEnv(true)
|
||||
viper.AutomaticEnv()
|
||||
viper.SetConfigName("openpdu") // name of config file (without extension)
|
||||
viper.SetConfigType("yaml")
|
||||
viper.AddConfigPath(".") // optionally look for config in the working directory
|
||||
viper.AddConfigPath("/etc/openpdu/") // path to look for the config file in
|
||||
err := viper.ReadInConfig() // Find and read the config file
|
||||
if err != nil { // Handle errors reading the config file
|
||||
log.Printf("Can't read config file: %s \n", err.Error())
|
||||
}
|
||||
|
||||
// temporary disabled because it screwsup the config on save
|
||||
// viper.OnConfigChange(func(e fsnotify.Event) {
|
||||
// //The Viper configuration has changed to perform the responding operation
|
||||
// fmt.Println("Config file changed:", e.Name)
|
||||
// events.FireEvent("config_changed")
|
||||
// })
|
||||
// viper.WatchConfig()
|
||||
}
|
||||
|
||||
func SetDefault(k string, v interface{}) {
|
||||
viper.SetDefault(k, v)
|
||||
}
|
||||
|
||||
func GetString(k string) string {
|
||||
return viper.GetString(k)
|
||||
}
|
||||
|
||||
func GetInt(k string) int {
|
||||
return viper.GetInt(k)
|
||||
}
|
||||
|
||||
func GetBool(k string) bool {
|
||||
return viper.GetBool(k)
|
||||
}
|
||||
|
||||
func Set(k string, v interface{}) {
|
||||
viper.Set(k, v)
|
||||
}
|
||||
|
||||
func WriteConfig() {
|
||||
viper.WriteConfig()
|
||||
}
|
||||
|
||||
func ConfigFileUsed() string {
|
||||
return viper.ConfigFileUsed()
|
||||
}
|
144
src/display.go
Normal file
@ -0,0 +1,144 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"git.openpdu.org/OpenPDU/openpdu/config"
|
||||
|
||||
"git.openpdu.org/OpenPDU/openpdu/i2c"
|
||||
"git.openpdu.org/OpenPDU/openpdu/outlet"
|
||||
"git.openpdu.org/OpenPDU/openpdu/syslog"
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/font/basicfont"
|
||||
"golang.org/x/image/math/fixed"
|
||||
"periph.io/x/devices/v3/ssd1306"
|
||||
)
|
||||
|
||||
func init() {
|
||||
syslog.Info("display setup")
|
||||
config.SetDefault("Display.Type", "none")
|
||||
config.SetDefault("Display.W", 128)
|
||||
config.SetDefault("Display.H", 32)
|
||||
config.SetDefault("Display.Rotated", false)
|
||||
config.SetDefault("Display.SwapTopBottom", false)
|
||||
config.SetDefault("Display.networkinterface", "eth0")
|
||||
}
|
||||
|
||||
func displayLoop() {
|
||||
for {
|
||||
if config.GetString("Display.Type") != "ssd1306" {
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
syslog.Info("ssd1306 display starting loop")
|
||||
|
||||
if i2c.I2Cbus == nil {
|
||||
syslog.Warning("ssd1306 i2cbus not found")
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
opts := ssd1306.Opts{
|
||||
W: config.GetInt("Display.W"),
|
||||
H: config.GetInt("Display.H"),
|
||||
Rotated: config.GetBool("Display.Rotated"),
|
||||
Sequential: true,
|
||||
SwapTopBottom: config.GetBool("Display.SwapTopBottom"),
|
||||
}
|
||||
|
||||
// Open a handle to a ssd1306 connected on the I²C bus:
|
||||
dev, err := ssd1306.NewI2C(i2c.I2Cbus, &opts)
|
||||
if err != nil {
|
||||
syslog.Err(err.Error())
|
||||
}
|
||||
|
||||
syslog.Info("ssd1306 display setup completed")
|
||||
|
||||
for {
|
||||
err := disp(dev)
|
||||
if err != nil {
|
||||
syslog.Err(err.Error())
|
||||
time.Sleep(1 * time.Second)
|
||||
syslog.Warning("ssd1306 display disp error")
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addLabel(img *image.RGBA, x, y int, label string) {
|
||||
col := color.RGBA{200, 100, 0, 255}
|
||||
point := fixed.Point26_6{fixed.Int26_6(x * 64), fixed.Int26_6(y * 64)}
|
||||
|
||||
d := &font.Drawer{
|
||||
Dst: img,
|
||||
Src: image.NewUniform(col),
|
||||
Face: basicfont.Face7x13,
|
||||
Dot: point,
|
||||
}
|
||||
d.DrawString(label)
|
||||
}
|
||||
|
||||
func getIPs() map[string]string {
|
||||
out := map[string]string{}
|
||||
ifaces, _ := net.Interfaces()
|
||||
// handle err
|
||||
for _, i := range ifaces {
|
||||
if i.Name == "lo" {
|
||||
continue
|
||||
}
|
||||
addrs, _ := i.Addrs()
|
||||
// handle err
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
}
|
||||
// process IP address
|
||||
if ip.To4() == nil {
|
||||
continue
|
||||
}
|
||||
if len(out) < 1 {
|
||||
out[i.Name] = ip.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func dispOutletStatusString() string {
|
||||
out := ""
|
||||
for o := range outlet.Outlets {
|
||||
if outlet.Outlets[o].Channel.Status() {
|
||||
out += fmt.Sprint(outlet.Outlets[o].Num)
|
||||
} else {
|
||||
out += "."
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func disp(dev *ssd1306.Dev) error {
|
||||
img := image.NewRGBA(image.Rect(0, 0, 128, 32))
|
||||
iface := config.GetString("Display.NetworkInterface")
|
||||
ips := getIPs()
|
||||
ip, found := ips[iface]
|
||||
if !found {
|
||||
ip = "unknown"
|
||||
}
|
||||
lineHeight := 12
|
||||
posX := 12
|
||||
posY := lineHeight
|
||||
addLabel(img, posX, posY, ip)
|
||||
posY += lineHeight
|
||||
addLabel(img, posX, posY, dispOutletStatusString())
|
||||
return dev.Draw(img.Bounds(), img, image.Point{})
|
||||
}
|
28
src/events/events.go
Normal file
@ -0,0 +1,28 @@
|
||||
package events
|
||||
|
||||
var handlers map[string][]func()
|
||||
|
||||
func init() {
|
||||
handlers = make(map[string][]func())
|
||||
}
|
||||
|
||||
func AddListener(event string, f func()) {
|
||||
l, isPresent := handlers[event]
|
||||
if !isPresent {
|
||||
handlers[event] = []func(){
|
||||
f,
|
||||
}
|
||||
} else {
|
||||
handlers[event] = append(l, f)
|
||||
}
|
||||
}
|
||||
|
||||
func FireEvent(event string) {
|
||||
l, isPresent := handlers[event]
|
||||
if !isPresent {
|
||||
return
|
||||
}
|
||||
for _, v := range l {
|
||||
v()
|
||||
}
|
||||
}
|
27
src/go.mod
Normal file
@ -0,0 +1,27 @@
|
||||
module git.openpdu.org/OpenPDU/openpdu
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91
|
||||
github.com/eclipse/paho.mqtt.golang v1.3.5
|
||||
github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3 // indirect
|
||||
github.com/go-macaron/binding v1.2.0
|
||||
github.com/go-macaron/inject v0.0.0-20200308113650-138e5925c53b // indirect
|
||||
github.com/go-macaron/pongo2 v0.0.0-20200329073512-6ca146b415a1
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/robbiet480/go.nut v0.0.0-20211005235800-e810489c32db
|
||||
github.com/spf13/viper v1.9.0
|
||||
github.com/unknwon/com v1.0.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e // indirect
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
|
||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
gopkg.in/ini.v1 v1.66.2 // indirect
|
||||
gopkg.in/macaron.v1 v1.4.0
|
||||
periph.io/x/conn/v3 v3.6.10
|
||||
periph.io/x/devices/v3 v3.6.15
|
||||
periph.io/x/host/v3 v3.7.2
|
||||
)
|
||||
|
||||
replace git.openpdu.org/OpenPDU/openpdu => ./
|
730
src/go.sum
Normal file
@ -0,0 +1,730 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
||||
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
||||
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
||||
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
|
||||
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
|
||||
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
|
||||
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
|
||||
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91 h1:vX+gnvBc56EbWYrmlhYbFYRaeikAke1GL84N4BEYOFE=
|
||||
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91/go.mod h1:cDLGBht23g0XQdLjzn6xOGXDkLK182YfINAaZEQLCHQ=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/eclipse/paho.mqtt.golang v1.3.5 h1:sWtmgNxYM9P2sP+xEItMozsR3w0cqZFlqnNN1bdl41Y=
|
||||
github.com/eclipse/paho.mqtt.golang v1.3.5/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
|
||||
github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3 h1:fmFk0Wt3bBxxwZnu48jqMdaOR/IZ4vdtJFuaFV8MpIE=
|
||||
github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3/go.mod h1:bJWSKrZyQvfTnb2OudyUjurSG4/edverV7n82+K3JiM=
|
||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-macaron/binding v1.2.0 h1:/A8x8ZVQNTzFO43ch8czTqhc4VzOEPXYU/ELjIyhR60=
|
||||
github.com/go-macaron/binding v1.2.0/go.mod h1:8pXMCyR9UPsXV02PYGLI+t2Xep/v2OgVuuLTNtCG03c=
|
||||
github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191/go.mod h1:VFI2o2q9kYsC4o7VP1HrEVosiZZTd+MVT3YZx4gqvJw=
|
||||
github.com/go-macaron/inject v0.0.0-20200308113650-138e5925c53b h1:/aWj44HoEycE4MDi2HZf4t+XI7hKwZRltZf4ih5tB2c=
|
||||
github.com/go-macaron/inject v0.0.0-20200308113650-138e5925c53b/go.mod h1:VFI2o2q9kYsC4o7VP1HrEVosiZZTd+MVT3YZx4gqvJw=
|
||||
github.com/go-macaron/pongo2 v0.0.0-20200329073512-6ca146b415a1 h1:WmSEGPUZkHtelzgoDBaZiJR03+5UZhr63ayrosbVY2Q=
|
||||
github.com/go-macaron/pongo2 v0.0.0-20200329073512-6ca146b415a1/go.mod h1:H7wnGulppt0tQHvcfv5IoExZQsPYSfE+ywHk7ZB2nXU=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
|
||||
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
||||
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
|
||||
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
||||
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
|
||||
github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||
github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/maruel/ansi256 v1.0.2/go.mod h1:x7uow2KFkUgjdzvYHyfZuMEOTGKvCYLyVUHIVg1vYic=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
|
||||
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
|
||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/robbiet480/go.nut v0.0.0-20211005235800-e810489c32db h1:OaY6XX9j5TfNCg9soV2FcJ3BaTs6L8k7CX9rw1qnXXM=
|
||||
github.com/robbiet480/go.nut v0.0.0-20211005235800-e810489c32db/go.mod h1:pL1huxuIlWub46MsMVJg4p7OXkzbPp/APxh9IH0eJjQ=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
|
||||
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
|
||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.9.0 h1:yR6EXjTp0y0cLN8OZg1CRZmOBdI88UcGkhgyJhu6nZk=
|
||||
github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
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/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/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/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
||||
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
|
||||
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8=
|
||||
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=
|
||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486 h1:5hpz5aRr+W1erYCL5JRhSUBJRph7l9XkNveoExlrKYk=
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
||||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
|
||||
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
|
||||
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
|
||||
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
|
||||
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
|
||||
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
||||
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
||||
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
|
||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/macaron.v1 v1.3.5/go.mod h1:uMZCFccv9yr5TipIalVOyAyZQuOH3OkmXvgcWwhJuP4=
|
||||
gopkg.in/macaron.v1 v1.4.0 h1:RJHC09fAnQ8tuGUiZNjG0uyL1BWSdSWd9SpufIcEArQ=
|
||||
gopkg.in/macaron.v1 v1.4.0/go.mod h1:uMZCFccv9yr5TipIalVOyAyZQuOH3OkmXvgcWwhJuP4=
|
||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210105161348-2e78108cf5f8/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
periph.io/x/conn/v3 v3.6.10 h1:gwU4ssmZkq1D/uz8hU91i/COo2c9DrRaS4PJZBbCd+c=
|
||||
periph.io/x/conn/v3 v3.6.10/go.mod h1:UqWNaPMosWmNCwtufoTSTTYhB2wXWsMRAJyo1PlxO4Q=
|
||||
periph.io/x/d2xx v0.0.4/go.mod h1:38Euaaj+s6l0faIRHh32a+PrjXvxFTFkPBEQI0TKg34=
|
||||
periph.io/x/devices/v3 v3.6.15 h1:fn52w2dMDaHYktmD+BhO1Cu3EeDPPtuAvDVR61+Ca2E=
|
||||
periph.io/x/devices/v3 v3.6.15/go.mod h1:Zm9F3T5iLO8YRcyOnCC5T/QV2ZCd7Th2PFC2w8ZqN2M=
|
||||
periph.io/x/host/v3 v3.7.2 h1:rCAUxkzy2xrzh18HP2AoVwTL/fEKqmcJ1icsZQGM58Q=
|
||||
periph.io/x/host/v3 v3.7.2/go.mod h1:nHMlzkPwmnHyP9Tn0I8FV+e0N3K7TjFXLZkIWzAicog=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
30
src/i2c/i2c.go
Normal file
@ -0,0 +1,30 @@
|
||||
package i2c
|
||||
|
||||
import (
|
||||
"git.openpdu.org/OpenPDU/openpdu/syslog"
|
||||
I2C "periph.io/x/conn/v3/i2c"
|
||||
I2CREG "periph.io/x/conn/v3/i2c/i2creg"
|
||||
"periph.io/x/host/v3"
|
||||
)
|
||||
|
||||
var I2Cbus I2C.Bus
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
|
||||
syslog.Info("i2c setup")
|
||||
|
||||
// Make sure periph is initialized.
|
||||
if _, err = host.Init(); err != nil {
|
||||
syslog.Err(err.Error())
|
||||
}
|
||||
|
||||
// Use i2creg I²C bus registry to find the first available I²C bus.
|
||||
// i2cbus, err = i2creg.Open("/dev/i2c-1")
|
||||
I2Cbus, err = I2CREG.Open("")
|
||||
if err != nil {
|
||||
syslog.Err(err.Error())
|
||||
}
|
||||
|
||||
syslog.Info("i2c setup completed")
|
||||
}
|
53
src/main.go
Normal file
@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.openpdu.org/OpenPDU/openpdu/config"
|
||||
"git.openpdu.org/OpenPDU/openpdu/syslog"
|
||||
"git.openpdu.org/OpenPDU/openpdu/ups"
|
||||
"git.openpdu.org/OpenPDU/openpdu/webui"
|
||||
)
|
||||
|
||||
const version = "0.1"
|
||||
|
||||
func init() {
|
||||
config.SetDefault("system.hostname", "openpdu")
|
||||
}
|
||||
|
||||
func main() {
|
||||
CreateBoards()
|
||||
CreateOutlets()
|
||||
InitBoards()
|
||||
go MQTTSetup()
|
||||
go MQTTRefreshLoop()
|
||||
go ups.UpsConnect()
|
||||
go displayLoop()
|
||||
syslog.Info("hostname: " + config.GetString("system.hostname"))
|
||||
webui.StartServer()
|
||||
}
|
||||
|
||||
// https://github.com/ColorlibHQ/AdminLTE/archive/v2.4.17.tar.gz
|
||||
|
||||
/* TODO
|
||||
|
||||
- config reset gpio
|
||||
- classi per board
|
||||
- classi per outlet
|
||||
- fai funzionare toggle
|
||||
- scan i2c
|
||||
- impostazioni log
|
||||
- sensori temperatura e umidità
|
||||
- master/slave
|
||||
- dispositivi collegati, ping, alimentazioni separate, linee separate
|
||||
- misuratori carico per linee e per utenze
|
||||
- limiti carico per linee e utenze
|
||||
- alert? email?
|
||||
- auto upgrade: openpdu / entireOS
|
||||
- ssh enable/disable
|
||||
- wlan config
|
||||
- serial port console
|
||||
- cli commands?
|
||||
- reboot/shutdown from web
|
||||
- shutdown behavior
|
||||
- user management + roles (admin, port-reboot, port-on, port-off, readonly, ..) + api key
|
||||
- ups: username and password are required in webui
|
||||
*/
|
114
src/mqtt/mqtt.go
Normal file
@ -0,0 +1,114 @@
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.openpdu.org/OpenPDU/openpdu/config"
|
||||
"git.openpdu.org/OpenPDU/openpdu/syslog"
|
||||
MQTT "github.com/eclipse/paho.mqtt.golang"
|
||||
)
|
||||
|
||||
var MQTTclient MQTT.Client
|
||||
|
||||
func init() {
|
||||
config.SetDefault("Mqtt.CliendID", "OpenPDU") // max 23 chars
|
||||
config.SetDefault("Mqtt.Prefix", "openpdu") // max 23 chars
|
||||
config.SetDefault("Mqtt.Schema", "tcp")
|
||||
config.SetDefault("Mqtt.Host", "localhost")
|
||||
config.SetDefault("Mqtt.Port", "1883")
|
||||
config.SetDefault("Mqtt.Username", "")
|
||||
config.SetDefault("Mqtt.Password", "")
|
||||
config.SetDefault("Mqtt.LWTTopic", "LWT")
|
||||
config.SetDefault("Mqtt.LWTMessageOnline", "Online")
|
||||
config.SetDefault("Mqtt.LWTMessageOffline", "Offline")
|
||||
config.SetDefault("Mqtt.HomeAssistant", false)
|
||||
|
||||
// MQTT.ERROR = log.New(os.Stdout, "[ERROR] ", 0)
|
||||
// MQTT.CRITICAL = log.New(os.Stdout, "[CRIT] ", 0)
|
||||
// MQTT.WARN = log.New(os.Stdout, "[WARN] ", 0)
|
||||
// MQTT.DEBUG = log.New(os.Stdout, "[DEBUG] ", 0)
|
||||
|
||||
}
|
||||
|
||||
// https://girishjoshi.io/post/golang-paho-mqtt/
|
||||
|
||||
func Setup() {
|
||||
uri := config.GetString("Mqtt.Schema") + "://" + config.GetString("Mqtt.Host") + ":" + config.GetString("Mqtt.Port")
|
||||
opts := MQTT.NewClientOptions().AddBroker(uri)
|
||||
|
||||
opts.SetClientID(config.GetString("Mqtt.CliendID"))
|
||||
opts.SetWill(config.GetString("Mqtt.Prefix")+"/"+config.GetString("Mqtt.LWTTopic"), config.GetString("Mqtt.LWTMessageOffline"), 0, false)
|
||||
|
||||
if username := config.GetString("Mqtt.Username"); username != "" {
|
||||
opts.SetUsername(username)
|
||||
}
|
||||
|
||||
if password := config.GetString("Mqtt.Password"); password != "" {
|
||||
opts.SetPassword(password)
|
||||
}
|
||||
|
||||
opts.SetAutoReconnect(true)
|
||||
opts.SetConnectRetryInterval(5 * time.Second)
|
||||
opts.SetConnectTimeout(5 * time.Second)
|
||||
opts.SetCleanSession(true)
|
||||
|
||||
opts.SetConnectionLostHandler(func(c MQTT.Client, err error) {
|
||||
syslog.Err("mqtt connection lost error: " + err.Error())
|
||||
})
|
||||
opts.SetReconnectingHandler(func(c MQTT.Client, options *MQTT.ClientOptions) {
|
||||
time.Sleep(5 * time.Second)
|
||||
syslog.Notice("mqtt reconnecting")
|
||||
})
|
||||
opts.SetOnConnectHandler(func(c MQTT.Client) {
|
||||
syslog.Notice("mqtt connected")
|
||||
PublishRoot(config.GetString("Mqtt.Prefix")+"/"+config.GetString("Mqtt.LWTTopic"), config.GetString("Mqtt.LWTMessageOnline"))
|
||||
})
|
||||
|
||||
MQTTclient = MQTT.NewClient(opts)
|
||||
|
||||
for {
|
||||
token := MQTTclient.Connect()
|
||||
token.Wait()
|
||||
if MQTTclient.IsConnected() {
|
||||
break
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Subscribe(topic string, handler func(MQTT.Client, MQTT.Message)) {
|
||||
// MQTTHandler(MQTT.Client, MQTT.Message)
|
||||
MQTTclient.Subscribe(config.GetString("Mqtt.Prefix")+"/switch/"+topic, 1, handler)
|
||||
syslog.Debug(fmt.Sprintf("MQTT subscribed to topic: %s", topic))
|
||||
}
|
||||
|
||||
func Connected() bool {
|
||||
if MQTTclient == nil {
|
||||
return false
|
||||
}
|
||||
return MQTTclient.IsConnected()
|
||||
}
|
||||
|
||||
func Disconnect() {
|
||||
MQTTclient.Disconnect(250)
|
||||
}
|
||||
|
||||
func Publish(topic string, value string) {
|
||||
if MQTTclient == nil {
|
||||
return
|
||||
}
|
||||
if MQTTclient.IsConnected() {
|
||||
MQTTclient.Publish(config.GetString("Mqtt.Prefix")+"/switch/"+topic, 0, false, value)
|
||||
}
|
||||
}
|
||||
|
||||
func PublishRoot(topic string, value string) {
|
||||
if MQTTclient == nil {
|
||||
return
|
||||
}
|
||||
if MQTTclient.IsConnected() {
|
||||
MQTTclient.Publish(topic, 0, false, value)
|
||||
}
|
||||
}
|
20
src/outlet/outlet.go
Normal file
@ -0,0 +1,20 @@
|
||||
package outlet
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"git.openpdu.org/OpenPDU/openpdu/board"
|
||||
)
|
||||
|
||||
type Outlet struct {
|
||||
ID string
|
||||
Num uint
|
||||
Description string
|
||||
Channel board.Channel
|
||||
}
|
||||
|
||||
var Outlets []*Outlet
|
||||
|
||||
func (o *Outlet) Dump() {
|
||||
log.Printf("Outlet %v: channel name: %v\n", o.Num, o.Channel.Name())
|
||||
}
|
138
src/outlets.go
Normal file
@ -0,0 +1,138 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.openpdu.org/OpenPDU/openpdu/board"
|
||||
"git.openpdu.org/OpenPDU/openpdu/config"
|
||||
"git.openpdu.org/OpenPDU/openpdu/events"
|
||||
"git.openpdu.org/OpenPDU/openpdu/mqtt"
|
||||
"git.openpdu.org/OpenPDU/openpdu/outlet"
|
||||
"git.openpdu.org/OpenPDU/openpdu/syslog"
|
||||
"git.openpdu.org/OpenPDU/openpdu/webui"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func CreateOutlets() {
|
||||
|
||||
outletsConfig := viper.Sub("outlets")
|
||||
if outletsConfig == nil {
|
||||
syslog.Warning("No outlet configured")
|
||||
return
|
||||
}
|
||||
|
||||
outlet.Outlets = make([]*outlet.Outlet, len(board.AllChannels))
|
||||
|
||||
for key := range outletsConfig.AllSettings() {
|
||||
outletConfig := outletsConfig.Sub(key)
|
||||
num := outletConfig.GetUint("num")
|
||||
outletConfig.SetDefault("description", "no description")
|
||||
description := outletConfig.GetString("description")
|
||||
channelID := strings.ToLower(outletConfig.GetString("channelID"))
|
||||
channel := board.AllChannels[channelID]
|
||||
// TODO: channel non esistente
|
||||
o := outlet.Outlet{
|
||||
ID: key,
|
||||
Num: num,
|
||||
Description: description,
|
||||
Channel: channel,
|
||||
}
|
||||
|
||||
outlet.Outlets[num] = &o
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
webui.SetMQTTReconfigFunction(MQTTreconfigure)
|
||||
events.AddListener("outlet_config_changed", MQTTreconfigure)
|
||||
// mqtt.MQTTReconfig = MQTTreconfigure()
|
||||
}
|
||||
|
||||
func MQTTSetup() {
|
||||
mqtt.Setup()
|
||||
for o := range outlet.Outlets {
|
||||
c := outlet.Outlets[o].Channel
|
||||
topic := c.MQTTCommandTopic()
|
||||
if topic == "" {
|
||||
continue
|
||||
}
|
||||
mqtt.Subscribe(topic, c.MQTTHandler)
|
||||
hAssTopic := "homeassistant/switch/" + config.GetString("Mqtt.Prefix") + "/" + fmt.Sprint(o) + "/config"
|
||||
v := ""
|
||||
if config.GetBool("Mqtt.HomeAssistant") {
|
||||
cfg := map[string]interface{}{
|
||||
"name": outlet.Outlets[o].Description,
|
||||
"state_topic": config.GetString("Mqtt.Prefix") + "/switch/" + c.MQTTStateTopic(),
|
||||
"command_topic": config.GetString("Mqtt.Prefix") + "/switch/" + c.MQTTCommandTopic(),
|
||||
"payload_off": "off",
|
||||
"payload_on": "on",
|
||||
"avty_t": config.GetString("Mqtt.Prefix") + "/" + config.GetString("Mqtt.LWTTopic"),
|
||||
"pl_avail": config.GetString("Mqtt.LWTMessageOnline"),
|
||||
"pl_not_avail": config.GetString("Mqtt.LWTMessageOffline"),
|
||||
"unique_id": config.GetString("Mqtt.Prefix") + "_" + fmt.Sprint(o),
|
||||
"device": map[string]interface{}{
|
||||
"identifiers": []string{config.GetString("Mqtt.Prefix")},
|
||||
},
|
||||
}
|
||||
|
||||
s, err := json.Marshal(cfg)
|
||||
if err != nil {
|
||||
v = ""
|
||||
} else {
|
||||
v = string(s)
|
||||
}
|
||||
}
|
||||
mqtt.PublishRoot(hAssTopic, v)
|
||||
}
|
||||
|
||||
hAssTopic := "homeassistant/switch/" + config.GetString("Mqtt.Prefix") + "/config"
|
||||
v := ""
|
||||
if config.GetBool("Mqtt.HomeAssistant") {
|
||||
cfg := map[string]interface{}{
|
||||
"name": config.GetString("system.hostname"),
|
||||
"avty_t": config.GetString("Mqtt.Prefix") + "/" + config.GetString("Mqtt.LWTTopic"),
|
||||
"pl_avail": config.GetString("Mqtt.LWTMessageOnline"),
|
||||
"pl_not_avail": config.GetString("Mqtt.LWTMessageOffline"),
|
||||
"unique_id": config.GetString("Mqtt.Prefix"),
|
||||
"device": map[string]interface{}{
|
||||
"identifiers": []string{config.GetString("Mqtt.Prefix")},
|
||||
"name": config.GetString("system.hostname"),
|
||||
"model": "OpenPDU",
|
||||
"sw_version": version,
|
||||
"manufacturer": "OpenPDU",
|
||||
},
|
||||
}
|
||||
|
||||
s, err := json.Marshal(cfg)
|
||||
if err != nil {
|
||||
v = ""
|
||||
} else {
|
||||
v = string(s)
|
||||
}
|
||||
}
|
||||
mqtt.PublishRoot(hAssTopic, v)
|
||||
|
||||
}
|
||||
|
||||
func MQTTreconfigure() {
|
||||
if mqtt.Connected() {
|
||||
mqtt.Disconnect()
|
||||
}
|
||||
MQTTSetup()
|
||||
}
|
||||
|
||||
func MQTTRefreshLoop() {
|
||||
for {
|
||||
if mqtt.Connected() {
|
||||
for o := range outlet.Outlets {
|
||||
topic := outlet.Outlets[o].Channel.MQTTStateTopic()
|
||||
value := outlet.Outlets[o].Channel.ToString()
|
||||
mqtt.Publish(topic, value)
|
||||
}
|
||||
}
|
||||
time.Sleep(30 * time.Second)
|
||||
}
|
||||
}
|
127
src/syslog/syslog.go
Normal file
@ -0,0 +1,127 @@
|
||||
package syslog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"git.openpdu.org/OpenPDU/openpdu/config"
|
||||
"git.openpdu.org/OpenPDU/openpdu/events"
|
||||
"github.com/RackSec/srslog"
|
||||
)
|
||||
|
||||
var logger *srslog.Writer
|
||||
var Connected bool
|
||||
|
||||
var defaults = map[string]interface{}{
|
||||
"Syslog.Host": "localhost",
|
||||
"Syslog.Port": 514,
|
||||
"Syslog.Protocol": "udp",
|
||||
"Syslog.Format": "RFC5424",
|
||||
}
|
||||
|
||||
func init() {
|
||||
for k, v := range defaults {
|
||||
config.SetDefault(k, v)
|
||||
}
|
||||
Connected = false
|
||||
events.AddListener("syslog_config_changed", Reconfigure)
|
||||
go Connect()
|
||||
}
|
||||
|
||||
func Reconfigure() {
|
||||
if logger != nil {
|
||||
logger.Close()
|
||||
}
|
||||
|
||||
Notice("Syslog disconnected")
|
||||
events.FireEvent("syslog_disconnected")
|
||||
Connected = false
|
||||
|
||||
go Connect()
|
||||
}
|
||||
|
||||
func Connect() {
|
||||
var err error
|
||||
|
||||
hostname := config.GetString("Syslog.Host")
|
||||
port := config.GetInt("Syslog.Port")
|
||||
protocol := config.GetString("Syslog.Protocol")
|
||||
format := config.GetString("Syslog.Format")
|
||||
conn := fmt.Sprintf("%s:%d", hostname, port)
|
||||
logger, err = srslog.Dial(protocol, conn, srslog.LOG_WARNING|srslog.LOG_DAEMON, "openpdu")
|
||||
|
||||
if err != nil {
|
||||
Connected = false
|
||||
Err(fmt.Sprintf("failed to connect to syslog: %s", err.Error()))
|
||||
time.Sleep(1 * time.Second)
|
||||
go Connect()
|
||||
return
|
||||
}
|
||||
|
||||
switch format {
|
||||
case "RFC3164":
|
||||
logger.SetFormatter(srslog.RFC3164Formatter)
|
||||
default: // RFC5424
|
||||
logger.SetFormatter(srslog.RFC5424Formatter)
|
||||
}
|
||||
|
||||
Connected = true
|
||||
logger.Info("OpenPDU connected to syslog")
|
||||
}
|
||||
|
||||
func Alert(msg string) {
|
||||
if logger == nil {
|
||||
log.Print(msg)
|
||||
} else {
|
||||
logger.Alert(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func Crit(msg string) {
|
||||
if logger == nil {
|
||||
log.Print(msg)
|
||||
} else {
|
||||
logger.Crit(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func Err(msg string) {
|
||||
if logger == nil {
|
||||
log.Print(msg)
|
||||
} else {
|
||||
logger.Err(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func Warning(msg string) {
|
||||
if logger == nil {
|
||||
log.Print(msg)
|
||||
} else {
|
||||
logger.Warning(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func Notice(msg string) {
|
||||
if logger == nil {
|
||||
log.Print(msg)
|
||||
} else {
|
||||
logger.Notice(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func Info(msg string) {
|
||||
if logger == nil {
|
||||
log.Print(msg)
|
||||
} else {
|
||||
logger.Info(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func Debug(msg string) {
|
||||
if logger == nil {
|
||||
log.Print(msg)
|
||||
} else {
|
||||
logger.Debug(msg)
|
||||
}
|
||||
}
|
88
src/ups/ups.go
Normal file
@ -0,0 +1,88 @@
|
||||
package ups
|
||||
|
||||
import (
|
||||
"git.openpdu.org/OpenPDU/openpdu/config"
|
||||
"git.openpdu.org/OpenPDU/openpdu/events"
|
||||
"git.openpdu.org/OpenPDU/openpdu/syslog"
|
||||
nut "github.com/robbiet480/go.nut"
|
||||
)
|
||||
|
||||
var upsClient nut.Client
|
||||
var ups nut.UPS
|
||||
var Vars map[string]nut.Variable
|
||||
var Connected bool
|
||||
|
||||
var defaults = map[string]interface{}{
|
||||
"Ups.Host": "localhost",
|
||||
"Ups.Port": 3493,
|
||||
"Ups.Name": "ups",
|
||||
"Ups.Username": "",
|
||||
"Ups.Password": "",
|
||||
}
|
||||
|
||||
func init() {
|
||||
for k, v := range defaults {
|
||||
config.SetDefault(k, v)
|
||||
}
|
||||
Connected = false
|
||||
events.AddListener("ups_config_changed", Reconfigure)
|
||||
}
|
||||
|
||||
func UpsConnect() {
|
||||
var port int
|
||||
var host, username, password string
|
||||
var connectErr, authenticationError error
|
||||
var u nut.UPS
|
||||
|
||||
host = config.GetString("Ups.Host")
|
||||
port = config.GetInt("Ups.Port")
|
||||
upsClient, connectErr = nut.Connect(host, port)
|
||||
if connectErr != nil {
|
||||
syslog.Err(connectErr.Error())
|
||||
return
|
||||
}
|
||||
|
||||
username = config.GetString("Ups.Username")
|
||||
password = config.GetString("Ups.Password")
|
||||
|
||||
if username != "" && password != "" {
|
||||
_, authenticationError = upsClient.Authenticate(username, password)
|
||||
if authenticationError != nil {
|
||||
syslog.Err(authenticationError.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
upsList, listErr := upsClient.GetUPSList()
|
||||
if listErr != nil {
|
||||
syslog.Err(listErr.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, u = range upsList {
|
||||
if u.Name == config.GetString("Ups.Name") {
|
||||
ups = u
|
||||
Connected = true
|
||||
UpsReadVars()
|
||||
syslog.Notice("UPS connected")
|
||||
events.FireEvent("ups_connected")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func UpsReadVars() {
|
||||
Vars = map[string]nut.Variable{}
|
||||
for _, v := range ups.Variables {
|
||||
Vars[v.Name] = v
|
||||
}
|
||||
}
|
||||
|
||||
func Reconfigure() {
|
||||
ups = nut.UPS{}
|
||||
upsClient = nut.Client{}
|
||||
syslog.Notice("UPS disconnected")
|
||||
events.FireEvent("ups_disconnected")
|
||||
Connected = false
|
||||
Vars = make(map[string]nut.Variable)
|
||||
go UpsConnect()
|
||||
}
|
18
src/webui/backup_ui.go
Normal file
@ -0,0 +1,18 @@
|
||||
package webui
|
||||
|
||||
import (
|
||||
"path"
|
||||
|
||||
"git.openpdu.org/OpenPDU/openpdu/config"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
func backupPage(ctx *macaron.Context) {
|
||||
ctx.HTML(200, "backup")
|
||||
}
|
||||
|
||||
func backupDownload(ctx *macaron.Context) {
|
||||
cfgFile := config.ConfigFileUsed()
|
||||
_, fileName := path.Split(cfgFile)
|
||||
ctx.ServeFile(cfgFile, fileName)
|
||||
}
|
7
src/webui/lan_ui.go
Normal file
@ -0,0 +1,7 @@
|
||||
package webui
|
||||
|
||||
import "gopkg.in/macaron.v1"
|
||||
|
||||
func lanPage(ctx *macaron.Context) {
|
||||
ctx.HTML(200, "lan")
|
||||
}
|
87
src/webui/mqtt_ui.go
Normal file
@ -0,0 +1,87 @@
|
||||
package webui
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"git.openpdu.org/OpenPDU/openpdu/config"
|
||||
"git.openpdu.org/OpenPDU/openpdu/mqtt"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
type MQTTReconfigFunction func()
|
||||
|
||||
var mqttReconfigFunction MQTTReconfigFunction
|
||||
|
||||
func SetMQTTReconfigFunction(f MQTTReconfigFunction) {
|
||||
mqttReconfigFunction = f
|
||||
}
|
||||
|
||||
func mqttPage(ctx *macaron.Context) {
|
||||
ctx.Data["schema"] = config.GetString("Mqtt.Schema")
|
||||
ctx.Data["host"] = config.GetString("Mqtt.Host")
|
||||
ctx.Data["port"] = config.GetString("Mqtt.Port")
|
||||
ctx.Data["clientid"] = config.GetString("Mqtt.CliendID")
|
||||
ctx.Data["prefix"] = config.GetString("Mqtt.Prefix")
|
||||
ctx.Data["username"] = config.GetString("Mqtt.Username")
|
||||
ctx.Data["password"] = config.GetString("Mqtt.Password")
|
||||
ctx.Data["lwttopic"] = config.GetString("Mqtt.LWTTopic")
|
||||
ctx.Data["lwtonline"] = config.GetString("Mqtt.LWTMessageOnline")
|
||||
ctx.Data["lwtoffline"] = config.GetString("Mqtt.LWTMessageOffline")
|
||||
ctx.Data["homeassistant"] = config.GetString("Mqtt.homeassistant")
|
||||
|
||||
if mqtt.Connected() {
|
||||
ctx.Data["mqttstatus"] = "Connected"
|
||||
} else {
|
||||
ctx.Data["mqttstatus"] = "Disconnected"
|
||||
}
|
||||
|
||||
ctx.Data["schemas"] = []string{"tcp", "ssl", "ws"}
|
||||
ctx.HTML(200, "mqtt")
|
||||
}
|
||||
|
||||
type MQTTPostForm struct {
|
||||
Schema string `form:"schema" binding:"Required"`
|
||||
Host string `form:"host" binding:"Required"`
|
||||
Port string `form:"port" binding:"Required"`
|
||||
Prefix string `form:"prefix" binding:"Required"`
|
||||
ClientID string `form:"clientid" binding:"Required"`
|
||||
Username string `form:"username"`
|
||||
Password string `form:"password"`
|
||||
LWTTopic string `form:"lwttopic"`
|
||||
LWTMessageOnline string `form:"lwtonline"`
|
||||
LWTMessageOffline string `form:"lwtoffline"`
|
||||
HomeAssistant bool `form:"homeassistant"`
|
||||
}
|
||||
|
||||
func mqttPost(ctx *macaron.Context, f MQTTPostForm) {
|
||||
schema := strings.ToLower(strings.TrimSpace(f.Schema))
|
||||
switch schema {
|
||||
case
|
||||
"tcp",
|
||||
"ssl",
|
||||
"ws":
|
||||
config.Set("Mqtt.Schema", schema)
|
||||
default:
|
||||
mqttPage(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
config.Set("Mqtt.Host", strings.ToLower(strings.TrimSpace(f.Host)))
|
||||
config.Set("Mqtt.Port", strings.ToLower(strings.TrimSpace(f.Port)))
|
||||
config.Set("Mqtt.CliendID", strings.TrimSpace(f.ClientID))
|
||||
config.Set("Mqtt.Prefix", strings.TrimSpace(f.Prefix))
|
||||
config.Set("Mqtt.Username", strings.TrimSpace(f.Username))
|
||||
config.Set("Mqtt.Password", f.Password)
|
||||
config.Set("Mqtt.LWTTopic", strings.TrimSpace(f.LWTTopic))
|
||||
config.Set("Mqtt.LWTMessageOnline", strings.TrimSpace(f.LWTMessageOnline))
|
||||
config.Set("Mqtt.LWTMessageOffline", strings.TrimSpace(f.LWTMessageOffline))
|
||||
config.Set("Mqtt.HomeAssistant", f.HomeAssistant)
|
||||
|
||||
config.WriteConfig()
|
||||
|
||||
if mqttReconfigFunction != nil {
|
||||
go mqttReconfigFunction()
|
||||
}
|
||||
|
||||
mqttPage(ctx)
|
||||
}
|
97
src/webui/outlets_ui.go
Normal file
@ -0,0 +1,97 @@
|
||||
package webui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.openpdu.org/OpenPDU/openpdu/config"
|
||||
"git.openpdu.org/OpenPDU/openpdu/events"
|
||||
"git.openpdu.org/OpenPDU/openpdu/outlet"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
func outletsPage(ctx *macaron.Context) {
|
||||
|
||||
ctx.Data["outlets"] = outlet.Outlets
|
||||
|
||||
ctx.HTML(200, "outlets")
|
||||
}
|
||||
|
||||
type OutletPostForm struct {
|
||||
Description string `form:"description" binding:"Required"`
|
||||
MQTTStateTopic string `form:"mqttstatetopic"`
|
||||
MQTTCommandTopic string `form:"mqttcommandtopic"`
|
||||
OnBoot string `form:"onboot" binding:"Required"`
|
||||
}
|
||||
|
||||
func outletEditPage(ctx *macaron.Context) {
|
||||
var err error
|
||||
var num uint64
|
||||
var o *outlet.Outlet
|
||||
num, err = strconv.ParseUint(ctx.Params(":num"), 10, 64)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"result": "error",
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
o = outlet.Outlets[num]
|
||||
ctx.Data["outlet"] = o
|
||||
|
||||
ctx.Data["onboot_values"] = []string{"on", "off", "last"}
|
||||
ctx.HTML(200, "outlet_edit")
|
||||
}
|
||||
|
||||
func outletEditPost(ctx *macaron.Context, f OutletPostForm) {
|
||||
var err error
|
||||
var num uint64
|
||||
|
||||
num, err = strconv.ParseUint(ctx.Params(":num"), 10, 64)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"result": "error",
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
onboot := "on"
|
||||
onbootForm := strings.ToLower(strings.TrimSpace(f.OnBoot))
|
||||
switch onbootForm {
|
||||
case
|
||||
"on",
|
||||
"off",
|
||||
"last":
|
||||
onboot = onbootForm
|
||||
default:
|
||||
outletsPage(ctx)
|
||||
return
|
||||
}
|
||||
outlet.Outlets[num].Channel.SetOnBoot(onboot)
|
||||
|
||||
outlet.Outlets[num].Description = strings.TrimSpace(f.Description)
|
||||
s2 := fmt.Sprintf("outlets.%s.description", outlet.Outlets[num].ID)
|
||||
config.Set(s2, outlet.Outlets[num].Description)
|
||||
|
||||
mqttstate := strings.TrimSpace(f.MQTTStateTopic)
|
||||
if mqttstate == "" {
|
||||
mqttstate = outlet.Outlets[num].Channel.Name()
|
||||
}
|
||||
outlet.Outlets[num].Channel.SetMQTTStateTopic(mqttstate)
|
||||
|
||||
mqttcommand := strings.TrimSpace(f.MQTTCommandTopic)
|
||||
if mqttcommand == "" {
|
||||
mqttcommand = outlet.Outlets[num].Channel.Name()
|
||||
}
|
||||
outlet.Outlets[num].Channel.SetMQTTCommandTopic(mqttcommand)
|
||||
|
||||
config.WriteConfig()
|
||||
|
||||
events.FireEvent("outlet_config_changed")
|
||||
|
||||
ctx.Redirect("/outlets")
|
||||
}
|
64
src/webui/status_ui.go
Normal file
@ -0,0 +1,64 @@
|
||||
package webui
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"git.openpdu.org/OpenPDU/openpdu/outlet"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
func statusPage(ctx *macaron.Context) {
|
||||
ctx.HTML(200, "status") // 200 is the response code.
|
||||
}
|
||||
|
||||
func jsonStatus(ctx *macaron.Context) {
|
||||
// MQTTpublish("openpdu/status", "asdss")
|
||||
|
||||
var data = make([]Dictionary, 0)
|
||||
var o *outlet.Outlet
|
||||
for i := range outlet.Outlets {
|
||||
o = outlet.Outlets[i]
|
||||
// d := []string{fmt.Sprintf("%d", o.Num), o.Channel.Name, fmt.Sprintf("%v", o.Channel.Value)}
|
||||
data = append(data, Dictionary{
|
||||
"Num": o.Num,
|
||||
"Description": o.Description,
|
||||
"Status": o.Channel.Status(),
|
||||
})
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": data,
|
||||
})
|
||||
}
|
||||
|
||||
func jsonOutletToggle(ctx *macaron.Context) {
|
||||
var err error
|
||||
var num uint64
|
||||
var o *outlet.Outlet
|
||||
|
||||
num, err = strconv.ParseUint(ctx.Params(":id"), 10, 64)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "error1",
|
||||
})
|
||||
}
|
||||
|
||||
if num >= uint64(len(outlet.Outlets)) || num < 0 {
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "error2",
|
||||
})
|
||||
}
|
||||
|
||||
o = outlet.Outlets[num]
|
||||
_, err = o.Channel.Toggle()
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "error3",
|
||||
})
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Dictionary{
|
||||
"data": "ok",
|
||||
})
|
||||
}
|
46
src/webui/syslog_ui.go
Normal file
@ -0,0 +1,46 @@
|
||||
package webui
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"git.openpdu.org/OpenPDU/openpdu/config"
|
||||
"git.openpdu.org/OpenPDU/openpdu/events"
|
||||
"git.openpdu.org/OpenPDU/openpdu/syslog"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
func syslogPage(ctx *macaron.Context) {
|
||||
ctx.Data["host"] = config.GetString("Syslog.Host")
|
||||
ctx.Data["port"] = config.GetInt("Syslog.Port")
|
||||
ctx.Data["protocol"] = config.GetString("Syslog.Protocol")
|
||||
ctx.Data["format"] = config.GetString("Syslog.Format")
|
||||
|
||||
if syslog.Connected {
|
||||
ctx.Data["status"] = "Connected"
|
||||
} else {
|
||||
ctx.Data["status"] = "Disconnected"
|
||||
}
|
||||
|
||||
ctx.HTML(200, "syslog")
|
||||
}
|
||||
|
||||
type SyslogPostForm struct {
|
||||
Host string `form:"host" binding:"Required"`
|
||||
Port int `form:"port" binding:"Required"`
|
||||
Protocol string `form:"protocol" binding:"Required"`
|
||||
Format string `form:"format" binding:"Required"`
|
||||
}
|
||||
|
||||
func syslogPost(ctx *macaron.Context, f SyslogPostForm) {
|
||||
// TODO: check protocol, it should be 'udp' or 'tcp'
|
||||
config.Set("Syslog.Host", strings.ToLower(strings.TrimSpace(f.Host)))
|
||||
config.Set("Syslog.Port", f.Port)
|
||||
config.Set("Syslog.Protocol", strings.TrimSpace(f.Protocol))
|
||||
// TODO: check format, it should be 'RFC5424' or 'RFC3164'
|
||||
config.Set("Syslog.Format", strings.TrimSpace(f.Format))
|
||||
config.WriteConfig()
|
||||
|
||||
events.FireEvent("syslog_config_changed")
|
||||
|
||||
syslogPage(ctx)
|
||||
}
|
54
src/webui/ups_ui.go
Normal file
@ -0,0 +1,54 @@
|
||||
package webui
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"git.openpdu.org/OpenPDU/openpdu/config"
|
||||
"git.openpdu.org/OpenPDU/openpdu/events"
|
||||
"git.openpdu.org/OpenPDU/openpdu/ups"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
func upsPage(ctx *macaron.Context) {
|
||||
ctx.Data["host"] = config.GetString("Ups.Host")
|
||||
ctx.Data["port"] = config.GetInt("Ups.Port")
|
||||
ctx.Data["upsname"] = config.GetString("Ups.Name")
|
||||
ctx.Data["username"] = config.GetString("Ups.Username")
|
||||
ctx.Data["password"] = config.GetString("Ups.Password")
|
||||
|
||||
if ups.Connected {
|
||||
ctx.Data["serverstatus"] = "Connected"
|
||||
} else {
|
||||
ctx.Data["serverstatus"] = "Disconnected"
|
||||
}
|
||||
|
||||
ctx.Data["upsstatus"] = ups.Vars["ups.status"].Value
|
||||
ctx.Data["upsmfr"] = ups.Vars["device.mfr"].Value
|
||||
ctx.Data["upsmodel"] = ups.Vars["device.model"].Value
|
||||
ctx.Data["upsserial"] = ups.Vars["device.serial"].Value
|
||||
ctx.Data["upsload"] = ups.Vars["ups.load"].Value
|
||||
ctx.Data["upscharge"] = ups.Vars["battery.charge"].Value
|
||||
|
||||
ctx.HTML(200, "ups")
|
||||
}
|
||||
|
||||
type UPSPostForm struct {
|
||||
Host string `form:"host" binding:"Required"`
|
||||
Port int `form:"port" binding:"Required"`
|
||||
UpsName string `form:"upsname" binding:"Required"`
|
||||
Username string `form:"username"`
|
||||
Password string `form:"password"`
|
||||
}
|
||||
|
||||
func upsPost(ctx *macaron.Context, f UPSPostForm) {
|
||||
config.Set("Ups.Host", strings.ToLower(strings.TrimSpace(f.Host)))
|
||||
config.Set("Ups.Port", f.Port)
|
||||
config.Set("Ups.Name", strings.TrimSpace(f.UpsName))
|
||||
config.Set("Ups.Username", strings.TrimSpace(f.Username))
|
||||
config.Set("Ups.Password", f.Password)
|
||||
config.WriteConfig()
|
||||
|
||||
events.FireEvent("ups_config_changed")
|
||||
|
||||
upsPage(ctx)
|
||||
}
|
43
src/webui/webui.go
Normal file
@ -0,0 +1,43 @@
|
||||
package webui
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"git.openpdu.org/OpenPDU/openpdu/config"
|
||||
"git.openpdu.org/OpenPDU/openpdu/syslog"
|
||||
"github.com/go-macaron/binding"
|
||||
"github.com/go-macaron/pongo2"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
// Dictionary aaa
|
||||
type Dictionary map[string]interface{}
|
||||
|
||||
func init() {
|
||||
config.SetDefault("system.listeningport", 4000)
|
||||
}
|
||||
|
||||
func StartServer() {
|
||||
m := macaron.Classic()
|
||||
m.Use(pongo2.Pongoer())
|
||||
m.Use(macaron.Static("static"))
|
||||
|
||||
m.Get("/", statusPage)
|
||||
m.Get("/outlets", outletsPage)
|
||||
m.Get("/outlet/:num", outletEditPage)
|
||||
m.Post("/outlet/:num", binding.Bind(OutletPostForm{}), outletEditPost)
|
||||
m.Get("/lan", lanPage)
|
||||
m.Get("/mqtt", mqttPage)
|
||||
m.Post("/mqtt", binding.Bind(MQTTPostForm{}), mqttPost)
|
||||
m.Get("/ups", upsPage)
|
||||
m.Post("/ups", binding.Bind(UPSPostForm{}), upsPost)
|
||||
m.Get("/syslog", syslogPage)
|
||||
m.Post("/syslog", binding.Bind(SyslogPostForm{}), syslogPost)
|
||||
m.Get("/backup", backupPage)
|
||||
m.Post("/backup", backupDownload)
|
||||
m.Get("/json/status", jsonStatus)
|
||||
m.Post("/json/outlet/:id/toggle", jsonOutletToggle)
|
||||
|
||||
syslog.Info("Web interface ready")
|
||||
http.ListenAndServe("0.0.0.0:"+config.GetString("system.listeningport"), m)
|
||||
}
|
5558
static/adminlte/css/AdminLTE.css
Normal file
8
static/adminlte/css/AdminLTE.min.css
vendored
Normal file
140
static/adminlte/css/adminlte.css.map
Normal file
140
static/adminlte/css/adminlte.min.css.map
Normal file
1260
static/adminlte/css/alt/AdminLTE-bootstrap-social.css
Normal file
1
static/adminlte/css/alt/AdminLTE-bootstrap-social.min.css
vendored
Normal file
93
static/adminlte/css/alt/AdminLTE-fullcalendar.css
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Plugin: Full Calendar
|
||||
* ---------------------
|
||||
*/
|
||||
.fc-button {
|
||||
background: #f4f4f4;
|
||||
background-image: none;
|
||||
color: #444;
|
||||
border-color: #ddd;
|
||||
border-bottom-color: #ddd;
|
||||
}
|
||||
.fc-button:hover,
|
||||
.fc-button:active,
|
||||
.fc-button.hover {
|
||||
background-color: #e9e9e9;
|
||||
}
|
||||
.fc-header-title h2 {
|
||||
font-size: 15px;
|
||||
line-height: 1.6em;
|
||||
color: #666;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.fc-header-right {
|
||||
padding-right: 10px;
|
||||
}
|
||||
.fc-header-left {
|
||||
padding-left: 10px;
|
||||
}
|
||||
.fc-widget-header {
|
||||
background: #fafafa;
|
||||
}
|
||||
.fc-grid {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
}
|
||||
.fc-widget-header:first-of-type,
|
||||
.fc-widget-content:first-of-type {
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
}
|
||||
.fc-widget-header:last-of-type,
|
||||
.fc-widget-content:last-of-type {
|
||||
border-right: 0;
|
||||
}
|
||||
.fc-toolbar {
|
||||
padding: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
.fc-day-number {
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.fc-color-picker {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.fc-color-picker > li {
|
||||
float: left;
|
||||
font-size: 30px;
|
||||
margin-right: 5px;
|
||||
line-height: 30px;
|
||||
}
|
||||
.fc-color-picker > li .fa {
|
||||
-webkit-transition: -webkit-transform linear 0.3s;
|
||||
-moz-transition: -moz-transform linear 0.3s;
|
||||
-o-transition: -o-transform linear 0.3s;
|
||||
transition: transform linear 0.3s;
|
||||
}
|
||||
.fc-color-picker > li .fa:hover {
|
||||
-webkit-transform: rotate(30deg);
|
||||
-ms-transform: rotate(30deg);
|
||||
-o-transform: rotate(30deg);
|
||||
transform: rotate(30deg);
|
||||
}
|
||||
#add-new-event {
|
||||
-webkit-transition: all linear 0.3s;
|
||||
-o-transition: all linear 0.3s;
|
||||
transition: all linear 0.3s;
|
||||
}
|
||||
.external-event {
|
||||
padding: 5px 10px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 3px;
|
||||
cursor: move;
|
||||
}
|
||||
.external-event:hover {
|
||||
box-shadow: inset 0 0 90px rgba(0, 0, 0, 0.2);
|
||||
}
|
1
static/adminlte/css/alt/AdminLTE-fullcalendar.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.fc-button{background:#f4f4f4;background-image:none;color:#444;border-color:#ddd;border-bottom-color:#ddd}.fc-button:hover,.fc-button:active,.fc-button.hover{background-color:#e9e9e9}.fc-header-title h2{font-size:15px;line-height:1.6em;color:#666;margin-left:10px}.fc-header-right{padding-right:10px}.fc-header-left{padding-left:10px}.fc-widget-header{background:#fafafa}.fc-grid{width:100%;border:0}.fc-widget-header:first-of-type,.fc-widget-content:first-of-type{border-left:0;border-right:0}.fc-widget-header:last-of-type,.fc-widget-content:last-of-type{border-right:0}.fc-toolbar{padding:10px;margin:0}.fc-day-number{font-size:20px;font-weight:300;padding-right:10px}.fc-color-picker{list-style:none;margin:0;padding:0}.fc-color-picker>li{float:left;font-size:30px;margin-right:5px;line-height:30px}.fc-color-picker>li .fa{-webkit-transition:-webkit-transform linear .3s;-moz-transition:-moz-transform linear .3s;-o-transition:-o-transform linear .3s;transition:transform linear .3s}.fc-color-picker>li .fa:hover{-webkit-transform:rotate(30deg);-ms-transform:rotate(30deg);-o-transform:rotate(30deg);transform:rotate(30deg)}#add-new-event{-webkit-transition:all linear .3s;-o-transition:all linear .3s;transition:all linear .3s}.external-event{padding:5px 10px;font-weight:bold;margin-bottom:4px;box-shadow:0 1px 1px rgba(0,0,0,0.1);text-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px;cursor:move}.external-event:hover{box-shadow:inset 0 0 90px rgba(0,0,0,0.2)}
|
100
static/adminlte/css/alt/AdminLTE-select2.css
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Plugin: Select2
|
||||
* ---------------
|
||||
*/
|
||||
.select2-container--default.select2-container--focus,
|
||||
.select2-selection.select2-container--focus,
|
||||
.select2-container--default:focus,
|
||||
.select2-selection:focus,
|
||||
.select2-container--default:active,
|
||||
.select2-selection:active {
|
||||
outline: none;
|
||||
}
|
||||
.select2-container--default .select2-selection--single,
|
||||
.select2-selection .select2-selection--single {
|
||||
border: 1px solid #d2d6de;
|
||||
border-radius: 0;
|
||||
padding: 6px 12px;
|
||||
height: 34px;
|
||||
}
|
||||
.select2-container--default.select2-container--open {
|
||||
border-color: #3c8dbc;
|
||||
}
|
||||
.select2-dropdown {
|
||||
border: 1px solid #d2d6de;
|
||||
border-radius: 0;
|
||||
}
|
||||
.select2-container--default .select2-results__option--highlighted[aria-selected] {
|
||||
background-color: #3c8dbc;
|
||||
color: white;
|
||||
}
|
||||
.select2-results__option {
|
||||
padding: 6px 12px;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
.select2-container .select2-selection--single .select2-selection__rendered {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
height: auto;
|
||||
margin-top: -4px;
|
||||
}
|
||||
.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered {
|
||||
padding-right: 6px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
||||
height: 28px;
|
||||
right: 3px;
|
||||
}
|
||||
.select2-container--default .select2-selection--single .select2-selection__arrow b {
|
||||
margin-top: 0;
|
||||
}
|
||||
.select2-dropdown .select2-search__field,
|
||||
.select2-search--inline .select2-search__field {
|
||||
border: 1px solid #d2d6de;
|
||||
}
|
||||
.select2-dropdown .select2-search__field:focus,
|
||||
.select2-search--inline .select2-search__field:focus {
|
||||
outline: none;
|
||||
}
|
||||
.select2-container--default.select2-container--focus .select2-selection--multiple,
|
||||
.select2-container--default .select2-search--dropdown .select2-search__field {
|
||||
border-color: #3c8dbc !important;
|
||||
}
|
||||
.select2-container--default .select2-results__option[aria-disabled=true] {
|
||||
color: #999;
|
||||
}
|
||||
.select2-container--default .select2-results__option[aria-selected=true] {
|
||||
background-color: #ddd;
|
||||
}
|
||||
.select2-container--default .select2-results__option[aria-selected=true],
|
||||
.select2-container--default .select2-results__option[aria-selected=true]:hover {
|
||||
color: #444;
|
||||
}
|
||||
.select2-container--default .select2-selection--multiple {
|
||||
border: 1px solid #d2d6de;
|
||||
border-radius: 0;
|
||||
}
|
||||
.select2-container--default .select2-selection--multiple:focus {
|
||||
border-color: #3c8dbc;
|
||||
}
|
||||
.select2-container--default.select2-container--focus .select2-selection--multiple {
|
||||
border-color: #d2d6de;
|
||||
}
|
||||
.select2-container--default .select2-selection--multiple .select2-selection__choice {
|
||||
background-color: #3c8dbc;
|
||||
border-color: #367fa9;
|
||||
padding: 1px 10px;
|
||||
color: #fff;
|
||||
}
|
||||
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
|
||||
margin-right: 5px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
|
||||
color: #fff;
|
||||
}
|
||||
.select2-container .select2-selection--single .select2-selection__rendered {
|
||||
padding-right: 10px;
|
||||
}
|
1
static/adminlte/css/alt/AdminLTE-select2.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.select2-container--default.select2-container--focus,.select2-selection.select2-container--focus,.select2-container--default:focus,.select2-selection:focus,.select2-container--default:active,.select2-selection:active{outline:none}.select2-container--default .select2-selection--single,.select2-selection .select2-selection--single{border:1px solid #d2d6de;border-radius:0;padding:6px 12px;height:34px}.select2-container--default.select2-container--open{border-color:#3c8dbc}.select2-dropdown{border:1px solid #d2d6de;border-radius:0}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#3c8dbc;color:white}.select2-results__option{padding:6px 12px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{padding-left:0;padding-right:0;height:auto;margin-top:-4px}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:6px;padding-left:20px}.select2-container--default .select2-selection--single .select2-selection__arrow{height:28px;right:3px}.select2-container--default .select2-selection--single .select2-selection__arrow b{margin-top:0}.select2-dropdown .select2-search__field,.select2-search--inline .select2-search__field{border:1px solid #d2d6de}.select2-dropdown .select2-search__field:focus,.select2-search--inline .select2-search__field:focus{outline:none}.select2-container--default.select2-container--focus .select2-selection--multiple,.select2-container--default .select2-search--dropdown .select2-search__field{border-color:#3c8dbc !important}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option[aria-selected=true],.select2-container--default .select2-results__option[aria-selected=true]:hover{color:#444}.select2-container--default .select2-selection--multiple{border:1px solid #d2d6de;border-radius:0}.select2-container--default .select2-selection--multiple:focus{border-color:#3c8dbc}.select2-container--default.select2-container--focus .select2-selection--multiple{border-color:#d2d6de}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#3c8dbc;border-color:#367fa9;padding:1px 10px;color:#fff}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{margin-right:5px;color:rgba(255,255,255,0.7)}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#fff}.select2-container .select2-selection--single .select2-selection__rendered{padding-right:10px}
|
4084
static/adminlte/css/alt/AdminLTE-without-plugins.css
Normal file
9
static/adminlte/css/alt/AdminLTE-without-plugins.min.css
vendored
Normal file
1782
static/adminlte/css/skins/_all-skins.css
Normal file
1
static/adminlte/css/skins/_all-skins.min.css
vendored
Normal file
172
static/adminlte/css/skins/skin-black-light.css
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Skin: Black
|
||||
* -----------
|
||||
*/
|
||||
/* skin-black navbar */
|
||||
.skin-black-light .main-header {
|
||||
-webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.skin-black-light .main-header .navbar-toggle {
|
||||
color: #333;
|
||||
}
|
||||
.skin-black-light .main-header .navbar-brand {
|
||||
color: #333;
|
||||
border-right: 1px solid #d2d6de;
|
||||
}
|
||||
.skin-black-light .main-header .navbar {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.skin-black-light .main-header .navbar .nav > li > a {
|
||||
color: #333333;
|
||||
}
|
||||
.skin-black-light .main-header .navbar .nav > li > a:hover,
|
||||
.skin-black-light .main-header .navbar .nav > li > a:active,
|
||||
.skin-black-light .main-header .navbar .nav > li > a:focus,
|
||||
.skin-black-light .main-header .navbar .nav .open > a,
|
||||
.skin-black-light .main-header .navbar .nav .open > a:hover,
|
||||
.skin-black-light .main-header .navbar .nav .open > a:focus,
|
||||
.skin-black-light .main-header .navbar .nav > .active > a {
|
||||
background: #ffffff;
|
||||
color: #999999;
|
||||
}
|
||||
.skin-black-light .main-header .navbar .sidebar-toggle {
|
||||
color: #333333;
|
||||
}
|
||||
.skin-black-light .main-header .navbar .sidebar-toggle:hover {
|
||||
color: #999999;
|
||||
background: #ffffff;
|
||||
}
|
||||
.skin-black-light .main-header .navbar > .sidebar-toggle {
|
||||
color: #333;
|
||||
border-right: 1px solid #d2d6de;
|
||||
}
|
||||
.skin-black-light .main-header .navbar .navbar-nav > li > a {
|
||||
border-right: 1px solid #d2d6de;
|
||||
}
|
||||
.skin-black-light .main-header .navbar .navbar-custom-menu .navbar-nav > li > a,
|
||||
.skin-black-light .main-header .navbar .navbar-right > li > a {
|
||||
border-left: 1px solid #d2d6de;
|
||||
border-right-width: 0;
|
||||
}
|
||||
.skin-black-light .main-header .logo {
|
||||
background-color: #ffffff;
|
||||
color: #333333;
|
||||
border-bottom: 0 solid transparent;
|
||||
border-right: 1px solid #d2d6de;
|
||||
}
|
||||
.skin-black-light .main-header .logo:hover {
|
||||
background-color: #fcfcfc;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.skin-black-light .main-header .logo {
|
||||
background-color: #222222;
|
||||
color: #ffffff;
|
||||
border-bottom: 0 solid transparent;
|
||||
border-right: none;
|
||||
}
|
||||
.skin-black-light .main-header .logo:hover {
|
||||
background-color: #1f1f1f;
|
||||
}
|
||||
}
|
||||
.skin-black-light .main-header li.user-header {
|
||||
background-color: #222;
|
||||
}
|
||||
.skin-black-light .content-header {
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
.skin-black-light .wrapper,
|
||||
.skin-black-light .main-sidebar,
|
||||
.skin-black-light .left-side {
|
||||
background-color: #f9fafc;
|
||||
}
|
||||
.skin-black-light .main-sidebar {
|
||||
border-right: 1px solid #d2d6de;
|
||||
}
|
||||
.skin-black-light .user-panel > .info,
|
||||
.skin-black-light .user-panel > .info > a {
|
||||
color: #444444;
|
||||
}
|
||||
.skin-black-light .sidebar-menu > li {
|
||||
-webkit-transition: border-left-color 0.3s ease;
|
||||
-o-transition: border-left-color 0.3s ease;
|
||||
transition: border-left-color 0.3s ease;
|
||||
}
|
||||
.skin-black-light .sidebar-menu > li.header {
|
||||
color: #848484;
|
||||
background: #f9fafc;
|
||||
}
|
||||
.skin-black-light .sidebar-menu > li > a {
|
||||
border-left: 3px solid transparent;
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-black-light .sidebar-menu > li:hover > a,
|
||||
.skin-black-light .sidebar-menu > li.active > a {
|
||||
color: #000000;
|
||||
background: #f4f4f5;
|
||||
}
|
||||
.skin-black-light .sidebar-menu > li.active {
|
||||
border-left-color: #ffffff;
|
||||
}
|
||||
.skin-black-light .sidebar-menu > li.active > a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-black-light .sidebar-menu > li > .treeview-menu {
|
||||
background: #f4f4f5;
|
||||
}
|
||||
.skin-black-light .sidebar a {
|
||||
color: #444444;
|
||||
}
|
||||
.skin-black-light .sidebar a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.skin-black-light .sidebar-menu .treeview-menu > li > a {
|
||||
color: #777777;
|
||||
}
|
||||
.skin-black-light .sidebar-menu .treeview-menu > li.active > a,
|
||||
.skin-black-light .sidebar-menu .treeview-menu > li > a:hover {
|
||||
color: #000000;
|
||||
}
|
||||
.skin-black-light .sidebar-menu .treeview-menu > li.active > a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-black-light .sidebar-form {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #d2d6de;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
.skin-black-light .sidebar-form input[type="text"],
|
||||
.skin-black-light .sidebar-form .btn {
|
||||
box-shadow: none;
|
||||
background-color: #fff;
|
||||
border: 1px solid transparent;
|
||||
height: 35px;
|
||||
}
|
||||
.skin-black-light .sidebar-form input[type="text"] {
|
||||
color: #666;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
.skin-black-light .sidebar-form input[type="text"]:focus,
|
||||
.skin-black-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
background-color: #fff;
|
||||
color: #666;
|
||||
}
|
||||
.skin-black-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.skin-black-light .sidebar-form .btn {
|
||||
color: #999;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.skin-black-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
|
||||
border-left: 1px solid #d2d6de;
|
||||
}
|
||||
}
|
1
static/adminlte/css/skins/skin-black-light.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.skin-black-light .main-header{-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.skin-black-light .main-header .navbar-toggle{color:#333}.skin-black-light .main-header .navbar-brand{color:#333;border-right:1px solid #d2d6de}.skin-black-light .main-header .navbar{background-color:#fff}.skin-black-light .main-header .navbar .nav>li>a{color:#333}.skin-black-light .main-header .navbar .nav>li>a:hover,.skin-black-light .main-header .navbar .nav>li>a:active,.skin-black-light .main-header .navbar .nav>li>a:focus,.skin-black-light .main-header .navbar .nav .open>a,.skin-black-light .main-header .navbar .nav .open>a:hover,.skin-black-light .main-header .navbar .nav .open>a:focus,.skin-black-light .main-header .navbar .nav>.active>a{background:#fff;color:#999}.skin-black-light .main-header .navbar .sidebar-toggle{color:#333}.skin-black-light .main-header .navbar .sidebar-toggle:hover{color:#999;background:#fff}.skin-black-light .main-header .navbar>.sidebar-toggle{color:#333;border-right:1px solid #d2d6de}.skin-black-light .main-header .navbar .navbar-nav>li>a{border-right:1px solid #d2d6de}.skin-black-light .main-header .navbar .navbar-custom-menu .navbar-nav>li>a,.skin-black-light .main-header .navbar .navbar-right>li>a{border-left:1px solid #d2d6de;border-right-width:0}.skin-black-light .main-header .logo{background-color:#fff;color:#333;border-bottom:0 solid transparent;border-right:1px solid #d2d6de}.skin-black-light .main-header .logo:hover{background-color:#fcfcfc}@media (max-width:767px){.skin-black-light .main-header .logo{background-color:#222;color:#fff;border-bottom:0 solid transparent;border-right:none}.skin-black-light .main-header .logo:hover{background-color:#1f1f1f}}.skin-black-light .main-header li.user-header{background-color:#222}.skin-black-light .content-header{background:transparent;box-shadow:none}.skin-black-light .wrapper,.skin-black-light .main-sidebar,.skin-black-light .left-side{background-color:#f9fafc}.skin-black-light .main-sidebar{border-right:1px solid #d2d6de}.skin-black-light .user-panel>.info,.skin-black-light .user-panel>.info>a{color:#444}.skin-black-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-black-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-black-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-black-light .sidebar-menu>li:hover>a,.skin-black-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-black-light .sidebar-menu>li.active{border-left-color:#fff}.skin-black-light .sidebar-menu>li.active>a{font-weight:600}.skin-black-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-black-light .sidebar a{color:#444}.skin-black-light .sidebar a:hover{text-decoration:none}.skin-black-light .sidebar-menu .treeview-menu>li>a{color:#777}.skin-black-light .sidebar-menu .treeview-menu>li.active>a,.skin-black-light .sidebar-menu .treeview-menu>li>a:hover{color:#000}.skin-black-light .sidebar-menu .treeview-menu>li.active>a{font-weight:600}.skin-black-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-black-light .sidebar-form input[type="text"],.skin-black-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px}.skin-black-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-black-light .sidebar-form input[type="text"]:focus,.skin-black-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-black-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-black-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-black-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}
|
161
static/adminlte/css/skins/skin-black.css
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Skin: Black
|
||||
* -----------
|
||||
*/
|
||||
/* skin-black navbar */
|
||||
.skin-black .main-header {
|
||||
-webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.skin-black .main-header .navbar-toggle {
|
||||
color: #333;
|
||||
}
|
||||
.skin-black .main-header .navbar-brand {
|
||||
color: #333;
|
||||
border-right: 1px solid #eee;
|
||||
}
|
||||
.skin-black .main-header .navbar {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.skin-black .main-header .navbar .nav > li > a {
|
||||
color: #333333;
|
||||
}
|
||||
.skin-black .main-header .navbar .nav > li > a:hover,
|
||||
.skin-black .main-header .navbar .nav > li > a:active,
|
||||
.skin-black .main-header .navbar .nav > li > a:focus,
|
||||
.skin-black .main-header .navbar .nav .open > a,
|
||||
.skin-black .main-header .navbar .nav .open > a:hover,
|
||||
.skin-black .main-header .navbar .nav .open > a:focus,
|
||||
.skin-black .main-header .navbar .nav > .active > a {
|
||||
background: #ffffff;
|
||||
color: #999999;
|
||||
}
|
||||
.skin-black .main-header .navbar .sidebar-toggle {
|
||||
color: #333333;
|
||||
}
|
||||
.skin-black .main-header .navbar .sidebar-toggle:hover {
|
||||
color: #999999;
|
||||
background: #ffffff;
|
||||
}
|
||||
.skin-black .main-header .navbar > .sidebar-toggle {
|
||||
color: #333;
|
||||
border-right: 1px solid #eee;
|
||||
}
|
||||
.skin-black .main-header .navbar .navbar-nav > li > a {
|
||||
border-right: 1px solid #eee;
|
||||
}
|
||||
.skin-black .main-header .navbar .navbar-custom-menu .navbar-nav > li > a,
|
||||
.skin-black .main-header .navbar .navbar-right > li > a {
|
||||
border-left: 1px solid #eee;
|
||||
border-right-width: 0;
|
||||
}
|
||||
.skin-black .main-header .logo {
|
||||
background-color: #ffffff;
|
||||
color: #333333;
|
||||
border-bottom: 0 solid transparent;
|
||||
border-right: 1px solid #eee;
|
||||
}
|
||||
.skin-black .main-header .logo:hover {
|
||||
background-color: #fcfcfc;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.skin-black .main-header .logo {
|
||||
background-color: #222222;
|
||||
color: #ffffff;
|
||||
border-bottom: 0 solid transparent;
|
||||
border-right: none;
|
||||
}
|
||||
.skin-black .main-header .logo:hover {
|
||||
background-color: #1f1f1f;
|
||||
}
|
||||
}
|
||||
.skin-black .main-header li.user-header {
|
||||
background-color: #222;
|
||||
}
|
||||
.skin-black .content-header {
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
.skin-black .wrapper,
|
||||
.skin-black .main-sidebar,
|
||||
.skin-black .left-side {
|
||||
background-color: #222d32;
|
||||
}
|
||||
.skin-black .user-panel > .info,
|
||||
.skin-black .user-panel > .info > a {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-black .sidebar-menu > li.header {
|
||||
color: #4b646f;
|
||||
background: #1a2226;
|
||||
}
|
||||
.skin-black .sidebar-menu > li > a {
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
.skin-black .sidebar-menu > li:hover > a,
|
||||
.skin-black .sidebar-menu > li.active > a,
|
||||
.skin-black .sidebar-menu > li.menu-open > a {
|
||||
color: #ffffff;
|
||||
background: #1e282c;
|
||||
}
|
||||
.skin-black .sidebar-menu > li.active > a {
|
||||
border-left-color: #ffffff;
|
||||
}
|
||||
.skin-black .sidebar-menu > li > .treeview-menu {
|
||||
margin: 0 1px;
|
||||
background: #2c3b41;
|
||||
}
|
||||
.skin-black .sidebar a {
|
||||
color: #b8c7ce;
|
||||
}
|
||||
.skin-black .sidebar a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.skin-black .sidebar-menu .treeview-menu > li > a {
|
||||
color: #8aa4af;
|
||||
}
|
||||
.skin-black .sidebar-menu .treeview-menu > li.active > a,
|
||||
.skin-black .sidebar-menu .treeview-menu > li > a:hover {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-black .sidebar-form {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #374850;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
.skin-black .sidebar-form input[type="text"],
|
||||
.skin-black .sidebar-form .btn {
|
||||
box-shadow: none;
|
||||
background-color: #374850;
|
||||
border: 1px solid transparent;
|
||||
height: 35px;
|
||||
}
|
||||
.skin-black .sidebar-form input[type="text"] {
|
||||
color: #666;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
.skin-black .sidebar-form input[type="text"]:focus,
|
||||
.skin-black .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
background-color: #fff;
|
||||
color: #666;
|
||||
}
|
||||
.skin-black .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.skin-black .sidebar-form .btn {
|
||||
color: #999;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
.skin-black .pace .pace-progress {
|
||||
background: #222;
|
||||
}
|
||||
.skin-black .pace .pace-activity {
|
||||
border-top-color: #222;
|
||||
border-left-color: #222;
|
||||
}
|
1
static/adminlte/css/skins/skin-black.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.skin-black .main-header{-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.skin-black .main-header .navbar-toggle{color:#333}.skin-black .main-header .navbar-brand{color:#333;border-right:1px solid #eee}.skin-black .main-header .navbar{background-color:#fff}.skin-black .main-header .navbar .nav>li>a{color:#333}.skin-black .main-header .navbar .nav>li>a:hover,.skin-black .main-header .navbar .nav>li>a:active,.skin-black .main-header .navbar .nav>li>a:focus,.skin-black .main-header .navbar .nav .open>a,.skin-black .main-header .navbar .nav .open>a:hover,.skin-black .main-header .navbar .nav .open>a:focus,.skin-black .main-header .navbar .nav>.active>a{background:#fff;color:#999}.skin-black .main-header .navbar .sidebar-toggle{color:#333}.skin-black .main-header .navbar .sidebar-toggle:hover{color:#999;background:#fff}.skin-black .main-header .navbar>.sidebar-toggle{color:#333;border-right:1px solid #eee}.skin-black .main-header .navbar .navbar-nav>li>a{border-right:1px solid #eee}.skin-black .main-header .navbar .navbar-custom-menu .navbar-nav>li>a,.skin-black .main-header .navbar .navbar-right>li>a{border-left:1px solid #eee;border-right-width:0}.skin-black .main-header .logo{background-color:#fff;color:#333;border-bottom:0 solid transparent;border-right:1px solid #eee}.skin-black .main-header .logo:hover{background-color:#fcfcfc}@media (max-width:767px){.skin-black .main-header .logo{background-color:#222;color:#fff;border-bottom:0 solid transparent;border-right:none}.skin-black .main-header .logo:hover{background-color:#1f1f1f}}.skin-black .main-header li.user-header{background-color:#222}.skin-black .content-header{background:transparent;box-shadow:none}.skin-black .wrapper,.skin-black .main-sidebar,.skin-black .left-side{background-color:#222d32}.skin-black .user-panel>.info,.skin-black .user-panel>.info>a{color:#fff}.skin-black .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-black .sidebar-menu>li>a{border-left:3px solid transparent}.skin-black .sidebar-menu>li:hover>a,.skin-black .sidebar-menu>li.active>a,.skin-black .sidebar-menu>li.menu-open>a{color:#fff;background:#1e282c}.skin-black .sidebar-menu>li.active>a{border-left-color:#fff}.skin-black .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-black .sidebar a{color:#b8c7ce}.skin-black .sidebar a:hover{text-decoration:none}.skin-black .sidebar-menu .treeview-menu>li>a{color:#8aa4af}.skin-black .sidebar-menu .treeview-menu>li.active>a,.skin-black .sidebar-menu .treeview-menu>li>a:hover{color:#fff}.skin-black .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-black .sidebar-form input[type="text"],.skin-black .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px}.skin-black .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-black .sidebar-form input[type="text"]:focus,.skin-black .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-black .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-black .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-black .pace .pace-progress{background:#222}.skin-black .pace .pace-activity{border-top-color:#222;border-left-color:#222}
|
163
static/adminlte/css/skins/skin-blue-light.css
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Skin: Blue
|
||||
* ----------
|
||||
*/
|
||||
.skin-blue-light .main-header .navbar {
|
||||
background-color: #3c8dbc;
|
||||
}
|
||||
.skin-blue-light .main-header .navbar .nav > li > a {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-blue-light .main-header .navbar .nav > li > a:hover,
|
||||
.skin-blue-light .main-header .navbar .nav > li > a:active,
|
||||
.skin-blue-light .main-header .navbar .nav > li > a:focus,
|
||||
.skin-blue-light .main-header .navbar .nav .open > a,
|
||||
.skin-blue-light .main-header .navbar .nav .open > a:hover,
|
||||
.skin-blue-light .main-header .navbar .nav .open > a:focus,
|
||||
.skin-blue-light .main-header .navbar .nav > .active > a {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
color: #f6f6f6;
|
||||
}
|
||||
.skin-blue-light .main-header .navbar .sidebar-toggle {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-blue-light .main-header .navbar .sidebar-toggle:hover {
|
||||
color: #f6f6f6;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.skin-blue-light .main-header .navbar .sidebar-toggle {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-blue-light .main-header .navbar .sidebar-toggle:hover {
|
||||
background-color: #367fa9;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.skin-blue-light .main-header .navbar .dropdown-menu li.divider {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.skin-blue-light .main-header .navbar .dropdown-menu li a {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-blue-light .main-header .navbar .dropdown-menu li a:hover {
|
||||
background: #367fa9;
|
||||
}
|
||||
}
|
||||
.skin-blue-light .main-header .logo {
|
||||
background-color: #3c8dbc;
|
||||
color: #ffffff;
|
||||
border-bottom: 0 solid transparent;
|
||||
}
|
||||
.skin-blue-light .main-header .logo:hover {
|
||||
background-color: #3b8ab8;
|
||||
}
|
||||
.skin-blue-light .main-header li.user-header {
|
||||
background-color: #3c8dbc;
|
||||
}
|
||||
.skin-blue-light .content-header {
|
||||
background: transparent;
|
||||
}
|
||||
.skin-blue-light .wrapper,
|
||||
.skin-blue-light .main-sidebar,
|
||||
.skin-blue-light .left-side {
|
||||
background-color: #f9fafc;
|
||||
}
|
||||
.skin-blue-light .main-sidebar {
|
||||
border-right: 1px solid #d2d6de;
|
||||
}
|
||||
.skin-blue-light .user-panel > .info,
|
||||
.skin-blue-light .user-panel > .info > a {
|
||||
color: #444444;
|
||||
}
|
||||
.skin-blue-light .sidebar-menu > li {
|
||||
-webkit-transition: border-left-color 0.3s ease;
|
||||
-o-transition: border-left-color 0.3s ease;
|
||||
transition: border-left-color 0.3s ease;
|
||||
}
|
||||
.skin-blue-light .sidebar-menu > li.header {
|
||||
color: #848484;
|
||||
background: #f9fafc;
|
||||
}
|
||||
.skin-blue-light .sidebar-menu > li > a {
|
||||
border-left: 3px solid transparent;
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-blue-light .sidebar-menu > li:hover > a,
|
||||
.skin-blue-light .sidebar-menu > li.active > a {
|
||||
color: #000000;
|
||||
background: #f4f4f5;
|
||||
}
|
||||
.skin-blue-light .sidebar-menu > li.active {
|
||||
border-left-color: #3c8dbc;
|
||||
}
|
||||
.skin-blue-light .sidebar-menu > li.active > a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-blue-light .sidebar-menu > li > .treeview-menu {
|
||||
background: #f4f4f5;
|
||||
}
|
||||
.skin-blue-light .sidebar a {
|
||||
color: #444444;
|
||||
}
|
||||
.skin-blue-light .sidebar a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.skin-blue-light .sidebar-menu .treeview-menu > li > a {
|
||||
color: #777777;
|
||||
}
|
||||
.skin-blue-light .sidebar-menu .treeview-menu > li.active > a,
|
||||
.skin-blue-light .sidebar-menu .treeview-menu > li > a:hover {
|
||||
color: #000000;
|
||||
}
|
||||
.skin-blue-light .sidebar-menu .treeview-menu > li.active > a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-blue-light .sidebar-form {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #d2d6de;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
.skin-blue-light .sidebar-form input[type="text"],
|
||||
.skin-blue-light .sidebar-form .btn {
|
||||
box-shadow: none;
|
||||
background-color: #fff;
|
||||
border: 1px solid transparent;
|
||||
height: 35px;
|
||||
}
|
||||
.skin-blue-light .sidebar-form input[type="text"] {
|
||||
color: #666;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
.skin-blue-light .sidebar-form input[type="text"]:focus,
|
||||
.skin-blue-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
background-color: #fff;
|
||||
color: #666;
|
||||
}
|
||||
.skin-blue-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.skin-blue-light .sidebar-form .btn {
|
||||
color: #999;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.skin-blue-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
|
||||
border-left: 1px solid #d2d6de;
|
||||
}
|
||||
}
|
||||
.skin-blue-light .main-footer {
|
||||
border-top-color: #d2d6de;
|
||||
}
|
||||
.skin-blue.layout-top-nav .main-header > .logo {
|
||||
background-color: #3c8dbc;
|
||||
color: #ffffff;
|
||||
border-bottom: 0 solid transparent;
|
||||
}
|
||||
.skin-blue.layout-top-nav .main-header > .logo:hover {
|
||||
background-color: #3b8ab8;
|
||||
}
|
1
static/adminlte/css/skins/skin-blue-light.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.skin-blue-light .main-header .navbar{background-color:#3c8dbc}.skin-blue-light .main-header .navbar .nav>li>a{color:#fff}.skin-blue-light .main-header .navbar .nav>li>a:hover,.skin-blue-light .main-header .navbar .nav>li>a:active,.skin-blue-light .main-header .navbar .nav>li>a:focus,.skin-blue-light .main-header .navbar .nav .open>a,.skin-blue-light .main-header .navbar .nav .open>a:hover,.skin-blue-light .main-header .navbar .nav .open>a:focus,.skin-blue-light .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-blue-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-blue-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue-light .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-blue-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-blue-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-blue-light .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-blue-light .main-header .logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue-light .main-header .logo:hover{background-color:#3b8ab8}.skin-blue-light .main-header li.user-header{background-color:#3c8dbc}.skin-blue-light .content-header{background:transparent}.skin-blue-light .wrapper,.skin-blue-light .main-sidebar,.skin-blue-light .left-side{background-color:#f9fafc}.skin-blue-light .main-sidebar{border-right:1px solid #d2d6de}.skin-blue-light .user-panel>.info,.skin-blue-light .user-panel>.info>a{color:#444}.skin-blue-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-blue-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-blue-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-blue-light .sidebar-menu>li:hover>a,.skin-blue-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-blue-light .sidebar-menu>li.active{border-left-color:#3c8dbc}.skin-blue-light .sidebar-menu>li.active>a{font-weight:600}.skin-blue-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-blue-light .sidebar a{color:#444}.skin-blue-light .sidebar a:hover{text-decoration:none}.skin-blue-light .sidebar-menu .treeview-menu>li>a{color:#777}.skin-blue-light .sidebar-menu .treeview-menu>li.active>a,.skin-blue-light .sidebar-menu .treeview-menu>li>a:hover{color:#000}.skin-blue-light .sidebar-menu .treeview-menu>li.active>a{font-weight:600}.skin-blue-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-blue-light .sidebar-form input[type="text"],.skin-blue-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px}.skin-blue-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-blue-light .sidebar-form input[type="text"]:focus,.skin-blue-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-blue-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}.skin-blue-light .main-footer{border-top-color:#d2d6de}.skin-blue.layout-top-nav .main-header>.logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue.layout-top-nav .main-header>.logo:hover{background-color:#3b8ab8}
|
142
static/adminlte/css/skins/skin-blue.css
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Skin: Blue
|
||||
* ----------
|
||||
*/
|
||||
.skin-blue .main-header .navbar {
|
||||
background-color: #3c8dbc;
|
||||
}
|
||||
.skin-blue .main-header .navbar .nav > li > a {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-blue .main-header .navbar .nav > li > a:hover,
|
||||
.skin-blue .main-header .navbar .nav > li > a:active,
|
||||
.skin-blue .main-header .navbar .nav > li > a:focus,
|
||||
.skin-blue .main-header .navbar .nav .open > a,
|
||||
.skin-blue .main-header .navbar .nav .open > a:hover,
|
||||
.skin-blue .main-header .navbar .nav .open > a:focus,
|
||||
.skin-blue .main-header .navbar .nav > .active > a {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
color: #f6f6f6;
|
||||
}
|
||||
.skin-blue .main-header .navbar .sidebar-toggle {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-blue .main-header .navbar .sidebar-toggle:hover {
|
||||
color: #f6f6f6;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.skin-blue .main-header .navbar .sidebar-toggle {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-blue .main-header .navbar .sidebar-toggle:hover {
|
||||
background-color: #367fa9;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.skin-blue .main-header .navbar .dropdown-menu li.divider {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.skin-blue .main-header .navbar .dropdown-menu li a {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-blue .main-header .navbar .dropdown-menu li a:hover {
|
||||
background: #367fa9;
|
||||
}
|
||||
}
|
||||
.skin-blue .main-header .logo {
|
||||
background-color: #367fa9;
|
||||
color: #ffffff;
|
||||
border-bottom: 0 solid transparent;
|
||||
}
|
||||
.skin-blue .main-header .logo:hover {
|
||||
background-color: #357ca5;
|
||||
}
|
||||
.skin-blue .main-header li.user-header {
|
||||
background-color: #3c8dbc;
|
||||
}
|
||||
.skin-blue .content-header {
|
||||
background: transparent;
|
||||
}
|
||||
.skin-blue .wrapper,
|
||||
.skin-blue .main-sidebar,
|
||||
.skin-blue .left-side {
|
||||
background-color: #222d32;
|
||||
}
|
||||
.skin-blue .user-panel > .info,
|
||||
.skin-blue .user-panel > .info > a {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-blue .sidebar-menu > li.header {
|
||||
color: #4b646f;
|
||||
background: #1a2226;
|
||||
}
|
||||
.skin-blue .sidebar-menu > li > a {
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
.skin-blue .sidebar-menu > li:hover > a,
|
||||
.skin-blue .sidebar-menu > li.active > a,
|
||||
.skin-blue .sidebar-menu > li.menu-open > a {
|
||||
color: #ffffff;
|
||||
background: #1e282c;
|
||||
}
|
||||
.skin-blue .sidebar-menu > li.active > a {
|
||||
border-left-color: #3c8dbc;
|
||||
}
|
||||
.skin-blue .sidebar-menu > li > .treeview-menu {
|
||||
margin: 0 1px;
|
||||
background: #2c3b41;
|
||||
}
|
||||
.skin-blue .sidebar a {
|
||||
color: #b8c7ce;
|
||||
}
|
||||
.skin-blue .sidebar a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.skin-blue .sidebar-menu .treeview-menu > li > a {
|
||||
color: #8aa4af;
|
||||
}
|
||||
.skin-blue .sidebar-menu .treeview-menu > li.active > a,
|
||||
.skin-blue .sidebar-menu .treeview-menu > li > a:hover {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-blue .sidebar-form {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #374850;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
.skin-blue .sidebar-form input[type="text"],
|
||||
.skin-blue .sidebar-form .btn {
|
||||
box-shadow: none;
|
||||
background-color: #374850;
|
||||
border: 1px solid transparent;
|
||||
height: 35px;
|
||||
}
|
||||
.skin-blue .sidebar-form input[type="text"] {
|
||||
color: #666;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
.skin-blue .sidebar-form input[type="text"]:focus,
|
||||
.skin-blue .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
background-color: #fff;
|
||||
color: #666;
|
||||
}
|
||||
.skin-blue .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.skin-blue .sidebar-form .btn {
|
||||
color: #999;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
.skin-blue.layout-top-nav .main-header > .logo {
|
||||
background-color: #3c8dbc;
|
||||
color: #ffffff;
|
||||
border-bottom: 0 solid transparent;
|
||||
}
|
||||
.skin-blue.layout-top-nav .main-header > .logo:hover {
|
||||
background-color: #3b8ab8;
|
||||
}
|
1
static/adminlte/css/skins/skin-blue.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.skin-blue .main-header .navbar{background-color:#3c8dbc}.skin-blue .main-header .navbar .nav>li>a{color:#fff}.skin-blue .main-header .navbar .nav>li>a:hover,.skin-blue .main-header .navbar .nav>li>a:active,.skin-blue .main-header .navbar .nav>li>a:focus,.skin-blue .main-header .navbar .nav .open>a,.skin-blue .main-header .navbar .nav .open>a:hover,.skin-blue .main-header .navbar .nav .open>a:focus,.skin-blue .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-blue .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-blue .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-blue .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-blue .main-header .navbar .dropdown-menu li a{color:#fff}.skin-blue .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-blue .main-header .logo{background-color:#367fa9;color:#fff;border-bottom:0 solid transparent}.skin-blue .main-header .logo:hover{background-color:#357ca5}.skin-blue .main-header li.user-header{background-color:#3c8dbc}.skin-blue .content-header{background:transparent}.skin-blue .wrapper,.skin-blue .main-sidebar,.skin-blue .left-side{background-color:#222d32}.skin-blue .user-panel>.info,.skin-blue .user-panel>.info>a{color:#fff}.skin-blue .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-blue .sidebar-menu>li>a{border-left:3px solid transparent}.skin-blue .sidebar-menu>li:hover>a,.skin-blue .sidebar-menu>li.active>a,.skin-blue .sidebar-menu>li.menu-open>a{color:#fff;background:#1e282c}.skin-blue .sidebar-menu>li.active>a{border-left-color:#3c8dbc}.skin-blue .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-blue .sidebar a{color:#b8c7ce}.skin-blue .sidebar a:hover{text-decoration:none}.skin-blue .sidebar-menu .treeview-menu>li>a{color:#8aa4af}.skin-blue .sidebar-menu .treeview-menu>li.active>a,.skin-blue .sidebar-menu .treeview-menu>li>a:hover{color:#fff}.skin-blue .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-blue .sidebar-form input[type="text"],.skin-blue .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px}.skin-blue .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-blue .sidebar-form input[type="text"]:focus,.skin-blue .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-blue.layout-top-nav .main-header>.logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue.layout-top-nav .main-header>.logo:hover{background-color:#3b8ab8}
|
152
static/adminlte/css/skins/skin-green-light.css
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Skin: Green
|
||||
* -----------
|
||||
*/
|
||||
.skin-green-light .main-header .navbar {
|
||||
background-color: #00a65a;
|
||||
}
|
||||
.skin-green-light .main-header .navbar .nav > li > a {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-green-light .main-header .navbar .nav > li > a:hover,
|
||||
.skin-green-light .main-header .navbar .nav > li > a:active,
|
||||
.skin-green-light .main-header .navbar .nav > li > a:focus,
|
||||
.skin-green-light .main-header .navbar .nav .open > a,
|
||||
.skin-green-light .main-header .navbar .nav .open > a:hover,
|
||||
.skin-green-light .main-header .navbar .nav .open > a:focus,
|
||||
.skin-green-light .main-header .navbar .nav > .active > a {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
color: #f6f6f6;
|
||||
}
|
||||
.skin-green-light .main-header .navbar .sidebar-toggle {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-green-light .main-header .navbar .sidebar-toggle:hover {
|
||||
color: #f6f6f6;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.skin-green-light .main-header .navbar .sidebar-toggle {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-green-light .main-header .navbar .sidebar-toggle:hover {
|
||||
background-color: #008d4c;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.skin-green-light .main-header .navbar .dropdown-menu li.divider {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.skin-green-light .main-header .navbar .dropdown-menu li a {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-green-light .main-header .navbar .dropdown-menu li a:hover {
|
||||
background: #008d4c;
|
||||
}
|
||||
}
|
||||
.skin-green-light .main-header .logo {
|
||||
background-color: #00a65a;
|
||||
color: #ffffff;
|
||||
border-bottom: 0 solid transparent;
|
||||
}
|
||||
.skin-green-light .main-header .logo:hover {
|
||||
background-color: #00a157;
|
||||
}
|
||||
.skin-green-light .main-header li.user-header {
|
||||
background-color: #00a65a;
|
||||
}
|
||||
.skin-green-light .content-header {
|
||||
background: transparent;
|
||||
}
|
||||
.skin-green-light .wrapper,
|
||||
.skin-green-light .main-sidebar,
|
||||
.skin-green-light .left-side {
|
||||
background-color: #f9fafc;
|
||||
}
|
||||
.skin-green-light .main-sidebar {
|
||||
border-right: 1px solid #d2d6de;
|
||||
}
|
||||
.skin-green-light .user-panel > .info,
|
||||
.skin-green-light .user-panel > .info > a {
|
||||
color: #444444;
|
||||
}
|
||||
.skin-green-light .sidebar-menu > li {
|
||||
-webkit-transition: border-left-color 0.3s ease;
|
||||
-o-transition: border-left-color 0.3s ease;
|
||||
transition: border-left-color 0.3s ease;
|
||||
}
|
||||
.skin-green-light .sidebar-menu > li.header {
|
||||
color: #848484;
|
||||
background: #f9fafc;
|
||||
}
|
||||
.skin-green-light .sidebar-menu > li > a {
|
||||
border-left: 3px solid transparent;
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-green-light .sidebar-menu > li:hover > a,
|
||||
.skin-green-light .sidebar-menu > li.active > a {
|
||||
color: #000000;
|
||||
background: #f4f4f5;
|
||||
}
|
||||
.skin-green-light .sidebar-menu > li.active {
|
||||
border-left-color: #00a65a;
|
||||
}
|
||||
.skin-green-light .sidebar-menu > li.active > a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-green-light .sidebar-menu > li > .treeview-menu {
|
||||
background: #f4f4f5;
|
||||
}
|
||||
.skin-green-light .sidebar a {
|
||||
color: #444444;
|
||||
}
|
||||
.skin-green-light .sidebar a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.skin-green-light .sidebar-menu .treeview-menu > li > a {
|
||||
color: #777777;
|
||||
}
|
||||
.skin-green-light .sidebar-menu .treeview-menu > li.active > a,
|
||||
.skin-green-light .sidebar-menu .treeview-menu > li > a:hover {
|
||||
color: #000000;
|
||||
}
|
||||
.skin-green-light .sidebar-menu .treeview-menu > li.active > a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-green-light .sidebar-form {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #d2d6de;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
.skin-green-light .sidebar-form input[type="text"],
|
||||
.skin-green-light .sidebar-form .btn {
|
||||
box-shadow: none;
|
||||
background-color: #fff;
|
||||
border: 1px solid transparent;
|
||||
height: 35px;
|
||||
}
|
||||
.skin-green-light .sidebar-form input[type="text"] {
|
||||
color: #666;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
.skin-green-light .sidebar-form input[type="text"]:focus,
|
||||
.skin-green-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
background-color: #fff;
|
||||
color: #666;
|
||||
}
|
||||
.skin-green-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.skin-green-light .sidebar-form .btn {
|
||||
color: #999;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.skin-green-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
|
||||
border-left: 1px solid #d2d6de;
|
||||
}
|
||||
}
|
1
static/adminlte/css/skins/skin-green-light.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.skin-green-light .main-header .navbar{background-color:#00a65a}.skin-green-light .main-header .navbar .nav>li>a{color:#fff}.skin-green-light .main-header .navbar .nav>li>a:hover,.skin-green-light .main-header .navbar .nav>li>a:active,.skin-green-light .main-header .navbar .nav>li>a:focus,.skin-green-light .main-header .navbar .nav .open>a,.skin-green-light .main-header .navbar .nav .open>a:hover,.skin-green-light .main-header .navbar .nav .open>a:focus,.skin-green-light .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-green-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-green-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-green-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-green-light .main-header .navbar .sidebar-toggle:hover{background-color:#008d4c}@media (max-width:767px){.skin-green-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-green-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-green-light .main-header .navbar .dropdown-menu li a:hover{background:#008d4c}}.skin-green-light .main-header .logo{background-color:#00a65a;color:#fff;border-bottom:0 solid transparent}.skin-green-light .main-header .logo:hover{background-color:#00a157}.skin-green-light .main-header li.user-header{background-color:#00a65a}.skin-green-light .content-header{background:transparent}.skin-green-light .wrapper,.skin-green-light .main-sidebar,.skin-green-light .left-side{background-color:#f9fafc}.skin-green-light .main-sidebar{border-right:1px solid #d2d6de}.skin-green-light .user-panel>.info,.skin-green-light .user-panel>.info>a{color:#444}.skin-green-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-green-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-green-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-green-light .sidebar-menu>li:hover>a,.skin-green-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-green-light .sidebar-menu>li.active{border-left-color:#00a65a}.skin-green-light .sidebar-menu>li.active>a{font-weight:600}.skin-green-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-green-light .sidebar a{color:#444}.skin-green-light .sidebar a:hover{text-decoration:none}.skin-green-light .sidebar-menu .treeview-menu>li>a{color:#777}.skin-green-light .sidebar-menu .treeview-menu>li.active>a,.skin-green-light .sidebar-menu .treeview-menu>li>a:hover{color:#000}.skin-green-light .sidebar-menu .treeview-menu>li.active>a{font-weight:600}.skin-green-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-green-light .sidebar-form input[type="text"],.skin-green-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px}.skin-green-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-green-light .sidebar-form input[type="text"]:focus,.skin-green-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-green-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-green-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-green-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}
|
134
static/adminlte/css/skins/skin-green.css
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Skin: Green
|
||||
* -----------
|
||||
*/
|
||||
.skin-green .main-header .navbar {
|
||||
background-color: #00a65a;
|
||||
}
|
||||
.skin-green .main-header .navbar .nav > li > a {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-green .main-header .navbar .nav > li > a:hover,
|
||||
.skin-green .main-header .navbar .nav > li > a:active,
|
||||
.skin-green .main-header .navbar .nav > li > a:focus,
|
||||
.skin-green .main-header .navbar .nav .open > a,
|
||||
.skin-green .main-header .navbar .nav .open > a:hover,
|
||||
.skin-green .main-header .navbar .nav .open > a:focus,
|
||||
.skin-green .main-header .navbar .nav > .active > a {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
color: #f6f6f6;
|
||||
}
|
||||
.skin-green .main-header .navbar .sidebar-toggle {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-green .main-header .navbar .sidebar-toggle:hover {
|
||||
color: #f6f6f6;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.skin-green .main-header .navbar .sidebar-toggle {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-green .main-header .navbar .sidebar-toggle:hover {
|
||||
background-color: #008d4c;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.skin-green .main-header .navbar .dropdown-menu li.divider {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.skin-green .main-header .navbar .dropdown-menu li a {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-green .main-header .navbar .dropdown-menu li a:hover {
|
||||
background: #008d4c;
|
||||
}
|
||||
}
|
||||
.skin-green .main-header .logo {
|
||||
background-color: #008d4c;
|
||||
color: #ffffff;
|
||||
border-bottom: 0 solid transparent;
|
||||
}
|
||||
.skin-green .main-header .logo:hover {
|
||||
background-color: #008749;
|
||||
}
|
||||
.skin-green .main-header li.user-header {
|
||||
background-color: #00a65a;
|
||||
}
|
||||
.skin-green .content-header {
|
||||
background: transparent;
|
||||
}
|
||||
.skin-green .wrapper,
|
||||
.skin-green .main-sidebar,
|
||||
.skin-green .left-side {
|
||||
background-color: #222d32;
|
||||
}
|
||||
.skin-green .user-panel > .info,
|
||||
.skin-green .user-panel > .info > a {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-green .sidebar-menu > li.header {
|
||||
color: #4b646f;
|
||||
background: #1a2226;
|
||||
}
|
||||
.skin-green .sidebar-menu > li > a {
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
.skin-green .sidebar-menu > li:hover > a,
|
||||
.skin-green .sidebar-menu > li.active > a,
|
||||
.skin-green .sidebar-menu > li.menu-open > a {
|
||||
color: #ffffff;
|
||||
background: #1e282c;
|
||||
}
|
||||
.skin-green .sidebar-menu > li.active > a {
|
||||
border-left-color: #00a65a;
|
||||
}
|
||||
.skin-green .sidebar-menu > li > .treeview-menu {
|
||||
margin: 0 1px;
|
||||
background: #2c3b41;
|
||||
}
|
||||
.skin-green .sidebar a {
|
||||
color: #b8c7ce;
|
||||
}
|
||||
.skin-green .sidebar a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.skin-green .sidebar-menu .treeview-menu > li > a {
|
||||
color: #8aa4af;
|
||||
}
|
||||
.skin-green .sidebar-menu .treeview-menu > li.active > a,
|
||||
.skin-green .sidebar-menu .treeview-menu > li > a:hover {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-green .sidebar-form {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #374850;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
.skin-green .sidebar-form input[type="text"],
|
||||
.skin-green .sidebar-form .btn {
|
||||
box-shadow: none;
|
||||
background-color: #374850;
|
||||
border: 1px solid transparent;
|
||||
height: 35px;
|
||||
}
|
||||
.skin-green .sidebar-form input[type="text"] {
|
||||
color: #666;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
.skin-green .sidebar-form input[type="text"]:focus,
|
||||
.skin-green .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
background-color: #fff;
|
||||
color: #666;
|
||||
}
|
||||
.skin-green .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.skin-green .sidebar-form .btn {
|
||||
color: #999;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
1
static/adminlte/css/skins/skin-green.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.skin-green .main-header .navbar{background-color:#00a65a}.skin-green .main-header .navbar .nav>li>a{color:#fff}.skin-green .main-header .navbar .nav>li>a:hover,.skin-green .main-header .navbar .nav>li>a:active,.skin-green .main-header .navbar .nav>li>a:focus,.skin-green .main-header .navbar .nav .open>a,.skin-green .main-header .navbar .nav .open>a:hover,.skin-green .main-header .navbar .nav .open>a:focus,.skin-green .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-green .main-header .navbar .sidebar-toggle{color:#fff}.skin-green .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-green .main-header .navbar .sidebar-toggle{color:#fff}.skin-green .main-header .navbar .sidebar-toggle:hover{background-color:#008d4c}@media (max-width:767px){.skin-green .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-green .main-header .navbar .dropdown-menu li a{color:#fff}.skin-green .main-header .navbar .dropdown-menu li a:hover{background:#008d4c}}.skin-green .main-header .logo{background-color:#008d4c;color:#fff;border-bottom:0 solid transparent}.skin-green .main-header .logo:hover{background-color:#008749}.skin-green .main-header li.user-header{background-color:#00a65a}.skin-green .content-header{background:transparent}.skin-green .wrapper,.skin-green .main-sidebar,.skin-green .left-side{background-color:#222d32}.skin-green .user-panel>.info,.skin-green .user-panel>.info>a{color:#fff}.skin-green .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-green .sidebar-menu>li>a{border-left:3px solid transparent}.skin-green .sidebar-menu>li:hover>a,.skin-green .sidebar-menu>li.active>a,.skin-green .sidebar-menu>li.menu-open>a{color:#fff;background:#1e282c}.skin-green .sidebar-menu>li.active>a{border-left-color:#00a65a}.skin-green .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-green .sidebar a{color:#b8c7ce}.skin-green .sidebar a:hover{text-decoration:none}.skin-green .sidebar-menu .treeview-menu>li>a{color:#8aa4af}.skin-green .sidebar-menu .treeview-menu>li.active>a,.skin-green .sidebar-menu .treeview-menu>li>a:hover{color:#fff}.skin-green .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-green .sidebar-form input[type="text"],.skin-green .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px}.skin-green .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-green .sidebar-form input[type="text"]:focus,.skin-green .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-green .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-green .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}
|
152
static/adminlte/css/skins/skin-purple-light.css
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Skin: Purple
|
||||
* ------------
|
||||
*/
|
||||
.skin-purple-light .main-header .navbar {
|
||||
background-color: #605ca8;
|
||||
}
|
||||
.skin-purple-light .main-header .navbar .nav > li > a {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-purple-light .main-header .navbar .nav > li > a:hover,
|
||||
.skin-purple-light .main-header .navbar .nav > li > a:active,
|
||||
.skin-purple-light .main-header .navbar .nav > li > a:focus,
|
||||
.skin-purple-light .main-header .navbar .nav .open > a,
|
||||
.skin-purple-light .main-header .navbar .nav .open > a:hover,
|
||||
.skin-purple-light .main-header .navbar .nav .open > a:focus,
|
||||
.skin-purple-light .main-header .navbar .nav > .active > a {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
color: #f6f6f6;
|
||||
}
|
||||
.skin-purple-light .main-header .navbar .sidebar-toggle {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-purple-light .main-header .navbar .sidebar-toggle:hover {
|
||||
color: #f6f6f6;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.skin-purple-light .main-header .navbar .sidebar-toggle {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-purple-light .main-header .navbar .sidebar-toggle:hover {
|
||||
background-color: #555299;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.skin-purple-light .main-header .navbar .dropdown-menu li.divider {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.skin-purple-light .main-header .navbar .dropdown-menu li a {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-purple-light .main-header .navbar .dropdown-menu li a:hover {
|
||||
background: #555299;
|
||||
}
|
||||
}
|
||||
.skin-purple-light .main-header .logo {
|
||||
background-color: #605ca8;
|
||||
color: #ffffff;
|
||||
border-bottom: 0 solid transparent;
|
||||
}
|
||||
.skin-purple-light .main-header .logo:hover {
|
||||
background-color: #5d59a6;
|
||||
}
|
||||
.skin-purple-light .main-header li.user-header {
|
||||
background-color: #605ca8;
|
||||
}
|
||||
.skin-purple-light .content-header {
|
||||
background: transparent;
|
||||
}
|
||||
.skin-purple-light .wrapper,
|
||||
.skin-purple-light .main-sidebar,
|
||||
.skin-purple-light .left-side {
|
||||
background-color: #f9fafc;
|
||||
}
|
||||
.skin-purple-light .main-sidebar {
|
||||
border-right: 1px solid #d2d6de;
|
||||
}
|
||||
.skin-purple-light .user-panel > .info,
|
||||
.skin-purple-light .user-panel > .info > a {
|
||||
color: #444444;
|
||||
}
|
||||
.skin-purple-light .sidebar-menu > li {
|
||||
-webkit-transition: border-left-color 0.3s ease;
|
||||
-o-transition: border-left-color 0.3s ease;
|
||||
transition: border-left-color 0.3s ease;
|
||||
}
|
||||
.skin-purple-light .sidebar-menu > li.header {
|
||||
color: #848484;
|
||||
background: #f9fafc;
|
||||
}
|
||||
.skin-purple-light .sidebar-menu > li > a {
|
||||
border-left: 3px solid transparent;
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-purple-light .sidebar-menu > li:hover > a,
|
||||
.skin-purple-light .sidebar-menu > li.active > a {
|
||||
color: #000000;
|
||||
background: #f4f4f5;
|
||||
}
|
||||
.skin-purple-light .sidebar-menu > li.active {
|
||||
border-left-color: #605ca8;
|
||||
}
|
||||
.skin-purple-light .sidebar-menu > li.active > a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-purple-light .sidebar-menu > li > .treeview-menu {
|
||||
background: #f4f4f5;
|
||||
}
|
||||
.skin-purple-light .sidebar a {
|
||||
color: #444444;
|
||||
}
|
||||
.skin-purple-light .sidebar a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.skin-purple-light .sidebar-menu .treeview-menu > li > a {
|
||||
color: #777777;
|
||||
}
|
||||
.skin-purple-light .sidebar-menu .treeview-menu > li.active > a,
|
||||
.skin-purple-light .sidebar-menu .treeview-menu > li > a:hover {
|
||||
color: #000000;
|
||||
}
|
||||
.skin-purple-light .sidebar-menu .treeview-menu > li.active > a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-purple-light .sidebar-form {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #d2d6de;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
.skin-purple-light .sidebar-form input[type="text"],
|
||||
.skin-purple-light .sidebar-form .btn {
|
||||
box-shadow: none;
|
||||
background-color: #fff;
|
||||
border: 1px solid transparent;
|
||||
height: 35px;
|
||||
}
|
||||
.skin-purple-light .sidebar-form input[type="text"] {
|
||||
color: #666;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
.skin-purple-light .sidebar-form input[type="text"]:focus,
|
||||
.skin-purple-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
background-color: #fff;
|
||||
color: #666;
|
||||
}
|
||||
.skin-purple-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.skin-purple-light .sidebar-form .btn {
|
||||
color: #999;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.skin-purple-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
|
||||
border-left: 1px solid #d2d6de;
|
||||
}
|
||||
}
|
1
static/adminlte/css/skins/skin-purple-light.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.skin-purple-light .main-header .navbar{background-color:#605ca8}.skin-purple-light .main-header .navbar .nav>li>a{color:#fff}.skin-purple-light .main-header .navbar .nav>li>a:hover,.skin-purple-light .main-header .navbar .nav>li>a:active,.skin-purple-light .main-header .navbar .nav>li>a:focus,.skin-purple-light .main-header .navbar .nav .open>a,.skin-purple-light .main-header .navbar .nav .open>a:hover,.skin-purple-light .main-header .navbar .nav .open>a:focus,.skin-purple-light .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-purple-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-purple-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-purple-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-purple-light .main-header .navbar .sidebar-toggle:hover{background-color:#555299}@media (max-width:767px){.skin-purple-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-purple-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-purple-light .main-header .navbar .dropdown-menu li a:hover{background:#555299}}.skin-purple-light .main-header .logo{background-color:#605ca8;color:#fff;border-bottom:0 solid transparent}.skin-purple-light .main-header .logo:hover{background-color:#5d59a6}.skin-purple-light .main-header li.user-header{background-color:#605ca8}.skin-purple-light .content-header{background:transparent}.skin-purple-light .wrapper,.skin-purple-light .main-sidebar,.skin-purple-light .left-side{background-color:#f9fafc}.skin-purple-light .main-sidebar{border-right:1px solid #d2d6de}.skin-purple-light .user-panel>.info,.skin-purple-light .user-panel>.info>a{color:#444}.skin-purple-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-purple-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-purple-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-purple-light .sidebar-menu>li:hover>a,.skin-purple-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-purple-light .sidebar-menu>li.active{border-left-color:#605ca8}.skin-purple-light .sidebar-menu>li.active>a{font-weight:600}.skin-purple-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-purple-light .sidebar a{color:#444}.skin-purple-light .sidebar a:hover{text-decoration:none}.skin-purple-light .sidebar-menu .treeview-menu>li>a{color:#777}.skin-purple-light .sidebar-menu .treeview-menu>li.active>a,.skin-purple-light .sidebar-menu .treeview-menu>li>a:hover{color:#000}.skin-purple-light .sidebar-menu .treeview-menu>li.active>a{font-weight:600}.skin-purple-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-purple-light .sidebar-form input[type="text"],.skin-purple-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px}.skin-purple-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-purple-light .sidebar-form input[type="text"]:focus,.skin-purple-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-purple-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-purple-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-purple-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}
|
134
static/adminlte/css/skins/skin-purple.css
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Skin: Purple
|
||||
* ------------
|
||||
*/
|
||||
.skin-purple .main-header .navbar {
|
||||
background-color: #605ca8;
|
||||
}
|
||||
.skin-purple .main-header .navbar .nav > li > a {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-purple .main-header .navbar .nav > li > a:hover,
|
||||
.skin-purple .main-header .navbar .nav > li > a:active,
|
||||
.skin-purple .main-header .navbar .nav > li > a:focus,
|
||||
.skin-purple .main-header .navbar .nav .open > a,
|
||||
.skin-purple .main-header .navbar .nav .open > a:hover,
|
||||
.skin-purple .main-header .navbar .nav .open > a:focus,
|
||||
.skin-purple .main-header .navbar .nav > .active > a {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
color: #f6f6f6;
|
||||
}
|
||||
.skin-purple .main-header .navbar .sidebar-toggle {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-purple .main-header .navbar .sidebar-toggle:hover {
|
||||
color: #f6f6f6;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.skin-purple .main-header .navbar .sidebar-toggle {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-purple .main-header .navbar .sidebar-toggle:hover {
|
||||
background-color: #555299;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.skin-purple .main-header .navbar .dropdown-menu li.divider {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.skin-purple .main-header .navbar .dropdown-menu li a {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-purple .main-header .navbar .dropdown-menu li a:hover {
|
||||
background: #555299;
|
||||
}
|
||||
}
|
||||
.skin-purple .main-header .logo {
|
||||
background-color: #555299;
|
||||
color: #ffffff;
|
||||
border-bottom: 0 solid transparent;
|
||||
}
|
||||
.skin-purple .main-header .logo:hover {
|
||||
background-color: #545096;
|
||||
}
|
||||
.skin-purple .main-header li.user-header {
|
||||
background-color: #605ca8;
|
||||
}
|
||||
.skin-purple .content-header {
|
||||
background: transparent;
|
||||
}
|
||||
.skin-purple .wrapper,
|
||||
.skin-purple .main-sidebar,
|
||||
.skin-purple .left-side {
|
||||
background-color: #222d32;
|
||||
}
|
||||
.skin-purple .user-panel > .info,
|
||||
.skin-purple .user-panel > .info > a {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-purple .sidebar-menu > li.header {
|
||||
color: #4b646f;
|
||||
background: #1a2226;
|
||||
}
|
||||
.skin-purple .sidebar-menu > li > a {
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
.skin-purple .sidebar-menu > li:hover > a,
|
||||
.skin-purple .sidebar-menu > li.active > a,
|
||||
.skin-purple .sidebar-menu > li.menu-open > a {
|
||||
color: #ffffff;
|
||||
background: #1e282c;
|
||||
}
|
||||
.skin-purple .sidebar-menu > li.active > a {
|
||||
border-left-color: #605ca8;
|
||||
}
|
||||
.skin-purple .sidebar-menu > li > .treeview-menu {
|
||||
margin: 0 1px;
|
||||
background: #2c3b41;
|
||||
}
|
||||
.skin-purple .sidebar a {
|
||||
color: #b8c7ce;
|
||||
}
|
||||
.skin-purple .sidebar a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.skin-purple .sidebar-menu .treeview-menu > li > a {
|
||||
color: #8aa4af;
|
||||
}
|
||||
.skin-purple .sidebar-menu .treeview-menu > li.active > a,
|
||||
.skin-purple .sidebar-menu .treeview-menu > li > a:hover {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-purple .sidebar-form {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #374850;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
.skin-purple .sidebar-form input[type="text"],
|
||||
.skin-purple .sidebar-form .btn {
|
||||
box-shadow: none;
|
||||
background-color: #374850;
|
||||
border: 1px solid transparent;
|
||||
height: 35px;
|
||||
}
|
||||
.skin-purple .sidebar-form input[type="text"] {
|
||||
color: #666;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
.skin-purple .sidebar-form input[type="text"]:focus,
|
||||
.skin-purple .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
background-color: #fff;
|
||||
color: #666;
|
||||
}
|
||||
.skin-purple .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.skin-purple .sidebar-form .btn {
|
||||
color: #999;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
1
static/adminlte/css/skins/skin-purple.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.skin-purple .main-header .navbar{background-color:#605ca8}.skin-purple .main-header .navbar .nav>li>a{color:#fff}.skin-purple .main-header .navbar .nav>li>a:hover,.skin-purple .main-header .navbar .nav>li>a:active,.skin-purple .main-header .navbar .nav>li>a:focus,.skin-purple .main-header .navbar .nav .open>a,.skin-purple .main-header .navbar .nav .open>a:hover,.skin-purple .main-header .navbar .nav .open>a:focus,.skin-purple .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-purple .main-header .navbar .sidebar-toggle{color:#fff}.skin-purple .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-purple .main-header .navbar .sidebar-toggle{color:#fff}.skin-purple .main-header .navbar .sidebar-toggle:hover{background-color:#555299}@media (max-width:767px){.skin-purple .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-purple .main-header .navbar .dropdown-menu li a{color:#fff}.skin-purple .main-header .navbar .dropdown-menu li a:hover{background:#555299}}.skin-purple .main-header .logo{background-color:#555299;color:#fff;border-bottom:0 solid transparent}.skin-purple .main-header .logo:hover{background-color:#545096}.skin-purple .main-header li.user-header{background-color:#605ca8}.skin-purple .content-header{background:transparent}.skin-purple .wrapper,.skin-purple .main-sidebar,.skin-purple .left-side{background-color:#222d32}.skin-purple .user-panel>.info,.skin-purple .user-panel>.info>a{color:#fff}.skin-purple .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-purple .sidebar-menu>li>a{border-left:3px solid transparent}.skin-purple .sidebar-menu>li:hover>a,.skin-purple .sidebar-menu>li.active>a,.skin-purple .sidebar-menu>li.menu-open>a{color:#fff;background:#1e282c}.skin-purple .sidebar-menu>li.active>a{border-left-color:#605ca8}.skin-purple .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-purple .sidebar a{color:#b8c7ce}.skin-purple .sidebar a:hover{text-decoration:none}.skin-purple .sidebar-menu .treeview-menu>li>a{color:#8aa4af}.skin-purple .sidebar-menu .treeview-menu>li.active>a,.skin-purple .sidebar-menu .treeview-menu>li>a:hover{color:#fff}.skin-purple .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-purple .sidebar-form input[type="text"],.skin-purple .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px}.skin-purple .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-purple .sidebar-form input[type="text"]:focus,.skin-purple .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-purple .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-purple .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}
|
152
static/adminlte/css/skins/skin-red-light.css
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Skin: Red
|
||||
* ---------
|
||||
*/
|
||||
.skin-red-light .main-header .navbar {
|
||||
background-color: #dd4b39;
|
||||
}
|
||||
.skin-red-light .main-header .navbar .nav > li > a {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-red-light .main-header .navbar .nav > li > a:hover,
|
||||
.skin-red-light .main-header .navbar .nav > li > a:active,
|
||||
.skin-red-light .main-header .navbar .nav > li > a:focus,
|
||||
.skin-red-light .main-header .navbar .nav .open > a,
|
||||
.skin-red-light .main-header .navbar .nav .open > a:hover,
|
||||
.skin-red-light .main-header .navbar .nav .open > a:focus,
|
||||
.skin-red-light .main-header .navbar .nav > .active > a {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
color: #f6f6f6;
|
||||
}
|
||||
.skin-red-light .main-header .navbar .sidebar-toggle {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-red-light .main-header .navbar .sidebar-toggle:hover {
|
||||
color: #f6f6f6;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.skin-red-light .main-header .navbar .sidebar-toggle {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-red-light .main-header .navbar .sidebar-toggle:hover {
|
||||
background-color: #d73925;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.skin-red-light .main-header .navbar .dropdown-menu li.divider {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.skin-red-light .main-header .navbar .dropdown-menu li a {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-red-light .main-header .navbar .dropdown-menu li a:hover {
|
||||
background: #d73925;
|
||||
}
|
||||
}
|
||||
.skin-red-light .main-header .logo {
|
||||
background-color: #dd4b39;
|
||||
color: #ffffff;
|
||||
border-bottom: 0 solid transparent;
|
||||
}
|
||||
.skin-red-light .main-header .logo:hover {
|
||||
background-color: #dc4735;
|
||||
}
|
||||
.skin-red-light .main-header li.user-header {
|
||||
background-color: #dd4b39;
|
||||
}
|
||||
.skin-red-light .content-header {
|
||||
background: transparent;
|
||||
}
|
||||
.skin-red-light .wrapper,
|
||||
.skin-red-light .main-sidebar,
|
||||
.skin-red-light .left-side {
|
||||
background-color: #f9fafc;
|
||||
}
|
||||
.skin-red-light .main-sidebar {
|
||||
border-right: 1px solid #d2d6de;
|
||||
}
|
||||
.skin-red-light .user-panel > .info,
|
||||
.skin-red-light .user-panel > .info > a {
|
||||
color: #444444;
|
||||
}
|
||||
.skin-red-light .sidebar-menu > li {
|
||||
-webkit-transition: border-left-color 0.3s ease;
|
||||
-o-transition: border-left-color 0.3s ease;
|
||||
transition: border-left-color 0.3s ease;
|
||||
}
|
||||
.skin-red-light .sidebar-menu > li.header {
|
||||
color: #848484;
|
||||
background: #f9fafc;
|
||||
}
|
||||
.skin-red-light .sidebar-menu > li > a {
|
||||
border-left: 3px solid transparent;
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-red-light .sidebar-menu > li:hover > a,
|
||||
.skin-red-light .sidebar-menu > li.active > a {
|
||||
color: #000000;
|
||||
background: #f4f4f5;
|
||||
}
|
||||
.skin-red-light .sidebar-menu > li.active {
|
||||
border-left-color: #dd4b39;
|
||||
}
|
||||
.skin-red-light .sidebar-menu > li.active > a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-red-light .sidebar-menu > li > .treeview-menu {
|
||||
background: #f4f4f5;
|
||||
}
|
||||
.skin-red-light .sidebar a {
|
||||
color: #444444;
|
||||
}
|
||||
.skin-red-light .sidebar a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.skin-red-light .sidebar-menu .treeview-menu > li > a {
|
||||
color: #777777;
|
||||
}
|
||||
.skin-red-light .sidebar-menu .treeview-menu > li.active > a,
|
||||
.skin-red-light .sidebar-menu .treeview-menu > li > a:hover {
|
||||
color: #000000;
|
||||
}
|
||||
.skin-red-light .sidebar-menu .treeview-menu > li.active > a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-red-light .sidebar-form {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #d2d6de;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
.skin-red-light .sidebar-form input[type="text"],
|
||||
.skin-red-light .sidebar-form .btn {
|
||||
box-shadow: none;
|
||||
background-color: #fff;
|
||||
border: 1px solid transparent;
|
||||
height: 35px;
|
||||
}
|
||||
.skin-red-light .sidebar-form input[type="text"] {
|
||||
color: #666;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
.skin-red-light .sidebar-form input[type="text"]:focus,
|
||||
.skin-red-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
background-color: #fff;
|
||||
color: #666;
|
||||
}
|
||||
.skin-red-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.skin-red-light .sidebar-form .btn {
|
||||
color: #999;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.skin-red-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
|
||||
border-left: 1px solid #d2d6de;
|
||||
}
|
||||
}
|
1
static/adminlte/css/skins/skin-red-light.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.skin-red-light .main-header .navbar{background-color:#dd4b39}.skin-red-light .main-header .navbar .nav>li>a{color:#fff}.skin-red-light .main-header .navbar .nav>li>a:hover,.skin-red-light .main-header .navbar .nav>li>a:active,.skin-red-light .main-header .navbar .nav>li>a:focus,.skin-red-light .main-header .navbar .nav .open>a,.skin-red-light .main-header .navbar .nav .open>a:hover,.skin-red-light .main-header .navbar .nav .open>a:focus,.skin-red-light .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-red-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-red-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-red-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-red-light .main-header .navbar .sidebar-toggle:hover{background-color:#d73925}@media (max-width:767px){.skin-red-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-red-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-red-light .main-header .navbar .dropdown-menu li a:hover{background:#d73925}}.skin-red-light .main-header .logo{background-color:#dd4b39;color:#fff;border-bottom:0 solid transparent}.skin-red-light .main-header .logo:hover{background-color:#dc4735}.skin-red-light .main-header li.user-header{background-color:#dd4b39}.skin-red-light .content-header{background:transparent}.skin-red-light .wrapper,.skin-red-light .main-sidebar,.skin-red-light .left-side{background-color:#f9fafc}.skin-red-light .main-sidebar{border-right:1px solid #d2d6de}.skin-red-light .user-panel>.info,.skin-red-light .user-panel>.info>a{color:#444}.skin-red-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-red-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-red-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-red-light .sidebar-menu>li:hover>a,.skin-red-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-red-light .sidebar-menu>li.active{border-left-color:#dd4b39}.skin-red-light .sidebar-menu>li.active>a{font-weight:600}.skin-red-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-red-light .sidebar a{color:#444}.skin-red-light .sidebar a:hover{text-decoration:none}.skin-red-light .sidebar-menu .treeview-menu>li>a{color:#777}.skin-red-light .sidebar-menu .treeview-menu>li.active>a,.skin-red-light .sidebar-menu .treeview-menu>li>a:hover{color:#000}.skin-red-light .sidebar-menu .treeview-menu>li.active>a{font-weight:600}.skin-red-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-red-light .sidebar-form input[type="text"],.skin-red-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px}.skin-red-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-red-light .sidebar-form input[type="text"]:focus,.skin-red-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-red-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-red-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-red-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}
|
134
static/adminlte/css/skins/skin-red.css
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Skin: Red
|
||||
* ---------
|
||||
*/
|
||||
.skin-red .main-header .navbar {
|
||||
background-color: #dd4b39;
|
||||
}
|
||||
.skin-red .main-header .navbar .nav > li > a {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-red .main-header .navbar .nav > li > a:hover,
|
||||
.skin-red .main-header .navbar .nav > li > a:active,
|
||||
.skin-red .main-header .navbar .nav > li > a:focus,
|
||||
.skin-red .main-header .navbar .nav .open > a,
|
||||
.skin-red .main-header .navbar .nav .open > a:hover,
|
||||
.skin-red .main-header .navbar .nav .open > a:focus,
|
||||
.skin-red .main-header .navbar .nav > .active > a {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
color: #f6f6f6;
|
||||
}
|
||||
.skin-red .main-header .navbar .sidebar-toggle {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-red .main-header .navbar .sidebar-toggle:hover {
|
||||
color: #f6f6f6;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.skin-red .main-header .navbar .sidebar-toggle {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-red .main-header .navbar .sidebar-toggle:hover {
|
||||
background-color: #d73925;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.skin-red .main-header .navbar .dropdown-menu li.divider {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.skin-red .main-header .navbar .dropdown-menu li a {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-red .main-header .navbar .dropdown-menu li a:hover {
|
||||
background: #d73925;
|
||||
}
|
||||
}
|
||||
.skin-red .main-header .logo {
|
||||
background-color: #d73925;
|
||||
color: #ffffff;
|
||||
border-bottom: 0 solid transparent;
|
||||
}
|
||||
.skin-red .main-header .logo:hover {
|
||||
background-color: #d33724;
|
||||
}
|
||||
.skin-red .main-header li.user-header {
|
||||
background-color: #dd4b39;
|
||||
}
|
||||
.skin-red .content-header {
|
||||
background: transparent;
|
||||
}
|
||||
.skin-red .wrapper,
|
||||
.skin-red .main-sidebar,
|
||||
.skin-red .left-side {
|
||||
background-color: #222d32;
|
||||
}
|
||||
.skin-red .user-panel > .info,
|
||||
.skin-red .user-panel > .info > a {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-red .sidebar-menu > li.header {
|
||||
color: #4b646f;
|
||||
background: #1a2226;
|
||||
}
|
||||
.skin-red .sidebar-menu > li > a {
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
.skin-red .sidebar-menu > li:hover > a,
|
||||
.skin-red .sidebar-menu > li.active > a,
|
||||
.skin-red .sidebar-menu > li.menu-open > a {
|
||||
color: #ffffff;
|
||||
background: #1e282c;
|
||||
}
|
||||
.skin-red .sidebar-menu > li.active > a {
|
||||
border-left-color: #dd4b39;
|
||||
}
|
||||
.skin-red .sidebar-menu > li > .treeview-menu {
|
||||
margin: 0 1px;
|
||||
background: #2c3b41;
|
||||
}
|
||||
.skin-red .sidebar a {
|
||||
color: #b8c7ce;
|
||||
}
|
||||
.skin-red .sidebar a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.skin-red .sidebar-menu .treeview-menu > li > a {
|
||||
color: #8aa4af;
|
||||
}
|
||||
.skin-red .sidebar-menu .treeview-menu > li.active > a,
|
||||
.skin-red .sidebar-menu .treeview-menu > li > a:hover {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-red .sidebar-form {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #374850;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
.skin-red .sidebar-form input[type="text"],
|
||||
.skin-red .sidebar-form .btn {
|
||||
box-shadow: none;
|
||||
background-color: #374850;
|
||||
border: 1px solid transparent;
|
||||
height: 35px;
|
||||
}
|
||||
.skin-red .sidebar-form input[type="text"] {
|
||||
color: #666;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
.skin-red .sidebar-form input[type="text"]:focus,
|
||||
.skin-red .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
background-color: #fff;
|
||||
color: #666;
|
||||
}
|
||||
.skin-red .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.skin-red .sidebar-form .btn {
|
||||
color: #999;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
1
static/adminlte/css/skins/skin-red.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.skin-red .main-header .navbar{background-color:#dd4b39}.skin-red .main-header .navbar .nav>li>a{color:#fff}.skin-red .main-header .navbar .nav>li>a:hover,.skin-red .main-header .navbar .nav>li>a:active,.skin-red .main-header .navbar .nav>li>a:focus,.skin-red .main-header .navbar .nav .open>a,.skin-red .main-header .navbar .nav .open>a:hover,.skin-red .main-header .navbar .nav .open>a:focus,.skin-red .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-red .main-header .navbar .sidebar-toggle{color:#fff}.skin-red .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-red .main-header .navbar .sidebar-toggle{color:#fff}.skin-red .main-header .navbar .sidebar-toggle:hover{background-color:#d73925}@media (max-width:767px){.skin-red .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-red .main-header .navbar .dropdown-menu li a{color:#fff}.skin-red .main-header .navbar .dropdown-menu li a:hover{background:#d73925}}.skin-red .main-header .logo{background-color:#d73925;color:#fff;border-bottom:0 solid transparent}.skin-red .main-header .logo:hover{background-color:#d33724}.skin-red .main-header li.user-header{background-color:#dd4b39}.skin-red .content-header{background:transparent}.skin-red .wrapper,.skin-red .main-sidebar,.skin-red .left-side{background-color:#222d32}.skin-red .user-panel>.info,.skin-red .user-panel>.info>a{color:#fff}.skin-red .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-red .sidebar-menu>li>a{border-left:3px solid transparent}.skin-red .sidebar-menu>li:hover>a,.skin-red .sidebar-menu>li.active>a,.skin-red .sidebar-menu>li.menu-open>a{color:#fff;background:#1e282c}.skin-red .sidebar-menu>li.active>a{border-left-color:#dd4b39}.skin-red .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-red .sidebar a{color:#b8c7ce}.skin-red .sidebar a:hover{text-decoration:none}.skin-red .sidebar-menu .treeview-menu>li>a{color:#8aa4af}.skin-red .sidebar-menu .treeview-menu>li.active>a,.skin-red .sidebar-menu .treeview-menu>li>a:hover{color:#fff}.skin-red .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-red .sidebar-form input[type="text"],.skin-red .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px}.skin-red .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-red .sidebar-form input[type="text"]:focus,.skin-red .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-red .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-red .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}
|
152
static/adminlte/css/skins/skin-yellow-light.css
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Skin: Yellow
|
||||
* ------------
|
||||
*/
|
||||
.skin-yellow-light .main-header .navbar {
|
||||
background-color: #f39c12;
|
||||
}
|
||||
.skin-yellow-light .main-header .navbar .nav > li > a {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-yellow-light .main-header .navbar .nav > li > a:hover,
|
||||
.skin-yellow-light .main-header .navbar .nav > li > a:active,
|
||||
.skin-yellow-light .main-header .navbar .nav > li > a:focus,
|
||||
.skin-yellow-light .main-header .navbar .nav .open > a,
|
||||
.skin-yellow-light .main-header .navbar .nav .open > a:hover,
|
||||
.skin-yellow-light .main-header .navbar .nav .open > a:focus,
|
||||
.skin-yellow-light .main-header .navbar .nav > .active > a {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
color: #f6f6f6;
|
||||
}
|
||||
.skin-yellow-light .main-header .navbar .sidebar-toggle {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-yellow-light .main-header .navbar .sidebar-toggle:hover {
|
||||
color: #f6f6f6;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.skin-yellow-light .main-header .navbar .sidebar-toggle {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-yellow-light .main-header .navbar .sidebar-toggle:hover {
|
||||
background-color: #e08e0b;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.skin-yellow-light .main-header .navbar .dropdown-menu li.divider {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.skin-yellow-light .main-header .navbar .dropdown-menu li a {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-yellow-light .main-header .navbar .dropdown-menu li a:hover {
|
||||
background: #e08e0b;
|
||||
}
|
||||
}
|
||||
.skin-yellow-light .main-header .logo {
|
||||
background-color: #f39c12;
|
||||
color: #ffffff;
|
||||
border-bottom: 0 solid transparent;
|
||||
}
|
||||
.skin-yellow-light .main-header .logo:hover {
|
||||
background-color: #f39a0d;
|
||||
}
|
||||
.skin-yellow-light .main-header li.user-header {
|
||||
background-color: #f39c12;
|
||||
}
|
||||
.skin-yellow-light .content-header {
|
||||
background: transparent;
|
||||
}
|
||||
.skin-yellow-light .wrapper,
|
||||
.skin-yellow-light .main-sidebar,
|
||||
.skin-yellow-light .left-side {
|
||||
background-color: #f9fafc;
|
||||
}
|
||||
.skin-yellow-light .main-sidebar {
|
||||
border-right: 1px solid #d2d6de;
|
||||
}
|
||||
.skin-yellow-light .user-panel > .info,
|
||||
.skin-yellow-light .user-panel > .info > a {
|
||||
color: #444444;
|
||||
}
|
||||
.skin-yellow-light .sidebar-menu > li {
|
||||
-webkit-transition: border-left-color 0.3s ease;
|
||||
-o-transition: border-left-color 0.3s ease;
|
||||
transition: border-left-color 0.3s ease;
|
||||
}
|
||||
.skin-yellow-light .sidebar-menu > li.header {
|
||||
color: #848484;
|
||||
background: #f9fafc;
|
||||
}
|
||||
.skin-yellow-light .sidebar-menu > li > a {
|
||||
border-left: 3px solid transparent;
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-yellow-light .sidebar-menu > li:hover > a,
|
||||
.skin-yellow-light .sidebar-menu > li.active > a {
|
||||
color: #000000;
|
||||
background: #f4f4f5;
|
||||
}
|
||||
.skin-yellow-light .sidebar-menu > li.active {
|
||||
border-left-color: #f39c12;
|
||||
}
|
||||
.skin-yellow-light .sidebar-menu > li.active > a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-yellow-light .sidebar-menu > li > .treeview-menu {
|
||||
background: #f4f4f5;
|
||||
}
|
||||
.skin-yellow-light .sidebar a {
|
||||
color: #444444;
|
||||
}
|
||||
.skin-yellow-light .sidebar a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.skin-yellow-light .sidebar-menu .treeview-menu > li > a {
|
||||
color: #777777;
|
||||
}
|
||||
.skin-yellow-light .sidebar-menu .treeview-menu > li.active > a,
|
||||
.skin-yellow-light .sidebar-menu .treeview-menu > li > a:hover {
|
||||
color: #000000;
|
||||
}
|
||||
.skin-yellow-light .sidebar-menu .treeview-menu > li.active > a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.skin-yellow-light .sidebar-form {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #d2d6de;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
.skin-yellow-light .sidebar-form input[type="text"],
|
||||
.skin-yellow-light .sidebar-form .btn {
|
||||
box-shadow: none;
|
||||
background-color: #fff;
|
||||
border: 1px solid transparent;
|
||||
height: 35px;
|
||||
}
|
||||
.skin-yellow-light .sidebar-form input[type="text"] {
|
||||
color: #666;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
.skin-yellow-light .sidebar-form input[type="text"]:focus,
|
||||
.skin-yellow-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
background-color: #fff;
|
||||
color: #666;
|
||||
}
|
||||
.skin-yellow-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.skin-yellow-light .sidebar-form .btn {
|
||||
color: #999;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.skin-yellow-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
|
||||
border-left: 1px solid #d2d6de;
|
||||
}
|
||||
}
|
1
static/adminlte/css/skins/skin-yellow-light.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.skin-yellow-light .main-header .navbar{background-color:#f39c12}.skin-yellow-light .main-header .navbar .nav>li>a{color:#fff}.skin-yellow-light .main-header .navbar .nav>li>a:hover,.skin-yellow-light .main-header .navbar .nav>li>a:active,.skin-yellow-light .main-header .navbar .nav>li>a:focus,.skin-yellow-light .main-header .navbar .nav .open>a,.skin-yellow-light .main-header .navbar .nav .open>a:hover,.skin-yellow-light .main-header .navbar .nav .open>a:focus,.skin-yellow-light .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-yellow-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-yellow-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-yellow-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-yellow-light .main-header .navbar .sidebar-toggle:hover{background-color:#e08e0b}@media (max-width:767px){.skin-yellow-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-yellow-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-yellow-light .main-header .navbar .dropdown-menu li a:hover{background:#e08e0b}}.skin-yellow-light .main-header .logo{background-color:#f39c12;color:#fff;border-bottom:0 solid transparent}.skin-yellow-light .main-header .logo:hover{background-color:#f39a0d}.skin-yellow-light .main-header li.user-header{background-color:#f39c12}.skin-yellow-light .content-header{background:transparent}.skin-yellow-light .wrapper,.skin-yellow-light .main-sidebar,.skin-yellow-light .left-side{background-color:#f9fafc}.skin-yellow-light .main-sidebar{border-right:1px solid #d2d6de}.skin-yellow-light .user-panel>.info,.skin-yellow-light .user-panel>.info>a{color:#444}.skin-yellow-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-yellow-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-yellow-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-yellow-light .sidebar-menu>li:hover>a,.skin-yellow-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-yellow-light .sidebar-menu>li.active{border-left-color:#f39c12}.skin-yellow-light .sidebar-menu>li.active>a{font-weight:600}.skin-yellow-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-yellow-light .sidebar a{color:#444}.skin-yellow-light .sidebar a:hover{text-decoration:none}.skin-yellow-light .sidebar-menu .treeview-menu>li>a{color:#777}.skin-yellow-light .sidebar-menu .treeview-menu>li.active>a,.skin-yellow-light .sidebar-menu .treeview-menu>li>a:hover{color:#000}.skin-yellow-light .sidebar-menu .treeview-menu>li.active>a{font-weight:600}.skin-yellow-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-yellow-light .sidebar-form input[type="text"],.skin-yellow-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px}.skin-yellow-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-yellow-light .sidebar-form input[type="text"]:focus,.skin-yellow-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-yellow-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-yellow-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-yellow-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}
|
134
static/adminlte/css/skins/skin-yellow.css
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Skin: Yellow
|
||||
* ------------
|
||||
*/
|
||||
.skin-yellow .main-header .navbar {
|
||||
background-color: #f39c12;
|
||||
}
|
||||
.skin-yellow .main-header .navbar .nav > li > a {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-yellow .main-header .navbar .nav > li > a:hover,
|
||||
.skin-yellow .main-header .navbar .nav > li > a:active,
|
||||
.skin-yellow .main-header .navbar .nav > li > a:focus,
|
||||
.skin-yellow .main-header .navbar .nav .open > a,
|
||||
.skin-yellow .main-header .navbar .nav .open > a:hover,
|
||||
.skin-yellow .main-header .navbar .nav .open > a:focus,
|
||||
.skin-yellow .main-header .navbar .nav > .active > a {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
color: #f6f6f6;
|
||||
}
|
||||
.skin-yellow .main-header .navbar .sidebar-toggle {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-yellow .main-header .navbar .sidebar-toggle:hover {
|
||||
color: #f6f6f6;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.skin-yellow .main-header .navbar .sidebar-toggle {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-yellow .main-header .navbar .sidebar-toggle:hover {
|
||||
background-color: #e08e0b;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.skin-yellow .main-header .navbar .dropdown-menu li.divider {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.skin-yellow .main-header .navbar .dropdown-menu li a {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-yellow .main-header .navbar .dropdown-menu li a:hover {
|
||||
background: #e08e0b;
|
||||
}
|
||||
}
|
||||
.skin-yellow .main-header .logo {
|
||||
background-color: #e08e0b;
|
||||
color: #ffffff;
|
||||
border-bottom: 0 solid transparent;
|
||||
}
|
||||
.skin-yellow .main-header .logo:hover {
|
||||
background-color: #db8b0b;
|
||||
}
|
||||
.skin-yellow .main-header li.user-header {
|
||||
background-color: #f39c12;
|
||||
}
|
||||
.skin-yellow .content-header {
|
||||
background: transparent;
|
||||
}
|
||||
.skin-yellow .wrapper,
|
||||
.skin-yellow .main-sidebar,
|
||||
.skin-yellow .left-side {
|
||||
background-color: #222d32;
|
||||
}
|
||||
.skin-yellow .user-panel > .info,
|
||||
.skin-yellow .user-panel > .info > a {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-yellow .sidebar-menu > li.header {
|
||||
color: #4b646f;
|
||||
background: #1a2226;
|
||||
}
|
||||
.skin-yellow .sidebar-menu > li > a {
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
.skin-yellow .sidebar-menu > li:hover > a,
|
||||
.skin-yellow .sidebar-menu > li.active > a,
|
||||
.skin-yellow .sidebar-menu > li.menu-open > a {
|
||||
color: #ffffff;
|
||||
background: #1e282c;
|
||||
}
|
||||
.skin-yellow .sidebar-menu > li.active > a {
|
||||
border-left-color: #f39c12;
|
||||
}
|
||||
.skin-yellow .sidebar-menu > li > .treeview-menu {
|
||||
margin: 0 1px;
|
||||
background: #2c3b41;
|
||||
}
|
||||
.skin-yellow .sidebar a {
|
||||
color: #b8c7ce;
|
||||
}
|
||||
.skin-yellow .sidebar a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.skin-yellow .sidebar-menu .treeview-menu > li > a {
|
||||
color: #8aa4af;
|
||||
}
|
||||
.skin-yellow .sidebar-menu .treeview-menu > li.active > a,
|
||||
.skin-yellow .sidebar-menu .treeview-menu > li > a:hover {
|
||||
color: #ffffff;
|
||||
}
|
||||
.skin-yellow .sidebar-form {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #374850;
|
||||
margin: 10px 10px;
|
||||
}
|
||||
.skin-yellow .sidebar-form input[type="text"],
|
||||
.skin-yellow .sidebar-form .btn {
|
||||
box-shadow: none;
|
||||
background-color: #374850;
|
||||
border: 1px solid transparent;
|
||||
height: 35px;
|
||||
}
|
||||
.skin-yellow .sidebar-form input[type="text"] {
|
||||
color: #666;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
.skin-yellow .sidebar-form input[type="text"]:focus,
|
||||
.skin-yellow .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
background-color: #fff;
|
||||
color: #666;
|
||||
}
|
||||
.skin-yellow .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.skin-yellow .sidebar-form .btn {
|
||||
color: #999;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
1
static/adminlte/css/skins/skin-yellow.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.skin-yellow .main-header .navbar{background-color:#f39c12}.skin-yellow .main-header .navbar .nav>li>a{color:#fff}.skin-yellow .main-header .navbar .nav>li>a:hover,.skin-yellow .main-header .navbar .nav>li>a:active,.skin-yellow .main-header .navbar .nav>li>a:focus,.skin-yellow .main-header .navbar .nav .open>a,.skin-yellow .main-header .navbar .nav .open>a:hover,.skin-yellow .main-header .navbar .nav .open>a:focus,.skin-yellow .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-yellow .main-header .navbar .sidebar-toggle{color:#fff}.skin-yellow .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-yellow .main-header .navbar .sidebar-toggle{color:#fff}.skin-yellow .main-header .navbar .sidebar-toggle:hover{background-color:#e08e0b}@media (max-width:767px){.skin-yellow .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-yellow .main-header .navbar .dropdown-menu li a{color:#fff}.skin-yellow .main-header .navbar .dropdown-menu li a:hover{background:#e08e0b}}.skin-yellow .main-header .logo{background-color:#e08e0b;color:#fff;border-bottom:0 solid transparent}.skin-yellow .main-header .logo:hover{background-color:#db8b0b}.skin-yellow .main-header li.user-header{background-color:#f39c12}.skin-yellow .content-header{background:transparent}.skin-yellow .wrapper,.skin-yellow .main-sidebar,.skin-yellow .left-side{background-color:#222d32}.skin-yellow .user-panel>.info,.skin-yellow .user-panel>.info>a{color:#fff}.skin-yellow .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-yellow .sidebar-menu>li>a{border-left:3px solid transparent}.skin-yellow .sidebar-menu>li:hover>a,.skin-yellow .sidebar-menu>li.active>a,.skin-yellow .sidebar-menu>li.menu-open>a{color:#fff;background:#1e282c}.skin-yellow .sidebar-menu>li.active>a{border-left-color:#f39c12}.skin-yellow .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-yellow .sidebar a{color:#b8c7ce}.skin-yellow .sidebar a:hover{text-decoration:none}.skin-yellow .sidebar-menu .treeview-menu>li>a{color:#8aa4af}.skin-yellow .sidebar-menu .treeview-menu>li.active>a,.skin-yellow .sidebar-menu .treeview-menu>li>a:hover{color:#fff}.skin-yellow .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-yellow .sidebar-form input[type="text"],.skin-yellow .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px}.skin-yellow .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-yellow .sidebar-form input[type="text"]:focus,.skin-yellow .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-yellow .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-yellow .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}
|
BIN
static/adminlte/img/avatar.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
static/adminlte/img/avatar04.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
static/adminlte/img/avatar2.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
static/adminlte/img/avatar3.png
Normal file
After Width: | Height: | Size: 9.0 KiB |
BIN
static/adminlte/img/avatar5.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
static/adminlte/img/credit/american-express.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
static/adminlte/img/credit/cirrus.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
static/adminlte/img/credit/mastercard.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
static/adminlte/img/credit/mestro.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
static/adminlte/img/credit/paypal.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
static/adminlte/img/credit/paypal2.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
static/adminlte/img/credit/visa.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
static/adminlte/img/default-50x50.gif
Normal file
After Width: | Height: | Size: 184 B |
BIN
static/adminlte/img/icons.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
static/adminlte/img/photo1.png
Normal file
After Width: | Height: | Size: 656 KiB |
BIN
static/adminlte/img/photo2.png
Normal file
After Width: | Height: | Size: 412 KiB |
BIN
static/adminlte/img/photo3.jpg
Normal file
After Width: | Height: | Size: 383 KiB |
BIN
static/adminlte/img/photo4.jpg
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
static/adminlte/img/user1-128x128.jpg
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
static/adminlte/img/user2-160x160.jpg
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
static/adminlte/img/user3-128x128.jpg
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
static/adminlte/img/user4-128x128.jpg
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
static/adminlte/img/user5-128x128.jpg
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
static/adminlte/img/user6-128x128.jpg
Normal file
After Width: | Height: | Size: 4.2 KiB |