From 7f83d6a1dc617717d8a16e41c3612fa2c89deb38 Mon Sep 17 00:00:00 2001 From: Meng Sen Date: Mon, 28 Jul 2025 11:43:30 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BA=94=E7=94=A8=20Openpane?= =?UTF-8?q?l?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Meng Sen --- .../config/clickhouse-config.xml | 29 ++++ .../config/clickhouse-user-config.xml | 8 + .../25.6.5.41-alpine/config/init-db.sh | 6 + apps/openpanel/25.6.5.41-alpine/data.yml | 148 ++++++++++++++++++ .../25.6.5.41-alpine/docker-compose.yml | 42 +++++ .../25.6.5.41-alpine/envs/default.env | 2 + .../25.6.5.41-alpine/envs/global.env | 2 + .../25.6.5.41-alpine/scripts/init.sh | 22 +++ .../25.6.5.41-alpine/scripts/uninstall.sh | 10 ++ .../25.6.5.41-alpine/scripts/upgrade.sh | 17 ++ apps/openpanel/README.md | 50 ++++++ apps/openpanel/data.yml | 14 ++ apps/openpanel/logo.png | Bin 0 -> 11061 bytes 13 files changed, 350 insertions(+) create mode 100644 apps/openpanel/25.6.5.41-alpine/config/clickhouse-config.xml create mode 100644 apps/openpanel/25.6.5.41-alpine/config/clickhouse-user-config.xml create mode 100644 apps/openpanel/25.6.5.41-alpine/config/init-db.sh create mode 100644 apps/openpanel/25.6.5.41-alpine/data.yml create mode 100644 apps/openpanel/25.6.5.41-alpine/docker-compose.yml create mode 100644 apps/openpanel/25.6.5.41-alpine/envs/default.env create mode 100644 apps/openpanel/25.6.5.41-alpine/envs/global.env create mode 100644 apps/openpanel/25.6.5.41-alpine/scripts/init.sh create mode 100644 apps/openpanel/25.6.5.41-alpine/scripts/uninstall.sh create mode 100644 apps/openpanel/25.6.5.41-alpine/scripts/upgrade.sh create mode 100644 apps/openpanel/README.md create mode 100644 apps/openpanel/data.yml create mode 100644 apps/openpanel/logo.png diff --git a/apps/openpanel/25.6.5.41-alpine/config/clickhouse-config.xml b/apps/openpanel/25.6.5.41-alpine/config/clickhouse-config.xml new file mode 100644 index 000000000..659577864 --- /dev/null +++ b/apps/openpanel/25.6.5.41-alpine/config/clickhouse-config.xml @@ -0,0 +1,29 @@ + + + warning + true + + + 10 + + + + + + + + + + + + 0.0.0.0 + 0.0.0.0 + op-ch + + + + 1 + replica1 + openpanel_cluster + + diff --git a/apps/openpanel/25.6.5.41-alpine/config/clickhouse-user-config.xml b/apps/openpanel/25.6.5.41-alpine/config/clickhouse-user-config.xml new file mode 100644 index 000000000..ae6a6b948 --- /dev/null +++ b/apps/openpanel/25.6.5.41-alpine/config/clickhouse-user-config.xml @@ -0,0 +1,8 @@ + + + + 0 + 0 + + + \ No newline at end of file diff --git a/apps/openpanel/25.6.5.41-alpine/config/init-db.sh b/apps/openpanel/25.6.5.41-alpine/config/init-db.sh new file mode 100644 index 000000000..e86d8af32 --- /dev/null +++ b/apps/openpanel/25.6.5.41-alpine/config/init-db.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -e + +clickhouse client -n <<-EOSQL + CREATE DATABASE IF NOT EXISTS openpanel; +EOSQL \ No newline at end of file diff --git a/apps/openpanel/25.6.5.41-alpine/data.yml b/apps/openpanel/25.6.5.41-alpine/data.yml new file mode 100644 index 000000000..23c435d68 --- /dev/null +++ b/apps/openpanel/25.6.5.41-alpine/data.yml @@ -0,0 +1,148 @@ +additionalProperties: + formFields: + - child: + default: "" + envKey: PANEL_POSTGRES_SERVICE + required: true + type: service + default: postgresql + envKey: PANEL_POSTGRES_TYPE + labelZh: Postgres 服务 (前置检查) + labelEn: Postgres Service (Pre-check) + required: false + type: apps + values: + - label: PostgreSQL + value: postgresql + - child: + default: "" + envKey: PANEL_REDIS_SERVICE + required: true + type: service + default: redis + envKey: PANEL_REDIS_TYPE + labelZh: Redis 服务 (前置检查) + labelEn: Redis Service (Pre-check) + required: true + type: apps + values: + - label: Redis + value: redis + - default: "/home/clickhouse-server" + edit: true + envKey: CLICKHOUSE_SERVER_ROOT_PATH + labelZh: 数据持久化路径 + labelEn: Data persistence path + required: true + type: text + - default: 8123 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelZh: WebUI 端口 + labelEn: WebUI port + required: true + rule: paramPort + type: number + - default: 9000 + edit: true + envKey: PANEL_APP_PORT_NATIVE_TCP + labelZh: Native/TCP 通讯端口 + labelEn: Native/TCP interface + required: true + rule: paramPort + type: number + - default: 9009 + edit: true + envKey: PANEL_APP_PORT_INTER_SERVER + labelZh: 服务器通信端口 + labelEn: Inter-server communication + required: true + rule: paramPort + type: number + - default: "http://127.0.0.1:8123/openpanel" + edit: true + envKey: CLICKHOUSE_URL + labelZh: ClickHouse 访问地址 + labelEn: ClickHouse URL + required: true + type: text + - default: "redis://127.0.0.1:6379" + edit: true + envKey: REDIS_URL + labelZh: Redis 地址 + labelEn: Redis Url + required: true + type: text + - default: "postgresql://postgres_user:postgres_pass@127.0.0.1:5432/postgres_db_name?schema=public" + edit: true + envKey: DATABASE_URL + labelZh: Redis 地址 + labelEn: Redis Url + required: true + type: text + - default: "false" + edit: true + envKey: ALLOW_REGISTRATION + labelZh: 开放注册 + labelEn: Open Registration + required: true + type: select + values: + - label: 允许注册 + value: "true" + - label: 禁止注册 + value: "false" + - default: "false" + edit: true + envKey: ALLOW_INVITATION + labelZh: 开放邀请 + labelEn: Open Invitation + required: true + type: select + values: + - label: 允许邀请 + value: "true" + - label: 禁止邀请 + value: "false" + - default: "" + edit: true + envKey: RESEND_API_KEY + labelZh: 邮件发送 (Resend) API KEY + labelEn: Email (Resend) Send API KEY + required: false + type: text + - default: "" + edit: true + envKey: EMAIL_SENDER + labelZh: 邮件发送者地址 + labelEn: Email Sender Address + required: false + type: text + - default: "" + edit: true + envKey: GITHUB_CLIENT_ID + labelZh: Github 客户端 ID (OAuth2) + labelEn: Github Client ID (OAuth2) + required: false + type: text + - default: "" + edit: true + envKey: GITHUB_CLIENT_SECRET + labelZh: Github 客户端密钥 (OAuth2) + labelEn: Github Client Secret (OAuth2) + required: false + type: text + - default: "" + edit: true + envKey: GOOGLE_CLIENT_ID + labelZh: Google 客户端 ID (OAuth2) + labelEn: Google Client ID (OAuth2) + required: false + type: text + - default: "" + edit: true + envKey: GOOGLE_CLIENT_SECRET + labelZh: Google 客户端密钥 (OAuth2) + labelEn: Google Client Secret (OAuth2) + required: false + type: text diff --git a/apps/openpanel/25.6.5.41-alpine/docker-compose.yml b/apps/openpanel/25.6.5.41-alpine/docker-compose.yml new file mode 100644 index 000000000..92fed07f5 --- /dev/null +++ b/apps/openpanel/25.6.5.41-alpine/docker-compose.yml @@ -0,0 +1,42 @@ +networks: + 1panel-network: + external: true + +services: + clickhouse-server: + image: clickhouse/clickhouse-server:25.6.5.41-alpine + container_name: ${CONTAINER_NAME} + labels: + createdBy: "Apps" + restart: always + networks: + - 1panel-network + ulimits: + nofile: + soft: 262144 + hard: 262144 + ports: + - ${PANEL_APP_PORT_HTTP}:8123 + - ${PANEL_APP_PORT_NATIVE_TCP}:9000 + - ${PANEL_APP_PORT_INTER_SERVER}:9009 + env_file: + - ${GLOBAL_ENV_FILE:-/etc/1panel/envs/global.env} + - ${ENV_FILE:-/etc/1panel/envs/default.env} + volumes: + - ${CLICKHOUSE_SERVER_ROOT_PATH}/data:/var/lib/clickhouse + - ${CLICKHOUSE_SERVER_ROOT_PATH}/logs:/var/log/clickhouse-server + - ${CLICKHOUSE_SERVER_ROOT_PATH}/config/op-config.xml:/etc/clickhouse-server/config.d/op-config.xml + - ${CLICKHOUSE_SERVER_ROOT_PATH}/config/op-user-config.xml:/etc/clickhouse-server/users.d/op-user-config.xml + - ${CLICKHOUSE_SERVER_ROOT_PATH}/config/init-db.sh:/docker-entrypoint-initdb.d/init-db.sh + environment: + - TZ=Asia/Shanghai + - DATABASE_URL_DIRECT=${DATABASE_URL} + - BATCH_SIZE=5000 + - BATCH_INTERVAL=10000 + - CONCURRENCY=10 + - WORKER_PORT=9999 + - API_PORT=3333 + - NEXT_PUBLIC_API_URL="http://localhost:3333" + - NEXT_PUBLIC_DASHBOARD_URL="http://localhost:3000" + - GITHUB_REDIRECT_URI=https://openpanel.domain.com/api/oauth/github/callback + - GOOGLE_REDIRECT_URI=https://openpanel.domain.com/api/oauth/google/callback diff --git a/apps/openpanel/25.6.5.41-alpine/envs/default.env b/apps/openpanel/25.6.5.41-alpine/envs/default.env new file mode 100644 index 000000000..cd05f46e6 --- /dev/null +++ b/apps/openpanel/25.6.5.41-alpine/envs/default.env @@ -0,0 +1,2 @@ +# copyright© 2024 XinJiang Ms Studio +ENV_FILE=.env diff --git a/apps/openpanel/25.6.5.41-alpine/envs/global.env b/apps/openpanel/25.6.5.41-alpine/envs/global.env new file mode 100644 index 000000000..e10989fe4 --- /dev/null +++ b/apps/openpanel/25.6.5.41-alpine/envs/global.env @@ -0,0 +1,2 @@ +# copyright© 2024 XinJiang Ms Studio +TZ=Asia/Shanghai diff --git a/apps/openpanel/25.6.5.41-alpine/scripts/init.sh b/apps/openpanel/25.6.5.41-alpine/scripts/init.sh new file mode 100644 index 000000000..80c647873 --- /dev/null +++ b/apps/openpanel/25.6.5.41-alpine/scripts/init.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +if [ -f .env ]; then + source .env + + # setup-1 add default values + CURRENT_DIR=$(pwd) + sed -i '/^ENV_FILE=/d' .env + sed -i '/^GLOBAL_ENV_FILE=/d' .env + echo "ENV_FILE=${CURRENT_DIR}/.env" >> .env + echo "GLOBAL_ENV_FILE=${CURRENT_DIR}/envs/global.env" >> .env + + if [ ! -d $CLICKHOUSE_SERVER_ROOT_PATH/config ]; then + mkdir -p $CLICKHOUSE_SERVER_ROOT_PATH/config + fi + cp -rn ./config/* $CLICKHOUSE_SERVER_ROOT_PATH/config/ + + echo "Check Finish." + +else + echo "Error: .env file not found." +fi diff --git a/apps/openpanel/25.6.5.41-alpine/scripts/uninstall.sh b/apps/openpanel/25.6.5.41-alpine/scripts/uninstall.sh new file mode 100644 index 000000000..c86c4fbca --- /dev/null +++ b/apps/openpanel/25.6.5.41-alpine/scripts/uninstall.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +if [ -f .env ]; then + source .env + + echo "Check Finish." + +else + echo "Error: .env file not found." +fi diff --git a/apps/openpanel/25.6.5.41-alpine/scripts/upgrade.sh b/apps/openpanel/25.6.5.41-alpine/scripts/upgrade.sh new file mode 100644 index 000000000..07fb8c3fe --- /dev/null +++ b/apps/openpanel/25.6.5.41-alpine/scripts/upgrade.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +if [ -f .env ]; then + source .env + + # setup-1 add default values + CURRENT_DIR=$(pwd) + sed -i '/^ENV_FILE=/d' .env + sed -i '/^GLOBAL_ENV_FILE=/d' .env + echo "ENV_FILE=${CURRENT_DIR}/.env" >> .env + echo "GLOBAL_ENV_FILE=${CURRENT_DIR}/envs/global.env" >> .env + + echo "Check Finish." + +else + echo "Error: .env file not found." +fi diff --git a/apps/openpanel/README.md b/apps/openpanel/README.md new file mode 100644 index 000000000..c18c4d704 --- /dev/null +++ b/apps/openpanel/README.md @@ -0,0 +1,50 @@ +# Openpanel + +Openpanel 是一个开源网络和产品分析平台,它结合了 Mixpanel 的强大功能和 Plausible 的易用性,是最好的 Google Analytics +替代品之一。 + +![Openpanel](https://file.lifebus.top/imgs/openpanel_cover.png) + +![](https://img.shields.io/badge/%E6%96%B0%E7%96%86%E8%90%8C%E6%A3%AE%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E5%B7%A5%E4%BD%9C%E5%AE%A4-%E6%8F%90%E4%BE%9B%E6%8A%80%E6%9C%AF%E6%94%AF%E6%8C%81-blue) + +## 特性 + +### 网络分析 + ++ 实时数据 :实时查看访客活动 ++ 流量来源 :了解访客来自哪里 ++ 地理洞察 :追踪访客位置和趋势 ++ 设备分析 :监控不同设备的使用情况 ++ 页面性能 :分析访问量最大的页面 + +### 产品分析 + ++ 事件跟踪 :监控用户操作和交互 ++ 用户资料 :建立详细的用户旅程洞察 ++ 漏斗 :分析转化路径 ++ 留存率 :跟踪用户参与度 ++ 自定义属性 :为事件添加上下文 + +## 快速开始 SDK + +### 脚本标签 + +```html + + + +``` + +--- + +![Ms Studio](https://file.lifebus.top/imgs/ms_blank_001.png) diff --git a/apps/openpanel/data.yml b/apps/openpanel/data.yml new file mode 100644 index 000000000..ecb0a43fb --- /dev/null +++ b/apps/openpanel/data.yml @@ -0,0 +1,14 @@ +additionalProperties: + key: openpanel + name: Openpanel + tags: + - WebSite + - Local + shortDescZh: 开源网络和产品分析平台 + shortDescEn: An open source network and product analysis platform + type: website + crossVersionUpdate: true + limit: 0 + website: https://openpanel.dev/ + github: https://github.com/Openpanel-dev/openpanel/ + document: https://openpanel.dev/ diff --git a/apps/openpanel/logo.png b/apps/openpanel/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..827e9529e99648d871d489663b880d6e24e8d849 GIT binary patch literal 11061 zcmV-5E6UV~P)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00002VoOIv0RM-N%)bBt010qN zS#tmYH(US!H(UYB$E3Lc000McNliru=?5GFBnjTYS+M{BDg;SHK~#9!?R|Ni71fph zx%GO1ZlIf`LH13>1w;v;qM}3siA$o<==?^LsEK1TGclTRq7%1KqcIu`CgT|W#8KyC zOk9$mKX*}s8jXs8A}XjL``+wy)7z`_`{UJet6o*Ts(Wv}dd)rGkMjDxQ}>>wzEyRr zPMv$%7$E2jBWnW)*@6h5Ifww7g9xBG3IsssUvwns+cHFg0iA|-CnAYuLhz>Q6~!_k zWnYzsJJ-k@8AD*e02u%R11OeQ|MQZ9(2`)V1kl>RkpS3=7zQZ{F~Lz3X#%gP@I6{l zuq0SfN+x6%?p)shN?BX5Ad!e9h_>-0LMC8ncgYk%sHBt$Tbn%|Qgv97F)kK?KkoL;%fUpp!%d`@_jH z7D>R#KRR2wADo0wSb9>s$o0eJElVp3kzffEz;6Nq2KXAw1WaxM?<;uqS4y~ZjTRG_ z^Wo1TauN)cgtiGwN{T89m4qm&022Tr6=fT4hX4dUL1O^TK?KkoL;%e}1kfBr0L?)J z&>Tbn%|Qgv97F)kK?KkoL;%g zLN?JUdUYiLj>JthI$OFDZ#tHfaM{;F=guE-Oo&hvC_&c*=fm$2x~>IFVw(`^E1#5( z?W@XgLy2(b8p2YT3_~TM3>1`PZ9{U+oZfS@yI5U4qb0GfjcpgD*D znu7?SIfww7g9xBGhya>{2%tHL0GfjcpgD*Dn#1s97%f=&#bAk>+XN_*K#;^!VYOJQBcA375=b?gor6f`tGR zcH3tzua}WVE2N6JX5CJp?5kPYg0W=2@KywfQGzSqta}WVEM}Yv|`GkYF#Lyoi zB>wKg6-ka+y#zMcv2*L#XsL-n<8KB|@lZ zMcv2*8U$(%B7o)~0%#5*faV|qXbvKP<{$!S4kCc&AOdI(B7o)~0%#7tI@m(YxsTP( za9nXFWHQfl2EH1FBr>91&5fRlW>%;=wj_lu-4bMbGX#V}59sRvp&$#AxZlvPsL-F+ z1Qa#_L`?7~La!n~ZqwwxR#doiy$VENo(~=@*#^>I%4PynYC>iE8vZ5M8wfgs27#J` z2%tHL0GfjcpgD*Dnu7?SIfww7g9xBGhya>{2%tHL0GgwS0O5gB8&V0AjE%G!Ve5KUIqvrqq~fldAU^{MY!Q=MaA z<~!P3_wCxzymf2y&VB89#%Rc@92GzSG_ks2@aRLvju_NfR}*xk=iB#g-?VDQinYxx zrjb1f&@8Msfsp2@$;5JK$@~&vn$`WsOqe)sXhUf#wl%N*c*!U0_Mws8^}s|{*Z-iw z1N+uhn{9ixZ{DGKML~~eantPc zCiV;wu7BzAkMat34w}PGKj(;=U}1jsvw!(iH3c#ufQgM$&OB+boGWe1UwZNLj&hv_ z^1Z)y$x%_mlx3Fx4rh41^dcD z0fb4H&!{c2(dzpj-y?NOLgR$Xk1sJ6#^v`s)28wyP<8^CZ0uQQjZt;(Zdvfy{GF*& zz^uRUpJT+S^W{4}k#H>E@6wC=mENrFsk>Gyy!4fs046)+yt9U>++iJyADy=&T?*uf zU2}GItSwjE`XY*&l0WqNnele)hc~^c?zw!~oZ7y=EyYL0 z`SEkU8uO6rcXHd(yqG2C@xPuH^VB|Qdh>E&p84StAiPTI&{Z5g5s!)``6+WJ#kQsA@oaGyi|Q^!VxBtZ(AZjfpSW+S$)dW8FDVefS<1Gk zS)>HKB1aVlN%B2TyY(Liq;jBEjhQ`S?KWhkOepP9u_Qlv?qLZ`sh+~#v(5ntOh9wy zox>Ao>2ciNC2kK%6oKE@T)j7oXeW>DZ!WwNzlfKhuTi1edHi*kk4)|HUF}h)=2o`4 z>m~=8;7F4AY92oK@I;p6ChcEr&(0m&1oOCGjYy=WX3FM|{F9E0`C2pv5Sp2~u>?+8R^V88jD3VbaC0rF%1^9OxX*8%{* zM$YJ1-l2{G`6F)|AkuDR>pMxboN%MiP8hrSgYG~8gfGuIrEE!?-X{%Nx?9!B276q4 zf>1+t$h+%f8-WeC94VwFH}(w$7lE`1VD`M^=27XY@StkKq!k+!ode97KdBLGSzrHZ zS6m~^Z+%bj_~>W8F7IT@1jsjC`!7PP5{YBz^yW{M+znvw8{lS@yRM8>ZujDuzeq%pVHdUejY<%u|5*tqX*0t?!yI?PaQi z^Zev{zmmc0dwqU-eWNMgP0w?Ch&Mj*=y(=j-*Y5(!K_21{Y;Yp=Bf7_onrUlsJZ0! zgYvSS@^@3 z0R&ivaPBQa4^dR%q`MB5Vt_eT_!a;F0IE-ja}6+$Op*jFM(<;#ngf7gExT+_SM2}b z|K+AkF8t=1rP!K|7_80Dxuw6XZE~14XKdc*Q5;|GpKxNMoc?3_V1Oc@32btyHw{Z* zau3f$(wriQ=c~cZNbSsj?anz}n10&X!C))1XS)Wz5kHOf*Kd}(f~vr@J4WO!SKJTB zSFfbl*D9I8834e@p=N+0U$}c5C8_1m{-(7jxT(Uy-4P{yb(W8}8N(mqP>uK-ueL{YVtrMGn%q3&S^76yv3*=<&R8D~WSN}EDmp_wY^bLcB&H?gC%&Hi%m48!4wg=S&$RGZ*M#Wu9 z;>0UO2ZCBaF#)jg8o4K^Dd7K{O9Tjq>gfh9`fAo56`|*q6Qy&*2QJhSAV2K~Wt_Al zE=K;lbkg*|g;D~TO;=UG6X0UZyfAAEDi=x#U|b@X-A)U+%LMNQHGq{2@;y+-;=DzG z1idF#?3#K}g-+-?_{Y6nyJPz*3tka=6roB*g_VdsdR<9LUkz}xlMd{*ZuT%x0=yg%A?89AoP(^^+Z)g4%a4AebNkPHj0S83{$R9l`Yfnm|?!tOH1r$LM z0jkfh_!9uYiO10?pa^QJ_wz^0R%lb(j^^f_EgewPt8deQM(LL`9rQZ?%`Syz9vDzV z0OK2~>Hs>{E?K;6{m%WJCIU06>-r5pV$xB=6g^=&W&EPpkArl%4OBt^bJ*!hj4{{0 z_VSXg9bhnEi~(SK`;O0EtsQjCtm#8k?2!Ivy&rp9U4{$s2vB0q9h`Bn0#mv^dGdud zT?XS!V!#aOTEF)BF{hm~PQj}tvwy#~bSYFBkNpZ@S(WWGGV4F<2haDg@L*v%+g@kN zk0tT52cO=;+`V5=gba`|!~E>C$ItnW{B#qePkD$%`l@vAqa7coQ^%>m*ovxQ5UHq0 z;`)jVs?c7(m@5Wn*4o4F6wvni#m=h4jH?>YPnaz2#m+|`SW{RP%~~kgDm}GKFksVf zU-+l!FOQir=3JK7AbMk z%7w2C0Ku3oJrmsG%Whs{um}?vGge&tFCWS3`O*>gogk-(p9jJFmlWI*><~o?Uk3jjWf%4g)fsPNfs%15M)l0 zET!M~tE<<=8sX)>_{lx{<#f&HTeg8$_eduxAV99l(NId z)6+l?6b#=7$h``@`=*EDud~EDes^1ojJEodba}!P6Y>Nw$B&k2-g^5ZN$!VO{(pYm zE~D>+J{55DT|tm10GlRL2Rh6UWJDzIC;P%3~#G=8KK)ajB6%15A*Z=&JRu z&jg9yRg!_33LYFAWeXSMu|6mVyR$?H@EO2~DrIPklZrtAE}Het1g_TlnO%2Pl^W*1fC`NLfnMWMbLP9n)I_H`xK6a`s<>-wh8Vtly=c>es7lm@>~nyj!Y^xooIDG_gKb|f z+O%H-5x{}$T+`x1fKNK*RJRL{0(AqdE1lZ9k+O)5nW_3Ve7aX)o<~AkI z0JXxa&$qP7K5A(Zz7OnMF)!=vmhNGfm)lc0wxvZlR_bBL_48St7d|UXhp#i z3G@h{z1kd5gt2_M*%tAvb^$GGSj6 z*a!@2fW$7zk3LxhOF(W2fxa(^O%R3=I0jI!D27K1MIUBZc(*-5Q5+L0W;|R@RLU3- z!1!pDRQuiUNL!GZ@ANnp0NBuy-1xf9rxNv+b)&#|f^2m7Whw;Y&tFHJ{q|r%2M|S}g0%P$%JrVD25dVJZvkLWQ z?Kr_gZq1c9ZH}?szWcuSp2EGR=B-|W0vIj#ynDsW67SjP7tfv7sbKjm2Sk9%0fPmX zop$Ll5xWFe{O{wNC11q#fe27Z$hAE7*V*TPsXkcRvFtBTtzjw;f5?&m74|Hq}=b9`<*&ZeO$Xy(Jqu za*B_B03tvIg5_Y#rdN724jR0&29-HV@6u{ z>qLMG1uIkailz^OXas7*D4I$ZtgrmsvV3lgmnev%Q~#U51O;iK2t}$w8>~ns+`cFv zpZ~!CMHs_`D&V_kXs-(GQw#_}5*4BGc^JzC1-n075dyDBN{el_jIX7l!0%_jqHu|0 zLRI_&t30R8_$G+knxImGvQx#%H78^aO$UJl8$^H*=yGl&0%#5* zfaV|qgrIV!kx3UsfDq6afS?7md=6lCPqR~jBmpF5{LOGDbP)j@ND`n^e2$U~M_C<2 z00)5ti2X>Jb%@V!gh>>|lEg+EciyUqx}X$&E!CajR9!mhB44GUn}jUXWjagL0s^!P z&u?QeH;oyr$dQ0oV%Ii`zaZISmiARqygeCNuOfkPBj1nR6u9oZ60f4*a925I z{U~!9Ftoog0I+NVz(RvI;i+zGs{k3V$kpm7vQ6M6?snBRqPq8*Mxd`=6I1{lvx|LA z9`2whFsr<|AYtfk$6O=AOaO!`A49mzvqRx<3AFDM4NYB7%kU!I(Izna8`cT0RSIe zvx=?&E@TOS%ft`kvxW)}1I;m~3#;9#rFKg?o`sqt*{LC49duh0FmwbSUft1Uu^Kb`Ao3kTI;B@Y=%!X4IKNkyTOuxwyN0KB43nRV~Jb9_EnQFQ$CUp`-4=ax_T zF`&}JJqqE95{29O6_a-tD3N<53;@{s;)4Kax@KU=I55ftbNJN_;yRaY^7tI4NI5H{ za^LAGl3#dmWwM#>y_o*v>f~wdo>xtgQ2FkDS(gteNE3kVd{3tJ_x~|@n~N`$(AXlq z-u8h7`CH=rn?h?|S*X8$cESe<=2=(QCTrorO6m2s4=l(>!&tmtrum>dPEN!C^J~B8 zC!_D}?erXwhXMj@Tp-nc^l!eL;DMld`dve0v~|2Du~zee2nFZE_E+SxYh&*@JMM57 zjSKD`CZ%lUBDw;2pkndSx5YDp001!j?#t_9aVX#W-|rkGr|XR^L;w#|5Mce^Bzx$4 z{oF(IB~JpFAOGJ!?JcLN^+mb@c%kBW=zLE2y)X+k|M2LAb{kA6dTYU7U= zr!eDsw}uJ=d}xs^r-p0ASOL3fw)g;Ff4IXJ%6y$N&q)hZwL|c**mZ4wW~h@x0UC zd*<~u`@vvNVIu;XP&ek3Gbahxf$^CCnc+GW-&bLT1T}HEk%ddbY_=q&e66zTPLb0C z_9Vt``6eeRfGaa)XngkaMJnReYsSf&KY087RonOHu~6(<)wBPYNyi^OP|Z$hd7`}v zkpTlN<*S-pkfVr;o=~`f4>#}zI<>L@L5fQHTIK1+`w=Yhnp60v^YODK@^c)FAwy1U z-L`K1rtNz>p{95LA;X6@$-F%5#XIj9U{_L?_EjGKD&6p)CJT45ff%Z&l&@6~q>P?8HNYP`F)<;^^8lMIhy-W{*il8VY z#v5;B?MX>=JwjE%#ZW{5w)eq`KLsp$mQDc0P!$v7wfR}QQUaY1Zle=GF;o$N?Ym!i zwwUtZ?{oqPg0i6G-h3u&Un0@+JDL?B2ia)%>?f+Q5s#q2|u;ch91WTefh@WS$zgy+GU{#I+ zUzF;T*yXEk&8t(nZApwz@2tp+!25T1LXbxhzN%4-jv@$ABol61VjCRL9de3-O)%_C zqnk@)zBz|^3HO_|4dW%o|NUduJ39coZd>C_y6yFdMfxhe`|TJ3FpN+n9Bz-qQUqb$ zg_vN3*d6Xs;BU61?NcFRnC1polMI=jR0Bc)3uYro^}j2ZC7 zk6x45|7^STbb?#D#atAA0th`E4-m1oU;a8%2CV$i{G6~y2&{RRtOaYGi-_V$LX~!B zxbsP)ol|swu>WxKNLFr%mw2;IX#c_5?zp{0HiqI@_QUyx<&obwCs7IzTt*PwaA2Ab+`*mO2WDp?3vo54Es6=3;*@qWEZz0k25$@};()+gLMiZ2u~k?M02{L^L2)9gP4 zU5{S2Tsi^37VnVj;Jv-w;U*wm0swpYqSwp*Ky2TfpKO#*02wPKR>A;)_BXo4V*tq2 zq}si)X@0{IugT+@S1c2ptb&o7W$^-yo&-~pJ>&xd4zVqYf)J*e) zEob(UX#U7E-D3ZzP5@w?3qBktHNSHb?0@*Gk8^4wZ|s;bUcB}8o7Z=bN`%x20F14C zweGNL`Ho^>)eUoZD!brxww@^-oM#^B9?8Hm5CFi~_3G-w`lqj_?i`32YgcB9I} zztHQ7Q<_BDb^Y?0?y~>OMgU;Op1=LAc9hI|Pocn-b8cK{s*gEq-ZfJsKF|KVy?fjP zLKz7Fz}SYD7WEmXekicv{{MKPRr%o$0Kh(Ln3}xb`FX5)d2DcXm%v|xphYf?(QefjlKYM(8j`1eAN{dVg@zpDFd}aCf4@rC{ zug#nXv>W#PQXZAZuNG^oV38?Y-_v#&s92s0`tbVFFV!4}TJ%SvU3k*@IP_u{%C~ z^64*9xc2ifkGwb0LbWY7|97`|8`wtq2><~3>O;@?`jL_^i{SRR{`~qDmipB;KmIq< z;%~kCmixQ9%>WP}BLV=J*mUd}Cl422u%_jsmtI_!&dAS?{K3EcT3niKy#6t>TkZd> z2mpZj>fzI7O%=K+*th(RmzV5-a;2Hg0asrVFHDPn_Imdj{}+%M0RX@>YllvnK6Ol= zB*#qG){ozM>ysU5l<`0_YtOy@1yG;{sNPMUn=h=w?kYu~o= zgZGxMZ-H{|eE;&diN~1c(z~8k@g_={kud=P08OlJ9Cqjthm9K4&?78>T`fB{ zto&s8%FQjNkx7uv+8IBX9hSI?U2 zoWZ~}yE@xj_wH)mykWz}EjwGgz+l;hINxW+1t;|lmUVvm=OAH6cj%wGtFfE4x2jZ(80ZH4FCY_YTdqi>4Fc|wP$l(Qh~}NKxG3>tZ5iH zsHty#wb{0(dCQg^t!9=NCTS1>5~DCTYhj;Pz?Df7Di)8{4BdT~dtqO@yzj6g_fx=! zJ40(LND{cZvNe%$(EHFOx>II? z)u;?6WbdmQ6LRHwVV_W`=(e{n1T`Q6XbvKP<{$!S4kCc&AOdI(B7o)~0%#5*faV|q zXbvKP<{$!Sj>4-=g}?2|gq`_|OGuEHfGhDYXNpjiw?uJ8$SXqs1`ykXvis_nR7SXS zRfQ^1aSRE4;Z9=#8H7)@N2PkL|s5yuLnu7?SIfww7g9xBGhya>{2%tHL z0GfjcpgD*Dnu7?SIgCQ~5c%Ap{ozQ^F9E;A{SEE4Zo|7vAc3-*5MNQKM9|l4!kw$) z%S)XcWl>40{ZnBcYfnik^GG0Ag1}-bO2Y(u1I3C$B-#3!(1ctL1d@-@4b-lJTmdE| zR%9jayMwQk4%Qqr2-F-z0L?)J&>Tbn%|Qgv97F)kK?KkoL;%e}1kfBr0L?)J&>Vbq zFnIEZ2!Q;Q%ij7_f0-?CSyFFz{>_RML9_|+6!9P7iXu!1@>RqHDPPMMZs$v`3f+2D zF1KV)hwo$;-QVngizM!%w(jb-ZU&MoaP=xe<}rc!On@L0uvmiOCO}CO{N0r_A=+1n z>1zdqJJ;{2%tHL0GfjcpgD*Dnu7?SIfww7g9xBGhya?ylMW8a z9EM9EGOd}oJ5fbsg192X$rZY(M0X25&&WyK6ea?nru0hCt#{^2nFAGtn!qaxGXcX) zDD=rMmt4wMEahuy6Fj~Om{8_$=X}=y2H=~WT1FCq5}C_xYC`w(HAjO0y+Qk5a}WVE z2N6JX5CJp?5kPYg0W=2@KywfQGzSqta}WVE2N6JX5CJrY;hmM*PGMSUMz@L;++-+v zYF+~+;LcC&cITV^Iz$m~0>(1I(;ae(WKBrL*RXKM^Hq7ct!$t-xyXEfvr-aYuF+c9 zI0y`RUCb*HP~@9AJEjTg_!?wFxUZEGZb}Dh4%+{kg9xBGhya>{2%tHL0GfjcpgD*D znu7?SIfww7g9xBGhya>{2%tHt007WlJ{N%jAOmy~N9d7Q>(F{6fJG8}F}xh%vW9#?Q4Ju6%g)RP$E%mqGA+~2q2}* zp@X0SXbhk^hya>{2%tHL0GfjcpgD*Dnu7?SIfww7g9xBGhya>{2%tHF37~ks