mirror of
https://github.com/Quiq/docker-registry-ui.git
synced 2025-07-19 16:46:58 +00:00
Add feature to change the base path.
This commit is contained in:
parent
eabcf48494
commit
a5fc3a5d30
@ -1,5 +1,7 @@
|
|||||||
# Listen interface.
|
# Listen interface.
|
||||||
listen_addr: 0.0.0.0:8000
|
listen_addr: 0.0.0.0:8000
|
||||||
|
# Base path of Docker Registry UI.
|
||||||
|
base_path: /
|
||||||
|
|
||||||
# Registry URL with schema and port.
|
# Registry URL with schema and port.
|
||||||
registry_url: https://docker-registry.local
|
registry_url: https://docker-registry.local
|
||||||
|
@ -9,8 +9,8 @@ $ docker-compose up -d
|
|||||||
As an example, push the docker-registry-ui image to the local Docker Registry.
|
As an example, push the docker-registry-ui image to the local Docker Registry.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ docker tag quiq/docker-registry-ui localhost:5000/quiq/docker-registry-ui
|
$ docker tag quiq/docker-registry-ui localhost/quiq/docker-registry-ui
|
||||||
$ docker push localhost:5000/quiq/docker-registry-ui
|
$ docker push localhost/quiq/docker-registry-ui
|
||||||
The push refers to repository [localhost:5000/quiq/docker-registry-ui]
|
The push refers to repository [localhost:5000/quiq/docker-registry-ui]
|
||||||
ab414a599bf8: Pushed
|
ab414a599bf8: Pushed
|
||||||
a8da33adf86e: Pushed
|
a8da33adf86e: Pushed
|
||||||
@ -22,4 +22,4 @@ latest: digest: sha256:d88c1ca40986a358e59795992e87e364a0b3b97833aade5abcd79dda0
|
|||||||
```
|
```
|
||||||
|
|
||||||
Then you will find the pushed repository 'quiq/docker-registry-ui' in the following URL.
|
Then you will find the pushed repository 'quiq/docker-registry-ui' in the following URL.
|
||||||
http://localhost:8000/quiq/docker-registry-ui
|
http://localhost/ui/quiq/docker-registry-ui
|
||||||
|
79
example/config/httpd.conf
Normal file
79
example/config/httpd.conf
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
LoadModule mpm_event_module modules/mod_mpm_event.so
|
||||||
|
LoadModule headers_module modules/mod_headers.so
|
||||||
|
|
||||||
|
LoadModule authn_file_module modules/mod_authn_file.so
|
||||||
|
LoadModule authn_core_module modules/mod_authn_core.so
|
||||||
|
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
|
||||||
|
LoadModule authz_user_module modules/mod_authz_user.so
|
||||||
|
LoadModule authz_core_module modules/mod_authz_core.so
|
||||||
|
LoadModule auth_basic_module modules/mod_auth_basic.so
|
||||||
|
LoadModule access_compat_module modules/mod_access_compat.so
|
||||||
|
|
||||||
|
LoadModule log_config_module modules/mod_log_config.so
|
||||||
|
|
||||||
|
LoadModule proxy_module modules/mod_proxy.so
|
||||||
|
LoadModule proxy_http_module modules/mod_proxy_http.so
|
||||||
|
|
||||||
|
LoadModule unixd_module modules/mod_unixd.so
|
||||||
|
|
||||||
|
<IfModule unixd_module>
|
||||||
|
User daemon
|
||||||
|
Group daemon
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
ServerAdmin you@example.com
|
||||||
|
|
||||||
|
ErrorLog /proc/self/fd/2
|
||||||
|
|
||||||
|
LogLevel warn
|
||||||
|
|
||||||
|
<IfModule log_config_module>
|
||||||
|
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
|
||||||
|
LogFormat "%h %l %u %t \"%r\" %>s %b" common
|
||||||
|
|
||||||
|
<IfModule logio_module>
|
||||||
|
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
CustomLog /proc/self/fd/1 common
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
ServerRoot "/usr/local/apache2"
|
||||||
|
|
||||||
|
Listen 80
|
||||||
|
|
||||||
|
<Directory />
|
||||||
|
AllowOverride none
|
||||||
|
Require all denied
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
<VirtualHost *:80>
|
||||||
|
|
||||||
|
ServerName myregistrydomain.com
|
||||||
|
|
||||||
|
Header always set "Docker-Distribution-Api-Version" "registry/2.0"
|
||||||
|
Header onsuccess set "Docker-Distribution-Api-Version" "registry/2.0"
|
||||||
|
|
||||||
|
ProxyRequests off
|
||||||
|
ProxyPreserveHost on
|
||||||
|
|
||||||
|
# no proxy for /error/ (Apache HTTPd errors messages)
|
||||||
|
ProxyPass /error/ !
|
||||||
|
|
||||||
|
ProxyPass /v2 http://registry:5000/v2
|
||||||
|
ProxyPassReverse /v2 http://registry:5000/v2
|
||||||
|
|
||||||
|
<Location /v2>
|
||||||
|
Order deny,allow
|
||||||
|
Allow from all
|
||||||
|
</Location>
|
||||||
|
|
||||||
|
ProxyPass /ui/ http://registry-ui:8000/ui/
|
||||||
|
ProxyPassReverse /ui/ http://registry-ui:8000/ui/
|
||||||
|
|
||||||
|
<Location /ui>
|
||||||
|
Order deny,allow
|
||||||
|
Allow from all
|
||||||
|
</Location>
|
||||||
|
|
||||||
|
</VirtualHost>
|
@ -1,5 +1,7 @@
|
|||||||
# Listen interface.
|
# Listen interface.
|
||||||
listen_addr: 0.0.0.0:8000
|
listen_addr: 0.0.0.0:8000
|
||||||
|
# Base path of Docker Registry UI.
|
||||||
|
base_path: /ui
|
||||||
|
|
||||||
# Registry URL with schema and port.
|
# Registry URL with schema and port.
|
||||||
registry_url: http://registry:5000
|
registry_url: http://registry:5000
|
@ -1,10 +1,17 @@
|
|||||||
version: "2.3"
|
version: "2.3"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
httpd:
|
||||||
|
image: httpd:2.4
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
volumes:
|
||||||
|
- "./config/httpd.conf:/usr/local/apache2/conf/httpd.conf:ro"
|
||||||
|
|
||||||
registry:
|
registry:
|
||||||
image: registry:2
|
image: registry:2
|
||||||
ports:
|
ports:
|
||||||
- "5000:5000"
|
- "5000"
|
||||||
volumes:
|
volumes:
|
||||||
- "./data/registry:/var/lib/registry"
|
- "./data/registry:/var/lib/registry"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
@ -15,10 +22,10 @@ services:
|
|||||||
registry-ui:
|
registry-ui:
|
||||||
image: quiq/docker-registry-ui:latest
|
image: quiq/docker-registry-ui:latest
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000"
|
||||||
volumes:
|
volumes:
|
||||||
- "./data/registry-ui:/opt/data"
|
- "./data/registry-ui:/opt/data"
|
||||||
- "./config.yml:/opt/config.yml:ro"
|
- "./config/registry-ui.yml:/opt/config.yml:ro"
|
||||||
depends_on:
|
depends_on:
|
||||||
registry:
|
registry:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
30
main.go
30
main.go
@ -21,6 +21,7 @@ import (
|
|||||||
|
|
||||||
type configData struct {
|
type configData struct {
|
||||||
ListenAddr string `yaml:"listen_addr"`
|
ListenAddr string `yaml:"listen_addr"`
|
||||||
|
BasePath string `yaml:"base_path"`
|
||||||
RegistryURL string `yaml:"registry_url"`
|
RegistryURL string `yaml:"registry_url"`
|
||||||
VerifyTLS bool `yaml:"verify_tls"`
|
VerifyTLS bool `yaml:"verify_tls"`
|
||||||
Username string `yaml:"registry_username"`
|
Username string `yaml:"registry_username"`
|
||||||
@ -75,6 +76,15 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
// Normalize base path.
|
||||||
|
if a.config.BasePath != "" {
|
||||||
|
if !strings.HasPrefix(a.config.BasePath, "/") {
|
||||||
|
a.config.BasePath = "/" + a.config.BasePath
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(a.config.BasePath, "/") {
|
||||||
|
a.config.BasePath = a.config.BasePath[0 : len(a.config.BasePath)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Init registry API client.
|
// Init registry API client.
|
||||||
a.client = registry.NewClient(a.config.RegistryURL, a.config.VerifyTLS, a.config.Username, a.config.Password)
|
a.client = registry.NewClient(a.config.RegistryURL, a.config.VerifyTLS, a.config.Username, a.config.Password)
|
||||||
@ -99,6 +109,7 @@ func main() {
|
|||||||
// Template engine init.
|
// Template engine init.
|
||||||
view := jet.NewHTMLSet("templates")
|
view := jet.NewHTMLSet("templates")
|
||||||
view.SetDevelopmentMode(a.config.Debug)
|
view.SetDevelopmentMode(a.config.Debug)
|
||||||
|
view.AddGlobal("base_path", a.config.BasePath)
|
||||||
view.AddGlobal("registryHost", u.Host)
|
view.AddGlobal("registryHost", u.Host)
|
||||||
view.AddGlobal("pretty_size", func(size interface{}) string {
|
view.AddGlobal("pretty_size", func(size interface{}) string {
|
||||||
var value float64
|
var value float64
|
||||||
@ -126,22 +137,22 @@ func main() {
|
|||||||
e.Renderer = &template{View: view}
|
e.Renderer = &template{View: view}
|
||||||
|
|
||||||
// Web routes.
|
// Web routes.
|
||||||
e.Static("/static", "static")
|
e.Static(a.config.BasePath+"/static", "static")
|
||||||
e.GET("/", a.viewRepositories)
|
e.GET(a.config.BasePath+"/", a.viewRepositories)
|
||||||
e.GET("/:namespace", a.viewRepositories)
|
e.GET(a.config.BasePath+"/:namespace", a.viewRepositories)
|
||||||
e.GET("/:namespace/:repo", a.viewTags)
|
e.GET(a.config.BasePath+"/:namespace/:repo", a.viewTags)
|
||||||
e.GET("/:namespace/:repo/:tag", a.viewTagInfo)
|
e.GET(a.config.BasePath+"/:namespace/:repo/:tag", a.viewTagInfo)
|
||||||
e.GET("/:namespace/:repo/:tag/delete", a.deleteTag)
|
e.GET(a.config.BasePath+"/:namespace/:repo/:tag/delete", a.deleteTag)
|
||||||
e.GET("/events", a.viewLog)
|
e.GET(a.config.BasePath+"/events", a.viewLog)
|
||||||
|
|
||||||
// Protected event listener.
|
// Protected event listener.
|
||||||
p := e.Group("/api")
|
p := e.Group(a.config.BasePath + "/api")
|
||||||
p.Use(middleware.KeyAuthWithConfig(middleware.KeyAuthConfig{
|
p.Use(middleware.KeyAuthWithConfig(middleware.KeyAuthConfig{
|
||||||
Validator: middleware.KeyAuthValidator(func(token string, c echo.Context) (bool, error) {
|
Validator: middleware.KeyAuthValidator(func(token string, c echo.Context) (bool, error) {
|
||||||
return token == a.config.EventListenerToken, nil
|
return token == a.config.EventListenerToken, nil
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
p.POST("/events", a.receiveEvents)
|
p.POST(a.config.BasePath+"/events", a.receiveEvents)
|
||||||
|
|
||||||
e.Logger.Fatal(e.Start(a.config.ListenAddr))
|
e.Logger.Fatal(e.Start(a.config.ListenAddr))
|
||||||
}
|
}
|
||||||
@ -170,7 +181,6 @@ func (a *apiClient) viewRepositories(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repos, _ := a.client.Repositories(true)[namespace]
|
repos, _ := a.client.Repositories(true)[namespace]
|
||||||
|
|
||||||
data := jet.VarMap{}
|
data := jet.VarMap{}
|
||||||
data.Set("namespace", namespace)
|
data.Set("namespace", namespace)
|
||||||
data.Set("namespaces", a.client.Namespaces())
|
data.Set("namespaces", a.client.Namespaces())
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<h2>Docker Registry UI</h2>
|
<h2>Docker Registry UI</h2>
|
||||||
</div>
|
</div>
|
||||||
<div style="float: right">
|
<div style="float: right">
|
||||||
<a href="/events">Event Log</a>
|
<a href="{{ base_path }}/events">Event Log</a>
|
||||||
</div>
|
</div>
|
||||||
<div style="clear: both"></div>
|
<div style="clear: both"></div>
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
{{block body()}}
|
{{block body()}}
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="{{ base_path }}/">Home</a></li>
|
||||||
<li class="active">Event Log</li>
|
<li class="active">Event Log</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
@ -8,12 +8,14 @@
|
|||||||
"stateSave": true
|
"stateSave": true
|
||||||
});
|
});
|
||||||
$('#namespace').on('change', function (e) {
|
$('#namespace').on('change', function (e) {
|
||||||
window.location = '/' + this.value;
|
window.location = '{{ base_path }}/' + this.value;
|
||||||
});
|
});
|
||||||
if (window.location.pathname == '/') {
|
namespace = window.location.pathname;
|
||||||
|
namespace = namespace.replace("{{ base_path }}", "");
|
||||||
|
if (namespace == '/') {
|
||||||
namespace = 'library';
|
namespace = 'library';
|
||||||
} else {
|
} else {
|
||||||
namespace = window.location.pathname.split('/')[1]
|
namespace = namespace.split('/')[1]
|
||||||
}
|
}
|
||||||
$('#namespace').val(namespace);
|
$('#namespace').val(namespace);
|
||||||
});
|
});
|
||||||
@ -31,7 +33,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="{{ base_path }}/">Home</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<table id="datatable" class="table table-striped table-bordered">
|
<table id="datatable" class="table table-striped table-bordered">
|
||||||
@ -44,7 +46,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{{range repo := repos}}
|
{{range repo := repos}}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="/{{ namespace }}/{{ repo }}">{{ repo }}</a></td>
|
<td><a href="{{ base_path }}/{{ namespace }}/{{ repo }}">{{ repo }}</a></td>
|
||||||
<td>{{ tagCounts[namespace+"/"+repo] }}</td>
|
<td>{{ tagCounts[namespace+"/"+repo] }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
{{block body()}}
|
{{block body()}}
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="{{ base_path }}/">Home</a></li>
|
||||||
{{if namespace != "library"}}
|
{{if namespace != "library"}}
|
||||||
<li><a href="/{{ namespace }}">{{ namespace }}</a></li>
|
<li><a href="{{ base_path }}/{{ namespace }}">{{ namespace }}</a></li>
|
||||||
{{end}}
|
{{end}}
|
||||||
<li><a href="/{{ namespace }}/{{ repo }}">{{ repo }}</a></li>
|
<li><a href="{{ base_path }}/{{ namespace }}/{{ repo }}">{{ repo }}</a></li>
|
||||||
<li class="active">{{ tag }}</li>
|
<li class="active">{{ tag }}</li>
|
||||||
</ol>
|
</ol>
|
||||||
<table class="table table-striped table-bordered">
|
<table class="table table-striped table-bordered">
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{{extends "base.html"}}
|
{{extends "base.html"}}
|
||||||
|
|
||||||
{{block head()}}
|
{{block head()}}
|
||||||
<script type="text/javascript" src="/static/bootstrap-confirmation.min.js"></script>
|
<script type="text/javascript" src="{{ base_path }}/static/bootstrap-confirmation.min.js"></script>
|
||||||
<script type="text/javascript" src="https://cdn.datatables.net/plug-ins/1.10.16/sorting/natural.js"></script>
|
<script type="text/javascript" src="https://cdn.datatables.net/plug-ins/1.10.16/sorting/natural.js"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
@ -29,9 +29,9 @@
|
|||||||
|
|
||||||
{{block body()}}
|
{{block body()}}
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="{{ base_path }}/">Home</a></li>
|
||||||
{{if namespace != "library"}}
|
{{if namespace != "library"}}
|
||||||
<li><a href="/{{ namespace }}">{{ namespace }}</a></li>
|
<li><a href="{{ base_path }}/{{ namespace }}">{{ namespace }}</a></li>
|
||||||
{{end}}
|
{{end}}
|
||||||
<li class="active">{{ repo }}</li>
|
<li class="active">{{ repo }}</li>
|
||||||
</ol>
|
</ol>
|
||||||
@ -46,9 +46,9 @@
|
|||||||
{{range tag := tags}}
|
{{range tag := tags}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="/{{ namespace }}/{{ repo }}/{{ tag }}">{{ tag }}</a>
|
<a href="{{ base_path }}/{{ namespace }}/{{ repo }}/{{ tag }}">{{ tag }}</a>
|
||||||
{{if deleteAllowed}}
|
{{if deleteAllowed}}
|
||||||
<a href="/{{ namespace }}/{{ repo }}/{{ tag }}/delete" data-toggle="confirmation" class="btn btn-danger btn-xs pull-right" role="button">Delete</a>
|
<a href="{{ base_path }}/{{ namespace }}/{{ repo }}/{{ tag }}/delete" data-toggle="confirmation" class="btn btn-danger btn-xs pull-right" role="button">Delete</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
Loading…
Reference in New Issue
Block a user