From e28ae2a4f608570d0f250d2cfa77f73646fc1edf Mon Sep 17 00:00:00 2001 From: okxlin Date: Fri, 10 May 2024 23:54:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=B7=BB=E5=8A=A0amprobe=E5=88=B0?= =?UTF-8?q?=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/amprobe/README.md | 29 +++++++ apps/amprobe/data.yml | 19 +++++ apps/amprobe/latest/.env.sample | 3 + apps/amprobe/latest/data.yml | 17 ++++ .../latest/data/configs/config.dev.toml | 78 ++++++++++++++++++ apps/amprobe/latest/data/configs/config.toml | 78 ++++++++++++++++++ apps/amprobe/latest/data/configs/init.yaml | 9 ++ apps/amprobe/latest/data/nginx/nginx.conf | 53 ++++++++++++ .../amprobe/latest/data/supervisor/nginx.conf | 8 ++ .../latest/data/supervisor/server.conf | 16 ++++ apps/amprobe/latest/docker-compose.yml | 22 +++++ apps/amprobe/logo.png | Bin 0 -> 5312 bytes 12 files changed, 332 insertions(+) create mode 100644 apps/amprobe/README.md create mode 100644 apps/amprobe/data.yml create mode 100644 apps/amprobe/latest/.env.sample create mode 100644 apps/amprobe/latest/data.yml create mode 100644 apps/amprobe/latest/data/configs/config.dev.toml create mode 100644 apps/amprobe/latest/data/configs/config.toml create mode 100644 apps/amprobe/latest/data/configs/init.yaml create mode 100644 apps/amprobe/latest/data/nginx/nginx.conf create mode 100644 apps/amprobe/latest/data/supervisor/nginx.conf create mode 100644 apps/amprobe/latest/data/supervisor/server.conf create mode 100644 apps/amprobe/latest/docker-compose.yml create mode 100644 apps/amprobe/logo.png diff --git a/apps/amprobe/README.md b/apps/amprobe/README.md new file mode 100644 index 000000000..093d497e7 --- /dev/null +++ b/apps/amprobe/README.md @@ -0,0 +1,29 @@ +# Amprobe + +![MIT License](https://img.shields.io/badge/License-MIT-green.svg) ![Go Reference](https://pkg.go.dev/badge/github.com/shirou/gopsutil/v3.svg) + +## 简介 + +`Amprobe` 是一款轻量级主机及 `Docker` 容器监控工具,它可以轻松的帮助我们完成以下几方面的工作: + +- 监控主机的 CPU、内存、磁盘 IO、网络 IO情况 +- 监控部署于主机上 Docker 容器的运行状态、CPU、内存使用情况 +- 实时查看 Docker 容器的日志,并支持日志下载 +- 针对管理员用户提供简单的 Docker 容器、镜像运维操作 +- 操作日志记录 + +安装文档:[文档 | Amprobe (amuluze.com)](https://amprobedoc.amuluze.com/) + +## 注意 + +配置文件在应用数据目录的`data/configs`文件夹下,默认账户的设置也在里面,注意修改。 + +默认存在两个账户。 + +- 管理员账户 + - admin + - 123456 + +- 普通账户 + - amprobe + - 123456 diff --git a/apps/amprobe/data.yml b/apps/amprobe/data.yml new file mode 100644 index 000000000..620091dfe --- /dev/null +++ b/apps/amprobe/data.yml @@ -0,0 +1,19 @@ +name: Amprobe +tags: + - 实用工具 +title: 一款轻量级主机及 Docker 容器监控工具 +description: 一款轻量级主机及 Docker 容器监控工具 +additionalProperties: + key: amprobe + name: Amprobe + tags: + - Tool + shortDescZh: 一款轻量级主机及 Docker 容器监控工具 + shortDescEn: A lightweight host and Docker container monitoring tool + type: tool + crossVersionUpdate: true + limit: 0 + recommend: 0 + website: https://github.com/amuluze/amprobe + github: https://github.com/amuluze/amprobe + document: https://amprobedoc.amuluze.com diff --git a/apps/amprobe/latest/.env.sample b/apps/amprobe/latest/.env.sample new file mode 100644 index 000000000..fbd13921f --- /dev/null +++ b/apps/amprobe/latest/.env.sample @@ -0,0 +1,3 @@ +CONTAINER_NAME="amprobe" +DATA_PATH="./data" +PANEL_APP_PORT_HTTP="40235" diff --git a/apps/amprobe/latest/data.yml b/apps/amprobe/latest/data.yml new file mode 100644 index 000000000..79f2fa2d9 --- /dev/null +++ b/apps/amprobe/latest/data.yml @@ -0,0 +1,17 @@ +additionalProperties: + formFields: + - default: 40235 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Port + labelZh: 端口 + required: true + rule: paramPort + type: number + - default: "./data" + disabled: true + envKey: DATA_PATH + labelEn: Data Directory + labelZh: 数据目录 + required: true + type: text diff --git a/apps/amprobe/latest/data/configs/config.dev.toml b/apps/amprobe/latest/data/configs/config.dev.toml new file mode 100644 index 000000000..445f2dcfb --- /dev/null +++ b/apps/amprobe/latest/data/configs/config.dev.toml @@ -0,0 +1,78 @@ +[Fiber] +# http监听地址 +Host = "0.0.0.0" +# http监听端口 +Port = 8000 +# http优雅关闭等待超时时长(单位秒) +ShutdownTimeout = 30 +SeverHeader = "probe" +AppName = "probe" +Prefork = false + +# 数据库文件存放位置的配置 +# 需要监控的磁盘设备配置 +# 定时任务执行间隔配置 +[Gorm] +# 是否开启调试模式 +Debug = true +# 数据库类型(目前支持的数据库类型:postgres) +DBType = "sqlite" +# 设置连接可以重用的最长时间(单位:秒) +MaxLifetime = 7200 +# 设置数据库的最大打开连接数 +MaxOpenConns = 150 +# 设置空闲连接池中的最大连接数 +MaxIdleConns = 50 +# 数据库表名前缀 +TablePrefix = "s_" +# 是否启用自动映射数据库表结构 +EnableAutoMigrate = true + +[DB] +# 连接地址 +Host = "" +# 连接端口 +Port = "" +# 用户名 +User = "" +# 密码 +Password = "" +# 数据库 +DBName = "/Users/amu/Desktop/amprobe" +# SSL模式 +SSLMode = "" + +[Disk] +# 需要监控的磁盘设备,可以根据 lsblk 命令查看 +Devices = ["disk3s1s1"] + +[Ethernet] +# 需要监控的网口,可以通过 ifconfig 命令查看 +Names = ["en0"] + +[Task] +# 主机监控数据的采集频率 +Interval = 30 # 单位 s + +[Logger] +File = "/Users/amu/Desktop/probe.log" +Level = "debug" +RotationTime = "1" # 每天切分一个日志文件 +MaxAge = "7" # 最多保留 7 天的日志 + +[Auth] +# 是否启用 +Enable = true +# 签名方式(支持:HS512/HS384/HS512) +SigningMethod = "HS512" +# 签名key +SigningKey = "amprobe" +# 过期时间(单位秒) +Expired = 7200 +# key 前缀 +Prefix = "auth_" + + +[InitData] +Enable = true +InitConfigFile = "./configs/init.yaml" \ No newline at end of file diff --git a/apps/amprobe/latest/data/configs/config.toml b/apps/amprobe/latest/data/configs/config.toml new file mode 100644 index 000000000..284384689 --- /dev/null +++ b/apps/amprobe/latest/data/configs/config.toml @@ -0,0 +1,78 @@ +[Fiber] +# http监听地址 +Host = "0.0.0.0" +# http监听端口 +Port = 8000 +# http优雅关闭等待超时时长(单位秒) +ShutdownTimeout = 30 +SeverHeader = "probe" +AppName = "probe" +Prefork = false + +# 数据库文件存放位置的配置 +# 需要监控的磁盘设备配置 +# 定时任务执行间隔配置 +[Gorm] +# 是否开启调试模式 +Debug = true +# 数据库类型(目前支持的数据库类型:postgres) +DBType = "sqlite" +# 设置连接可以重用的最长时间(单位:秒) +MaxLifetime = 7200 +# 设置数据库的最大打开连接数 +MaxOpenConns = 150 +# 设置空闲连接池中的最大连接数 +MaxIdleConns = 50 +# 数据库表名前缀 +TablePrefix = "s_" +# 是否启用自动映射数据库表结构 +EnableAutoMigrate = true + +[DB] +# 连接地址 +Host = "" +# 连接端口 +Port = "" +# 用户名 +User = "" +# 密码 +Password = "" +# 数据库 +DBName = "/app/probe" +# SSL模式 +SSLMode = "" + +[Disk] +# 需要监控的磁盘设备,可以根据 lsblk 命令查看 +Devices = ["vda2"] + +[Ethernet] +# 需要监控的网口,可以通过 ifconfig 命令查看 +Names = ["eth0"] + +[Task] +# 主机监控数据的采集频率 +Interval = 30 # 单位 s + +[Logger] +File = "/app/probe.log" +Level = "debug" +RotationTime = "1" # 每天切分一个日志文件 +MaxAge = "7" # 最多保留 7 天的日志 + +[Auth] +# 是否启用 +Enable = true +# 签名方式(支持:HS512/HS384/HS512) +SigningMethod = "HS512" +# 签名key +SigningKey = "amprobe" +# 过期时间(单位秒) +Expired = 7200 +# key 前缀 +Prefix = "auth_" + + +[InitData] +Enable = true +InitConfigFile = "/app/configs/init.yaml" \ No newline at end of file diff --git a/apps/amprobe/latest/data/configs/init.yaml b/apps/amprobe/latest/data/configs/init.yaml new file mode 100644 index 000000000..8e6e497ed --- /dev/null +++ b/apps/amprobe/latest/data/configs/init.yaml @@ -0,0 +1,9 @@ +users: + - username: admin + password: 123456 + status: 1 + is_admin: 1 + - username: amprobe + password: 123456 + status: 1 + is_admin: 0 diff --git a/apps/amprobe/latest/data/nginx/nginx.conf b/apps/amprobe/latest/data/nginx/nginx.conf new file mode 100644 index 000000000..509a6a6c2 --- /dev/null +++ b/apps/amprobe/latest/data/nginx/nginx.conf @@ -0,0 +1,53 @@ +worker_processes 1; +daemon off; + +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; + + sendfile on; + keepalive_timeout 65; + + server { + listen 80; + #server_name amprobe.amuluze.com; # 服务器地址或绑定域名 + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + location /api/ { + rewrite ^/api/(.*)$ /$1 break; #必须的写这个,使用nginx的rewrite对uri进行重写 下面这行也要改为api + proxy_pass http://127.0.0.1:8000/; #跨域转发路由地址 + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location /ws/ { + # rewrite ^/wsUrl/(.*)$ /$1 break; #拦截标识去除 + proxy_pass http://127.0.0.1:8000/ws/; + + proxy_http_version 1.1; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + # 错误页配置 + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + } +} diff --git a/apps/amprobe/latest/data/supervisor/nginx.conf b/apps/amprobe/latest/data/supervisor/nginx.conf new file mode 100644 index 000000000..1d7d9c211 --- /dev/null +++ b/apps/amprobe/latest/data/supervisor/nginx.conf @@ -0,0 +1,8 @@ +[program:nginx] +command=/usr/sbin/nginx -c /etc/nginx/nginx.conf +directory=/etc/nginx +autostart=true +autorestart=true +redirect_stderr=true +priority=10 +stdout_logfile=/app/nginx.log diff --git a/apps/amprobe/latest/data/supervisor/server.conf b/apps/amprobe/latest/data/supervisor/server.conf new file mode 100644 index 000000000..7d8853e39 --- /dev/null +++ b/apps/amprobe/latest/data/supervisor/server.conf @@ -0,0 +1,16 @@ +[program:server] +directory=/app +command=/app/amprobe run -c /app/configs/config.toml +autostart=true +startsecs=10 +autorestart=true +startretries=3 +user=root +priority=999 +stopsignal=INT +redirect_stderr=true +stdout_logfile_maxbytes=200MB +stdout_logfile_backups = 100 +stdout_logfile=/app/app.log +stopasgroup=false +killasgroup=false diff --git a/apps/amprobe/latest/docker-compose.yml b/apps/amprobe/latest/docker-compose.yml new file mode 100644 index 000000000..be3db083b --- /dev/null +++ b/apps/amprobe/latest/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3' +services: + amprobe: + image: amuluze/amprobe:v1.3.3 + container_name: ${CONTAINER_NAME} + restart: always + ports: + - "${PANEL_APP_PORT_HTTP}:80" + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /proc:/host/proc:ro + - /sys:/host/sys:ro + - /dev:/host/dev:ro + - ./data/configs:/app/configs + - ./data/nginx/nginx.conf:/etc/nginx/nginx.conf + - ./data/supervisor:/etc/supervisor/conf.d + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/amprobe/logo.png b/apps/amprobe/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..9433d15ef05421e6016373c0f66367cb7ed69628 GIT binary patch literal 5312 zcmXw7XFQzU)Ak?;Aw=6~i4rZk6+KobO7uhtLb6uxB}Af^C{dzYtQwu@UG!c;^cHQG z)w^ZguRPEHJ-<0~U+2sU+!=%O=VJII$|6g98y&k1)bY(_1{f|ck78M&u%>~ zR!dz^@fOHr0&+QkYz_dq1-Squ3s8;)AaFo24v@(OZlkyNkPJXE?Qcl-U;NkKS{5J& z2V}GUf?L9^m;N{Ux7sZ;`(Mt#%)do$3IExx%D=f{#ea3>^ZzoR-`=GDOMnA18UHg` z#Q&uKYv4bLzfI-iM_$?YiYl z!~c$XJHlhC&C^S);rGktslD7@)VDTdX4j@lHlUh~J-)a; zIJ>?b`D@GeEb46g7_+c_HuC4#qw1i2X1{WDTM2mX1DD@10We3}960^oh=nRm@WV8ibBfvw^}RP!QG_8SOl2Rs^q zz0<3RW_0S$%|8b~-Z&7^1sD_p{E5H|1m^4tm_h^n>p=G^U{eL?=3f;LZMQE2p&h`R zQsD6Xdg~C=I=!FXiP|^>9BKf$e4t|b#^uLxRMTc$^Lq6h5Z?zR_TNPJ+_WuV5Cg}l zJqLbud*!1$b@RZRVrxCEb3d^I z?NWz*SAI3TdiJs5EU$l8BL@qCU)fil*GwJ;G#&}swQb!#08KX?OKAwQTrE2CYs`F7LWr-uF%7mT#>Rs0^l*Eq zTW-HWm4nGyv|=Se^kiM9jgtE%4h{vIs)DSZ*WAw3R&=TsRr}$H0hIzo`zbdocaA%w zGd-iHRkfOn{2rSn!Z23ey1Q+JW1k(!IFRye55g5#VpZ6P?sfLiTQKXQ zPrWkAO`RVFc0~0^Ew7nHMgCaq*0pmQK9qCGQ@3e0Fy&!W9Y~s*mNgu`qiUp*Mz~v~ zD@^4(9Y&~~{-8kV+<&l6UwNgEv%yuSEp)kGdu(T~RdU3!=Hk1~_>KW>4ci)8PG`@UJv-3du9xmp zb^+Y#ao7Wa1ysq`KViN?h1MQL8!!I2oqEU*JJs_n>}4+a1kNO}D&CJGzbVs5p)`97 zPaCfkU-C=Y&%B_aZ=p{O>AX{_{p)IH8XjItp<#!vFY$SK!X}ezE0GSX!g6{mwoT&C zWmEX`6bLgV{jzbhVjdt@`Gz(fpTEjcoK}%k(G>!f2P<2GJ8Xv5rYJdZ^9AP8Uzy!7 zv=k-68YMp|f0dq6Zi&wkGMXZ0>iv~GkQ8EPc)4$i36+z-)OV7cOYgQhs0Lyg7T-T3 z2VN7f5}E6F?!tPsm31e^BXgpA2$UG5w_#G|ljFAsypWh2dE8E1#1LW?)m~p;9Y@S* zr6noBCLkcd!vobmH_^L%fytVq|4RA~CvHf~dR_XGD&tA!6Iz>iuwp_j z!$H687#Dc(*rMh7WA&tp3dQ`G;fuu6#r8g^_~XVPf)cIa=6Y#OY?0s9Oc|pK*cJK3H z@%U#cV;f)7FzWd;LK6XJ_`EHFn?NXmITqrBT8L5i8f#g!*!HTi(2XL4&v<{h&t@ex z$ee)`fA}-fmJGL#^;OFhVF(CA8s+wM!mP4q^-YX0zgkzgcT6u_U9DQh0Xr z)R_N~ALx!;Cd%%G-4qUy3$I;VdA!je6uY({ktVhL+SGAp_5 zF4|-CI`W^6`B*kJ;`_agab)q-kIQ{AKopZ)4$kd5zAXADdvyq=VbjcMg>wd{=6Dfq z_w4YyR?wIs6YuXITM9zmxb_G~sNFi-2d4|mAR`jSmZOu(d!#YKGTeVo?=9jyj!#c~ z6b>d%k13xox|_ezz5n^sR~dg6p;*-LG*^!HVCQ>_%1a?{ z9?@#BA-An2VOeavln2IB zFDX=;r48s0yALCXYC=!8qxEx44htMYf_}A42>Ep>qA`z$9Eg1F1J&z0$&7qpEPez@ zBX7q>X5xL<6RIqG=LzaHf_W70FqDa~jVH#NegXq?Rljd7!IAX1L`P0FRiC$w+ddLnYWo5mGPs;3%bC(T2=D`Dv z$HcYCHX6(}0Z%H=U3OZ|m!zMsjLa+wAd`cEJ4jiv_(z_5P5Z4fAGi+f`KYR)(MRq4 zyzX>ETncocPOVTCAAvSfr z?TjEUEB;1D$ECT$$%9Y*`*)q0)CB>2kDc5by6a=a0eK}bY28%+3VAJOjxIJl4kob< zoQbSs6AMB6`l^sdX|T@o^Hy)xlWit1?RubtcurmFBJ0!GMT+Qgkrl!UK0(!_t|6ev z4JJI%Uze3<(83r+)6@-uo@aX@JB6#mWLz99g(eHnu0)H9q27{$)#?ogsbdxjbX@Eu zA%?KVt~y8=|6xcRmU}p~_-PP9tQAk~oc1FH^BKp7Tk+&{Tr_5vyBO`1zA37#K?8Vj zBl^B}ls6GnZB>UA-=M5bBh9!p)pytSA53-dLsdgHkAK`+{B2Ni%y&KcURBth(+BhF z{bN++yCIQmqAYJ^T7m=n`bTH2Vy5dyZJA5d{)$9j6~4V~4fo3(txk^)IZ_oujmNWP zG9?pldl>X7K;CYW&GL->TIF*|c;7|cHV=&uXAc9m*umZociyQb7ufp>4VU4?Fba|^ zF0;)FMZYkpqsFb9bl@Mhj@7Cv3fOdb7{WeS2fssO(qNXs@5lRnFet-x86T}BY#-gI z(yb}-k?8&-JCs6>qt&VWS1l6) z`7I9mAeNrcC7GG~kJ=$E7;u~A6^$R$bNj69Ad5YkdM>6fq$kXUS^}K&CVcIJ`eh+oPxu*gZ8C@+uKeNu;P0Fg`m)O)!TK3lX<*-x*AiEsJ^jj-J^TDJ z8+A2qnwt6Ky%1d1E#v5=*ee{k@FPjru_w=8Gbbu}d9P69R$g)1``mMs{!KZ~zgVFi zK2O!>n@A^+nDwllf?a|M#gaSM5XtvE@$2vn2CC*X9Bh;`Q14j<+S-9E2{KC8G2qe( zkMW=5RA-PZHgkm5RDI%V?HNleP75mQj%&%%caXwy>=r>L7RhHzTM*6_JvFIKK{OaE zlk0p!UshVqN#_5Yd}2HuF0!&Z$n4IXOii=h^8&Fil|s)`&#bhhU?ybqG0FW>EYpQ9 zVCIc{9E39W!HYO4xd{__dvTU;B;}C2m{;#{qgmgNk{dM>XPy3@T~bDGkgG>GGJBN@ z&};B!zZLSzAN6b?Ki4fFRd4PI&aEU@`Q=GCgeNmxqH0ayKOGxrg9wgD$=IK%Qp;Zu z!?#N~)H)Hrp81rDF#Tw@vXStcz3$*$_?7HDLSse#HH>E3&N_>l*etDVENktmcV$-k z5&u@GV$){sV(Z6J2bgB0zRcR8bYdLZ~L8fwQ?9LY&c z*D6H-EqSkrN6Apg+dE-g>Ba9yWYntbuFo+fpXe=wU;b(w+Pg$5E-=#}8al(|Jk4UG z8dShj|5R2%yB5Ua>I>HiPnnim>(osrH4`<98x8DchFvI~G|m(WZ7M%yu8?+XclM0v zGZ9hI4QVtJjZPNtn*_%yI1{iLZb~-8^dziMc~AYTf62bjX3@<+!tPU9DI#Z9@8ttJ8*-vGKetV{Tb*dyj1iQU6iw zlFLCm)=A8ya2l;f%WJgW10NaIM=sX}8Fze={{ak-98a_IHVB&Xfs5xyEG#^Otzw{D zX)C6D6_%oJ$K&EpGoX0g?DRSY0h|PM6cLI|8qJkFwrGMTCSDUPW-dw{S~2(TsJE!7 zNd2P}sAj3f5Z?dpLTt?m(X^kzE|lUWurxNQ-HI9L9AMIA86m)H$uzh-#-v-HUDP|F zu4^YX>*)4&(;|y{LsB%NS)d`s{PF2-V;HLBE?fMj$OUu1x%2Gz!8u3MKx&Y64R^IK z>E=Q`VlHW!SZVErx257kQh7Yt1*b(IxsN@1l#D~ZOmMabj-@xG9F0B^xgJA93q61-qmb?aLNj7|Pe3qULc07X`F=YC-sXy`@PYacNn>H+dzK%DQ ze?QE+wZvDfj*B|O%BX)+N+msEy8L723oGSL7})N(uK`!esjRYQmW>gz*BlkV@gL|G zEX#D&@0sIUqo5V^_KIKeJFt^~^~%$;bbJO(Yqz!f3$JM<;KkEx2Ftr*5=g-5Z2;u; zg{;zn*TNTxzJ)>Zjn%C2>)qWnnvJHkC*B4A9h7~s5w6+; z{>;JqKj<1eJD6ym8^n%PR5w2dD#((4(3pIMrFIVC3(!8vr4q$CrCCsuFJ>kM%O)=R zo-e&CdOol=sX~^Ux^mPlXY0Y2)XCQa`N?F!g1QcROIE^~2^LIeR6>-ebmDU| z?8d1Wrz0B}Gc~g~7gOe}5Y+(H!;SrNM7cU`mM0x9OgrFY{f(GTuj~!1#^?>-$M>qM zQlGD;EF~IHH`8X0Z60Nj-#~QC3KrB~@7bby&KB!k!i)-xE3a3|T)-K?)Hk+v@k?3B zz18GMGgw~98!X9j0NPBVW)0N%&f!x57h!2U?LC!k1Ek5Sqq5S?)*pu8owI8B9Nm71e=+^_Dlbg>$HwC>}n}cWY`780wa)J+VO%8|3HCA)?>n+fv%yXv@uqB?J zDU^OnQJCp{JeeIOu-iT>^L$P;)9v(hYJD=T!)EwvPEP|Y{ZY1CA#sOV;-C@8I^6k_ ziO90PC8F8<8ZbW)8*2_1&IcZPPYw029Y*e^_fzI$A9@c=s;(Ud-Atd2cyP#aMt6z{ za)_vKYeNJ-s)D#+Yy)BW_3w*!@AY7pUnn2hS_!d{?Oi+gSpoMyN{-@9ouC|H{osnA zkR-m$W%*j)AgkUj^SeX?RSL}c!_3Ta&ILX)Ta`GcNZpR3h6+FHOP`EZ^Sq z>Gl|D?($Nbvw_NFe;$N$TT$OnaQ&zyE~=RX{rnaJ|K3^y_C8wGU%u-qQHW_JCZ_p` zcgamHoO_dQKw_{rjdJWukMlOOTUcRqw(63}?Tb_(Nr{CJO3o?Xk%XzO3g>B?`>R_GkOOK}So#s|r`I3C@7x>&Q|FuHm!QplP}!TF{IegD6H>=P2cZ&xN0!kfPR P6N96wsHspUXCCl>Rd?!k literal 0 HcmV?d00001