From adde4e32c1e42a2c6a06c2e753115abd2a4ff5b7 Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Tue, 6 May 2025 11:11:48 +0300 Subject: [PATCH] Networks: create internal docker network for proxy server Prepare to use caddy in docker --- files/gitea/docker-compose.yml.j2 | 6 + files/gramps/docker-compose.yml.j2 | 58 ++++--- files/homepage/docker-compose.yml.j2 | 7 + files/keycloak/docker-compose.yml.j2 | 14 +- files/outline/docker-compose.yml.j2 | 18 +- playbook-docker.yml | 5 + playbook-gitea.yml | 7 - playbook-gramps.yml | 7 - playbook-homepage.yml | 7 - playbook-keycloak.yml | 9 +- playbook-netdata.yml | 2 + playbook-outline.yml | 9 +- roles/owner/tasks/main.yml | 3 +- vars/homepage.yml | 2 +- vars/vars.yml | 237 ++++++++++++++------------- 15 files changed, 207 insertions(+), 184 deletions(-) diff --git a/files/gitea/docker-compose.yml.j2 b/files/gitea/docker-compose.yml.j2 index cdbb802..e2fc653 100644 --- a/files/gitea/docker-compose.yml.j2 +++ b/files/gitea/docker-compose.yml.j2 @@ -12,6 +12,8 @@ services: - {{ backups_dir }}:/backups - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro + networks: + - "{{ web_proxy_network }}" environment: - "USER_UID=${USER_UID}" - "USER_GID=${USER_GID}" @@ -25,3 +27,7 @@ services: - "GITEA__mailer__USER={{ postbox_user }}" - "GITEA__mailer__PASSWD={{ postbox_pass }}" - "GITEA__mailer__FROM=gitea@vakhrushev.me" + +networks: + {{ web_proxy_network }}: + external: true diff --git a/files/gramps/docker-compose.yml.j2 b/files/gramps/docker-compose.yml.j2 index 80a2b10..4e650a9 100644 --- a/files/gramps/docker-compose.yml.j2 +++ b/files/gramps/docker-compose.yml.j2 @@ -10,27 +10,9 @@ services: restart: unless-stopped ports: - "127.0.0.1:{{ gramps_port }}:5000" # host:docker - environment: - GRAMPSWEB_TREE: "Gramps" # will create a new tree if not exists - GRAMPSWEB_SECRET_KEY: "{{ gramps_secret_key }}" - GRAMPSWEB_BASE_URL: "https://gramps.vakhrushev.me" - GRAMPSWEB_REGISTRATION_DISABLED: "true" - GRAMPSWEB_CELERY_CONFIG__broker_url: "redis://gramps_redis:6379/0" - GRAMPSWEB_CELERY_CONFIG__result_backend: "redis://gramps_redis:6379/0" - GRAMPSWEB_RATELIMIT_STORAGE_URI: "redis://gramps_redis:6379/1" - GRAMPSWEB_EMAIL_HOST: "{{ postbox_host }}" - GRAMPSWEB_EMAIL_PORT: "{{ postbox_port }}" - GRAMPSWEB_EMAIL_HOST_USER: "{{ postbox_user }}" - GRAMPSWEB_EMAIL_HOST_PASSWORD: "{{ postbox_pass }}" - GRAMPSWEB_EMAIL_USE_TLS: "false" - GRAMPSWEB_DEFAULT_FROM_EMAIL: "gramps@vakhrushev.me" - GUNICORN_NUM_WORKERS: 2 - # media storage at s3 - GRAMPSWEB_MEDIA_BASE_DIR: "s3://av-gramps-media-storage" - AWS_ENDPOINT_URL: "{{ gramps_s3_endpoint }}" - AWS_ACCESS_KEY_ID: "{{ gramps_s3_access_key_id }}" - AWS_SECRET_ACCESS_KEY: "{{ gramps_s3_secret_access_key }}" - AWS_DEFAULT_REGION: "{{ gramps_s3_region }}" + networks: + - "gramps_network" + - "{{ web_proxy_network }}" volumes: - "{{ (data_dir, 'gramps_db') | path_join }}:/root/.gramps/grampsdb" # persist Gramps database - "{{ (data_dir, 'gramps_users') | path_join }}:/app/users" # persist user database @@ -39,6 +21,30 @@ services: - "{{ (data_dir, 'gramps_cache') | path_join }}:/app/cache" # persist export and report caches - "{{ (data_dir, 'gramps_secret') | path_join }}:/app/secret" # persist flask secret - "{{ (data_dir, 'gramps_media') | path_join }}:/app/media" # persist media files + environment: + GRAMPSWEB_TREE: "Gramps" # will create a new tree if not exists + GRAMPSWEB_SECRET_KEY: "{{ gramps_secret_key }}" + GRAMPSWEB_BASE_URL: "https://gramps.vakhrushev.me" + GRAMPSWEB_REGISTRATION_DISABLED: "true" + GRAMPSWEB_CELERY_CONFIG__broker_url: "redis://gramps_redis:6379/0" + GRAMPSWEB_CELERY_CONFIG__result_backend: "redis://gramps_redis:6379/0" + GRAMPSWEB_RATELIMIT_STORAGE_URI: "redis://gramps_redis:6379/1" + GUNICORN_NUM_WORKERS: 2 + + # Email options + GRAMPSWEB_EMAIL_HOST: "{{ postbox_host }}" + GRAMPSWEB_EMAIL_PORT: "{{ postbox_port }}" + GRAMPSWEB_EMAIL_HOST_USER: "{{ postbox_user }}" + GRAMPSWEB_EMAIL_HOST_PASSWORD: "{{ postbox_pass }}" + GRAMPSWEB_EMAIL_USE_TLS: "false" + GRAMPSWEB_DEFAULT_FROM_EMAIL: "gramps@vakhrushev.me" + + # media storage at s3 + GRAMPSWEB_MEDIA_BASE_DIR: "s3://av-gramps-media-storage" + AWS_ENDPOINT_URL: "{{ gramps_s3_endpoint }}" + AWS_ACCESS_KEY_ID: "{{ gramps_s3_access_key_id }}" + AWS_SECRET_ACCESS_KEY: "{{ gramps_s3_secret_access_key }}" + AWS_DEFAULT_REGION: "{{ gramps_s3_region }}" gramps_celery: <<: *gramps_app # YAML merge key copying the entire grampsweb service config @@ -47,9 +53,19 @@ services: - gramps_redis restart: unless-stopped ports: [] + networks: + - "gramps_network" command: celery -A gramps_webapi.celery worker --loglevel=INFO --concurrency=2 gramps_redis: image: valkey/valkey:8.1.1-alpine container_name: gramps_redis restart: unless-stopped + networks: + - "gramps_network" + +networks: + gramps_network: + driver: bridge + {{ web_proxy_network }}: + external: true diff --git a/files/homepage/docker-compose.yml.j2 b/files/homepage/docker-compose.yml.j2 index 2fbca3a..7184015 100644 --- a/files/homepage/docker-compose.yml.j2 +++ b/files/homepage/docker-compose.yml.j2 @@ -5,3 +5,10 @@ services: restart: unless-stopped ports: - "127.0.0.1:{{ homepage_port }}:80" + networks: + - "{{ web_proxy_network }}" + +networks: + {{ web_proxy_network }}: + external: true + diff --git a/files/keycloak/docker-compose.yml.j2 b/files/keycloak/docker-compose.yml.j2 index c4fff37..1d067f2 100644 --- a/files/keycloak/docker-compose.yml.j2 +++ b/files/keycloak/docker-compose.yml.j2 @@ -12,12 +12,18 @@ services: container_name: keycloak_app command: ["start-dev"] restart: unless-stopped + ports: + - "127.0.0.1:{{ keycloak_port }}:8080" + networks: + - "{{ web_proxy_network }}" + volumes: + - "./data:/opt/keycloak/data" environment: KEYCLOAK_ADMIN: "{{ keycloak_admin_login }}" KEYCLOAK_ADMIN_PASSWORD: "{{ keycloak_admin_password }}" KC_HOSTNAME_URL: "https://kk.vakhrushev.me" KC_HOSTNAME_ADMIN_URL: "https://kk.vakhrushev.me" - ports: - - "127.0.0.1:{{ keycloak_port }}:8080" - volumes: - - "./data:/opt/keycloak/data" + +networks: + {{ web_proxy_network }}: + external: true diff --git a/files/outline/docker-compose.yml.j2 b/files/outline/docker-compose.yml.j2 index d2b36a2..8beedc9 100644 --- a/files/outline/docker-compose.yml.j2 +++ b/files/outline/docker-compose.yml.j2 @@ -6,11 +6,14 @@ services: image: outlinewiki/outline:0.83.0 container_name: outline_app restart: unless-stopped - ports: - - "127.0.0.1:{{ outline_port }}:3000" depends_on: - outline_postgres - outline_redis + ports: + - "127.0.0.1:{{ outline_port }}:3000" + networks: + - "outline_network" + - "{{ web_proxy_network }}" environment: NODE_ENV: 'production' URL: 'https://outline.vakhrushev.me' @@ -52,6 +55,9 @@ services: image: valkey/valkey:8.1.1-alpine container_name: outline_redis restart: unless-stopped + networks: + - "outline_network" + outline_postgres: image: postgres:16.3-bookworm @@ -59,7 +65,15 @@ services: restart: unless-stopped volumes: - {{ postgres_data_dir }}:/var/lib/postgresql/data + networks: + - "outline_network" environment: POSTGRES_USER: '{{ outline_postgres_user }}' POSTGRES_PASSWORD: '{{ outline_postgres_password }}' POSTGRES_DB: '{{ outline_postgres_database }}' + +networks: + outline_network: + driver: bridge + {{ web_proxy_network }}: + external: true diff --git a/playbook-docker.yml b/playbook-docker.yml index a300ea8..341035a 100644 --- a/playbook-docker.yml +++ b/playbook-docker.yml @@ -26,3 +26,8 @@ - name: "Login to yandex docker registry." ansible.builtin.script: cmd: "files/yandex-docker-registry-auth.sh" + + - name: Create a network for web proxy + community.docker.docker_network: + name: "{{ web_proxy_network }}" + driver: "bridge" diff --git a/playbook-gitea.yml b/playbook-gitea.yml index a4a6dc4..e61be37 100644 --- a/playbook-gitea.yml +++ b/playbook-gitea.yml @@ -23,13 +23,6 @@ - "docker" owner_ssh_keys: - "{{ lookup('file', 'files/av_id_rsa.pub') }}" - owner_env: - PROJECT_NAME: "{{ app_name }}" - DOCKER_PREFIX: "{{ app_name }}" - IMAGE_PREFIX: "{{ app_name }}" - CONTAINER_PREFIX: "{{ app_name }}" - USER_UID: "{{ user_create_result.uid }}" - USER_GID: "{{ user_create_result.group }}" - name: "Create internal application directories" ansible.builtin.file: diff --git a/playbook-gramps.yml b/playbook-gramps.yml index 67add14..b4c3f33 100644 --- a/playbook-gramps.yml +++ b/playbook-gramps.yml @@ -24,13 +24,6 @@ - "docker" owner_ssh_keys: - "{{ lookup('file', 'files/av_id_rsa.pub') }}" - owner_env: - PROJECT_NAME: "{{ app_name }}" - DOCKER_PREFIX: "{{ app_name }}" - IMAGE_PREFIX: "{{ app_name }}" - CONTAINER_PREFIX: "{{ app_name }}" - USER_UID: "{{ user_create_result.uid }}" - USER_GID: "{{ user_create_result.group }}" - name: "Create application internal directories" ansible.builtin.file: diff --git a/playbook-homepage.yml b/playbook-homepage.yml index 936ca47..da0d2d4 100644 --- a/playbook-homepage.yml +++ b/playbook-homepage.yml @@ -18,13 +18,6 @@ - "docker" owner_ssh_keys: - "{{ lookup('file', 'files/av_id_rsa.pub') }}" - owner_env: - PROJECT_NAME: "{{ app_name }}" - DOCKER_PREFIX: "{{ app_name }}" - IMAGE_PREFIX: "{{ app_name }}" - CONTAINER_PREFIX: "{{ app_name }}" - USER_UID: "{{ user_create_result.uid }}" - USER_GID: "{{ user_create_result.group }}" - name: "Login to yandex docker registry." ansible.builtin.script: diff --git a/playbook-keycloak.yml b/playbook-keycloak.yml index 1175c4c..c08a661 100644 --- a/playbook-keycloak.yml +++ b/playbook-keycloak.yml @@ -9,7 +9,7 @@ vars: app_name: "keycloak" app_user: "{{ app_name }}" - base_dir: "/home/{{ app_name }}" + base_dir: "/home/{{ app_user }}" tasks: - name: "Create user and environment" @@ -21,13 +21,6 @@ - "docker" owner_ssh_keys: - "{{ lookup('file', 'files/av_id_rsa.pub') }}" - owner_env: - PROJECT_NAME: "{{ app_name }}" - DOCKER_PREFIX: "{{ app_name }}" - IMAGE_PREFIX: "{{ app_name }}" - CONTAINER_PREFIX: "{{ app_name }}" - USER_UID: "{{ user_create_result.uid }}" - USER_GID: "{{ user_create_result.group }}" - name: "Create application data directory" ansible.builtin.file: diff --git a/playbook-netdata.yml b/playbook-netdata.yml index fe0734d..20ea8ad 100644 --- a/playbook-netdata.yml +++ b/playbook-netdata.yml @@ -29,6 +29,8 @@ restart_policy: "unless-stopped" published_ports: - "127.0.0.1:{{ netdata_exposed_port }}:19999" + networks: + - name: "{{ web_proxy_network }}" volumes: - "/:/host/root:ro,rslave" - "/etc/group:/host/etc/group:ro" diff --git a/playbook-outline.yml b/playbook-outline.yml index aeb1b08..14f9926 100644 --- a/playbook-outline.yml +++ b/playbook-outline.yml @@ -9,7 +9,7 @@ vars: app_name: "outline" app_user: "{{ app_name }}" - base_dir: "/home/{{ app_name }}" + base_dir: "/home/{{ app_user }}" data_dir: "{{ (base_dir, 'data') | path_join }}" postgres_data_dir: "{{ (base_dir, 'data', 'postgres') | path_join }}" postgres_backups_dir: "{{ (base_dir, 'backups', 'postgres') | path_join }}" @@ -24,13 +24,6 @@ - "docker" owner_ssh_keys: - "{{ lookup('file', 'files/av_id_rsa.pub') }}" - owner_env: - PROJECT_NAME: "{{ app_name }}" - DOCKER_PREFIX: "{{ app_name }}" - IMAGE_PREFIX: "{{ app_name }}" - CONTAINER_PREFIX: "{{ app_name }}" - USER_UID: "{{ user_create_result.uid }}" - USER_GID: "{{ user_create_result.group }}" - name: "Create internal directories" ansible.builtin.file: diff --git a/roles/owner/tasks/main.yml b/roles/owner/tasks/main.yml index f2b22bf..145f1a0 100644 --- a/roles/owner/tasks/main.yml +++ b/roles/owner/tasks/main.yml @@ -27,8 +27,7 @@ - name: "Prepare env variables." ansible.builtin.set_fact: - env_dict: '{{ owner_env | combine({"CURRENT_UID": user_create_result.uid | default(owner_name), "CURRENT_GID": user_create_result.group | default(owner_group)}) - }}' + env_dict: '{{ owner_env | combine({"USER_UID": user_create_result.uid, "USER_GID": user_create_result.group}) }}' - name: 'Set up environment variables for user "{{ owner_name }}".' ansible.builtin.template: diff --git a/vars/homepage.yml b/vars/homepage.yml index 1d32549..5140316 100644 --- a/vars/homepage.yml +++ b/vars/homepage.yml @@ -1,6 +1,6 @@ app_name: "homepage" app_user: "{{ app_name }}" -base_dir: "/home/{{ app_name }}" +base_dir: "/home/{{ app_user }}" docker_registry_prefix: "cr.yandex/crplfk0168i4o8kd7ade" homepage_web_image: "{{ homepage_web_image | default(omit) }}" diff --git a/vars/vars.yml b/vars/vars.yml index 53e0e78..1e37815 100644 --- a/vars/vars.yml +++ b/vars/vars.yml @@ -1,118 +1,121 @@ $ANSIBLE_VAULT;1.1;AES256 -61653632623534313563323536353134663639383236653566313666613431353539333931626631 -3264323462653132646562343266356261353864663232350a383964366432323263303963363334 -30663863646436393661653738656361393761373839353462313239313564626436323434373830 -3931646664373465320a323563623761386232306162643039613730336130393531373437633530 -33343662636535373533643265366364626663666136633730333836313337333631333061353535 -36363666623831366464333630366330323864656434633439353132633530663365386561616432 -61303462333836356133633866313666663733613032666331616635376562343131356564666337 -36646235623638303732346330373961336334646436633034653238323764373262313962343233 -63376338636666633436356166363463623832643435346334633465336432383134336432346633 -37663332323432663130306230666534323239646234363832306538306336656335663561613065 -65633635626532333438633466353030643839663666633365663738633137393834316233323436 -64333365663237373464393238323430316261366636333531653036383331656333373037323433 -38303961343836313532373466613232383030663430343031386334386263326163663532373264 -64396132613834663332616533346236623938353531613263666138306665376232666535376264 -63663934333461623332333964346435316465636366663634306165666361386332346338633638 -34316136633131356336613031306130363332633132316361633433323837313238623435376563 -62666636643235353966386237393334326261623233303935623136643439623365333930326661 -65303239363232636432623162626138313466323661636438393430306163356264656263333865 -64386231646364626564326466653831623563633036353161643731343133643933383632653162 -30336537366332313239396363633333386136396530613366343965663133396138316534336431 -35663038343533653065353639633065393636326662353235376666613630383338393337383634 -32623864323964383837333434343233326338353630356137303665653864346261336238653664 -31356334383535386562336131626431373533373038376238333830333136326337656234393565 -62363335346630346666383138333939326562303433396663663466326362633965323238656334 -61333335323664646166356665333639326437333539316466656533613365663165323138396237 -61386366336530626665363765373538356136393536353831623730313065346238393765313435 -34623634376133336534303461666561353864336130633435316566366434643435336231386265 -64353365393830373331653739663561336565643435656634643632336566393165623462383433 -64633065663065353561393365333137383465376365393639306561633561306335343132353636 -36366362353739613662383463373232663735333230613137396338373530373939386131663437 -66643634663761373431353339356439383035653932373532323765653466313430303637376261 -66333337363538643765613535363038633838323136663666386638363263643930346533663433 -61303231346562313538306461353763383861393835633731303539373964653661623362376363 -65396439336632353534326561393666396337376235373238613866626466666561656665323066 -38376337646430616231383933666532633536623232643166376336373331353233363738613037 -30393831316537343265393666383863323830303662326162336631346161306663646535343337 -65326330353831393139616566303432626634376234336661333966653835373437383233383537 -32333163393532643633646435663962613335623631366165653664653632303632623462313664 -36306132646265623438323237326633353463326433366430653766373836333130353833633863 -31616132393462386562623565353730333435303930323034353530353734313033356439623463 -66666436373434313437623064356133313632623932306630663136393336363431633162393764 -34383863653437353438306362353062303463303938366133333135366464653164316465646264 -65383536333064663866373233663265376536613462663637363965623834626362643439636463 -31633939653635366235323261666636623662303163623632356265626530303938306239666462 -62363237363836383130323364373765396531356232373133616464373434343166313065633538 -31353036396231643963623530303938666434653239656338363633663835653236626537333366 -32653631383938373163653830616238623363643537363139366465633935313464333463633165 -30373039396366623135346235656563623564393131353361323038346364323232633331363666 -66373330666135323532333537613263303765626532343034626637323663613863636362623930 -66326133373838626261323161313238396433643638623964376430666332336363393930383935 -39386432376361333935343736626166623761373262343561636661393135326566303866616335 -39333732333335646237616661396638336263623065393737323032626133333735613763366537 -30313930376463393566633965323630333038323433383433316262383561313835663733306166 -37303436383639326237313030333931646135636433636363346535306336393237636335346632 -39373064366434653438663234346430333738613865353937326464343031353866383536336436 -65326230623435633432316535393835336164623966366236623636643038646263373964316338 -39633833616233613630353230623463343936376539343961383738303463393866316363656632 -31343866313961633531663739323264643436663936643531306330623630353231393337666535 -39303462323439333166303239343931373363303161626132623933613163383536363233623865 -30616639316661313461633738323834653032633036373039636334383533613038326238323337 -35623736303332373433323334616263353233613365633936653864396565343731353535663233 -37363265383864393366363864626238303634626232306632303464613164363463643666636438 -39656637373337313338303837326435353039373162623536383232303134303833383235663262 -32393034626136316131633234326435356630323263393034623537653330356162643037323764 -38336562623262346334623263336465356262303830643331366566306531666139326530303762 -33316361336132303764353834663863313035333363313663643363383730646562653562383336 -32333734323139636531656433653365333937616532343662383262363331336332376464656134 -35353362653134343539336438366465663166343638386237386663323439643366373364323030 -63633638623064323566303734326638626632656663333135656636386163666438646139623330 -32373333653965303632373664396238313238373564613335616435636139653331396235653638 -64646166383039393166613331363936306337313839643162363033643536343565326231336666 -31353261626566366336393033373366316231633763373733356565346536343066333034313036 -37613436316532656665313965643634616531663438623866386538646562343832626465393064 -35613135643334613866343462336532303866343738623637626630666264393736336433633366 -65613531366332633565333664616538663061623962313965356666663839623139663238613364 -34373363363366323230626165623465366534303437313030366166623033666661393661373937 -66383436376262393139323062363535313166393134376433323166333864343635353165613432 -33636330643633373966323163646437613334366135323430616662616365366430313830363736 -64633535366465303131323738306434386137323466626463323237626638363464303230653465 -66656634306138663064373331386339363734383563326566373931316564323263353161366130 -32666631313661363938323964333064323538336234306637323164643139636564396332366136 -35336635383533353330316565393166663435613062643839353464623434346635373838646164 -34363264383265306337646336653637326362396566303339343933646534373066636363333866 -30633832373138386566353661636239386136366464346565613263653236393038363263376561 -64343635656437326162303436636633353566323632613234306364643863626532616566306363 -61393036353137626361633238386664353061396564333032356161643033636338353336623063 -65613731353264626563633361383334373633646436643039663335616466323737663536653539 -64333330383830636363663731613734373466363337386537333465323163656465623337303537 -63366462303039613161363162636435363232333235363136646363663733653937346563316261 -66626562623330373737393263343038316466646138633337656461396330626130333265616335 -32303462333231343264613131656134613034333138363036333730323134646461366233393533 -35643435343561343362313338663465653432306136306466616437643734313339306635653063 -62306537313262636239336165393138306232333433373163346265313538623330663166646465 -64353163386432656163623130316463353536613430313634393866383266303534323266386163 -65363730376239303935303433393535386137626136666166353631656661393265646630626362 -37313830393966666637306436303764333733346162393165343831386434373832633165313831 -39313861396238626538663633363139646638346137363439643065656334653365346533303535 -62373136363631326534313736666539323336396565633935613434373436353332396662323263 -64383031663937656563393336323462353962326638383762616163363963303966333265356538 -36373865663561333430383333323332623961396432326131376331663536663761633934653630 -31653539343933393663376563643139663732366431373536613461646133623037363336363736 -63356638306235313932346365303439313136336235613633636363303962373236613339356238 -37623432353662373534313266346461623338626135316537646339343962313335306534663966 -30363235636237323834386332656139633765623930646631636162353662323332303935636332 -35366631363035383065663337386239316235653237333431313665646333356530643637326464 -65616262326136376233383334393065623562326638613466653634363735336363653833376666 -39336231643461623932333631323362393230613164616562343266633064336431366161636639 -36323063313262333235663062393535303261313830363161636661663033616337383764376434 -61333034336431373633383739346639643765373930323162306230353063383730356439663336 -31373137316231316136356137353530646431363437333937633966633837393733333764343334 -63663537326361646165396438393230316461393039613831616534636534353365353838356262 -64353434636466633963323536323038343664333039306432326662363936323761646665363831 -64616661363665393764343262346234623636383330366363366332613662313230646163643430 -61363463306631336632393861333830376539366238343963306131306438623733386665626130 -33376536373364303565633132353565373365643964343737323632396131393865303166366331 -33353538613235343834623865373461613761653536363137346136353663333733616431643937 -38323532663163353734316635623236306331333233393366376563643661653763 +63336665643133366538363963613966643631396163333335386462303362346632336332663763 +6561306531386234636433663866373239396533363361610a373331666131393038346230666434 +31323362623166643732326431343766666663623431663964653335393863306264643136383935 +6230663135383161660a336461353136383735393631383333616665303836333561306663366434 +30313738316131306564626330623164323766666530346632613932343233316162313033323130 +37373330346539356534663837356131313631643665373432316434616431366438303461336566 +62386463353936383762353331646637393537666164346364643036323166663963653864633763 +35353230656333376465666531306333356533343836653933366230393466633638353865633762 +64663531613939663439396634343033303337363630383532333633393937396336363261313963 +61646163643339363831366633383134346536343332393366646237626335303130396435396166 +38613639373037326430363337623036613135346139633061386236363131646566616433346535 +62386633396232666538393066363139336233313161636365373632363161656132636238613162 +31326566616438393834336331383432373962343436626464363266636535626264353466623631 +39303164396261373739333137646266323939306133386366383233666235303636343337396261 +39623836383232303737363032613361323437336364346430333931373030643635663730383332 +62623864623238373861656664393061316433393832646431333636633662313037616665666536 +36396538346632393563386662393166626230623430626530643766643638663332613436323534 +34653336623539626632396364653337313430356338363131343363346530626133333938353834 +30336535333933333764346437376261343434383237336331653938613766386234656266366465 +33323531623838303862396462373835323366613866306234373737393263373438373635653861 +31343235323162393461666335386136386435646466383631623237633734336161303165633366 +34656631333038323039393338376232663930656164626334393139313531373437306534353133 +33636436666530616535326631363537323131313132623830316139346637386566316138383833 +36323633636366353932386235303239623333396636333834313833353232373461323231393066 +61323339623865306264353937373163383435623962383364343163306432353834613536636665 +36343837323937653533633364623435663038333038353663666566383336366366383132363164 +31353338613466343834313635373131626338313230653733666633646162316366343630613630 +64336539373231306336643437646161353230633763366230346338353264353561376333623132 +30363764656662396230343531306462343139333336343135613063386530343265663537383539 +61353137373235663038323166613564386631623230356263316463653764656464393866646536 +36366162333637633438343038336532346432633632613134646361393936306634353566376363 +64353766613361663434383638613637626333323538303433313235303961376536333338353237 +32346662313530356564653463376562366239626465646230636531356235396537663261663535 +34623638643565373164363339646464303864373437333232616339393465396133653262376636 +62633136356636623663633562623462303362353139373630313064663738363139636336316438 +66373665666661376131366432376437323439373863376136336565636337356166396561363662 +32646438346561623132633238636131373561336335336161366261393634323139363062663439 +37666664363461353131613637646434343361336236383433666230333964396531356362623862 +32636530306534386266643033316262333637653933373133363935646561663064306266323531 +38333936666266343133666563653363373635663465386235393463623361316663613136376134 +62643539633438636263313633343562613664626432623731393061333261363937613563343131 +30623137313063633636613733633237346464373234656333346336643532383931333938633433 +36363536323264346537653761653033633133626265613831306262343062613130643561363563 +37633664663039666663666362346636353139393265343165363466616338643666336534613033 +65343164316235356439366335316263383166326165326134653963656438343630643037353035 +63366532373464366662636330326334333732646336613730333537336335623265626537633634 +34623366656337623635653131383936643436653735636165643330393034383437616162346632 +39663235663362346563306130343266303834663433336364653563353466383535383532366539 +64356536613539636630373039663035316335313335353137366537333838363135653236393331 +37633065323762613330616535363430653865366666623730663230333436323839626131643331 +31353662383332333638666336656466646530376339373435633037323432333538656433623865 +63616531346265353238373531363232326331616265343233393161383064393866633536343437 +36636336633934633236393034346138376466613839653938353336343163313830303330633565 +30373833323435306138343933333837636161393862306430613161396233323661333430626364 +65356662336636653039626363623135386336626566613837613261336138623663316266316565 +32323865663662376335626432333934356239363563623965353436356536323339656461616233 +61653930666266363165303461643465333063366166346636303163333738663165353139626430 +63356466616635656565383938313730366635316533656261376436396463343434626564643963 +32663961623063343233323037366631356662343963313034626633326564633066326633353133 +34663737356237306363356237383461376534356235316433326535616465633936363238383232 +61616666656261336434356262663066316630613633373535656465353262633934353938643664 +32303234363461633438383037386364366134303933623332356165613335626266633132633932 +64333366353837353461353261356434356361303665346631636632653965336162373536393935 +39663531613233393364353631383033303132323962356462366561343333663335383831353736 +36306534653030666265343462653730306438343733633066353965393462353136656336333036 +64313733376565633264613133336162366130363633313336343131353362363638666634393330 +66613961616238373566386362316430353966333136626663653537653838346533353862303437 +33396630303764386534666666373737636666383361383765396466623639353265653831383761 +65363637386539643439343434383534646563343038626536653836346437323666333362623838 +39366332613631363363633064646134366639386164313636343130613461306237646564623061 +66363261303737633434383161326530646365366539623966396166626533636538633037396239 +30376639353566313637656362353862303937356431653733346363306138333162326166633236 +38336539333462383838653937343466376365393962303037306536633231343133313738343736 +65626264333265656535343339643361366261323066376436346331383365383135626262393139 +62316233303366303932343036316238643135616335393431323030396263653031663732306231 +39346131373963626433316363393830343330353433623136613532363833366633326262313636 +30323365343364336662643235386464373432343030333836326465663966336563366461626534 +61323163663330623766656439623837303263643038313566663139363036373666333732303237 +37626330663664636236613038373937306563343635343034663832313363613161303630323934 +66393966366537393466383764386434666132393038376663623333613061633465306235316130 +37393133303062346334386634333539356435643163336332386238643362373736376164323630 +64313661303738366565376564656665316166326532633036643533306135336564326334393436 +32656431383534383865333737376531306665376635616635623766396165613035393330336239 +30643230323737373262313363316238316533653265646661313736323835393362643961633030 +63646664303639316632383763323537363264663739633530346538643663373130353962323436 +66306561376638373436366233336535313761623532633862303533663762623838326239376664 +62663534363262346232666463373332646234623733346165613630643330356131616632353533 +66353434326233643561363730373964393039343766393862663563643939343831653666643564 +31626538633130313964636230343530333535353831613566346165303330663964666161666363 +66623736323464663731343431373938323331663364666634646165643030646266363261646130 +63373830316338303564343239343761623231356562316536626135316430386530613238353233 +32646438323637303038653737386363313162343535396632363030386333616561613966363163 +65326461653063323132623130336265303764653965323966663466376463623433623365353137 +62363039616630313034373734633261303830636334333233393562386563363963666263643561 +38363766386465643835383837353631353138313263346361366266666161633334343238396165 +65646139626537643261656564333131643566306636316137333636303430613535353831343436 +31373734333231393737643432353361623061613363383433366366313230366461663031303864 +65616337396161373733386463303730396437333330366563346437333331343435313134376536 +33313861376333386138336339313935623861653435323039323835633038653263656364656239 +39383561643834313635663464636261643962373434383733653631336265386638383430653533 +38613838316563393631366263306138353161616635386332656331343935336538626331386634 +37636133353637363932346462336539623366613066313336643739653661336336393131383665 +39613162613863643761623737326438313732363263666135623537636266363664316430393366 +34663161366337386436353333393637383231333564633932333238643362396232353430353136 +34343562366363613664646465313135316438386532653539656137366436663630623831323466 +32393230333330323761656665363731656462363536396632343934643932373939323834336238 +66666132326334353165366535326130333831353537383931366539616237633830386234626366 +30663162363531613937343135633032316338646433633262643462316631356434363563386339 +39366666303062346663653435313966663930373238613037386430313063386534666562303336 +64393763323336356631376633353037326464383639333734306161623463373466623734306231 +34616564656466336161346664326230633938393834323432303562666437323764336137663966 +66373238386663613631353964353339333738323032376465373736636132323535626164356464 +33366162646563613464383663323965663766376530613562666661643137656137366237303033 +34373733633738323566306261613135393533346332633339396436663438653761363530386639 +63396461623465343862323639396333313639636632306236393438386262393664646564333238 +65653462376138636332653933366162396337343530636338616336333263656266383736386535 +63643034393035633533663236366135363466653765383366336430646562313763336561376334 +64396430343330653335326133616566663533313233616432666264623231666338393730623531 +33376163666430623835376336363863356237336137323066356262333436623266323661613765 +61656662373435663132