Merge Go frontend (apiserver + xcecli) into meson build

This commit is contained in:
Rongsong Shen 2026-04-14 11:20:17 +08:00
parent 43dc12a36c
commit c9dc8c4676
205 changed files with 30793 additions and 328 deletions

View file

@ -0,0 +1,85 @@
app:
name: "xce_apiserver"
runmode: "dev" # debug, release, test
http_port: 8080
version: "1.0.0"
security:
https:
enabled: false
port: 8443
cert_file: "config/ssl/server.crt"
key_file: "config/ssl/server.key"
authentication:
token:
type: "jwt"
secretKey: "xce-api-key"
token_expire_time: 86400 # 1 day
rate_limit:
global:
rate: 1000 # requests per second
burst: 2000
window: second # second, minute
endpoints:
/xce/v1/auth/logon:
rate: 100 # requests per minute
burst: 200
window: second
/xce/v1/cluster:
rate: 1000 # requests per second
burst: 2000
window: second
/xce/v1/jobs/*:
rate: 1000 # requests per second
burst: 2000
window: second
/xce/v1/files/*:
rate: 1000 # requests per second
burst: 2000
window: second
whitelist:
#- "127.0.0.1"
#- "::1"
#- "172.24.100.0/24"
#- "172.29.6.0/24"
#- "192.168.0.0/16"
#- "172.19.0.0/16"
- "0.0.0.0/0"
- "::/0"
file_upload:
max_size: 100 MB # maximum file size, support unit MB or GB
allowed_types:
- "application/x-tar"
- "application/zip"
- "application/x-gzip"
- "application/x-bzip2"
- "application/x-xz"
- "application/gzip"
- "application/bzip2"
- "application/x-xz"
- "application/octet-stream"
- "application/x-zip-compressed"
disallowed_types: # disallowed file types
- "application/x-msdownload" # .exe file
- "application/javascript" # .js file
job:
max_array_size: 1000 # default max array size for job submission
cache:
refresh_interval: 30 # seconds, available values: 1-300
cors:
enabled: true
allowed_origins:
- "*"
backend:
address: "localhost"
port: 9990
from: "xce_apiserver"
key: "keepsecret$"

View file

@ -0,0 +1,19 @@
# Authentication provider: config_file | pam | ldap | oauth2
# - config_file: dev mode, users defined in this file
# - pam: Linux system PAM authentication (Linux only)
# - ldap: LDAP authentication (future)
# - oauth2: OAuth2/OIDC authentication (future)
provider: config_file
# ConfigFile provider settings
config_file:
users:
- username: admin
password: admin
roles: [cluster_admin]
- username: user1
password: user1
roles: [basic_user]
- username: user2
password: user2
roles: [basic_user]

View file

@ -0,0 +1,19 @@
# Authentication provider: config_file | pam | ldap | oauth2
# - config_file: dev mode, users defined in this file
# - pam: Linux system PAM authentication (Linux only)
# - ldap: LDAP authentication (future)
# - oauth2: OAuth2/OIDC authentication (future)
provider: config_file
# ConfigFile provider settings
config_file:
users:
- username: admin
password: admin
roles: [cluster_admin]
- username: user1
password: user1
roles: [basic_user]
- username: user2
password: user2
roles: [basic_user]

View file

@ -0,0 +1,27 @@
logging:
# Log level: debug, info, warn, error, fatal
level: "debug"
# Log encoding: json or console
encoding: console
# standard output
outputPath: "apiserver_out.log"
# File rotation settings
file:
maxSize: 100 # megabytes before rotation
maxBackups: 10 # number of old files to keep
maxAge: 14 # days to keep old files
compress: true # compress rotated files
# Encoder configuration
encoderConfig:
messageKey: "message"
levelKey: "level"
timeKey: "timestamp"
nameKey: "logger"
callerKey: "caller"
functionKey: "func"
stacktraceKey: "stacktrace"
timeEncoder: "ISO8601"
levelEncoder: "capital"

View file

@ -0,0 +1,60 @@
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
' $status $body_bytes_sent "$http_referer" '
' $http_user_agent $http_x_forwarded_for $request_time; '
' $upstream_response_time $upstream_status $upstream_addr';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
# define upstream cluster, configure primary and backup instances
upstream apiserver_backend {
# valid values include IP, domain, and port
server 172.29.6.117:8080 max_fails=3 fail_timeout=30s;
server 172.29.6.101:8080 max_fails=3 fail_timeout=30s;
# server apiserver2.example.com:80 max_fails=3 fail_timeout=30s;
# backup server
#server backup.example.com:80 backup;
}
server {
listen 8080;
server_name _;
location / {
proxy_pass http://apiserver_backend;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
proxy_connect_timeout 3s;
proxy_read_timeout 30s;
proxy_send_timeout 30s;
}
# optional configuration to detect and redirect to a maintenance page
# if all upstream servers are down
error_log /var/log/nginx/apiserver_error.log;
access_log /var/log/nginx/apiserver_access.log main;
}
}

View file

@ -0,0 +1,65 @@
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
' $status $body_bytes_sent "$http_referer" '
' $http_user_agent $http_x_forwarded_for $request_time; '
' $upstream_response_time $upstream_status $upstream_addr';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
# define upstream cluster, configure primary and backup instances
upstream apiserver_backend {
# valid values include IP, domain, and port
server 172.29.6.117:8443 max_fails=3 fail_timeout=30s;
server 172.29.6.101:8443 max_fails=3 fail_timeout=30s;
# server apiserver2.example.com:80 max_fails=3 fail_timeout=30s;
# backup server
#server backup.example.com:80 backup;
}
server {
listen 8443 ssl;
server_name *.akrostar-tech.com; # replace with your domain name
ssl_certificate /etc/nginx/ssl/server.crt; # cetificate file path
ssl_certificate_key /etc/nginx/ssl/server.key; # key file path
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass https://apiserver_backend;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
proxy_connect_timeout 3s;
proxy_read_timeout 30s;
proxy_send_timeout 30s;
}
access_log /var/log/nginx/apiserver_access.log main;
error_log /var/log/nginx/apiserver_error.log;
}
}

View file

@ -0,0 +1,151 @@
permissions:
- queue:view # view queue info
- queue:submit # submit job
- queue:control # close, open, active, deactive
- job:view # view job info
- job:control # stop, resume, kill, requeue, peek??, modify
- host:view # view host info
- host:control # close, open
- cluster:view # view cluster info
- cluster:control # shutdown, restart, reconfigure
- user:view # view user info)
- rbac:control # create, update, delete roles
- rbac:view # view role info
- file:control # upload, download,delete
- file:view # view file info
- hostgroup:view # view hostgroup info
- hostgroup:control # add
- usergroup:view # view usergroup info
- resource:view # view resource info
- ar:view # view advanced reservation info
- ar:submit # submit advanced reservation
- ar:control # delete, modify, create
roles:
basic_user:
permissions:
- action: cluster:view
domain:
- type: all
- action: queue:view
domain:
- type: all
- action: queue:submit
domain:
- type: all
- action: job:view
domain:
- type: self
- type: self_ug
- action: host:view
domain:
- type: all
- action: rbac:view
domain:
- type: all
- action: user:view
domain:
- type: all
- action: file:view
domain:
- type: self
- action: job:control
domain:
- type: self
- action: hostgroup:view
domain:
- type: all
- action: usergroup:view
domain:
- type: all
- action: resource:view
domain:
- type: all
cluster_admin:
permissions:
- action: cluster:control
domain:
- type: self
- action: cluster:view
domain:
- type: all
- action: queue:submit
domain:
- type: all
- action: queue:control
domain:
- type: all
- action: queue:view
domain:
- type: all
- action: job:control
domain:
- type: all
- action: job:view
domain:
- type: all
- action: host:control
domain:
- type: all
- action: host:view
domain:
- type: all
- action: user:view
domain:
- type: all
- action: rbac:view
domain:
- type: all
- action: rbac:control
domain:
- type: all
- action: file:control
domain:
- type: all
- action: file:view
domain:
- type: all
- action: hostgroup:view
domain:
- type: all
- action: hostgroup:control
domain:
- type: all
- action: usergroup:view
domain:
- type: all
- action: resource:view
domain:
- type: all
- action: ar:view
domain:
- type: all
- action: ar:control
domain:
- type: all
- action: ar:submit
domain:
- type: all
queue_admin:
permissions:
- action: queue:submit
domain:
- type: queue
values: [normal,priority]
- action: queue:view
domain:
- type: all
- action: queue:control
domain:
- type: self
- action: job:view
domain:
- type: queue
values: [normal]
- action: job:control
domain:
- type: queue
values: [normal]

View file

@ -0,0 +1,194 @@
permissions:
- queue:view # view queue info
- queue:submit # submit job
- queue:control # close, open, active, deactive
- job:view # view job info
- job:control # stop, resume, kill, requeue, peek??, modify
- host:view # view host info
- host:control # close, open
- cluster:view # view cluster info
- cluster:control # shutdown, restart, reconfigure
- user:view # view user info)
- rbac:control # create, update, delete roles
- file:control # upload, download,delete
- file:view # view file info
- hostgroup:view # view hostgroup info
- hostgroup:control # add
- usergroup:view # view usergroup info
- resource:view # view resource info
- ar:view # view advanced reservation info
- ar:submit # submit advanced reservation
- ar:control # delete, modify, create
roles:
basic_user:
permissions:
- action: cluster:view
domain:
- type: all
- action: queue:view
domain:
- type: all
- action: queue:submit
domain:
- type: all
- action: job:view
domain:
- type: self
- type: self_ug
- action: host:view
domain:
- type: all
- action: user:view
domain:
- type: all
- action: file:view
domain:
- type: self
- action: job:control
domain:
- type: self
- action: hostgroup:view
domain:
- type: all
- action: usergroup:view
domain:
- type: all
- action: resource:view
domain:
- type: all
cluster_admin:
permissions:
- action: cluster:control
domain:
- type: self
- action: cluster:view
domain:
- type: all
- action: queue:submit
domain:
- type: all
- action: queue:control
domain:
- type: all
- action: queue:view
domain:
- type: all
- action: job:control
domain:
- type: all
- action: job:view
domain:
- type: all
- action: host:control
domain:
- type: all
- action: host:view
domain:
- type: all
- action: user:view
domain:
- type: all
- action: rbac:control
domain:
- type: all
- action: file:control
domain:
- type: all
- action: file:view
domain:
- type: all
- action: hostgroup:view
domain:
- type: all
- action: hostgroup:control
domain:
- type: all
- action: usergroup:view
domain:
- type: all
- action: resource:view
domain:
- type: all
- action: ar:view
domain:
- type: all
- action: ar:control
domain:
- type: all
- action: ar:submit
domain:
- type: all
queue_admin:
includes:
- basic_user
permissions:
- action: queue:submit
domain:
- type: queues
values: [normal,priority]
- action: queue:view
domain:
- type: all
- action: queue:control
domain:
- type: self
- action: job:view
domain:
- type: queues
values: [normal]
- action: job:control
domain:
- type: queues
values: [normal]
host_group_admin:
includes:
- basic_user
permissions:
- action: hostgroup:control
domain:
- type: self_hg
user_group_admin:
includes:
- basic_user
permissions:
- action: job:control
domain:
- type: self_ug
host_group_user:
permissions:
- action: hostgroup:control
domain:
- type: self_hg
mock_host_user:
permissions:
- action: host:control
domain:
- type: hostgroups
values: [hostgroup1,hostgroup2]
mock_job_user:
permissions:
- action: job:view
domain:
- type: usergroups
values: [usergroup1, usergroup2]
users:
user01:
roles:
- name: cluster_admin
domain:
- type: all
user02:
roles:
- name: host_group_admin
domain:
- type: hostgroups
values: [hostgroup1, hostgroup2]
user1:
roles:
- name: basic_user
domain:
- type: all

View file

@ -0,0 +1,126 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "App Configuration Schema",
"type": "object",
"properties": {
"app": {
"type": "object",
"properties": {
"name": { "type": "string" },
"runmode": { "type": "string", "enum": ["dev", "debug", "release", "test"] },
"http_port": { "type": "number" },
"version": { "type": "string" }
},
"required": ["name", "runmode", "http_port", "version"]
},
"security": {
"type": "object",
"properties": {
"https": {
"type": "object",
"properties": {
"enabled": { "type": "boolean" },
"port": { "type": "number" },
"cert_file": { "type": "string" },
"key_file": { "type": "string" }
},
"required": ["enabled", "port", "cert_file", "key_file"]
}
},
"required": ["https"]
},
"authentication": {
"type": "object",
"properties": {
"token": {
"type": "object",
"properties": {
"type": { "type": "string" },
"secretKey": { "type": "string" },
"token_expire_time": { "type": "number" }
},
"required": ["type", "secretKey", "token_expire_time"]
}
},
"required": ["token"]
},
"rate_limit": {
"type": "object",
"properties": {
"global": {
"type": "object",
"properties": {
"rate": { "type": "number" },
"burst": { "type": "number" },
"window": { "type": "string", "enum": ["second", "minute"] }
},
"required": ["rate", "burst", "window"]
},
"endpoints": {
"type": "object",
"patternProperties": {
"^/": {
"type": "object",
"properties": {
"rate": { "type": "number" },
"burst": { "type": "number" },
"window": { "type": "string", "enum": ["second", "minute"] }
},
"required": ["rate", "burst", "window"]
}
},
"additionalProperties": false
}
},
"required": ["global", "endpoints"]
},
"whitelist": {
"type": "array",
"items": { "type": "string" }
},
"file_upload": {
"type": "object",
"properties": {
"max_size": {
"type": "string",
"pattern": "^[0-9]+\\s?(MB|GB)$",
"description": "Maximum file size with unit (e.g., '100 MB', '5 GB')"
},
"allowed_types": {
"type": "array",
"items": { "type": "string" },
"description": "List of allowed MIME types"
},
"disallowed_types": {
"type": "array",
"items": { "type": "string" },
"description": "List of explicitly disallowed MIME types"
}
},
"required": ["max_size", "allowed_types"]
},
"job": {
"type":["object","null"],
"properties": {
"max_array_size": {
"type": "integer",
"minimum": 1,
"maximum": 2147483646,
"description": "Default max array size for job submission"
}
}
},
"cache": {
"type": "object",
"properties": {
"refresh_interval": {
"type": "integer",
"minimum": 1,
"description": "Cache refresh interval in seconds"
}
},
"required": ["refresh_interval"]
}
},
"required": ["app", "security", "authentication", "rate_limit", "whitelist", "file_upload", "cache"]
}

View file

@ -0,0 +1,88 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Logger Configuration Schema",
"type": "object",
"properties": {
"logging": {
"type": "object",
"properties": {
"level": {
"type": "string",
"enum": ["debug", "info", "warn", "error", "fatal"]
},
"encoding": {
"type": "string",
"enum": ["json", "console"]
},
"outputPath": {
"type": "string"
},
"file": {
"type": "object",
"properties": {
"maxSize": {
"type": "number",
"description": "megabytes before rotation"
},
"maxBackups": {
"type": "number",
"description": "number of old files to keep"
},
"maxAge": {
"type": "number",
"description": "days to keep old files"
},
"compress": {
"type": "boolean",
"description": "compress rotated files"
}
},
"required": ["maxSize", "maxBackups", "maxAge", "compress"]
},
"encoderConfig": {
"type": "object",
"properties": {
"messageKey": {
"type": "string"
},
"levelKey": {
"type": "string"
},
"timeKey": {
"type": "string"
},
"nameKey": {
"type": "string"
},
"callerKey": {
"type": "string"
},
"functionKey": {
"type": "string"
},
"stacktraceKey": {
"type": "string"
},
"timeEncoder": {
"type": "string"
},
"levelEncoder": {
"type": "string"
}
},
"required": [
"messageKey",
"levelKey",
"timeKey",
"nameKey",
"callerKey",
"functionKey",
"stacktraceKey"
]
}
},
"required": ["level", "encoding", "outputPath", "file", "encoderConfig"]
}
},
"required": ["logging"]
}

View file

@ -0,0 +1,19 @@
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = *.akrostar-tech.com
C = CN
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = *.akrostar-tech.com
DNS.2 = localhost
IP.1 = 127.0.0.1
IP.2 = <API_SERVER_IP>

View file

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDYzCCAkugAwIBAgIUX9aDwrjm5WhjOTsU6I55xoLBjZUwDQYJKoZIhvcNAQEL
BQAwMDEhMB8GA1UEAwwYV2VpLUdlLmFrcm9zdGFyLXRlY2guY29tMQswCQYDVQQG
EwJDTjAeFw0yNTAxMDgwOTQ2MzhaFw0yNjAxMDgwOTQ2MzhaMDAxITAfBgNVBAMM
GFdlaS1HZS5ha3Jvc3Rhci10ZWNoLmNvbTELMAkGA1UEBhMCQ04wggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDxYvK9lu7x/OMHMFdYRUtWb7KWxiPTXHgq
y3AG3w2iymSxaQ9SIyxEH+GCyPAO4AP9WVjWX8+ZFHuG5klOjoID6hz1NFQoA60k
XEXhrlPoN8XdFE+aG4+28exOnKK2ZsdNqsYFVUWcJRUbHDwEjeuffEi0y5tgxzDX
WihN1XDSAPEltXstNHehzxmfCt0QyAYnHbVb1N8ec6/qFqzSf2NT6HEZwyC2+PZV
qI6ri0y6qnq0i5kyxLR19VgZKarEsD1FxSgZ7wndIErJ6RAx1mUW++OGh3BNOex8
T6UXW3Jzgax8h8l/H0ioPRK4VGP4M6j0+6St5z15DcxhSouEOTu7AgMBAAGjdTBz
MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMDoGA1UdEQQzMDGCGFdlaS1HZS5ha3Jv
c3Rhci10ZWNoLmNvbYIJbG9jYWxob3N0hwSsHWQQhwR/AAABMB0GA1UdDgQWBBQE
5YjdDgu+c3ZCGUkASROY+dQwVDANBgkqhkiG9w0BAQsFAAOCAQEA6BGyDOSSFnR9
j94zvGxL7JfplpzafqK3Aj1hdJ8bUlW3F8bPJOR/BWKkNxUwRwn/9gUBxyXLtb98
ZzimQCvDX9NdirGpiUiTDykHB3YCmuq3zM+6GlFyMD/fADMmF8hYWjHmub8Hf7TL
0Bo0Ij/RipBckI2acV8eY1lVGyFGzpAce/PY21fLfgFyeTLmJjJv+ez3u7/auHFp
y04fXoXzN4nsfIeVWFhcKXCHfqg0+CIiuCUFV2MfV+BXV4jzmugKtjEqwrLale62
+l7+o97FcjmCi1FvYgCnu/+8hPSf5lTpmx0MUr3Y5+CnLQNMhkjaVNRf75pm0ZsN
vyXwwDk83Q==
-----END CERTIFICATE-----

View file

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDxYvK9lu7x/OMH
MFdYRUtWb7KWxiPTXHgqy3AG3w2iymSxaQ9SIyxEH+GCyPAO4AP9WVjWX8+ZFHuG
5klOjoID6hz1NFQoA60kXEXhrlPoN8XdFE+aG4+28exOnKK2ZsdNqsYFVUWcJRUb
HDwEjeuffEi0y5tgxzDXWihN1XDSAPEltXstNHehzxmfCt0QyAYnHbVb1N8ec6/q
FqzSf2NT6HEZwyC2+PZVqI6ri0y6qnq0i5kyxLR19VgZKarEsD1FxSgZ7wndIErJ
6RAx1mUW++OGh3BNOex8T6UXW3Jzgax8h8l/H0ioPRK4VGP4M6j0+6St5z15Dcxh
SouEOTu7AgMBAAECggEAD3GWOD0M4pycZdxY6kPcsxYIZg8dQaub4JkNfNMOi0jJ
snQXT9kCPwWPJw/2E+2PM8lgnq6YmKGv2YyoSgdiGCAFZEbJz5mUnUvuCKdfpO7U
kPV4tlJnGnxV8x2gZ20h+T0g2DyBbKEOvaJL0LrHxXdtK2wPb9yuqkiRZZFvW3Am
LhfH0U/zQ4Gefbu+dFK1CSUQy+ct8YtsrarN6bJ+apKYTGe0ZTJeS/ZoqJUmilcS
FNGcR32O1zrOQH594NxEcAwCDJomU3ZfFCSYi4zrWIZstlwTTKC5IWeCxRogDJxn
tdn8axyldXFHh6kWNOUn2a0GbsQueDM8hEejcL2EKQKBgQD67ICNzzaQ2ioWDkHQ
VvSNomtRqgMwxv9YBOkLn0kcyeU4caQ6g4Re+3ugrvlJCIZRTBvG85m7Xa+GTN25
aSPEb5u4zunnHW4a7Hla8s4n5+QVrr/5GP4IhimDJ0ooHxE6lZ9BX7wIQ3sf6GNx
xwAnoQncZ9BsI2+fy68GLt7n1wKBgQD2RQ28ZWS+FwWprB+NTUMOSw4367Mpz1ei
YCxARskak3msmTkqKQeUVyQJ8y4AEE98BV1gfymcD6+JL7VcTY71AGJU0cEgNU4G
cFo8aUDXr1IYpbpMe/ifolfxLdxY4Hg9gHpfo0K8Y50Szu062QRTbOxXhP+aTSY4
VhoKxk4+vQKBgA38gsnkw/DdCD2Ee+yl/Cms5Ph/VoAwh2kblPXd4hBlvI4h33qX
fBhjQifZuPF2NS5w5Jd5WpdUCIWe4TkFgEXhxrdNaUJFpK6Bgd4s7IrS5SbEP9Ud
rINDHys9VGfaG3a9gkv9jxiS0lkb9fcQlZSf6FHNw5NTglRQ4SeiEzCvAoGBAOUT
DJje89vCDHlsaxkD8kj8Veh8bXaJsKmQrfjIH06COZ7sE/l8mtlISXSb6IEUqurh
Dp+xCWJqUMzaPOf0GPMa/VyKlAtCqiZhIf6ppTA7m7Djj6yPcJAE8aqdDyb4/b+v
n6UQdwZH3pbdNlwT++siGaG7TAsakl+675RwK949AoGBAMn3fEf9TGo9CNbACOUb
eDSjEf6TFd8s5lZ8B5xCP3ZVQLLVUgf1lmvXGjpYwj7QoHrgzpSaoBQIeZO7Bb0J
1eAKN5p58EoDeWpf9P+T7/QZZ1Ao4dZEYkPe9joIY4HUpKllkfyaUHOaOx/wnjuf
PBUe0tjuiXAHR9PDktXk5Z9x
-----END PRIVATE KEY-----