mirror of https://github.com/portainer/portainer
feat(motd): add the ability to display motd and dimiss information panels (#2191)
* feat(api): add motd handler * feat(app): add the motd api layer * feat(motd): display motd and add the ability to dismiss information messages * style(home): relocate important message before info01 * feat(api): silently fail when an error occurs during motd retrievalpull/2161/head
parent
74ca908759
commit
6ab6cfafb7
|
@ -3,7 +3,6 @@ package crypto
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/md5"
|
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
@ -97,9 +96,7 @@ func (service *ECDSAService) GenerateKeyPair() ([]byte, []byte, error) {
|
||||||
// that hash.
|
// that hash.
|
||||||
// It then encodes the generated signature in base64.
|
// It then encodes the generated signature in base64.
|
||||||
func (service *ECDSAService) Sign(message string) (string, error) {
|
func (service *ECDSAService) Sign(message string) (string, error) {
|
||||||
digest := md5.New()
|
hash := HashFromBytes([]byte(message))
|
||||||
digest.Write([]byte(message))
|
|
||||||
hash := digest.Sum(nil)
|
|
||||||
|
|
||||||
r := big.NewInt(0)
|
r := big.NewInt(0)
|
||||||
s := big.NewInt(0)
|
s := big.NewInt(0)
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import "crypto/md5"
|
||||||
|
|
||||||
|
// HashFromBytes returns the hash of the specified data
|
||||||
|
func HashFromBytes(data []byte) []byte {
|
||||||
|
digest := md5.New()
|
||||||
|
digest.Write(data)
|
||||||
|
return digest.Sum(nil)
|
||||||
|
}
|
|
@ -13,6 +13,10 @@ import (
|
||||||
"github.com/portainer/portainer"
|
"github.com/portainer/portainer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
errInvalidResponseStatus = portainer.Error("Invalid response status (expecting 200)")
|
||||||
|
)
|
||||||
|
|
||||||
// HTTPClient represents a client to send HTTP requests.
|
// HTTPClient represents a client to send HTTP requests.
|
||||||
type HTTPClient struct {
|
type HTTPClient struct {
|
||||||
*http.Client
|
*http.Client
|
||||||
|
@ -75,6 +79,10 @@ func Get(url string) ([]byte, error) {
|
||||||
}
|
}
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
if response.StatusCode != http.StatusOK {
|
||||||
|
return nil, errInvalidResponseStatus
|
||||||
|
}
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(response.Body)
|
body, err := ioutil.ReadAll(response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/portainer/portainer/http/handler/endpointproxy"
|
"github.com/portainer/portainer/http/handler/endpointproxy"
|
||||||
"github.com/portainer/portainer/http/handler/endpoints"
|
"github.com/portainer/portainer/http/handler/endpoints"
|
||||||
"github.com/portainer/portainer/http/handler/file"
|
"github.com/portainer/portainer/http/handler/file"
|
||||||
|
"github.com/portainer/portainer/http/handler/motd"
|
||||||
"github.com/portainer/portainer/http/handler/registries"
|
"github.com/portainer/portainer/http/handler/registries"
|
||||||
"github.com/portainer/portainer/http/handler/resourcecontrols"
|
"github.com/portainer/portainer/http/handler/resourcecontrols"
|
||||||
"github.com/portainer/portainer/http/handler/settings"
|
"github.com/portainer/portainer/http/handler/settings"
|
||||||
|
@ -33,6 +34,7 @@ type Handler struct {
|
||||||
EndpointHandler *endpoints.Handler
|
EndpointHandler *endpoints.Handler
|
||||||
EndpointProxyHandler *endpointproxy.Handler
|
EndpointProxyHandler *endpointproxy.Handler
|
||||||
FileHandler *file.Handler
|
FileHandler *file.Handler
|
||||||
|
MOTDHandler *motd.Handler
|
||||||
RegistryHandler *registries.Handler
|
RegistryHandler *registries.Handler
|
||||||
ResourceControlHandler *resourcecontrols.Handler
|
ResourceControlHandler *resourcecontrols.Handler
|
||||||
SettingsHandler *settings.Handler
|
SettingsHandler *settings.Handler
|
||||||
|
@ -67,6 +69,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
default:
|
default:
|
||||||
http.StripPrefix("/api", h.EndpointHandler).ServeHTTP(w, r)
|
http.StripPrefix("/api", h.EndpointHandler).ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
case strings.HasPrefix(r.URL.Path, "/api/motd"):
|
||||||
|
http.StripPrefix("/api", h.MOTDHandler).ServeHTTP(w, r)
|
||||||
case strings.HasPrefix(r.URL.Path, "/api/registries"):
|
case strings.HasPrefix(r.URL.Path, "/api/registries"):
|
||||||
http.StripPrefix("/api", h.RegistryHandler).ServeHTTP(w, r)
|
http.StripPrefix("/api", h.RegistryHandler).ServeHTTP(w, r)
|
||||||
case strings.HasPrefix(r.URL.Path, "/api/resource_controls"):
|
case strings.HasPrefix(r.URL.Path, "/api/resource_controls"):
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package motd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/portainer/portainer/http/security"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler is the HTTP handler used to handle MOTD operations.
|
||||||
|
type Handler struct {
|
||||||
|
*mux.Router
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHandler returns a new Handler
|
||||||
|
func NewHandler(bouncer *security.RequestBouncer) *Handler {
|
||||||
|
h := &Handler{
|
||||||
|
Router: mux.NewRouter(),
|
||||||
|
}
|
||||||
|
h.Handle("/motd",
|
||||||
|
bouncer.AuthenticatedAccess(http.HandlerFunc(h.motd))).Methods(http.MethodGet)
|
||||||
|
|
||||||
|
return h
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package motd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/portainer/portainer"
|
||||||
|
"github.com/portainer/portainer/crypto"
|
||||||
|
"github.com/portainer/portainer/http/client"
|
||||||
|
"github.com/portainer/portainer/http/response"
|
||||||
|
)
|
||||||
|
|
||||||
|
type motdResponse struct {
|
||||||
|
Message string `json:"Message"`
|
||||||
|
Hash []byte `json:"Hash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *Handler) motd(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
motd, err := client.Get(portainer.MessageOfTheDayURL)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := crypto.HashFromBytes(motd)
|
||||||
|
response.JSON(w, &motdResponse{Message: string(motd), Hash: hash})
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/portainer/portainer/http/handler/endpointproxy"
|
"github.com/portainer/portainer/http/handler/endpointproxy"
|
||||||
"github.com/portainer/portainer/http/handler/endpoints"
|
"github.com/portainer/portainer/http/handler/endpoints"
|
||||||
"github.com/portainer/portainer/http/handler/file"
|
"github.com/portainer/portainer/http/handler/file"
|
||||||
|
"github.com/portainer/portainer/http/handler/motd"
|
||||||
"github.com/portainer/portainer/http/handler/registries"
|
"github.com/portainer/portainer/http/handler/registries"
|
||||||
"github.com/portainer/portainer/http/handler/resourcecontrols"
|
"github.com/portainer/portainer/http/handler/resourcecontrols"
|
||||||
"github.com/portainer/portainer/http/handler/settings"
|
"github.com/portainer/portainer/http/handler/settings"
|
||||||
|
@ -115,6 +116,8 @@ func (server *Server) Start() error {
|
||||||
|
|
||||||
var fileHandler = file.NewHandler(filepath.Join(server.AssetsPath, "public"))
|
var fileHandler = file.NewHandler(filepath.Join(server.AssetsPath, "public"))
|
||||||
|
|
||||||
|
var motdHandler = motd.NewHandler(requestBouncer)
|
||||||
|
|
||||||
var registryHandler = registries.NewHandler(requestBouncer)
|
var registryHandler = registries.NewHandler(requestBouncer)
|
||||||
registryHandler.RegistryService = server.RegistryService
|
registryHandler.RegistryService = server.RegistryService
|
||||||
|
|
||||||
|
@ -175,6 +178,7 @@ func (server *Server) Start() error {
|
||||||
EndpointHandler: endpointHandler,
|
EndpointHandler: endpointHandler,
|
||||||
EndpointProxyHandler: endpointProxyHandler,
|
EndpointProxyHandler: endpointProxyHandler,
|
||||||
FileHandler: fileHandler,
|
FileHandler: fileHandler,
|
||||||
|
MOTDHandler: motdHandler,
|
||||||
RegistryHandler: registryHandler,
|
RegistryHandler: registryHandler,
|
||||||
ResourceControlHandler: resourceControlHandler,
|
ResourceControlHandler: resourceControlHandler,
|
||||||
SettingsHandler: settingsHandler,
|
SettingsHandler: settingsHandler,
|
||||||
|
|
146
api/portainer.go
146
api/portainer.go
|
@ -7,7 +7,7 @@ type (
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CLIFlags represents the available flags on the CLI.
|
// CLIFlags represents the available flags on the CLI
|
||||||
CLIFlags struct {
|
CLIFlags struct {
|
||||||
Addr *string
|
Addr *string
|
||||||
AdminPassword *string
|
AdminPassword *string
|
||||||
|
@ -35,7 +35,7 @@ type (
|
||||||
SnapshotInterval *string
|
SnapshotInterval *string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status represents the application status.
|
// Status represents the application status
|
||||||
Status struct {
|
Status struct {
|
||||||
Authentication bool `json:"Authentication"`
|
Authentication bool `json:"Authentication"`
|
||||||
EndpointManagement bool `json:"EndpointManagement"`
|
EndpointManagement bool `json:"EndpointManagement"`
|
||||||
|
@ -44,7 +44,7 @@ type (
|
||||||
Version string `json:"Version"`
|
Version string `json:"Version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDAPSettings represents the settings used to connect to a LDAP server.
|
// LDAPSettings represents the settings used to connect to a LDAP server
|
||||||
LDAPSettings struct {
|
LDAPSettings struct {
|
||||||
ReaderDN string `json:"ReaderDN"`
|
ReaderDN string `json:"ReaderDN"`
|
||||||
Password string `json:"Password"`
|
Password string `json:"Password"`
|
||||||
|
@ -56,7 +56,7 @@ type (
|
||||||
AutoCreateUsers bool `json:"AutoCreateUsers"`
|
AutoCreateUsers bool `json:"AutoCreateUsers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSConfiguration represents a TLS configuration.
|
// TLSConfiguration represents a TLS configuration
|
||||||
TLSConfiguration struct {
|
TLSConfiguration struct {
|
||||||
TLS bool `json:"TLS"`
|
TLS bool `json:"TLS"`
|
||||||
TLSSkipVerify bool `json:"TLSSkipVerify"`
|
TLSSkipVerify bool `json:"TLSSkipVerify"`
|
||||||
|
@ -65,21 +65,21 @@ type (
|
||||||
TLSKeyPath string `json:"TLSKey,omitempty"`
|
TLSKeyPath string `json:"TLSKey,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDAPSearchSettings represents settings used to search for users in a LDAP server.
|
// LDAPSearchSettings represents settings used to search for users in a LDAP server
|
||||||
LDAPSearchSettings struct {
|
LDAPSearchSettings struct {
|
||||||
BaseDN string `json:"BaseDN"`
|
BaseDN string `json:"BaseDN"`
|
||||||
Filter string `json:"Filter"`
|
Filter string `json:"Filter"`
|
||||||
UserNameAttribute string `json:"UserNameAttribute"`
|
UserNameAttribute string `json:"UserNameAttribute"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDAPGroupSearchSettings represents settings used to search for groups in a LDAP server.
|
// LDAPGroupSearchSettings represents settings used to search for groups in a LDAP server
|
||||||
LDAPGroupSearchSettings struct {
|
LDAPGroupSearchSettings struct {
|
||||||
GroupBaseDN string `json:"GroupBaseDN"`
|
GroupBaseDN string `json:"GroupBaseDN"`
|
||||||
GroupFilter string `json:"GroupFilter"`
|
GroupFilter string `json:"GroupFilter"`
|
||||||
GroupAttribute string `json:"GroupAttribute"`
|
GroupAttribute string `json:"GroupAttribute"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings represents the application settings.
|
// Settings represents the application settings
|
||||||
Settings struct {
|
Settings struct {
|
||||||
LogoURL string `json:"LogoURL"`
|
LogoURL string `json:"LogoURL"`
|
||||||
BlackListedLabels []Pair `json:"BlackListedLabels"`
|
BlackListedLabels []Pair `json:"BlackListedLabels"`
|
||||||
|
@ -95,7 +95,7 @@ type (
|
||||||
DisplayExternalContributors bool
|
DisplayExternalContributors bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// User represents a user account.
|
// User represents a user account
|
||||||
User struct {
|
User struct {
|
||||||
ID UserID `json:"Id"`
|
ID UserID `json:"Id"`
|
||||||
Username string `json:"Username"`
|
Username string `json:"Username"`
|
||||||
|
@ -110,10 +110,10 @@ type (
|
||||||
// or a regular user
|
// or a regular user
|
||||||
UserRole int
|
UserRole int
|
||||||
|
|
||||||
// AuthenticationMethod represents the authentication method used to authenticate a user.
|
// AuthenticationMethod represents the authentication method used to authenticate a user
|
||||||
AuthenticationMethod int
|
AuthenticationMethod int
|
||||||
|
|
||||||
// Team represents a list of user accounts.
|
// Team represents a list of user accounts
|
||||||
Team struct {
|
Team struct {
|
||||||
ID TeamID `json:"Id"`
|
ID TeamID `json:"Id"`
|
||||||
Name string `json:"Name"`
|
Name string `json:"Name"`
|
||||||
|
@ -136,20 +136,20 @@ type (
|
||||||
// MembershipRole represents the role of a user within a team
|
// MembershipRole represents the role of a user within a team
|
||||||
MembershipRole int
|
MembershipRole int
|
||||||
|
|
||||||
// TokenData represents the data embedded in a JWT token.
|
// TokenData represents the data embedded in a JWT token
|
||||||
TokenData struct {
|
TokenData struct {
|
||||||
ID UserID
|
ID UserID
|
||||||
Username string
|
Username string
|
||||||
Role UserRole
|
Role UserRole
|
||||||
}
|
}
|
||||||
|
|
||||||
// StackID represents a stack identifier (it must be composed of Name + "_" + SwarmID to create a unique identifier).
|
// StackID represents a stack identifier (it must be composed of Name + "_" + SwarmID to create a unique identifier)
|
||||||
StackID int
|
StackID int
|
||||||
|
|
||||||
// StackType represents the type of the stack (compose v2, stack deploy v3).
|
// StackType represents the type of the stack (compose v2, stack deploy v3)
|
||||||
StackType int
|
StackType int
|
||||||
|
|
||||||
// Stack represents a Docker stack created via docker stack deploy.
|
// Stack represents a Docker stack created via docker stack deploy
|
||||||
Stack struct {
|
Stack struct {
|
||||||
ID StackID `json:"Id"`
|
ID StackID `json:"Id"`
|
||||||
Name string `json:"Name"`
|
Name string `json:"Name"`
|
||||||
|
@ -161,11 +161,11 @@ type (
|
||||||
ProjectPath string
|
ProjectPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegistryID represents a registry identifier.
|
// RegistryID represents a registry identifier
|
||||||
RegistryID int
|
RegistryID int
|
||||||
|
|
||||||
// Registry represents a Docker registry with all the info required
|
// Registry represents a Docker registry with all the info required
|
||||||
// to connect to it.
|
// to connect to it
|
||||||
Registry struct {
|
Registry struct {
|
||||||
ID RegistryID `json:"Id"`
|
ID RegistryID `json:"Id"`
|
||||||
Name string `json:"Name"`
|
Name string `json:"Name"`
|
||||||
|
@ -178,24 +178,24 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
// DockerHub represents all the required information to connect and use the
|
// DockerHub represents all the required information to connect and use the
|
||||||
// Docker Hub.
|
// Docker Hub
|
||||||
DockerHub struct {
|
DockerHub struct {
|
||||||
Authentication bool `json:"Authentication"`
|
Authentication bool `json:"Authentication"`
|
||||||
Username string `json:"Username"`
|
Username string `json:"Username"`
|
||||||
Password string `json:"Password,omitempty"`
|
Password string `json:"Password,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EndpointID represents an endpoint identifier.
|
// EndpointID represents an endpoint identifier
|
||||||
EndpointID int
|
EndpointID int
|
||||||
|
|
||||||
// EndpointType represents the type of an endpoint.
|
// EndpointType represents the type of an endpoint
|
||||||
EndpointType int
|
EndpointType int
|
||||||
|
|
||||||
// EndpointStatus represents the status of an endpoint
|
// EndpointStatus represents the status of an endpoint
|
||||||
EndpointStatus int
|
EndpointStatus int
|
||||||
|
|
||||||
// Endpoint represents a Docker endpoint with all the info required
|
// Endpoint represents a Docker endpoint with all the info required
|
||||||
// to connect to it.
|
// to connect to it
|
||||||
Endpoint struct {
|
Endpoint struct {
|
||||||
ID EndpointID `json:"Id"`
|
ID EndpointID `json:"Id"`
|
||||||
Name string `json:"Name"`
|
Name string `json:"Name"`
|
||||||
|
@ -243,10 +243,10 @@ type (
|
||||||
StackCount int `json:"StackCount"`
|
StackCount int `json:"StackCount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EndpointGroupID represents an endpoint group identifier.
|
// EndpointGroupID represents an endpoint group identifier
|
||||||
EndpointGroupID int
|
EndpointGroupID int
|
||||||
|
|
||||||
// EndpointGroup represents a group of endpoints.
|
// EndpointGroup represents a group of endpoints
|
||||||
EndpointGroup struct {
|
EndpointGroup struct {
|
||||||
ID EndpointGroupID `json:"Id"`
|
ID EndpointGroupID `json:"Id"`
|
||||||
Name string `json:"Name"`
|
Name string `json:"Name"`
|
||||||
|
@ -259,17 +259,17 @@ type (
|
||||||
Labels []Pair `json:"Labels"`
|
Labels []Pair `json:"Labels"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EndpointExtension represents a extension associated to an endpoint.
|
// EndpointExtension represents a extension associated to an endpoint
|
||||||
EndpointExtension struct {
|
EndpointExtension struct {
|
||||||
Type EndpointExtensionType `json:"Type"`
|
Type EndpointExtensionType `json:"Type"`
|
||||||
URL string `json:"URL"`
|
URL string `json:"URL"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EndpointExtensionType represents the type of an endpoint extension. Only
|
// EndpointExtensionType represents the type of an endpoint extension. Only
|
||||||
// one extension of each type can be associated to an endpoint.
|
// one extension of each type can be associated to an endpoint
|
||||||
EndpointExtensionType int
|
EndpointExtensionType int
|
||||||
|
|
||||||
// ResourceControlID represents a resource control identifier.
|
// ResourceControlID represents a resource control identifier
|
||||||
ResourceControlID int
|
ResourceControlID int
|
||||||
|
|
||||||
// ResourceControl represent a reference to a Docker resource with specific access controls
|
// ResourceControl represent a reference to a Docker resource with specific access controls
|
||||||
|
@ -291,37 +291,37 @@ type (
|
||||||
AdministratorsOnly bool `json:"AdministratorsOnly,omitempty"`
|
AdministratorsOnly bool `json:"AdministratorsOnly,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResourceControlType represents the type of resource associated to the resource control (volume, container, service...).
|
// ResourceControlType represents the type of resource associated to the resource control (volume, container, service...)
|
||||||
ResourceControlType int
|
ResourceControlType int
|
||||||
|
|
||||||
// UserResourceAccess represents the level of control on a resource for a specific user.
|
// UserResourceAccess represents the level of control on a resource for a specific user
|
||||||
UserResourceAccess struct {
|
UserResourceAccess struct {
|
||||||
UserID UserID `json:"UserId"`
|
UserID UserID `json:"UserId"`
|
||||||
AccessLevel ResourceAccessLevel `json:"AccessLevel"`
|
AccessLevel ResourceAccessLevel `json:"AccessLevel"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TeamResourceAccess represents the level of control on a resource for a specific team.
|
// TeamResourceAccess represents the level of control on a resource for a specific team
|
||||||
TeamResourceAccess struct {
|
TeamResourceAccess struct {
|
||||||
TeamID TeamID `json:"TeamId"`
|
TeamID TeamID `json:"TeamId"`
|
||||||
AccessLevel ResourceAccessLevel `json:"AccessLevel"`
|
AccessLevel ResourceAccessLevel `json:"AccessLevel"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TagID represents a tag identifier.
|
// TagID represents a tag identifier
|
||||||
TagID int
|
TagID int
|
||||||
|
|
||||||
// Tag represents a tag that can be associated to a resource.
|
// Tag represents a tag that can be associated to a resource
|
||||||
Tag struct {
|
Tag struct {
|
||||||
ID TagID
|
ID TagID
|
||||||
Name string `json:"Name"`
|
Name string `json:"Name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TemplateID represents a template identifier.
|
// TemplateID represents a template identifier
|
||||||
TemplateID int
|
TemplateID int
|
||||||
|
|
||||||
// TemplateType represents the type of a template.
|
// TemplateType represents the type of a template
|
||||||
TemplateType int
|
TemplateType int
|
||||||
|
|
||||||
// Template represents an application template.
|
// Template represents an application template
|
||||||
Template struct {
|
Template struct {
|
||||||
// Mandatory container/stack fields
|
// Mandatory container/stack fields
|
||||||
ID TemplateID `json:"Id"`
|
ID TemplateID `json:"Id"`
|
||||||
|
@ -357,7 +357,7 @@ type (
|
||||||
Hostname string `json:"hostname,omitempty"`
|
Hostname string `json:"hostname,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TemplateEnv represents a template environment variable configuration.
|
// TemplateEnv represents a template environment variable configuration
|
||||||
TemplateEnv struct {
|
TemplateEnv struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Label string `json:"label,omitempty"`
|
Label string `json:"label,omitempty"`
|
||||||
|
@ -367,41 +367,41 @@ type (
|
||||||
Select []TemplateEnvSelect `json:"select,omitempty"`
|
Select []TemplateEnvSelect `json:"select,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TemplateVolume represents a template volume configuration.
|
// TemplateVolume represents a template volume configuration
|
||||||
TemplateVolume struct {
|
TemplateVolume struct {
|
||||||
Container string `json:"container"`
|
Container string `json:"container"`
|
||||||
Bind string `json:"bind,omitempty"`
|
Bind string `json:"bind,omitempty"`
|
||||||
ReadOnly bool `json:"readonly,omitempty"`
|
ReadOnly bool `json:"readonly,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TemplateRepository represents the git repository configuration for a template.
|
// TemplateRepository represents the git repository configuration for a template
|
||||||
TemplateRepository struct {
|
TemplateRepository struct {
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
StackFile string `json:"stackfile"`
|
StackFile string `json:"stackfile"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TemplateEnvSelect represents text/value pair that will be displayed as a choice for the
|
// TemplateEnvSelect represents text/value pair that will be displayed as a choice for the
|
||||||
// template user.
|
// template user
|
||||||
TemplateEnvSelect struct {
|
TemplateEnvSelect struct {
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
Default bool `json:"default"`
|
Default bool `json:"default"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResourceAccessLevel represents the level of control associated to a resource.
|
// ResourceAccessLevel represents the level of control associated to a resource
|
||||||
ResourceAccessLevel int
|
ResourceAccessLevel int
|
||||||
|
|
||||||
// TLSFileType represents a type of TLS file required to connect to a Docker endpoint.
|
// TLSFileType represents a type of TLS file required to connect to a Docker endpoint.
|
||||||
// It can be either a TLS CA file, a TLS certificate file or a TLS key file.
|
// It can be either a TLS CA file, a TLS certificate file or a TLS key file
|
||||||
TLSFileType int
|
TLSFileType int
|
||||||
|
|
||||||
// CLIService represents a service for managing CLI.
|
// CLIService represents a service for managing CLI
|
||||||
CLIService interface {
|
CLIService interface {
|
||||||
ParseFlags(version string) (*CLIFlags, error)
|
ParseFlags(version string) (*CLIFlags, error)
|
||||||
ValidateFlags(flags *CLIFlags) error
|
ValidateFlags(flags *CLIFlags) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// DataStore defines the interface to manage the data.
|
// DataStore defines the interface to manage the data
|
||||||
DataStore interface {
|
DataStore interface {
|
||||||
Open() error
|
Open() error
|
||||||
Init() error
|
Init() error
|
||||||
|
@ -409,12 +409,12 @@ type (
|
||||||
MigrateData() error
|
MigrateData() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server defines the interface to serve the API.
|
// Server defines the interface to serve the API
|
||||||
Server interface {
|
Server interface {
|
||||||
Start() error
|
Start() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserService represents a service for managing user data.
|
// UserService represents a service for managing user data
|
||||||
UserService interface {
|
UserService interface {
|
||||||
User(ID UserID) (*User, error)
|
User(ID UserID) (*User, error)
|
||||||
UserByUsername(username string) (*User, error)
|
UserByUsername(username string) (*User, error)
|
||||||
|
@ -425,7 +425,7 @@ type (
|
||||||
DeleteUser(ID UserID) error
|
DeleteUser(ID UserID) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// TeamService represents a service for managing user data.
|
// TeamService represents a service for managing user data
|
||||||
TeamService interface {
|
TeamService interface {
|
||||||
Team(ID TeamID) (*Team, error)
|
Team(ID TeamID) (*Team, error)
|
||||||
TeamByName(name string) (*Team, error)
|
TeamByName(name string) (*Team, error)
|
||||||
|
@ -435,7 +435,7 @@ type (
|
||||||
DeleteTeam(ID TeamID) error
|
DeleteTeam(ID TeamID) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// TeamMembershipService represents a service for managing team membership data.
|
// TeamMembershipService represents a service for managing team membership data
|
||||||
TeamMembershipService interface {
|
TeamMembershipService interface {
|
||||||
TeamMembership(ID TeamMembershipID) (*TeamMembership, error)
|
TeamMembership(ID TeamMembershipID) (*TeamMembership, error)
|
||||||
TeamMemberships() ([]TeamMembership, error)
|
TeamMemberships() ([]TeamMembership, error)
|
||||||
|
@ -448,7 +448,7 @@ type (
|
||||||
DeleteTeamMembershipByTeamID(teamID TeamID) error
|
DeleteTeamMembershipByTeamID(teamID TeamID) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// EndpointService represents a service for managing endpoint data.
|
// EndpointService represents a service for managing endpoint data
|
||||||
EndpointService interface {
|
EndpointService interface {
|
||||||
Endpoint(ID EndpointID) (*Endpoint, error)
|
Endpoint(ID EndpointID) (*Endpoint, error)
|
||||||
Endpoints() ([]Endpoint, error)
|
Endpoints() ([]Endpoint, error)
|
||||||
|
@ -459,7 +459,7 @@ type (
|
||||||
GetNextIdentifier() int
|
GetNextIdentifier() int
|
||||||
}
|
}
|
||||||
|
|
||||||
// EndpointGroupService represents a service for managing endpoint group data.
|
// EndpointGroupService represents a service for managing endpoint group data
|
||||||
EndpointGroupService interface {
|
EndpointGroupService interface {
|
||||||
EndpointGroup(ID EndpointGroupID) (*EndpointGroup, error)
|
EndpointGroup(ID EndpointGroupID) (*EndpointGroup, error)
|
||||||
EndpointGroups() ([]EndpointGroup, error)
|
EndpointGroups() ([]EndpointGroup, error)
|
||||||
|
@ -468,7 +468,7 @@ type (
|
||||||
DeleteEndpointGroup(ID EndpointGroupID) error
|
DeleteEndpointGroup(ID EndpointGroupID) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegistryService represents a service for managing registry data.
|
// RegistryService represents a service for managing registry data
|
||||||
RegistryService interface {
|
RegistryService interface {
|
||||||
Registry(ID RegistryID) (*Registry, error)
|
Registry(ID RegistryID) (*Registry, error)
|
||||||
Registries() ([]Registry, error)
|
Registries() ([]Registry, error)
|
||||||
|
@ -477,7 +477,7 @@ type (
|
||||||
DeleteRegistry(ID RegistryID) error
|
DeleteRegistry(ID RegistryID) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// StackService represents a service for managing stack data.
|
// StackService represents a service for managing stack data
|
||||||
StackService interface {
|
StackService interface {
|
||||||
Stack(ID StackID) (*Stack, error)
|
Stack(ID StackID) (*Stack, error)
|
||||||
StackByName(name string) (*Stack, error)
|
StackByName(name string) (*Stack, error)
|
||||||
|
@ -488,25 +488,25 @@ type (
|
||||||
GetNextIdentifier() int
|
GetNextIdentifier() int
|
||||||
}
|
}
|
||||||
|
|
||||||
// DockerHubService represents a service for managing the DockerHub object.
|
// DockerHubService represents a service for managing the DockerHub object
|
||||||
DockerHubService interface {
|
DockerHubService interface {
|
||||||
DockerHub() (*DockerHub, error)
|
DockerHub() (*DockerHub, error)
|
||||||
UpdateDockerHub(registry *DockerHub) error
|
UpdateDockerHub(registry *DockerHub) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// SettingsService represents a service for managing application settings.
|
// SettingsService represents a service for managing application settings
|
||||||
SettingsService interface {
|
SettingsService interface {
|
||||||
Settings() (*Settings, error)
|
Settings() (*Settings, error)
|
||||||
UpdateSettings(settings *Settings) error
|
UpdateSettings(settings *Settings) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// VersionService represents a service for managing version data.
|
// VersionService represents a service for managing version data
|
||||||
VersionService interface {
|
VersionService interface {
|
||||||
DBVersion() (int, error)
|
DBVersion() (int, error)
|
||||||
StoreDBVersion(version int) error
|
StoreDBVersion(version int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResourceControlService represents a service for managing resource control data.
|
// ResourceControlService represents a service for managing resource control data
|
||||||
ResourceControlService interface {
|
ResourceControlService interface {
|
||||||
ResourceControl(ID ResourceControlID) (*ResourceControl, error)
|
ResourceControl(ID ResourceControlID) (*ResourceControl, error)
|
||||||
ResourceControlByResourceID(resourceID string) (*ResourceControl, error)
|
ResourceControlByResourceID(resourceID string) (*ResourceControl, error)
|
||||||
|
@ -516,14 +516,14 @@ type (
|
||||||
DeleteResourceControl(ID ResourceControlID) error
|
DeleteResourceControl(ID ResourceControlID) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// TagService represents a service for managing tag data.
|
// TagService represents a service for managing tag data
|
||||||
TagService interface {
|
TagService interface {
|
||||||
Tags() ([]Tag, error)
|
Tags() ([]Tag, error)
|
||||||
CreateTag(tag *Tag) error
|
CreateTag(tag *Tag) error
|
||||||
DeleteTag(ID TagID) error
|
DeleteTag(ID TagID) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// TemplateService represents a service for managing template data.
|
// TemplateService represents a service for managing template data
|
||||||
TemplateService interface {
|
TemplateService interface {
|
||||||
Templates() ([]Template, error)
|
Templates() ([]Template, error)
|
||||||
Template(ID TemplateID) (*Template, error)
|
Template(ID TemplateID) (*Template, error)
|
||||||
|
@ -532,13 +532,13 @@ type (
|
||||||
DeleteTemplate(ID TemplateID) error
|
DeleteTemplate(ID TemplateID) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// CryptoService represents a service for encrypting/hashing data.
|
// CryptoService represents a service for encrypting/hashing data
|
||||||
CryptoService interface {
|
CryptoService interface {
|
||||||
Hash(data string) (string, error)
|
Hash(data string) (string, error)
|
||||||
CompareHashAndData(hash string, data string) error
|
CompareHashAndData(hash string, data string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// DigitalSignatureService represents a service to manage digital signatures.
|
// DigitalSignatureService represents a service to manage digital signatures
|
||||||
DigitalSignatureService interface {
|
DigitalSignatureService interface {
|
||||||
ParseKeyPair(private, public []byte) error
|
ParseKeyPair(private, public []byte) error
|
||||||
GenerateKeyPair() ([]byte, []byte, error)
|
GenerateKeyPair() ([]byte, []byte, error)
|
||||||
|
@ -547,13 +547,13 @@ type (
|
||||||
Sign(message string) (string, error)
|
Sign(message string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// JWTService represents a service for managing JWT tokens.
|
// JWTService represents a service for managing JWT tokens
|
||||||
JWTService interface {
|
JWTService interface {
|
||||||
GenerateToken(data *TokenData) (string, error)
|
GenerateToken(data *TokenData) (string, error)
|
||||||
ParseAndVerifyToken(token string) (*TokenData, error)
|
ParseAndVerifyToken(token string) (*TokenData, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileService represents a service for managing files.
|
// FileService represents a service for managing files
|
||||||
FileService interface {
|
FileService interface {
|
||||||
GetFileContent(filePath string) ([]byte, error)
|
GetFileContent(filePath string) ([]byte, error)
|
||||||
Rename(oldPath, newPath string) error
|
Rename(oldPath, newPath string) error
|
||||||
|
@ -571,13 +571,13 @@ type (
|
||||||
FileExists(path string) (bool, error)
|
FileExists(path string) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GitService represents a service for managing Git.
|
// GitService represents a service for managing Git
|
||||||
GitService interface {
|
GitService interface {
|
||||||
ClonePublicRepository(repositoryURL, referenceName string, destination string) error
|
ClonePublicRepository(repositoryURL, referenceName string, destination string) error
|
||||||
ClonePrivateRepositoryWithBasicAuth(repositoryURL, referenceName string, destination, username, password string) error
|
ClonePrivateRepositoryWithBasicAuth(repositoryURL, referenceName string, destination, username, password string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// JobScheduler represents a service to run jobs on a periodic basis.
|
// JobScheduler represents a service to run jobs on a periodic basis
|
||||||
JobScheduler interface {
|
JobScheduler interface {
|
||||||
ScheduleEndpointSyncJob(endpointFilePath, interval string) error
|
ScheduleEndpointSyncJob(endpointFilePath, interval string) error
|
||||||
ScheduleSnapshotJob(interval string) error
|
ScheduleSnapshotJob(interval string) error
|
||||||
|
@ -585,19 +585,19 @@ type (
|
||||||
Start()
|
Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snapshotter represents a service used to create endpoint snapshots.
|
// Snapshotter represents a service used to create endpoint snapshots
|
||||||
Snapshotter interface {
|
Snapshotter interface {
|
||||||
CreateSnapshot(endpoint *Endpoint) (*Snapshot, error)
|
CreateSnapshot(endpoint *Endpoint) (*Snapshot, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDAPService represents a service used to authenticate users against a LDAP/AD.
|
// LDAPService represents a service used to authenticate users against a LDAP/AD
|
||||||
LDAPService interface {
|
LDAPService interface {
|
||||||
AuthenticateUser(username, password string, settings *LDAPSettings) error
|
AuthenticateUser(username, password string, settings *LDAPSettings) error
|
||||||
TestConnectivity(settings *LDAPSettings) error
|
TestConnectivity(settings *LDAPSettings) error
|
||||||
GetUserGroups(username string, settings *LDAPSettings) ([]string, error)
|
GetUserGroups(username string, settings *LDAPSettings) ([]string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SwarmStackManager represents a service to manage Swarm stacks.
|
// SwarmStackManager represents a service to manage Swarm stacks
|
||||||
SwarmStackManager interface {
|
SwarmStackManager interface {
|
||||||
Login(dockerhub *DockerHub, registries []Registry, endpoint *Endpoint)
|
Login(dockerhub *DockerHub, registries []Registry, endpoint *Endpoint)
|
||||||
Logout(endpoint *Endpoint) error
|
Logout(endpoint *Endpoint) error
|
||||||
|
@ -605,7 +605,7 @@ type (
|
||||||
Remove(stack *Stack, endpoint *Endpoint) error
|
Remove(stack *Stack, endpoint *Endpoint) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ComposeStackManager represents a service to manage Compose stacks.
|
// ComposeStackManager represents a service to manage Compose stacks
|
||||||
ComposeStackManager interface {
|
ComposeStackManager interface {
|
||||||
Up(stack *Stack, endpoint *Endpoint) error
|
Up(stack *Stack, endpoint *Endpoint) error
|
||||||
Down(stack *Stack, endpoint *Endpoint) error
|
Down(stack *Stack, endpoint *Endpoint) error
|
||||||
|
@ -613,13 +613,15 @@ type (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// APIVersion is the version number of the Portainer API.
|
// APIVersion is the version number of the Portainer API
|
||||||
APIVersion = "1.19.2-dev"
|
APIVersion = "1.19.2-dev"
|
||||||
// DBVersion is the version number of the Portainer database.
|
// DBVersion is the version number of the Portainer database
|
||||||
DBVersion = 14
|
DBVersion = 14
|
||||||
|
// MessageOfTheDayURL represents the URL where Portainer MOTD message can be retrieved
|
||||||
|
MessageOfTheDayURL = "https://raw.githubusercontent.com/portainer/motd/master/message.html"
|
||||||
// PortainerAgentHeader represents the name of the header available in any agent response
|
// PortainerAgentHeader represents the name of the header available in any agent response
|
||||||
PortainerAgentHeader = "Portainer-Agent"
|
PortainerAgentHeader = "Portainer-Agent"
|
||||||
// PortainerAgentTargetHeader represent the name of the header containing the target node name.
|
// PortainerAgentTargetHeader represent the name of the header containing the target node name
|
||||||
PortainerAgentTargetHeader = "X-PortainerAgent-Target"
|
PortainerAgentTargetHeader = "X-PortainerAgent-Target"
|
||||||
// PortainerAgentSignatureHeader represent the name of the header containing the digital signature
|
// PortainerAgentSignatureHeader represent the name of the header containing the digital signature
|
||||||
PortainerAgentSignatureHeader = "X-PortainerAgent-Signature"
|
PortainerAgentSignatureHeader = "X-PortainerAgent-Signature"
|
||||||
|
@ -628,16 +630,16 @@ const (
|
||||||
// PortainerAgentSignatureMessage represents the message used to create a digital signature
|
// PortainerAgentSignatureMessage represents the message used to create a digital signature
|
||||||
// to be used when communicating with an agent
|
// to be used when communicating with an agent
|
||||||
PortainerAgentSignatureMessage = "Portainer-App"
|
PortainerAgentSignatureMessage = "Portainer-App"
|
||||||
// SupportedDockerAPIVersion is the minimum Docker API version supported by Portainer.
|
// SupportedDockerAPIVersion is the minimum Docker API version supported by Portainer
|
||||||
SupportedDockerAPIVersion = "1.24"
|
SupportedDockerAPIVersion = "1.24"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// TLSFileCA represents a TLS CA certificate file.
|
// TLSFileCA represents a TLS CA certificate file
|
||||||
TLSFileCA TLSFileType = iota
|
TLSFileCA TLSFileType = iota
|
||||||
// TLSFileCert represents a TLS certificate file.
|
// TLSFileCert represents a TLS certificate file
|
||||||
TLSFileCert
|
TLSFileCert
|
||||||
// TLSFileKey represents a TLS key file.
|
// TLSFileKey represents a TLS key file
|
||||||
TLSFileKey
|
TLSFileKey
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ angular.module('portainer')
|
||||||
.constant('API_ENDPOINT_DOCKERHUB', 'api/dockerhub')
|
.constant('API_ENDPOINT_DOCKERHUB', 'api/dockerhub')
|
||||||
.constant('API_ENDPOINT_ENDPOINTS', 'api/endpoints')
|
.constant('API_ENDPOINT_ENDPOINTS', 'api/endpoints')
|
||||||
.constant('API_ENDPOINT_ENDPOINT_GROUPS', 'api/endpoint_groups')
|
.constant('API_ENDPOINT_ENDPOINT_GROUPS', 'api/endpoint_groups')
|
||||||
|
.constant('API_ENDPOINT_MOTD', 'api/motd')
|
||||||
.constant('API_ENDPOINT_REGISTRIES', 'api/registries')
|
.constant('API_ENDPOINT_REGISTRIES', 'api/registries')
|
||||||
.constant('API_ENDPOINT_RESOURCE_CONTROLS', 'api/resource_controls')
|
.constant('API_ENDPOINT_RESOURCE_CONTROLS', 'api/resource_controls')
|
||||||
.constant('API_ENDPOINT_SETTINGS', 'api/settings')
|
.constant('API_ENDPOINT_SETTINGS', 'api/settings')
|
||||||
|
|
|
@ -9,14 +9,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" ng-if="!applicationState.endpoint.mode.agentProxy && applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'">
|
<information-panel
|
||||||
<div class="col-sm-12">
|
ng-if="!applicationState.UI.dismissedInfoPanels['docker-dashboard-info-01'] && !applicationState.endpoint.mode.agentProxy && applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE'"
|
||||||
<rd-widget>
|
title-text="Information"
|
||||||
<rd-widget-body>
|
dismiss-action="dismissInformationPanel('docker-dashboard-info-01')">
|
||||||
<div class="col-sm-12 form-section-title">
|
|
||||||
Information
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<span class="small">
|
<span class="small">
|
||||||
<p class="text-muted" ng-if="applicationState.endpoint.mode.role === 'MANAGER'">
|
<p class="text-muted" ng-if="applicationState.endpoint.mode.role === 'MANAGER'">
|
||||||
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||||
|
@ -28,11 +24,7 @@
|
||||||
Portainer is connected to a worker node. Swarm management features will not be available.
|
Portainer is connected to a worker node. Swarm management features will not be available.
|
||||||
</p>
|
</p>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</information-panel>
|
||||||
</rd-widget-body>
|
|
||||||
</rd-widget>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row" ng-if="(!applicationState.endpoint.mode.agentProxy || applicationState.endpoint.mode.provider !== 'DOCKER_SWARM_MODE') && info && endpoint">
|
<div class="row" ng-if="(!applicationState.endpoint.mode.agentProxy || applicationState.endpoint.mode.provider !== 'DOCKER_SWARM_MODE') && info && endpoint">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
angular.module('portainer.docker')
|
angular.module('portainer.docker')
|
||||||
.controller('DashboardController', ['$scope', '$q', 'ContainerService', 'ImageService', 'NetworkService', 'VolumeService', 'SystemService', 'ServiceService', 'StackService', 'EndpointService', 'Notifications', 'EndpointProvider',
|
.controller('DashboardController', ['$scope', '$q', 'ContainerService', 'ImageService', 'NetworkService', 'VolumeService', 'SystemService', 'ServiceService', 'StackService', 'EndpointService', 'Notifications', 'EndpointProvider', 'StateManager',
|
||||||
function ($scope, $q, ContainerService, ImageService, NetworkService, VolumeService, SystemService, ServiceService, StackService, EndpointService, Notifications, EndpointProvider) {
|
function ($scope, $q, ContainerService, ImageService, NetworkService, VolumeService, SystemService, ServiceService, StackService, EndpointService, Notifications, EndpointProvider, StateManager) {
|
||||||
|
|
||||||
|
$scope.dismissInformationPanel = function(id) {
|
||||||
|
StateManager.dismissInformationPanel(id);
|
||||||
|
};
|
||||||
|
|
||||||
function initView() {
|
function initView() {
|
||||||
var endpointMode = $scope.applicationState.endpoint.mode;
|
var endpointMode = $scope.applicationState.endpoint.mode;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
angular.module('portainer.app').component('informationPanel', {
|
angular.module('portainer.app').component('informationPanel', {
|
||||||
templateUrl: 'app/portainer/components/information-panel/informationPanel.html',
|
templateUrl: 'app/portainer/components/information-panel/informationPanel.html',
|
||||||
bindings: {
|
bindings: {
|
||||||
titleText: '@'
|
titleText: '@',
|
||||||
|
dismissAction: '&'
|
||||||
},
|
},
|
||||||
transclude: true
|
transclude: true
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,7 +3,12 @@
|
||||||
<rd-widget>
|
<rd-widget>
|
||||||
<rd-widget-body>
|
<rd-widget-body>
|
||||||
<div class="col-sm-12 form-section-title">
|
<div class="col-sm-12 form-section-title">
|
||||||
|
<span style="float: left;">
|
||||||
{{ $ctrl.titleText }}
|
{{ $ctrl.titleText }}
|
||||||
|
</span>
|
||||||
|
<span class="small" style="float: right;">
|
||||||
|
<a ng-click="$ctrl.dismissAction()"><i class="fa fa-times"></i> dismiss</a>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<ng-transclude></ng-transclude>
|
<ng-transclude></ng-transclude>
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
function MotdViewModel(data) {
|
||||||
|
this.Message = data.Message;
|
||||||
|
this.Hash = data.Hash;
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
angular.module('portainer.app')
|
||||||
|
.factory('Motd', ['$resource', 'API_ENDPOINT_MOTD', function MotdFactory($resource, API_ENDPOINT_MOTD) {
|
||||||
|
'use strict';
|
||||||
|
return $resource(API_ENDPOINT_MOTD, {}, {
|
||||||
|
get: { method: 'GET' }
|
||||||
|
});
|
||||||
|
}]);
|
|
@ -0,0 +1,22 @@
|
||||||
|
angular.module('portainer.app')
|
||||||
|
.factory('MotdService', ['$q', 'Motd', function MotdServiceFactory($q, Motd) {
|
||||||
|
'use strict';
|
||||||
|
var service = {};
|
||||||
|
|
||||||
|
service.motd = function() {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
|
||||||
|
Motd.get().$promise
|
||||||
|
.then(function success(data) {
|
||||||
|
var motd = new MotdViewModel(data);
|
||||||
|
deferred.resolve(motd);
|
||||||
|
})
|
||||||
|
.catch(function error(err) {
|
||||||
|
deferred.reject({msg: 'Unable to retrieve information message', err: err});
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
return service;
|
||||||
|
}]);
|
|
@ -26,6 +26,12 @@ angular.module('portainer.app')
|
||||||
getApplicationState: function() {
|
getApplicationState: function() {
|
||||||
return localStorageService.get('APPLICATION_STATE');
|
return localStorageService.get('APPLICATION_STATE');
|
||||||
},
|
},
|
||||||
|
storeUIState: function(state) {
|
||||||
|
localStorageService.cookie.set('UI_STATE', state);
|
||||||
|
},
|
||||||
|
getUIState: function() {
|
||||||
|
return localStorageService.cookie.get('UI_STATE');
|
||||||
|
},
|
||||||
storeJWT: function(jwt) {
|
storeJWT: function(jwt) {
|
||||||
localStorageService.set('JWT', jwt);
|
localStorageService.set('JWT', jwt);
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,7 +9,20 @@ function StateManagerFactory($q, SystemService, InfoHelper, LocalStorage, Settin
|
||||||
loading: true,
|
loading: true,
|
||||||
application: {},
|
application: {},
|
||||||
endpoint: {},
|
endpoint: {},
|
||||||
UI: {}
|
UI: {
|
||||||
|
dismissedInfoPanels: {},
|
||||||
|
dismissedInfoHash: ''
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
manager.dismissInformationPanel = function(id) {
|
||||||
|
state.UI.dismissedInfoPanels[id] = true;
|
||||||
|
LocalStorage.storeUIState(state.UI);
|
||||||
|
};
|
||||||
|
|
||||||
|
manager.dismissImportantInformation = function(hash) {
|
||||||
|
state.UI.dismissedInfoHash = hash;
|
||||||
|
LocalStorage.storeUIState(state.UI);
|
||||||
};
|
};
|
||||||
|
|
||||||
manager.getState = function() {
|
manager.getState = function() {
|
||||||
|
@ -68,6 +81,11 @@ function StateManagerFactory($q, SystemService, InfoHelper, LocalStorage, Settin
|
||||||
manager.initialize = function () {
|
manager.initialize = function () {
|
||||||
var deferred = $q.defer();
|
var deferred = $q.defer();
|
||||||
|
|
||||||
|
var UIState = LocalStorage.getUIState();
|
||||||
|
if (UIState) {
|
||||||
|
state.UI = UIState;
|
||||||
|
}
|
||||||
|
|
||||||
var endpointState = LocalStorage.getEndpointState();
|
var endpointState = LocalStorage.getEndpointState();
|
||||||
if (endpointState) {
|
if (endpointState) {
|
||||||
state.endpoint = endpointState;
|
state.endpoint = endpointState;
|
||||||
|
|
|
@ -7,7 +7,19 @@
|
||||||
<rd-header-content>Endpoints</rd-header-content>
|
<rd-header-content>Endpoints</rd-header-content>
|
||||||
</rd-header>
|
</rd-header>
|
||||||
|
|
||||||
<information-panel title-text="Information">
|
<information-panel
|
||||||
|
ng-if="motd && applicationState.UI.dismissedInfoHash !== motd.Hash"
|
||||||
|
title-text="Important message"
|
||||||
|
dismiss-action="dismissImportantInformation(motd.Hash)">
|
||||||
|
<span class="text-muted">
|
||||||
|
<p ng-bind-html="motd.Message"></p>
|
||||||
|
</span>
|
||||||
|
</information-panel>
|
||||||
|
|
||||||
|
<information-panel
|
||||||
|
ng-if="!applicationState.UI.dismissedInfoPanels['home-info-01']"
|
||||||
|
title-text="Information"
|
||||||
|
dismiss-action="dismissInformationPanel('home-info-01')">
|
||||||
<span class="small text-muted">
|
<span class="small text-muted">
|
||||||
<p ng-if="endpoints.length > 0">
|
<p ng-if="endpoints.length > 0">
|
||||||
Welcome to Portainer ! Click on any endpoint in the list below to access management features.
|
Welcome to Portainer ! Click on any endpoint in the list below to access management features.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
angular.module('portainer.app')
|
angular.module('portainer.app')
|
||||||
.controller('HomeController', ['$q', '$scope', '$state', 'Authentication', 'EndpointService', 'EndpointHelper', 'GroupService', 'Notifications', 'EndpointProvider', 'StateManager', 'ExtensionManager', 'ModalService',
|
.controller('HomeController', ['$q', '$scope', '$state', 'Authentication', 'EndpointService', 'EndpointHelper', 'GroupService', 'Notifications', 'EndpointProvider', 'StateManager', 'ExtensionManager', 'ModalService', 'MotdService',
|
||||||
function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, GroupService, Notifications, EndpointProvider, StateManager, ExtensionManager, ModalService) {
|
function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, GroupService, Notifications, EndpointProvider, StateManager, ExtensionManager, ModalService, MotdService) {
|
||||||
|
|
||||||
$scope.goToDashboard = function(endpoint) {
|
$scope.goToDashboard = function(endpoint) {
|
||||||
EndpointProvider.setEndpointID(endpoint.Id);
|
EndpointProvider.setEndpointID(endpoint.Id);
|
||||||
|
@ -12,6 +12,14 @@ function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, G
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.dismissImportantInformation = function(hash) {
|
||||||
|
StateManager.dismissImportantInformation(hash);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.dismissInformationPanel = function(id) {
|
||||||
|
StateManager.dismissInformationPanel(id);
|
||||||
|
};
|
||||||
|
|
||||||
function triggerSnapshot() {
|
function triggerSnapshot() {
|
||||||
EndpointService.snapshot()
|
EndpointService.snapshot()
|
||||||
.then(function success(data) {
|
.then(function success(data) {
|
||||||
|
@ -57,6 +65,11 @@ function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, G
|
||||||
function initView() {
|
function initView() {
|
||||||
$scope.isAdmin = Authentication.getUserDetails().role === 1;
|
$scope.isAdmin = Authentication.getUserDetails().role === 1;
|
||||||
|
|
||||||
|
MotdService.motd()
|
||||||
|
.then(function success(data) {
|
||||||
|
$scope.motd = data;
|
||||||
|
});
|
||||||
|
|
||||||
$q.all({
|
$q.all({
|
||||||
endpoints: EndpointService.endpoints(),
|
endpoints: EndpointService.endpoints(),
|
||||||
groups: GroupService.groups()
|
groups: GroupService.groups()
|
||||||
|
|
Loading…
Reference in New Issue