mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-22 18:25:56 +01:00
merge with dev branch
This commit is contained in:
commit
a33f4151e2
@ -101,7 +101,7 @@ script:
|
|||||||
|
|
||||||
- docker ps
|
- docker ps
|
||||||
- ./tests/notarytest.sh
|
- ./tests/notarytest.sh
|
||||||
- go run tests/startuptest.go https://localhost/
|
- ./tests/startuptest.sh
|
||||||
- go run tests/userlogintest.go -name ${HARBOR_ADMIN} -passwd ${HARBOR_ADMIN_PASSWD}
|
- go run tests/userlogintest.go -name ${HARBOR_ADMIN} -passwd ${HARBOR_ADMIN_PASSWD}
|
||||||
|
|
||||||
# - sudo ./tests/testprepare.sh
|
# - sudo ./tests/testprepare.sh
|
||||||
|
4
Makefile
4
Makefile
@ -390,9 +390,9 @@ down:
|
|||||||
[ $$CONTINUE = "y" ] || [ $$CONTINUE = "Y" ] || (echo "Exiting."; exit 1;)
|
[ $$CONTINUE = "y" ] || [ $$CONTINUE = "Y" ] || (echo "Exiting."; exit 1;)
|
||||||
@echo "stoping harbor instance..."
|
@echo "stoping harbor instance..."
|
||||||
@if [ "$(NOTARYFLAG)" = "true" ] ; then \
|
@if [ "$(NOTARYFLAG)" = "true" ] ; then \
|
||||||
$(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSENOTARYFILENAME) down ; \
|
$(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSENOTARYFILENAME) down -v ; \
|
||||||
else \
|
else \
|
||||||
$(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) down ; \
|
$(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) down -v ; \
|
||||||
fi
|
fi
|
||||||
@echo "Done."
|
@echo "Done."
|
||||||
|
|
||||||
|
@ -1,63 +1,32 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIFBDCCAuygAwIBAgIJAMbWdVJcKhXYMA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNV
|
MIIFdjCCA14CCQCeVwANSZmmiDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC
|
||||||
BAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEPMA0G
|
VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCVBhbG8gQWx0bzEVMBMG
|
||||||
A1UECgwGRG9ja2VyMScwJQYDVQQDDB5Ob3RhcnkgSW50ZXJtZWRpYXRlIFRlc3Rp
|
A1UECgwMVk13YXJlLCBJbmMuMQ8wDQYDVQQLDAZIYXJib3IxJDAiBgNVBAMMG1Nl
|
||||||
bmcgQ0EwHhcNMTcwMTIzMDYwMzM3WhcNMTkwMjEyMDYwMzM3WjBbMQswCQYDVQQG
|
bGYtc2lnbmVkIGJ5IFZNd2FyZSwgSW5jLjAeFw0xNzAzMjQwNTMyMDBaFw0yNzAz
|
||||||
EwJVUzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDzANBgNV
|
MjIwNTMyMDBaMHUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIw
|
||||||
BAoMBkRvY2tlcjEWMBQGA1UEAwwNbm90YXJ5LXNpZ25lcjCCASIwDQYJKoZIhvcN
|
EAYDVQQHDAlQYWxvIEFsdG8xFTATBgNVBAoMDFZNd2FyZSwgSW5jLjEPMA0GA1UE
|
||||||
AQEBBQADggEPADCCAQoCggEBANhO8+K9xT6M9dQC90Hxs6bmTXWQzE5oV2kLeVKq
|
CwwGSGFyYm9yMRUwEwYDVQQDDAxub3RhcnlzaWduZXIwggIiMA0GCSqGSIb3DQEB
|
||||||
OjwAvGt6wBE2XJCAbTS3FORIOyoOVQDVCv2Pk2lZXGWqSrH8SY2umjRJIhPDiqN9
|
AQUAA4ICDwAwggIKAoICAQC6TV2RCoH8d1g6xFvDo4FL9v+pGLe5+bu9ryjTaLbN
|
||||||
V5M/gcmMm2EUgwmp2l4bsDk1MQ6GSbud5kjYGZcp9uXxAVO8tfLVLQF7ohJYqiex
|
dH/Cmf5/8WrmgJ3vG2Ksk796J7qsVddwvQkZn6NwDm2Tm+ETMCG85yEA3jl4Kr9R
|
||||||
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
|
||||||
-----END CERTIFICATE-----
|
b0cz7JqJZgFXfEnykic3IwZs7m7Xrl9B/vvaVub9Fb5LQ7rIzrO7VkoILov/G41B
|
||||||
-----BEGIN CERTIFICATE-----
|
Pd4/kagjXDTWd+UBMvZF6YGjr+TUZi5ooi7bvQ3X6N9WNYKW4a1DOokz9janStiL
|
||||||
MIIF1TCCA72gAwIBAgIJAMk2DFRLRSRRMA0GCSqGSIb3DQEBCwUAMF8xCzAJBgNV
|
MrTKyOEOBi0Aew==
|
||||||
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
|
|
||||||
XJCKYD/1jYIB
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
@ -1,28 +1,52 @@
|
|||||||
-----BEGIN RSA PRIVATE KEY-----
|
-----BEGIN PRIVATE KEY-----
|
||||||
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
|
||||||
-----END RSA PRIVATE KEY-----
|
8zOHj+j6uV8ddG79D6FqNJHAQwpou/p+XO/BGDFgX22x4F68Z0gCQcmoyAE7ppOp
|
||||||
|
dqq3lPoDbRQ02/5cqaIA6dhmfjK2cpz4y1nUxffzY7qJjpoB/YSdR66cCNiYcJzp
|
||||||
|
fMVBXhF9Iyj/Cah1w+hc0NOy9dW15afFaLFK0zrtAzEaVxH/0QKCAQEAwRPOwSCl
|
||||||
|
XrMYXmc91TF6XbhErILHK/pIEOIMF09KNJvSjY0188Ram/pFbPRYh0cIyASmRGXL
|
||||||
|
Qq5B1Qi0vx5TCq1OCrW2yeE7zboAlnADhk1u9N8YmL6JrCKVGQO7wFD3V8uphXdM
|
||||||
|
tixNa5WvJ6eE5Vq+SVy99V5pQgb8ErrISlW4MYK7LI7DruSDuM2tHtiOcXcdTVej
|
||||||
|
1stXJZkH46RYvxxid9tRzfiB8K5ziZfLwPNf2wRyj1J4ojn5pPNhhfkjJ24LCZGt
|
||||||
|
JxwSXqdP+4x7by6x3mU+hutU/lF3jl+0edSnU0cZ6lvuq2T5YGgda/VXlv1ZFQUw
|
||||||
|
rwUXD9unU+aLgQKCAQEA9R74/pI5sthAVHFsKStb9dComtNGstI59aCF5h3oZvV1
|
||||||
|
Lvj/q9dARWqMS9qplOoV58MMCWikmhJNw3IMTvVZsjBgyzRVEJ4aDKttcQXde0Ys
|
||||||
|
w3m0LdTsxtSHu5XapY032FHG/gLlI+Pm48mjqbQsou6OyOOEJLNhO0qmqc/2tB4T
|
||||||
|
v6PdTM9enAYnqCcCTQSlTfSTNJJOYT2OTuRB4U7hUvQoGTSOInrmwLRDNBjQuCso
|
||||||
|
/zNQCQbu2P6EPYmam5yjZDTUxqZL+G/GvK49Fp9JXlQc5ycke7rD+uwa3s+3wCtG
|
||||||
|
rH9gJitfQZrxj+Cj9EOwj0bfJLbac6ZD0CkH5GNeIQKCAQBdoGFOPapzdZ2HicDu
|
||||||
|
NQQFlmmWzgQPS1rO9Q6v7v8o67b6dVOIVdsqb/5ii0qyrruPYtHNsR8TwrShvYsI
|
||||||
|
cogKUWfawatV0ibR6DSIvuC2q632iIjA6QSRuGNcsfbFl32Z0WTvF57XaDxSw08g
|
||||||
|
h5dmMM69fH+REKsyHXj3DCQ8B70+JQrm3IP/t0g4wWQF5TWNyBkpfCoy6n/j94Vf
|
||||||
|
2j4+zmDhhjTxEGTSdYYJXtarRllhN5Ll9TQSVtK8LllIQjvNzwsDJOU2ZeJyi+e5
|
||||||
|
L7Jbg+U01xuvCUc52/+Bxt8ZhQlu1Le4ccQW0Ows19AMnfhPe6NLEi09cdZxFi7Z
|
||||||
|
/J4BAoIBABCzkBDFxZdfWYt69VBt9PSG8eJ6avny3hXCtKaHIQb+aD5nKjRP0DVh
|
||||||
|
gyutCo6RasMEc6D1tJGyR/Xvhm64q4JPb5UbSaRQiVYKdgRtMM9pZeBkcBtNs18K
|
||||||
|
yMx5ajgYorrbi86hXHX7q+JYP8MCbcqqAUSl/Hi8nPxc1foTiCNDf4kGoHvXmoxt
|
||||||
|
0tA65tFFQhEA6KBn68SDkyTsl/zb5Sx0GJY4kZkOeF3GaxPFX12skgXv95GJUskX
|
||||||
|
88RJsH4Qqqtzbzj8R241BH8OrcOoyELc6xPioEqUHKVxSIf2ylITbj0UQHd2u0mN
|
||||||
|
tajKl+aoc+CDxUYbilzhhKetWWF/cJY=
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
@ -118,6 +118,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 80:80
|
- 80:80
|
||||||
- 443:443
|
- 443:443
|
||||||
|
- 4443:4443
|
||||||
depends_on:
|
depends_on:
|
||||||
- mysql
|
- mysql
|
||||||
- registry
|
- registry
|
||||||
|
@ -20,19 +20,10 @@ max_job_workers = 3
|
|||||||
|
|
||||||
#Determine whether or not to generate certificate for the registry's token.
|
#Determine whether or not to generate certificate for the registry's token.
|
||||||
#If the value is on, the prepare script creates new root cert and private key
|
#If the value is on, the prepare script creates new root cert and private key
|
||||||
#for generating token to access the registry. If the value is off, a key/certificate must
|
#for generating token to access the registry. If the value is off the default key/cert will be used.
|
||||||
#be supplied for token generation.
|
#This flag also controls the creation of the notary signer's cert.
|
||||||
customize_crt = on
|
customize_crt = on
|
||||||
|
|
||||||
#Information of your organization for certificate
|
|
||||||
crt_country = CN
|
|
||||||
crt_state = State
|
|
||||||
crt_location = CN
|
|
||||||
crt_organization = organization
|
|
||||||
crt_organizationalunit = organizational unit
|
|
||||||
crt_commonname = example.com
|
|
||||||
crt_email = example@example.com
|
|
||||||
|
|
||||||
#The path of cert and key files for nginx, they are applied only the protocol is set to https
|
#The path of cert and key files for nginx, they are applied only the protocol is set to https
|
||||||
ssl_cert = /data/cert/server.crt
|
ssl_cert = /data/cert/server.crt
|
||||||
ssl_cert_key = /data/cert/server.key
|
ssl_cert_key = /data/cert/server.key
|
||||||
|
@ -166,13 +166,13 @@ then
|
|||||||
if [ -n "$(docker-compose -f docker-compose.yml -f docker-compose.notary.yml ps -q)" ]
|
if [ -n "$(docker-compose -f docker-compose.yml -f docker-compose.notary.yml ps -q)" ]
|
||||||
then
|
then
|
||||||
note "stopping existing Harbor instance ..."
|
note "stopping existing Harbor instance ..."
|
||||||
docker-compose -f docker-compose.yml -f docker-compose.notary.yml down
|
docker-compose -f docker-compose.yml -f docker-compose.notary.yml down -v
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if [ -n "$(docker-compose -f docker-compose.yml ps -q)" ]
|
if [ -n "$(docker-compose -f docker-compose.yml ps -q)" ]
|
||||||
then
|
then
|
||||||
note "stopping existing Harbor instance ..."
|
note "stopping existing Harbor instance ..."
|
||||||
docker-compose -f docker-compose.yml down
|
docker-compose -f docker-compose.yml down -v
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
45
make/prepare
45
make/prepare
@ -135,13 +135,6 @@ if protocol == "https":
|
|||||||
cert_path = rcp.get("configuration", "ssl_cert")
|
cert_path = rcp.get("configuration", "ssl_cert")
|
||||||
cert_key_path = rcp.get("configuration", "ssl_cert_key")
|
cert_key_path = rcp.get("configuration", "ssl_cert_key")
|
||||||
customize_crt = rcp.get("configuration", "customize_crt")
|
customize_crt = rcp.get("configuration", "customize_crt")
|
||||||
crt_country = rcp.get("configuration", "crt_country")
|
|
||||||
crt_state = rcp.get("configuration", "crt_state")
|
|
||||||
crt_location = rcp.get("configuration", "crt_location")
|
|
||||||
crt_organization = rcp.get("configuration", "crt_organization")
|
|
||||||
crt_organizationalunit = rcp.get("configuration", "crt_organizationalunit")
|
|
||||||
crt_commonname = rcp.get("configuration", "crt_commonname")
|
|
||||||
crt_email = rcp.get("configuration", "crt_email")
|
|
||||||
max_job_workers = rcp.get("configuration", "max_job_workers")
|
max_job_workers = rcp.get("configuration", "max_job_workers")
|
||||||
token_expiration = rcp.get("configuration", "token_expiration")
|
token_expiration = rcp.get("configuration", "token_expiration")
|
||||||
verify_remote_cert = rcp.get("configuration", "verify_remote_cert")
|
verify_remote_cert = rcp.get("configuration", "verify_remote_cert")
|
||||||
@ -273,44 +266,43 @@ def stat_decorator(func):
|
|||||||
|
|
||||||
@stat_decorator
|
@stat_decorator
|
||||||
def create_root_cert(subj, key_path="./k.key", cert_path="./cert.crt"):
|
def create_root_cert(subj, key_path="./k.key", cert_path="./cert.crt"):
|
||||||
rc = subprocess.call(["openssl", "genrsa", "-out", key_path, "4096"])
|
rc = subprocess.call(["openssl", "genrsa", "-out", key_path, "4096"], stdout=FNULL, stderr=subprocess.STDOUT)
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
return rc
|
return rc
|
||||||
return subprocess.call(["openssl", "req", "-new", "-x509", "-key", key_path,\
|
return subprocess.call(["openssl", "req", "-new", "-x509", "-key", key_path,\
|
||||||
"-out", cert_path, "-days", "3650", "-subj", subj])
|
"-out", cert_path, "-days", "3650", "-subj", subj], stdout=FNULL, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
@stat_decorator
|
@stat_decorator
|
||||||
def create_cert(subj, ca_key, ca_cert, key_path="./k.key", cert_path="./cert.crt"):
|
def create_cert(subj, ca_key, ca_cert, key_path="./k.key", cert_path="./cert.crt"):
|
||||||
cert_dir = os.path.dirname(cert_path)
|
cert_dir = os.path.dirname(cert_path)
|
||||||
csr_path = os.path.join(cert_dir, "tmp.csr")
|
csr_path = os.path.join(cert_dir, "tmp.csr")
|
||||||
rc = subprocess.call(["openssl", "req", "-newkey", "rsa:4096", "-nodes","-sha256","-keyout", key_path,\
|
rc = subprocess.call(["openssl", "req", "-newkey", "rsa:4096", "-nodes","-sha256","-keyout", key_path,\
|
||||||
"-out", csr_path, "-subj", subj])
|
"-out", csr_path, "-subj", subj], stdout=FNULL, stderr=subprocess.STDOUT)
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
return rc
|
return rc
|
||||||
return subprocess.call(["openssl", "x509", "-req", "-days", "3650", "-in", csr_path, "-CA", \
|
return subprocess.call(["openssl", "x509", "-req", "-days", "3650", "-in", csr_path, "-CA", \
|
||||||
ca_cert, "-CAkey", ca_key, "-CAcreateserial", "-out", cert_path])
|
ca_cert, "-CAkey", ca_key, "-CAcreateserial", "-out", cert_path], stdout=FNULL, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
def openssl_is_installed(stat):
|
def openssl_installed():
|
||||||
if stat == 0:
|
shell_stat = subprocess.check_call(["which", "openssl"], stdout=FNULL, stderr=subprocess.STDOUT)
|
||||||
return True
|
if shell_stat != 0:
|
||||||
else:
|
|
||||||
print("Cannot find openssl installed in this computer\nUse default SSL certificate file")
|
print("Cannot find openssl installed in this computer\nUse default SSL certificate file")
|
||||||
return False
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
if customize_crt == 'on':
|
if customize_crt == 'on' and openssl_installed():
|
||||||
shell_stat = subprocess.check_call(["which", "openssl"], stdout=FNULL, stderr=subprocess.STDOUT)
|
shell_stat = subprocess.check_call(["which", "openssl"], stdout=FNULL, stderr=subprocess.STDOUT)
|
||||||
if openssl_is_installed(shell_stat):
|
empty_subj = "/C=/ST=/L=/O=/CN=/"
|
||||||
empty_subj = "/C=/ST=/L=/O=/CN=/"
|
private_key_pem = os.path.join(config_dir, "ui", "private_key.pem")
|
||||||
private_key_pem = os.path.join(config_dir, "ui", "private_key.pem")
|
root_crt = os.path.join(config_dir, "registry", "root.crt")
|
||||||
root_crt = os.path.join(config_dir, "registry", "root.crt")
|
create_root_cert(empty_subj, key_path=private_key_pem, cert_path=root_crt)
|
||||||
create_root_cert(empty_subj, key_path=private_key_pem, cert_path=root_crt)
|
|
||||||
else:
|
else:
|
||||||
print("Copied configuration file: %s" % ui_config_dir + "private_key.pem")
|
print("Copied configuration file: %s" % ui_config_dir + "private_key.pem")
|
||||||
shutil.copyfile(os.path.join(templates_dir, "ui", "private_key.pem"), os.path.join(ui_config_dir, "private_key.pem"))
|
shutil.copyfile(os.path.join(templates_dir, "ui", "private_key.pem"), os.path.join(ui_config_dir, "private_key.pem"))
|
||||||
print("Copied configuration file: %s" % registry_config_dir + "root.crt")
|
print("Copied configuration file: %s" % registry_config_dir + "root.crt")
|
||||||
shutil.copyfile(os.path.join(templates_dir, "registry", "root.crt"), os.path.join(registry_config_dir, "root.crt"))
|
shutil.copyfile(os.path.join(templates_dir, "registry", "root.crt"), os.path.join(registry_config_dir, "root.crt"))
|
||||||
|
|
||||||
FNULL.close()
|
|
||||||
if args.notary_mode:
|
if args.notary_mode:
|
||||||
notary_config_dir = prep_conf_dir(config_dir, "notary")
|
notary_config_dir = prep_conf_dir(config_dir, "notary")
|
||||||
notary_temp_dir = os.path.join(templates_dir, "notary")
|
notary_temp_dir = os.path.join(templates_dir, "notary")
|
||||||
@ -318,13 +310,12 @@ if args.notary_mode:
|
|||||||
if os.path.exists(os.path.join(notary_config_dir, "mysql-initdb.d")):
|
if os.path.exists(os.path.join(notary_config_dir, "mysql-initdb.d")):
|
||||||
shutil.rmtree(os.path.join(notary_config_dir, "mysql-initdb.d"))
|
shutil.rmtree(os.path.join(notary_config_dir, "mysql-initdb.d"))
|
||||||
shutil.copytree(os.path.join(notary_temp_dir, "mysql-initdb.d"), os.path.join(notary_config_dir, "mysql-initdb.d"))
|
shutil.copytree(os.path.join(notary_temp_dir, "mysql-initdb.d"), os.path.join(notary_config_dir, "mysql-initdb.d"))
|
||||||
#TODO:generate certs?
|
if customize_crt == 'on' and openssl_installed():
|
||||||
if customize_crt == 'on':
|
|
||||||
temp_cert_dir = os.path.join(base_dir, "cert_tmp")
|
temp_cert_dir = os.path.join(base_dir, "cert_tmp")
|
||||||
if not os.path.exists(temp_cert_dir):
|
if not os.path.exists(temp_cert_dir):
|
||||||
os.makedirs(temp_cert_dir)
|
os.makedirs(temp_cert_dir)
|
||||||
ca_subj = "/C=US/ST=California/L=Palo Alto/O=Vmware/CN=Self Signed CA/"
|
ca_subj = "/C=US/ST=California/L=Palo Alto/O=VMware, Inc./OU=Harbor/CN=Self-signed by VMware, Inc."
|
||||||
cert_subj = "/C=US/ST=California/L=Palo Alto/O=Vmware/CN=notarysigner/"
|
cert_subj = "/C=US/ST=California/L=Palo Alto/O=VMware, Inc./OU=Harbor/CN=notarysigner"
|
||||||
signer_ca_cert = os.path.join(temp_cert_dir, "notary-signer-ca.crt")
|
signer_ca_cert = os.path.join(temp_cert_dir, "notary-signer-ca.crt")
|
||||||
signer_ca_key = os.path.join(temp_cert_dir, "notary-signer-ca.key")
|
signer_ca_key = os.path.join(temp_cert_dir, "notary-signer-ca.key")
|
||||||
signer_cert_path = os.path.join(temp_cert_dir, "notary-signer.crt")
|
signer_cert_path = os.path.join(temp_cert_dir, "notary-signer.crt")
|
||||||
@ -355,6 +346,6 @@ if args.notary_mode:
|
|||||||
default_alias = ''.join(random.choice(string.ascii_letters) for i in range(8))
|
default_alias = ''.join(random.choice(string.ascii_letters) for i in range(8))
|
||||||
render(os.path.join(notary_temp_dir, "signer_env"), os.path.join(notary_config_dir, "signer_env"), alias = default_alias)
|
render(os.path.join(notary_temp_dir, "signer_env"), os.path.join(notary_config_dir, "signer_env"), alias = default_alias)
|
||||||
|
|
||||||
|
FNULL.close()
|
||||||
print("The configuration files are ready, please use docker-compose to start the service.")
|
print("The configuration files are ready, please use docker-compose to start the service.")
|
||||||
|
|
||||||
|
21
src/ui/views/reset-password-mail.tpl
Normal file
21
src/ui/views/reset-password-mail.tpl
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<!--
|
||||||
|
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<p>Please click this link to reset your password:</p>
|
||||||
|
<a href="{{.URL}}/reset_password?reset_uuid={{.UUID}}">{{.URL}}/reset_password?reset_uuid={{.UUID}}</a>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -76,7 +76,10 @@ const harborRoutes: Routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tags/:id/:repo',
|
path: 'tags/:id/:repo',
|
||||||
component: TagRepositoryComponent
|
component: TagRepositoryComponent,
|
||||||
|
resolve: {
|
||||||
|
projectResolver: ProjectRoutingResolver
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'projects/:id',
|
path: 'projects/:id',
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Http, Headers, RequestOptions } from '@angular/http';
|
import { Http, Headers, RequestOptions } from '@angular/http';
|
||||||
|
|
||||||
import { BaseService } from '../service/base.service';
|
|
||||||
|
|
||||||
import { AuditLog } from './audit-log';
|
import { AuditLog } from './audit-log';
|
||||||
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
@ -13,7 +11,7 @@ import 'rxjs/add/observable/throw';
|
|||||||
export const logEndpoint = "/api/logs";
|
export const logEndpoint = "/api/logs";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuditLogService extends BaseService {
|
export class AuditLogService {
|
||||||
private httpOptions = new RequestOptions({
|
private httpOptions = new RequestOptions({
|
||||||
headers: new Headers({
|
headers: new Headers({
|
||||||
"Content-Type": 'application/json',
|
"Content-Type": 'application/json',
|
||||||
@ -21,9 +19,7 @@ export class AuditLogService extends BaseService {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
constructor(private http: Http) {
|
constructor(private http: Http) {}
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
listAuditLogs(queryParam: AuditLog): Observable<any> {
|
listAuditLogs(queryParam: AuditLog): Observable<any> {
|
||||||
return this.http
|
return this.http
|
||||||
@ -36,12 +32,12 @@ export class AuditLogService extends BaseService {
|
|||||||
username: queryParam.username
|
username: queryParam.username
|
||||||
})
|
})
|
||||||
.map(response => response)
|
.map(response => response)
|
||||||
.catch(error => this.handleError(error));
|
.catch(error => Observable.throw(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
getRecentLogs(lines: number): Observable<AuditLog[]> {
|
getRecentLogs(lines: number): Observable<AuditLog[]> {
|
||||||
return this.http.get(logEndpoint + "?lines=" + lines, this.httpOptions)
|
return this.http.get(logEndpoint + "?lines=" + lines, this.httpOptions)
|
||||||
.map(response => response.json() as AuditLog[])
|
.map(response => response.json() as AuditLog[])
|
||||||
.catch(error => this.handleError(error));
|
.catch(error => Observable.throw(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -31,6 +31,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline" (click)="onCancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
<button type="button" class="btn btn-outline" (click)="onCancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||||
<button type="button" class="btn btn-primary" [disabled]="!projectForm.form.valid" (click)="onSubmit()">{{'BUTTON.OK' | translate}}</button>
|
<button type="button" class="btn btn-primary" [disabled]="projectForm.form.invalid" (click)="onSubmit()">{{'BUTTON.OK' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<clr-datagrid (clrDgRefresh)="refresh($event)">
|
<clr-datagrid (clrDgRefresh)="refresh($event)">
|
||||||
<clr-dg-column>{{'PROJECT.NAME' | translate}}</clr-dg-column>
|
<clr-dg-column>{{'PROJECT.NAME' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column>{{'PROJECT.PUBLIC_OR_PRIVATE' | translate}}</clr-dg-column>
|
<clr-dg-column>{{'PROJECT.PUBLIC_OR_PRIVATE' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column *ngIf="showRoleInfo">{{'PROJECT.ROLE' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column>{{'PROJECT.REPO_COUNT'| translate}}</clr-dg-column>
|
<clr-dg-column>{{'PROJECT.REPO_COUNT'| translate}}</clr-dg-column>
|
||||||
<clr-dg-column>{{'PROJECT.CREATION_TIME' | translate}}</clr-dg-column>
|
<clr-dg-column>{{'PROJECT.CREATION_TIME' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column>{{'PROJECT.DESCRIPTION' | translate}}</clr-dg-column>
|
|
||||||
<clr-dg-row *ngFor="let p of projects" [clrDgItem]="p">
|
<clr-dg-row *ngFor="let p of projects" [clrDgItem]="p">
|
||||||
<clr-dg-action-overflow [hidden]="!listFullMode || p.current_user_role_id !== 1">
|
<clr-dg-action-overflow [hidden]="!listFullMode || p.current_user_role_id !== 1">
|
||||||
<button class="action-item" (click)="newReplicationRule(p)" [hidden]="!isSystemAdmin">{{'PROJECT.REPLICATION_RULE' | translate}}</button>
|
<button class="action-item" (click)="newReplicationRule(p)" [hidden]="!isSystemAdmin">{{'PROJECT.REPLICATION_RULE' | translate}}</button>
|
||||||
@ -12,9 +12,9 @@
|
|||||||
</clr-dg-action-overflow>
|
</clr-dg-action-overflow>
|
||||||
<clr-dg-cell><a href="javascript:void(0)" (click)="goToLink(p.project_id)">{{p.name}}</a></clr-dg-cell>
|
<clr-dg-cell><a href="javascript:void(0)" (click)="goToLink(p.project_id)">{{p.name}}</a></clr-dg-cell>
|
||||||
<clr-dg-cell>{{ (p.public === 1 ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}}</clr-dg-cell>
|
<clr-dg-cell>{{ (p.public === 1 ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell *ngIf="showRoleInfo">{{roleInfo[p.current_user_role_id] | translate}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{p.repo_count}}</clr-dg-cell>
|
<clr-dg-cell>{{p.repo_count}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{p.creation_time}}</clr-dg-cell>
|
<clr-dg-cell>{{p.creation_time}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{p.description}}</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
<clr-dg-footer>
|
<clr-dg-footer>
|
||||||
{{totalRecordCount || (projects ? projects.length : 0)}} {{'PROJECT.ITEMS' | translate}}
|
{{totalRecordCount || (projects ? projects.length : 0)}} {{'PROJECT.ITEMS' | translate}}
|
||||||
|
@ -5,7 +5,7 @@ import { ProjectService } from '../project.service';
|
|||||||
|
|
||||||
import { SessionService } from '../../shared/session.service';
|
import { SessionService } from '../../shared/session.service';
|
||||||
import { SearchTriggerService } from '../../base/global-search/search-trigger.service';
|
import { SearchTriggerService } from '../../base/global-search/search-trigger.service';
|
||||||
import { ListMode } from '../../shared/shared.const';
|
import { ListMode, ProjectTypes, RoleInfo } from '../../shared/shared.const';
|
||||||
|
|
||||||
import { State } from 'clarity-angular';
|
import { State } from 'clarity-angular';
|
||||||
|
|
||||||
@ -24,6 +24,8 @@ export class ListProjectComponent implements OnInit {
|
|||||||
@Input() totalRecordCount: number;
|
@Input() totalRecordCount: number;
|
||||||
pageOffset: number = 1;
|
pageOffset: number = 1;
|
||||||
|
|
||||||
|
@Input() filteredType: string;
|
||||||
|
|
||||||
@Output() paginate = new EventEmitter<State>();
|
@Output() paginate = new EventEmitter<State>();
|
||||||
|
|
||||||
@Output() toggle = new EventEmitter<Project>();
|
@Output() toggle = new EventEmitter<Project>();
|
||||||
@ -31,6 +33,8 @@ export class ListProjectComponent implements OnInit {
|
|||||||
|
|
||||||
@Input() mode: string = ListMode.FULL;
|
@Input() mode: string = ListMode.FULL;
|
||||||
|
|
||||||
|
roleInfo = RoleInfo;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private session: SessionService,
|
private session: SessionService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
@ -43,6 +47,10 @@ export class ListProjectComponent implements OnInit {
|
|||||||
return this.mode === ListMode.FULL && this.session.getCurrentUser() != null;
|
return this.mode === ListMode.FULL && this.session.getCurrentUser() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get showRoleInfo(): boolean {
|
||||||
|
return this.listFullMode && this.filteredType === ProjectTypes[0];
|
||||||
|
}
|
||||||
|
|
||||||
public get isSystemAdmin(): boolean {
|
public get isSystemAdmin(): boolean {
|
||||||
let account = this.session.getCurrentUser();
|
let account = this.session.getCurrentUser();
|
||||||
return account != null && account.has_admin_role > 0;
|
return account != null && account.has_admin_role > 0;
|
||||||
|
@ -36,6 +36,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline" (click)="onCancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
<button type="button" class="btn btn-outline" (click)="onCancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||||
<button type="button" class="btn btn-primary" [disabled]="!memberForm.form.valid" (click)="onSubmit()">{{'BUTTON.OK' | translate}}</button>
|
<button type="button" class="btn btn-primary" [disabled]="memberForm.form.invalid" (click)="onSubmit()">{{'BUTTON.OK' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
|
@ -17,15 +17,15 @@
|
|||||||
<clr-datagrid>
|
<clr-datagrid>
|
||||||
<clr-dg-column>{{'MEMBER.NAME' | translate}}</clr-dg-column>
|
<clr-dg-column>{{'MEMBER.NAME' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column>{{'MEMBER.ROLE' | translate}}</clr-dg-column>
|
<clr-dg-column>{{'MEMBER.ROLE' | translate}}</clr-dg-column>
|
||||||
<clr-dg-row *ngFor="let u of members">
|
<clr-dg-row *ngFor="let m of members">
|
||||||
<clr-dg-action-overflow [hidden]="u.user_id === currentUser.user_id || !hasProjectAdminRole">
|
<clr-dg-action-overflow [hidden]="m.user_id === currentUser.user_id || !hasProjectAdminRole">
|
||||||
<button class="action-item" (click)="changeRole(u.user_id, 1)">{{'MEMBER.PROJECT_ADMIN' | translate}}</button>
|
<button class="action-item" (click)="changeRole(m, 1)">{{'MEMBER.PROJECT_ADMIN' | translate}}</button>
|
||||||
<button class="action-item" (click)="changeRole(u.user_id, 2)">{{'MEMBER.DEVELOPER' | translate}}</button>
|
<button class="action-item" (click)="changeRole(m, 2)">{{'MEMBER.DEVELOPER' | translate}}</button>
|
||||||
<button class="action-item" (click)="changeRole(u.user_id, 3)">{{'MEMBER.GUEST' | translate}}</button>
|
<button class="action-item" (click)="changeRole(m, 3)">{{'MEMBER.GUEST' | translate}}</button>
|
||||||
<button class="action-item" (click)="deleteMember(u.user_id)">{{'MEMBER.DELETE' | translate}}</button>
|
<button class="action-item" (click)="deleteMember(m)">{{'MEMBER.DELETE' | translate}}</button>
|
||||||
</clr-dg-action-overflow>
|
</clr-dg-action-overflow>
|
||||||
<clr-dg-cell>{{u.username}}</clr-dg-cell>
|
<clr-dg-cell>{{m.username}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{roleInfo[u.role_id] | translate}}</clr-dg-cell>
|
<clr-dg-cell>{{roleInfo[m.role_id] | translate}}</clr-dg-cell>
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
<clr-dg-footer>{{ (members ? members.length : 0) }} {{'MEMBER.ITEMS' | translate}}</clr-dg-footer>
|
<clr-dg-footer>{{ (members ? members.length : 0) }} {{'MEMBER.ITEMS' | translate}}</clr-dg-footer>
|
||||||
</clr-datagrid>
|
</clr-datagrid>
|
||||||
|
@ -15,6 +15,8 @@ import { ConfirmationDialogService } from '../../shared/confirmation-dialog/conf
|
|||||||
import { ConfirmationMessage } from '../../shared/confirmation-dialog/confirmation-message';
|
import { ConfirmationMessage } from '../../shared/confirmation-dialog/confirmation-message';
|
||||||
import { SessionService } from '../../shared/session.service';
|
import { SessionService } from '../../shared/session.service';
|
||||||
|
|
||||||
|
import { RoleInfo } from '../../shared/shared.const';
|
||||||
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import 'rxjs/add/operator/switchMap';
|
import 'rxjs/add/operator/switchMap';
|
||||||
import 'rxjs/add/operator/catch';
|
import 'rxjs/add/operator/catch';
|
||||||
@ -22,7 +24,7 @@ import 'rxjs/add/operator/map';
|
|||||||
import 'rxjs/add/observable/throw';
|
import 'rxjs/add/observable/throw';
|
||||||
import { Subscription } from 'rxjs/Subscription';
|
import { Subscription } from 'rxjs/Subscription';
|
||||||
|
|
||||||
export const roleInfo: {} = { 1: 'MEMBER.PROJECT_ADMIN', 2: 'MEMBER.DEVELOPER', 3: 'MEMBER.GUEST' };
|
import { Project } from '../../project/project';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
moduleId: module.id,
|
moduleId: module.id,
|
||||||
@ -31,31 +33,22 @@ export const roleInfo: {} = { 1: 'MEMBER.PROJECT_ADMIN', 2: 'MEMBER.DEVELOPER',
|
|||||||
})
|
})
|
||||||
export class MemberComponent implements OnInit, OnDestroy {
|
export class MemberComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
currentUser: SessionUser;
|
|
||||||
members: Member[];
|
members: Member[];
|
||||||
projectId: number;
|
projectId: number;
|
||||||
roleInfo = roleInfo;
|
roleInfo = RoleInfo;
|
||||||
private delSub: Subscription;
|
private delSub: Subscription;
|
||||||
|
|
||||||
@ViewChild(AddMemberComponent)
|
@ViewChild(AddMemberComponent)
|
||||||
addMemberComponent: AddMemberComponent;
|
addMemberComponent: AddMemberComponent;
|
||||||
|
|
||||||
|
currentUser: SessionUser;
|
||||||
hasProjectAdminRole: boolean;
|
hasProjectAdminRole: boolean;
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private router: Router,
|
constructor(private route: ActivatedRoute, private router: Router,
|
||||||
private memberService: MemberService, private messageService: MessageService,
|
private memberService: MemberService, private messageService: MessageService,
|
||||||
private deletionDialogService: ConfirmationDialogService,
|
private deletionDialogService: ConfirmationDialogService,
|
||||||
session: SessionService) {
|
private session: SessionService) {
|
||||||
//Get current user from registered resolver.
|
|
||||||
this.currentUser = session.getCurrentUser();
|
|
||||||
let projectMembers: Member[] = session.getProjectMembers();
|
|
||||||
if(this.currentUser && projectMembers) {
|
|
||||||
let currentMember = projectMembers.find(m=>m.user_id === this.currentUser.user_id);
|
|
||||||
if(currentMember) {
|
|
||||||
this.hasProjectAdminRole = (currentMember.role_name === 'projectAdmin');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.delSub = deletionDialogService.confirmationConfirm$.subscribe(message => {
|
this.delSub = deletionDialogService.confirmationConfirm$.subscribe(message => {
|
||||||
if (message &&
|
if (message &&
|
||||||
message.state === ConfirmationState.CONFIRMED &&
|
message.state === ConfirmationState.CONFIRMED &&
|
||||||
@ -82,8 +75,7 @@ export class MemberComponent implements OnInit, OnDestroy {
|
|||||||
error => {
|
error => {
|
||||||
this.router.navigate(['/harbor', 'projects']);
|
this.router.navigate(['/harbor', 'projects']);
|
||||||
this.messageService.announceMessage(error.status, 'Failed to get project member with project ID:' + projectId, AlertType.DANGER);
|
this.messageService.announceMessage(error.status, 'Failed to get project member with project ID:' + projectId, AlertType.DANGER);
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@ -97,6 +89,15 @@ export class MemberComponent implements OnInit, OnDestroy {
|
|||||||
this.projectId = +this.route.snapshot.parent.params['id'];
|
this.projectId = +this.route.snapshot.parent.params['id'];
|
||||||
console.log('Get projectId from route params snapshot:' + this.projectId);
|
console.log('Get projectId from route params snapshot:' + this.projectId);
|
||||||
|
|
||||||
|
this.currentUser = this.session.getCurrentUser();
|
||||||
|
//Get current user from registered resolver.
|
||||||
|
let resolverData = this.route.snapshot.parent.data;
|
||||||
|
if(resolverData) {
|
||||||
|
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.retrieve(this.projectId, '');
|
this.retrieve(this.projectId, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,25 +109,27 @@ export class MemberComponent implements OnInit, OnDestroy {
|
|||||||
this.retrieve(this.projectId, '');
|
this.retrieve(this.projectId, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
changeRole(userId: number, roleId: number) {
|
changeRole(m: Member, roleId: number) {
|
||||||
this.memberService
|
if(m) {
|
||||||
.changeMemberRole(this.projectId, userId, roleId)
|
this.memberService
|
||||||
.subscribe(
|
.changeMemberRole(this.projectId, m.user_id, roleId)
|
||||||
response => {
|
.subscribe(
|
||||||
this.messageService.announceMessage(response, 'MEMBER.SWITCHED_SUCCESS', AlertType.SUCCESS);
|
response => {
|
||||||
console.log('Successful change role with user ' + userId + ' to roleId ' + roleId);
|
this.messageService.announceMessage(response, 'MEMBER.SWITCHED_SUCCESS', AlertType.SUCCESS);
|
||||||
this.retrieve(this.projectId, '');
|
console.log('Successful change role with user ' + m.user_id + ' to roleId ' + roleId);
|
||||||
},
|
this.retrieve(this.projectId, '');
|
||||||
error => this.messageService.announceMessage(error.status, 'Failed to change role with user ' + userId + ' to roleId ' + roleId, AlertType.DANGER)
|
},
|
||||||
);
|
error => this.messageService.announceMessage(error.status, 'Failed to change role with user ' + m.user_id + ' to roleId ' + roleId, AlertType.DANGER)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteMember(userId: number) {
|
deleteMember(m: Member) {
|
||||||
let deletionMessage: ConfirmationMessage = new ConfirmationMessage(
|
let deletionMessage: ConfirmationMessage = new ConfirmationMessage(
|
||||||
'MEMBER.DELETION_TITLE',
|
'MEMBER.DELETION_TITLE',
|
||||||
'MEMBER.DELETION_SUMMARY',
|
'MEMBER.DELETION_SUMMARY',
|
||||||
userId + "",
|
m.username,
|
||||||
userId,
|
m.user_id,
|
||||||
ConfirmationTargets.PROJECT_MEMBER
|
ConfirmationTargets.PROJECT_MEMBER
|
||||||
);
|
);
|
||||||
this.deletionDialogService.openComfirmDialog(deletionMessage);
|
this.deletionDialogService.openComfirmDialog(deletionMessage);
|
||||||
|
@ -6,22 +6,19 @@ import 'rxjs/add/operator/catch';
|
|||||||
import 'rxjs/add/operator/map';
|
import 'rxjs/add/operator/map';
|
||||||
import 'rxjs/add/observable/throw';
|
import 'rxjs/add/observable/throw';
|
||||||
|
|
||||||
import { BaseService } from '../../service/base.service';
|
|
||||||
import { Member } from './member';
|
import { Member } from './member';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MemberService extends BaseService {
|
export class MemberService {
|
||||||
|
|
||||||
constructor(private http: Http) {
|
constructor(private http: Http) {}
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
listMembers(projectId: number, username: string): Observable<Member[]> {
|
listMembers(projectId: number, username: string): Observable<Member[]> {
|
||||||
console.log('Get member from project_id:' + projectId + ', username:' + username);
|
console.log('Get member from project_id:' + projectId + ', username:' + username);
|
||||||
return this.http
|
return this.http
|
||||||
.get(`/api/projects/${projectId}/members?username=${username}`)
|
.get(`/api/projects/${projectId}/members?username=${username}`)
|
||||||
.map(response=>response.json())
|
.map(response=>response.json() as Member[])
|
||||||
.catch(error=>this.handleError(error));
|
.catch(error=>Observable.throw(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
addMember(projectId: number, username: string, roleId: number): Observable<any> {
|
addMember(projectId: number, username: string, roleId: number): Observable<any> {
|
||||||
|
@ -5,10 +5,10 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" routerLink="repository" routerLinkActive="active">{{'PROJECT_DETAIL.REPOSITORIES' | translate}}</a>
|
<a class="nav-link" routerLink="repository" routerLinkActive="active">{{'PROJECT_DETAIL.REPOSITORIES' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" *ngIf="isSessionValid">
|
<li class="nav-item" *ngIf="isSystemAdmin || isMember">
|
||||||
<a class="nav-link" routerLink="member" routerLinkActive="active">{{'PROJECT_DETAIL.USERS' | translate}}</a>
|
<a class="nav-link" routerLink="member" routerLinkActive="active">{{'PROJECT_DETAIL.USERS' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" *ngIf="isSessionValid">
|
<li class="nav-item" *ngIf="isSystemAdmin || isMember">
|
||||||
<a class="nav-link" routerLink="log" routerLinkActive="active">{{'PROJECT_DETAIL.LOGS' | translate}}</a>
|
<a class="nav-link" routerLink="log" routerLinkActive="active">{{'PROJECT_DETAIL.LOGS' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" *ngIf="isSessionValid && isSystemAdmin">
|
<li class="nav-item" *ngIf="isSessionValid && isSystemAdmin">
|
||||||
|
@ -4,6 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router';
|
|||||||
import { Project } from '../project';
|
import { Project } from '../project';
|
||||||
|
|
||||||
import { SessionService } from '../../shared/session.service';
|
import { SessionService } from '../../shared/session.service';
|
||||||
|
import { ProjectService } from '../../project/project.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'project-detail',
|
selector: 'project-detail',
|
||||||
@ -13,13 +14,18 @@ import { SessionService } from '../../shared/session.service';
|
|||||||
export class ProjectDetailComponent {
|
export class ProjectDetailComponent {
|
||||||
|
|
||||||
currentProject: Project;
|
currentProject: Project;
|
||||||
|
isMember: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private sessionService: SessionService) {
|
private sessionService: SessionService,
|
||||||
this.route.data.subscribe(data=>this.currentProject = <Project>data['projectResolver']);
|
private projectService: ProjectService) {
|
||||||
|
|
||||||
|
this.route.data.subscribe(data=>{
|
||||||
|
this.currentProject = <Project>data['projectResolver'];
|
||||||
|
this.isMember = this.currentProject.is_member;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isSystemAdmin(): boolean {
|
public get isSystemAdmin(): boolean {
|
||||||
|
@ -3,23 +3,43 @@ import { Router, Resolve, RouterStateSnapshot, ActivatedRouteSnapshot } from '@a
|
|||||||
|
|
||||||
import { Project } from './project';
|
import { Project } from './project';
|
||||||
import { ProjectService } from './project.service';
|
import { ProjectService } from './project.service';
|
||||||
|
import { SessionService } from '../shared/session.service';
|
||||||
|
import 'rxjs/add/operator/mergeMap';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ProjectRoutingResolver implements Resolve<Project>{
|
export class ProjectRoutingResolver implements Resolve<Project>{
|
||||||
|
|
||||||
constructor(private projectService: ProjectService, private router: Router) {}
|
constructor(
|
||||||
|
private sessionService: SessionService,
|
||||||
|
private projectService: ProjectService,
|
||||||
|
private router: Router) {}
|
||||||
|
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Project> {
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Project> {
|
||||||
let projectId = route.params['id'];
|
let projectId = route.params['id'];
|
||||||
|
console.log('Project resolver, projectID:' + projectId);
|
||||||
return this.projectService
|
return this.projectService
|
||||||
.getProject(projectId)
|
.getProject(projectId)
|
||||||
.then(project=> {
|
.toPromise()
|
||||||
if(project) {
|
.then((project: Project)=> {
|
||||||
return project;
|
if(project) {
|
||||||
} else {
|
let currentUser = this.sessionService.getCurrentUser();
|
||||||
this.router.navigate(['/harbor', 'projects']);
|
let projectMembers = this.sessionService.getProjectMembers();
|
||||||
return null;
|
if(currentUser && projectMembers) {
|
||||||
}
|
let currentMember = projectMembers.find(m=>m.user_id === currentUser.user_id);
|
||||||
|
if(currentMember) {
|
||||||
|
project.is_member = true;
|
||||||
|
project.has_project_admin_role = (currentMember.role_name === 'projectAdmin') || currentUser.has_admin_role === 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return project;
|
||||||
|
} else {
|
||||||
|
this.router.navigate(['/harbor', 'projects']);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
this.router.navigate(['/harbor', 'projects']);
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,11 +1,13 @@
|
|||||||
.header-title {
|
.header-title {
|
||||||
margin-top: 0;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.option-left {
|
.option-left {
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.option-right {
|
.option-right {
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
margin-top: 18px;
|
margin-top: 18px;
|
||||||
}
|
}
|
@ -1,6 +1,9 @@
|
|||||||
<div class="row">
|
<div class="row" style="margin-right: 12px;">
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
<h2 class="header-title">{{'PROJECT.PROJECTS' | translate}}</h2>
|
<h2 class="header-title">{{'PROJECT.PROJECTS' | translate}}</h2>
|
||||||
|
<div>
|
||||||
|
<statistics-panel></statistics-panel>
|
||||||
|
</div>
|
||||||
<div class="row flex-items-xs-between">
|
<div class="row flex-items-xs-between">
|
||||||
<div class="option-left">
|
<div class="option-left">
|
||||||
<button *ngIf="projectCreationRestriction" class="btn btn-primary" (click)="openModal()"><clr-icon shape="add"></clr-icon> {{'PROJECT.PROJECT' | translate}}</button>
|
<button *ngIf="projectCreationRestriction" class="btn btn-primary" (click)="openModal()"><clr-icon shape="add"></clr-icon> {{'PROJECT.PROJECT' | translate}}</button>
|
||||||
@ -18,11 +21,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</clr-dropdown>
|
</clr-dropdown>
|
||||||
<grid-filter filterPlaceholder='{{"PROJECT.FILTER_PLACEHOLDER" | translate}}' (filter)="doSearchProjects($event)"></grid-filter>
|
<grid-filter filterPlaceholder='{{"PROJECT.FILTER_PLACEHOLDER" | translate}}' (filter)="doSearchProjects($event)"></grid-filter>
|
||||||
<a href="javascript:void(0)" (click)="refresh()"><clr-icon shape="refresh"></clr-icon></a>
|
<a href="javascript:void(0)" (click)="refresh()">
|
||||||
|
<clr-icon shape="refresh"></clr-icon>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<list-project [projects]="changedProjects" [filteredType]="projectTypes[currentFilteredType]" (toggle)="toggleProject($event)" (delete)="deleteProject($event)" (paginate)="retrieve($event)" [totalPage]="totalPage" [totalRecordCount]="totalRecordCount"></list-project>
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
</div>
|
||||||
<list-project [projects]="changedProjects" (toggle)="toggleProject($event)" (delete)="deleteProject($event)" (paginate)="retrieve($event)" [totalPage]="totalPage" [totalRecordCount]="totalRecordCount"></list-project>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,9 +25,7 @@ import { State } from 'clarity-angular';
|
|||||||
|
|
||||||
import { AppConfigService } from '../app-config.service';
|
import { AppConfigService } from '../app-config.service';
|
||||||
import { SessionService } from '../shared/session.service';
|
import { SessionService } from '../shared/session.service';
|
||||||
|
import { ProjectTypes } from '../shared/shared.const';
|
||||||
|
|
||||||
const types: {} = { 0: 'PROJECT.MY_PROJECTS', 1: 'PROJECT.PUBLIC_PROJECTS' };
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
moduleId: module.id,
|
moduleId: module.id,
|
||||||
@ -39,7 +37,7 @@ export class ProjectComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
selected = [];
|
selected = [];
|
||||||
changedProjects: Project[];
|
changedProjects: Project[];
|
||||||
projectTypes = types;
|
projectTypes = ProjectTypes;
|
||||||
|
|
||||||
@ViewChild(CreateProjectComponent)
|
@ViewChild(CreateProjectComponent)
|
||||||
creationProject: CreateProjectComponent;
|
creationProject: CreateProjectComponent;
|
||||||
@ -145,7 +143,7 @@ export class ProjectComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
doFilterProjects(filteredType: number): void {
|
doFilterProjects(filteredType: number): void {
|
||||||
console.log('Filter projects with type:' + types[filteredType]);
|
console.log('Filter projects with type:' + this.projectTypes[filteredType]);
|
||||||
this.isPublic = filteredType;
|
this.isPublic = filteredType;
|
||||||
this.currentFilteredType = filteredType;
|
this.currentFilteredType = filteredType;
|
||||||
this.retrieve();
|
this.retrieve();
|
||||||
|
@ -3,8 +3,6 @@ import { Injectable } from '@angular/core';
|
|||||||
import { Http, Headers, RequestOptions, Response, URLSearchParams } from '@angular/http';
|
import { Http, Headers, RequestOptions, Response, URLSearchParams } from '@angular/http';
|
||||||
import { Project } from './project';
|
import { Project } from './project';
|
||||||
|
|
||||||
import { BaseService } from '../service/base.service';
|
|
||||||
|
|
||||||
import { Message } from '../global-message/message';
|
import { Message } from '../global-message/message';
|
||||||
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
@ -22,11 +20,10 @@ export class ProjectService {
|
|||||||
|
|
||||||
constructor(private http: Http) {}
|
constructor(private http: Http) {}
|
||||||
|
|
||||||
getProject(projectId: number): Promise<Project> {
|
getProject(projectId: number): Observable<any> {
|
||||||
return this.http
|
return this.http
|
||||||
.get(`/api/projects/${projectId}`)
|
.get(`/api/projects/${projectId}`)
|
||||||
.toPromise()
|
.map(response=>response.json())
|
||||||
.then(response=>response.json() as Project)
|
|
||||||
.catch(error=>Observable.throw(error));
|
.catch(error=>Observable.throw(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,4 +29,6 @@ export class Project {
|
|||||||
update_time: Date;
|
update_time: Date;
|
||||||
current_user_role_id: number;
|
current_user_role_id: number;
|
||||||
repo_count: number;
|
repo_count: number;
|
||||||
|
has_project_admin_role: boolean;
|
||||||
|
is_member: boolean;
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Http, URLSearchParams, Response } from '@angular/http';
|
import { Http, URLSearchParams, Response } from '@angular/http';
|
||||||
|
|
||||||
import { BaseService } from '../service/base.service';
|
|
||||||
|
|
||||||
import { Policy } from './policy';
|
import { Policy } from './policy';
|
||||||
import { Job } from './job';
|
import { Job } from './job';
|
||||||
import { Target } from './target';
|
import { Target } from './target';
|
||||||
@ -14,10 +12,8 @@ import 'rxjs/add/observable/throw';
|
|||||||
import 'rxjs/add/operator/mergeMap';
|
import 'rxjs/add/operator/mergeMap';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ReplicationService extends BaseService {
|
export class ReplicationService {
|
||||||
constructor(private http: Http) {
|
constructor(private http: Http) {}
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
listPolicies(policyName: string, projectId?: any): Observable<Policy[]> {
|
listPolicies(policyName: string, projectId?: any): Observable<Policy[]> {
|
||||||
if(!projectId) {
|
if(!projectId) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
||||||
import { Router, NavigationExtras } from '@angular/router';
|
import { Router, NavigationExtras } from '@angular/router';
|
||||||
import { Repository } from '../repository';
|
import { Repository } from '../repository';
|
||||||
import { State } from 'clarity-angular';
|
import { State } from 'clarity-angular';
|
||||||
@ -8,16 +8,17 @@ import { SessionService } from '../../shared/session.service';
|
|||||||
import { ListMode } from '../../shared/shared.const';
|
import { ListMode } from '../../shared/shared.const';
|
||||||
|
|
||||||
import { SessionUser } from '../../shared/session-user';
|
import { SessionUser } from '../../shared/session-user';
|
||||||
import { Member } from '../../project/member/member';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'list-repository',
|
selector: 'list-repository',
|
||||||
templateUrl: 'list-repository.component.html'
|
templateUrl: 'list-repository.component.html'
|
||||||
})
|
})
|
||||||
export class ListRepositoryComponent {
|
export class ListRepositoryComponent implements OnInit {
|
||||||
|
|
||||||
@Input() projectId: number;
|
@Input() projectId: number;
|
||||||
@Input() repositories: Repository[];
|
@Input() repositories: Repository[];
|
||||||
|
|
||||||
|
|
||||||
@Output() delete = new EventEmitter<string>();
|
@Output() delete = new EventEmitter<string>();
|
||||||
|
|
||||||
@Input() totalPage: number;
|
@Input() totalPage: number;
|
||||||
@ -25,25 +26,16 @@ export class ListRepositoryComponent {
|
|||||||
@Output() paginate = new EventEmitter<State>();
|
@Output() paginate = new EventEmitter<State>();
|
||||||
|
|
||||||
@Input() mode: string = ListMode.FULL;
|
@Input() mode: string = ListMode.FULL;
|
||||||
|
@Input() hasProjectAdminRole: boolean;
|
||||||
|
|
||||||
pageOffset: number = 1;
|
pageOffset: number = 1;
|
||||||
|
|
||||||
hasProjectAdminRole: boolean;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private searchTrigger: SearchTriggerService,
|
private searchTrigger: SearchTriggerService,
|
||||||
private session: SessionService) {
|
private session: SessionService) { }
|
||||||
//Get current user from registered resolver.
|
|
||||||
let currentUser = session.getCurrentUser();
|
ngOnInit() {}
|
||||||
let projectMembers: Member[] = session.getProjectMembers();
|
|
||||||
if(currentUser && projectMembers) {
|
|
||||||
let currentMember = projectMembers.find(m=>m.user_id === currentUser.user_id);
|
|
||||||
if(currentMember) {
|
|
||||||
this.hasProjectAdminRole = (currentMember.role_name === 'projectAdmin');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteRepo(repoName: string) {
|
deleteRepo(repoName: string) {
|
||||||
this.delete.emit(repoName);
|
this.delete.emit(repoName);
|
||||||
|
@ -8,6 +8,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
<list-repository [projectId]="projectId" [repositories]="changedRepositories" (delete)="deleteRepo($event)" [totalPage]="totalPage" [totalRecordCount]="totalRecordCount" (paginate)="retrieve($event)"></list-repository>
|
<list-repository [projectId]="projectId" [repositories]="changedRepositories" (delete)="deleteRepo($event)" [totalPage]="totalPage" [totalRecordCount]="totalRecordCount" [hasProjectAdminRole]="hasProjectAdminRole" (paginate)="retrieve($event)"></list-repository>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -14,6 +14,8 @@ import { Subscription } from 'rxjs/Subscription';
|
|||||||
|
|
||||||
import { State } from 'clarity-angular';
|
import { State } from 'clarity-angular';
|
||||||
|
|
||||||
|
import { Project } from '../project/project';
|
||||||
|
|
||||||
const repositoryTypes = [
|
const repositoryTypes = [
|
||||||
{ key: '0', description: 'REPOSITORY.MY_REPOSITORY' },
|
{ key: '0', description: 'REPOSITORY.MY_REPOSITORY' },
|
||||||
{ key: '1', description: 'REPOSITORY.PUBLIC_REPOSITORY' }
|
{ key: '1', description: 'REPOSITORY.PUBLIC_REPOSITORY' }
|
||||||
@ -39,6 +41,8 @@ export class RepositoryComponent implements OnInit {
|
|||||||
totalPage: number;
|
totalPage: number;
|
||||||
totalRecordCount: number;
|
totalRecordCount: number;
|
||||||
|
|
||||||
|
hasProjectAdminRole: boolean;
|
||||||
|
|
||||||
subscription: Subscription;
|
subscription: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -66,12 +70,16 @@ export class RepositoryComponent implements OnInit {
|
|||||||
error => this.messageService.announceMessage(error.status, 'Failed to delete repo:' + repoName, AlertType.DANGER)
|
error => this.messageService.announceMessage(error.status, 'Failed to delete repo:' + repoName, AlertType.DANGER)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.projectId = this.route.snapshot.parent.params['id'];
|
this.projectId = this.route.snapshot.parent.params['id'];
|
||||||
|
let resolverData = this.route.snapshot.parent.data;
|
||||||
|
if(resolverData) {
|
||||||
|
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
||||||
|
}
|
||||||
this.currentRepositoryType = this.repositoryTypes[0];
|
this.currentRepositoryType = this.repositoryTypes[0];
|
||||||
this.lastFilteredRepoName = '';
|
this.lastFilteredRepoName = '';
|
||||||
this.retrieve();
|
this.retrieve();
|
||||||
|
@ -16,7 +16,8 @@ import { TagView } from '../tag-view';
|
|||||||
import { AppConfigService } from '../../app-config.service';
|
import { AppConfigService } from '../../app-config.service';
|
||||||
|
|
||||||
import { SessionService } from '../../shared/session.service';
|
import { SessionService } from '../../shared/session.service';
|
||||||
import { Member } from '../../project/member/member';
|
|
||||||
|
import { Project } from '../../project/project';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
moduleId: module.id,
|
moduleId: module.id,
|
||||||
@ -29,7 +30,7 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
|||||||
projectId: number;
|
projectId: number;
|
||||||
repoName: string;
|
repoName: string;
|
||||||
|
|
||||||
hasProjectAdminRole: boolean;
|
hasProjectAdminRole: boolean = false;
|
||||||
|
|
||||||
tags: TagView[];
|
tags: TagView[];
|
||||||
registryUrl: string;
|
registryUrl: string;
|
||||||
@ -45,15 +46,6 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
|||||||
private appConfigService: AppConfigService,
|
private appConfigService: AppConfigService,
|
||||||
private session: SessionService){
|
private session: SessionService){
|
||||||
|
|
||||||
let currentUser = session.getCurrentUser();
|
|
||||||
let projectMembers: Member[] = session.getProjectMembers();
|
|
||||||
if(currentUser && projectMembers) {
|
|
||||||
let currentMember = projectMembers.find(m=>m.user_id === currentUser.user_id);
|
|
||||||
if(currentMember) {
|
|
||||||
this.hasProjectAdminRole = (currentMember.role_name === 'projectAdmin');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.subscription = this.deletionDialogService.confirmationConfirm$.subscribe(
|
this.subscription = this.deletionDialogService.confirmationConfirm$.subscribe(
|
||||||
message => {
|
message => {
|
||||||
if (message &&
|
if (message &&
|
||||||
@ -78,11 +70,15 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
let resolverData = this.route.snapshot.data;
|
||||||
|
console.log(JSON.stringify(resolverData));
|
||||||
|
if(resolverData) {
|
||||||
|
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
|
||||||
|
}
|
||||||
this.projectId = this.route.snapshot.params['id'];
|
this.projectId = this.route.snapshot.params['id'];
|
||||||
this.repoName = this.route.snapshot.params['repo'];
|
this.repoName = this.route.snapshot.params['repo'];
|
||||||
this.tags = [];
|
this.tags = [];
|
||||||
@ -100,17 +96,17 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
|||||||
retrieve() {
|
retrieve() {
|
||||||
this.tags = [];
|
this.tags = [];
|
||||||
if(this.withNotary) {
|
if(this.withNotary) {
|
||||||
this.repositoryService
|
this.repositoryService
|
||||||
.listTagsWithVerifiedSignatures(this.repoName)
|
.listTagsWithVerifiedSignatures(this.repoName)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
items => this.listTags(items),
|
items => this.listTags(items),
|
||||||
error => this.messageService.announceMessage(error.status, 'Failed to list tags with repo:' + this.repoName, AlertType.DANGER));
|
error => this.messageService.announceMessage(error.status, 'Failed to list tags with repo:' + this.repoName, AlertType.DANGER));
|
||||||
} else {
|
} else {
|
||||||
this.repositoryService
|
this.repositoryService
|
||||||
.listTags(this.repoName)
|
.listTags(this.repoName)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
items => this.listTags(items),
|
items => this.listTags(items),
|
||||||
error => this.messageService.announceMessage(error.status, 'Failed to list tags with repo:' + this.repoName, AlertType.DANGER));
|
error => this.messageService.announceMessage(error.status, 'Failed to list tags with repo:' + this.repoName, AlertType.DANGER));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { CanActivate } from '@angular/router';
|
|
||||||
|
|
||||||
export class AuthGuard implements CanActivate {
|
|
||||||
canActivate() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
import { Http, Response,} from '@angular/http';
|
|
||||||
|
|
||||||
export class BaseService {
|
|
||||||
|
|
||||||
protected handleError(error: Response | any): Promise<any> {
|
|
||||||
// In a real world app, we might use a remote logging infrastructure
|
|
||||||
let errMsg: string;
|
|
||||||
console.log(typeof error);
|
|
||||||
if (error instanceof Response) {
|
|
||||||
const body = error.json() || '';
|
|
||||||
const err = body.error || JSON.stringify(body);
|
|
||||||
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
|
|
||||||
} else {
|
|
||||||
errMsg = error.message ? error.message : error.toString();
|
|
||||||
}
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,7 +17,8 @@ export class MemberGuard implements CanActivate, CanActivateChild {
|
|||||||
private router: Router) {}
|
private router: Router) {}
|
||||||
|
|
||||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> | boolean {
|
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> | boolean {
|
||||||
let projectId: number = route.params['id'];
|
let projectId = route.params['id'];
|
||||||
|
this.sessionService.setProjectMembers([]);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.projectService.checkProjectMember(projectId)
|
this.projectService.checkProjectMember(projectId)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
@ -26,6 +27,10 @@ export class MemberGuard implements CanActivate, CanActivateChild {
|
|||||||
return resolve(true)
|
return resolve(true)
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
|
//Add exception for repository in project detail router activation.
|
||||||
|
if(state.url.endsWith('repository')) {
|
||||||
|
return resolve(true);
|
||||||
|
}
|
||||||
this.router.navigate([CommonRoutes.HARBOR_DEFAULT]);
|
this.router.navigate([CommonRoutes.HARBOR_DEFAULT]);
|
||||||
return resolve(false);
|
return resolve(false);
|
||||||
});
|
});
|
||||||
|
@ -52,4 +52,7 @@ export const CookieKeyOfAdmiral = "admiral.endpoint.latest";
|
|||||||
|
|
||||||
export const enum ConfirmationState {
|
export const enum ConfirmationState {
|
||||||
NA, CONFIRMED, CANCEL
|
NA, CONFIRMED, CANCEL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ProjectTypes = { 0: 'PROJECT.MY_PROJECTS', 1: 'PROJECT.PUBLIC_PROJECTS' };
|
||||||
|
export const RoleInfo = { 1: 'MEMBER.PROJECT_ADMIN', 2: 'MEMBER.DEVELOPER', 3: 'MEMBER.GUEST' };
|
||||||
|
@ -1,25 +1,41 @@
|
|||||||
<div class="card card-block">
|
<div class="row flex-items-xs-between flex-items-xs-middle">
|
||||||
<h3 class="card-title">{{'STATISTICS.TITLE' | translate }}</h3>
|
<div></div>
|
||||||
<span class="card-text">
|
<div id="right_statistic_panel" style="margin-right: 18px;">
|
||||||
<div class="row">
|
<div class="statistic-block">
|
||||||
<div class="col-xs-2 col-sm-2 col-md-2 col-lg-2 col-xl-2">
|
<div class="statistic-column-block">
|
||||||
<span class="statistic-column-title">{{'STATISTICS.PRO_ITEM' | translate }}</span>
|
<div>
|
||||||
</div>
|
<span class="statistic-column-title statistic-column-title-pro">{{'STATISTICS.PRO_ITEM' | translate }}</span>
|
||||||
<div class="col-xs-10 col-sm-10 col-md-10 col-lg-10 col-xl-10">
|
</div>
|
||||||
<statistics [data]='{number: originalCopy.my_project_count, label: "my"}'></statistics>
|
<div>
|
||||||
<statistics [data]='{number: originalCopy.public_project_count, label: "pub"}'></statistics>
|
<span class="statistic-column-title statistic-column-title-repo">{{'STATISTICS.REPO_ITEM' | translate }}</span>
|
||||||
<statistics [data]='{number: originalCopy.total_project_count, label: "total"}' *ngIf="isValidSession"></statistics>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="statistic-column-block" style="margin-left: 16px;">
|
||||||
<div class="row">
|
<div>
|
||||||
<div class="col-xs-2 col-sm-2 col-md-2 col-lg-2 col-xl-2">
|
<statistics [data]='originalCopy.my_project_count' [label]='"STATISTICS.INDEX_MY" | translate'></statistics>
|
||||||
<span class="statistic-column-title">{{'STATISTICS.REPO_ITEM' | translate }}</span>
|
</div>
|
||||||
|
<div>
|
||||||
|
<statistics [data]='originalCopy.my_repo_count' [label]='"STATISTICS.INDEX_MY" | translate'></statistics>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="statistic-column-block" style="margin-left: 28px;">
|
||||||
|
<div>
|
||||||
|
<statistics [data]='originalCopy.public_project_count' [label]='"STATISTICS.INDEX_PUB" | translate'></statistics>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<statistics [data]='originalCopy.public_repo_count' [label]='"STATISTICS.INDEX_PUB" | translate'></statistics>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="statistic-column-block" style="margin-left: 28px;">
|
||||||
|
<div>
|
||||||
|
<statistics [data]='originalCopy.total_project_count' [label]='"STATISTICS.INDEX_TOTAL" | translate' *ngIf="isValidSession"></statistics>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<statistics [data]='originalCopy.total_repo_count' [label]='"STATISTICS.INDEX_TOTAL" | translate' *ngIf="isValidSession"></statistics>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="statistic-item-divider"></div>
|
||||||
|
<div class="statistic-block">Storage</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-10 col-sm-10 col-md-10 col-lg-10 col-xl-10">
|
|
||||||
<statistics [data]='{number: originalCopy.my_repo_count, label: "my"}'></statistics>
|
|
||||||
<statistics [data]='{number: originalCopy.public_repo_count, label: "pub"}'></statistics>
|
|
||||||
<statistics [data]='{number: originalCopy.total_repo_count, label: "total"}' *ngIf="isValidSession"></statistics>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
@ -1,30 +1,57 @@
|
|||||||
.statistic-wrapper {
|
.statistic-wrapper {
|
||||||
padding: 12px;
|
padding: 4px;
|
||||||
margin: 12px;
|
margin: 4px;
|
||||||
text-align: center;
|
text-align: right;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
height: 72px;
|
height: 30px;
|
||||||
min-width: 108px;
|
|
||||||
max-width: 216px;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.statistic-data {
|
.statistic-data {
|
||||||
font-size: 48px;
|
font-size: 16px;
|
||||||
font-weight: bolder;
|
font-weight: 900;
|
||||||
font-family: "Metropolis";
|
font-family: "semibold";
|
||||||
line-height: 48px;
|
line-height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.statistic-text {
|
.statistic-text {
|
||||||
font-size: 24px;
|
font-size: 10px;
|
||||||
font-weight: 400;
|
line-height: 10px;
|
||||||
line-height: 24px;
|
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-family: "Metropolis";
|
font-family: "semibold";
|
||||||
|
}
|
||||||
|
|
||||||
|
.statistic-column-block {
|
||||||
|
display: inline-block;
|
||||||
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.statistic-column-title {
|
.statistic-column-title {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 40%;
|
text-transform: uppercase;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statistic-column-title-pro {
|
||||||
|
top: -10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statistic-column-title-repo {
|
||||||
|
top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statistic-item-divider {
|
||||||
|
height: 54px;
|
||||||
|
display: inline-block;
|
||||||
|
width: 1px;
|
||||||
|
background-color: #ccc;
|
||||||
|
opacity: 0.55;
|
||||||
|
margin-left: 4px;
|
||||||
|
margin-right: 12px;
|
||||||
|
position: relative;
|
||||||
|
top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statistic-block {
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
<div class="statistic-wrapper">
|
<div class="statistic-wrapper">
|
||||||
<span class="statistic-data">{{data.number}}</span>
|
<span class="statistic-data">{{data}}</span>
|
||||||
<span class="statistic-text">{{data.label}}</span>
|
<span class="statistic-text">{{label}}</span>
|
||||||
</div>
|
</div>
|
@ -7,5 +7,6 @@ import { Component, Input } from '@angular/core';
|
|||||||
})
|
})
|
||||||
|
|
||||||
export class StatisticsComponent {
|
export class StatisticsComponent {
|
||||||
@Input() data: any;
|
@Input() label: string;
|
||||||
|
@Input() data: number = 0;
|
||||||
}
|
}
|
@ -110,10 +110,10 @@
|
|||||||
"PROJECT": {
|
"PROJECT": {
|
||||||
"PROJECTS": "Projects",
|
"PROJECTS": "Projects",
|
||||||
"NAME": "Project Name",
|
"NAME": "Project Name",
|
||||||
|
"ROLE": "Role",
|
||||||
"PUBLIC_OR_PRIVATE": "Public",
|
"PUBLIC_OR_PRIVATE": "Public",
|
||||||
"REPO_COUNT": "Repositories Count",
|
"REPO_COUNT": "Repositories Count",
|
||||||
"CREATION_TIME": "Creation Time",
|
"CREATION_TIME": "Creation Time",
|
||||||
"DESCRIPTION": "Description",
|
|
||||||
"PUBLIC": "Public",
|
"PUBLIC": "Public",
|
||||||
"PRIVATE": "Private",
|
"PRIVATE": "Private",
|
||||||
"MAKE": "Make",
|
"MAKE": "Make",
|
||||||
@ -290,7 +290,7 @@
|
|||||||
"DELETION_SUMMARY_TAG": "Do you want to delete tag {{param}}?",
|
"DELETION_SUMMARY_TAG": "Do you want to delete tag {{param}}?",
|
||||||
"DELETION_TITLE_TAG_DENIED": "Signed Tag can't be deleted",
|
"DELETION_TITLE_TAG_DENIED": "Signed Tag can't be deleted",
|
||||||
"DELETION_SUMMARY_TAG_DENIED": "The tag must be removed from the Notary before it can be deleted. {{param}}",
|
"DELETION_SUMMARY_TAG_DENIED": "The tag must be removed from the Notary before it can be deleted. {{param}}",
|
||||||
"FILTER_FOR_REPOSITORIES": "Filter for repositories",
|
"FILTER_FOR_REPOSITORIES": "Filter Repositories",
|
||||||
"TAG": "Tag",
|
"TAG": "Tag",
|
||||||
"SIGNED": "Signed",
|
"SIGNED": "Signed",
|
||||||
"AUTHOR": "Author",
|
"AUTHOR": "Author",
|
||||||
|
@ -110,10 +110,10 @@
|
|||||||
"PROJECT": {
|
"PROJECT": {
|
||||||
"PROJECTS": "项目",
|
"PROJECTS": "项目",
|
||||||
"NAME": "项目名称",
|
"NAME": "项目名称",
|
||||||
|
"ROLE": "角色",
|
||||||
"PUBLIC_OR_PRIVATE": "公开",
|
"PUBLIC_OR_PRIVATE": "公开",
|
||||||
"REPO_COUNT": "镜像仓库数",
|
"REPO_COUNT": "镜像仓库数",
|
||||||
"CREATION_TIME": "创建时间",
|
"CREATION_TIME": "创建时间",
|
||||||
"DESCRIPTION": "描述",
|
|
||||||
"PUBLIC": "公开",
|
"PUBLIC": "公开",
|
||||||
"PRIVATE": "私有",
|
"PRIVATE": "私有",
|
||||||
"MAKE": "设为",
|
"MAKE": "设为",
|
||||||
@ -395,8 +395,8 @@
|
|||||||
"TITLE": "统计",
|
"TITLE": "统计",
|
||||||
"PRO_ITEM": "项目",
|
"PRO_ITEM": "项目",
|
||||||
"REPO_ITEM": "镜像库",
|
"REPO_ITEM": "镜像库",
|
||||||
"INDEX_MY": "私有的",
|
"INDEX_MY": "私有",
|
||||||
"INDEX_PUB": "公开的",
|
"INDEX_PUB": "公开",
|
||||||
"INDEX_TOTAL": "总计"
|
"INDEX_TOTAL": "总计"
|
||||||
},
|
},
|
||||||
"SEARCH": {
|
"SEARCH": {
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
// Fetch prints the content found at a URL.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
time.Sleep(60 * time.Second)
|
|
||||||
tr := &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
||||||
}
|
|
||||||
var client = &http.Client{
|
|
||||||
Timeout: time.Second * 30,
|
|
||||||
Transport: tr,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, url := range os.Args[1:] {
|
|
||||||
|
|
||||||
resp, err := client.Get(url)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
|
||||||
resp.Body.Close()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
// fmt.Printf("%s", b)
|
|
||||||
|
|
||||||
if strings.Contains(string(b), "Harbor") {
|
|
||||||
fmt.Printf("sucess!\n")
|
|
||||||
} else {
|
|
||||||
fmt.Println("the response does not contain \"Harbor\"!")
|
|
||||||
|
|
||||||
fmt.Println(string(b))
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
28
tests/startuptest.sh
Executable file
28
tests/startuptest.sh
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set +e
|
||||||
|
|
||||||
|
TIMEOUT=12
|
||||||
|
while [ $TIMEOUT -gt 0 ]; do
|
||||||
|
STATUS=$(curl --insecure -s -o /dev/null -w '%{http_code}' https://localhost/)
|
||||||
|
if [ $STATUS -eq 200 ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
TIMEOUT=$(($TIMEOUT - 1))
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $TIMEOUT -eq 0 ]; then
|
||||||
|
echo "Harbor cannot reach within one minute."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl --insecure -s -L -H "Accept: application/json" https://localhost/ | grep "Harbor" > /dev/null
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Harbor is running success."
|
||||||
|
else
|
||||||
|
echo "Harbor is running fail."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user