From 6b1f93f380b85af324d38ab273c10ecf72d78770 Mon Sep 17 00:00:00 2001 From: MaksymLeus Date: Thu, 22 Jan 2026 20:06:44 +0200 Subject: [PATCH 1/2] feat: Initial commit --- .dockerignore | 3 + .github/workflows/ci.yml | 121 ++++++++ .github/workflows/template-docker.yml | 221 +++++++++++++ .../workflows/template-semantic-release.yml | 89 ++++++ .gitignore | 33 ++ .releaserc.json | 17 + CHANGELOG.md | 6 + LICENSE.md | 21 ++ README copy.md | 240 ++++++++++++++ cmd/server/hostinfo.go | 263 ++++++++++++++++ docker/Dockerfile | 98 ++++++ docker/docker-compose.yml | 21 ++ docs/00-overview.md | 167 ++++++++++ docs/01-getting-started.md | 145 +++++++++ docs/02-installation.md | 213 +++++++++++++ docs/03-usage.md | 158 ++++++++++ docs/05-architecture.md | 239 ++++++++++++++ docs/06-deployment.md | 293 ++++++++++++++++++ docs/07-cloud-detection.md | 178 +++++++++++ docs/08-api.md | 206 ++++++++++++ docs/09-releasing.md | 174 +++++++++++ docs/TODO.md | 60 ++++ docs/semantic.md | 128 ++++++++ docs/test.md | 0 go.mod | 3 + .../favicon_io/android-chrome-192x192.png | Bin 0 -> 3108 bytes .../favicon_io/android-chrome-512x512.png | Bin 0 -> 9026 bytes internal/favicon_io/apple-touch-icon.png | Bin 0 -> 2917 bytes internal/favicon_io/favicon-16x16.png | Bin 0 -> 542 bytes internal/favicon_io/favicon-32x32.png | Bin 0 -> 806 bytes internal/favicon_io/favicon.ico | Bin 0 -> 15406 bytes new-readme.md | 183 +++++++++++ scripts/hooks/commit-msg | 24 ++ scripts/hooks/pre-commit | 28 ++ tools/build.sh | 60 ++++ tools/setup-hooks.sh | 6 + web/image.png | Bin 0 -> 205603 bytes web/templates/index.html | 95 ++++++ web/templates/index_to_update.html | 215 +++++++++++++ 39 files changed, 3708 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/template-docker.yml create mode 100644 .github/workflows/template-semantic-release.yml create mode 100644 .gitignore create mode 100644 .releaserc.json create mode 100644 CHANGELOG.md create mode 100644 LICENSE.md create mode 100644 README copy.md create mode 100644 cmd/server/hostinfo.go create mode 100644 docker/Dockerfile create mode 100644 docker/docker-compose.yml create mode 100644 docs/00-overview.md create mode 100644 docs/01-getting-started.md create mode 100644 docs/02-installation.md create mode 100644 docs/03-usage.md create mode 100644 docs/05-architecture.md create mode 100644 docs/06-deployment.md create mode 100644 docs/07-cloud-detection.md create mode 100644 docs/08-api.md create mode 100644 docs/09-releasing.md create mode 100644 docs/TODO.md create mode 100644 docs/semantic.md create mode 100644 docs/test.md create mode 100644 go.mod create mode 100644 internal/favicon_io/android-chrome-192x192.png create mode 100644 internal/favicon_io/android-chrome-512x512.png create mode 100644 internal/favicon_io/apple-touch-icon.png create mode 100644 internal/favicon_io/favicon-16x16.png create mode 100644 internal/favicon_io/favicon-32x32.png create mode 100644 internal/favicon_io/favicon.ico create mode 100644 new-readme.md create mode 100755 scripts/hooks/commit-msg create mode 100755 scripts/hooks/pre-commit create mode 100644 tools/build.sh create mode 100755 tools/setup-hooks.sh create mode 100644 web/image.png create mode 100644 web/templates/index.html create mode 100644 web/templates/index_to_update.html diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c43fcfd --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +.git +.github +*.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f33e95f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,121 @@ +name: Continuous Integration Pipeline + +on: + release: + types: + - published + # - released + # - created + + workflow_dispatch: + push: + branches: [ main ] + # tags: + # - 'v[0-9]+.[0-9]+.[0-9]+' + + pull_request: + branches: [ main ] + +env: + GO_VERSION: '1.24' + MAIN_FILE: './cmd/server/hostinfo.go' + +jobs: + + Verification: + if: github.ref_name != 'main' + name: Verification of all checks + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go ${{ env.GO_VERSION }} + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + + # - name: Cache Go modules + # uses: actions/cache@v3 + # with: + # path: | + # ~/go/pkg/mod + # ~/.cache/go-build + # key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + # restore-keys: | + # ${{ runner.os }}-go- + + + - name: Run Lint + run: | + echo "Running lint..." + fmt_out=$(gofmt -l .) + if [ -n "$fmt_out" ]; then + echo "Go code is not formatted:" + echo "$fmt_out" + gofmt -d . + exit 1 + fi + go vet ./... + + - name: Run tests + run: | + echo "Running tests..." + go mod download + go test -v -race ./... + go build -v ${{ env.MAIN_FILE }} + + - name: Run Security + run: | + echo "Running security checks..." + + # Hardcoded credentials check + # Exclude common directories and files + EXCLUDES=(".git" "frontend" "node_modules" "build" "docs") + # Exclude file patterns + FILE_EXCLUDES=("*.yml" "*.yaml" "*.md" "*.js" "*.jsx" "*.json" "*_test.go") + # Build grep exclude params + EXCLUDE_PARAMS=() + for dir in "${EXCLUDES[@]}"; do EXCLUDE_PARAMS+=(--exclude-dir="$dir"); done + for file in "${FILE_EXCLUDES[@]}"; do EXCLUDE_PARAMS+=(--exclude="$file"); done + # Run grep safely + if grep -rI "password.*=" . "${EXCLUDE_PARAMS[@]}" | \ + grep -v "^[[:space:]]*//" | \ + grep -v 'json:' | \ + grep -v '`json:' | \ + grep -v "Password.*string" | \ + grep -v "r.BasicAuth()" | \ + grep -v "subtle.ConstantTimeCompare"; then + echo "Potential hardcoded credentials found" + exit 1 + fi + + # build-and-publish-docker: + # if: github.ref_name != 'main' + # needs: [Verification] + # uses: ./.github/workflows/template-docker.yml + # with: + # dockerfile_path: "./docker/Dockerfile" + # docker_push: true + # image_name: "hostinfo" + # version: ${{ github.ref_name }} + # registry: "docker.io" + # organization: "maximleus" + # platforms: "linux/amd64,linux/arm64" + # secrets: + # DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + # DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + + semantic-release: + if: > + github.event_name == 'push' && github.ref == 'refs/heads/main' + permissions: + contents: write # to be able to publish a GitHub release + uses: ./.github/workflows/template-semantic-release.yml + with: + dry_run: false + semantic_version: 20 + secrets: + MY_GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} + \ No newline at end of file diff --git a/.github/workflows/template-docker.yml b/.github/workflows/template-docker.yml new file mode 100644 index 0000000..8009b67 --- /dev/null +++ b/.github/workflows/template-docker.yml @@ -0,0 +1,221 @@ +name: Docker Template Workflow for GitHub Actions + +on: + workflow_call: + inputs: + dockerfile_path: + description: "Path to the Dockerfile" + required: false + default: "./Dockerfile" + type: string + docker_image_overwrite: + description: "Overwrite existing Docker image if it exists" + required: false + type: boolean + default: false + docker_push: + description: "If true, the Docker image will be built and pushed" + required: false + type: boolean + default: false + image_name: + description: "Docker image name (without registry)" + required: true + type: string + version: + description: "Tag version (ex: 1.0.0, latest, dev)" + required: true + type: string + registry: + description: "Registry (docker.io, ghcr.io, etc)" + required: false + default: "docker.io" + type: string + organization: + description: "Docker Hub or GHCR organization/user" + required: true + type: string + platforms: + description: "Platforms to build (comma separated)" + required: false + default: "linux/amd64,linux/arm64" + type: string + secrets: + DOCKER_USERNAME: + required: true + DOCKER_PASSWORD: + required: true + +permissions: + contents: read + # packages: write + # security-events: write + +jobs: + docker-build-push: + if: "!contains(github.event.head_commit.message, '[docker skip]')" + name: Build & Publish Docker Image + runs-on: ubuntu-latest + + steps: + - name: Checkout source + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + if: ${{ inputs.registry == 'docker.io' }} + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Log in to GHCR + if: ${{ inputs.registry == 'ghcr.io' }} + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set dynamic version + id: set_version + run: | + # detect PR source branch or fallback to ref_name + VERSION="${{ github.event_name == 'pull_request' && github.head_ref || inputs.version }}" + + # Sanitize for Docker tags: replace / , with - + VERSION="${VERSION//\//-}" + VERSION="${VERSION//,/ -}" + + echo "Dynamic version: $VERSION" + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Verification if Dockerfile exists + if: ${{ inputs.dockerfile_path != '' }} + run: | + if [ ! -f "${{ inputs.dockerfile_path }}" ]; then + echo "Dockerfile not found at path: ${{ inputs.dockerfile_path }}" + exit 1 + fi + + - name: Verification if Docker image already exists + id: check_image + run: | + IMAGE="${{ inputs.organization }}/${{ inputs.image_name }}:${{ steps.set_version.outputs.version }}" + OVERWRITE="${{ inputs.docker_image_overwrite }}" + EXISTS=false + + echo "Checking if image exists: $IMAGE" + + if docker manifest inspect "$IMAGE" > /dev/null 2>&1; then + echo "Image exists: $IMAGE" + if [ "$OVERWRITE" = "true" ]; then + echo "Overwrite enabled → will rebuild" + EXISTS=false + else + EXISTS=true + fi + else + echo "Image does not exist → will build" + fi + + echo "exists=$EXISTS" >> $GITHUB_OUTPUT + echo "overwrite=$OVERWRITE" >> $GITHUB_OUTPUT + + - name: Set docker_push for feature branches + id: check_feature_branch + run: | + # Determine ref name + REF_NAME="${{ github.event_name == 'pull_request' && github.head_ref || github.ref_name }}" + + # Decide docker_push + if [[ "$REF_NAME" == feature/* ]]; then + echo "Feature branch detected, setting docker_push to false" + echo "docker_push=false" >> $GITHUB_OUTPUT + else + echo "Not a feature branch, using input docker_push value" + echo "docker_push=${{ inputs.docker_push }}" >> $GITHUB_OUTPUT + fi + + - name: Set Docker tags + id: docker_tags + run: | + VERSION_TAG="${{ inputs.organization }}/${{ inputs.image_name }}:${{ steps.set_version.outputs.version }}" + echo "tags=$VERSION_TAG" >> "$GITHUB_OUTPUT" + + if [ "${{ github.event_name }}" = "release" ] && [ "${{ github.event.action }}" = "published" ]; then + LATEST_TAG="${{ inputs.organization }}/${{ inputs.image_name }}:latest" + echo "tags=${VERSION_TAG},${LATEST_TAG}" >> "$GITHUB_OUTPUT" + fi + + # - name: Extract metadata for Docker + # id: meta + # uses: docker/metadata-action@v5 + # with: + # images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + # tags: | + # # Branch name + # type=ref,event=branch + # # Tag name + # type=ref,event=tag + # # Short SHA + # type=sha,prefix=sha- + # # Latest tag for main branch + # type=raw,value=latest,enable={{is_default_branch}} + # # Semver tags + # type=semver,pattern={{version}} + # type=semver,pattern={{major}}.{{minor}} + # type=semver,pattern={{major}} + + - name: Build and push Docker image + if: steps.check_image.outputs.exists == 'false' || steps.check_image.outputs.overwrite == 'true' + uses: docker/build-push-action@v6 + with: + context: . + file: ${{ inputs.dockerfile_path }} + push: ${{ steps.check_feature_branch.outputs.docker_push }} + platforms: ${{ inputs.platforms }} + tags: ${{ steps.docker_tags.outputs.tags }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Summary + run: | + if [ "${{ inputs.docker_push }}" = "false" ]; then + echo "Dry run mode: image was built but not pushed." + else + echo "Image published to:" + echo "${{ inputs.registry }}/${{ inputs.organization }}/${{ inputs.image_name }}:${{ steps.set_version.outputs.version }}" + fi + + # security-scan: + # name: Security Scan + # runs-on: ubuntu-latest + # needs: build + # if: github.event_name != 'pull_request' + + # steps: + # - name: Checkout code + # uses: actions/checkout@v4 + + # - name: Log in to Docker Hub + # uses: docker/login-action@v3 + # with: + # username: ${{ secrets.DOCKERHUB_USERNAME }} + # password: ${{ secrets.DOCKERHUB_TOKEN }} + + # - name: Run Trivy vulnerability scanner + # uses: aquasecurity/trivy-action@master + # with: + # image-ref: '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}' + # format: 'sarif' + # output: 'trivy-results.sarif' + # severity: 'CRITICAL,HIGH' + + # - name: Upload Trivy scan results + # uses: github/codeql-action/upload-sarif@v4 + # if: always() + # with: + # sarif_file: 'trivy-results.sarif' diff --git a/.github/workflows/template-semantic-release.yml b/.github/workflows/template-semantic-release.yml new file mode 100644 index 0000000..2d15264 --- /dev/null +++ b/.github/workflows/template-semantic-release.yml @@ -0,0 +1,89 @@ +name: Semantic Release +on: + workflow_call: + inputs: + dry_run: + description: "Run the release in dry run mode" + required: false + type: boolean + default: false + semantic_version: + description: "Semantic versioning mode (e.g., 20 for v2.0.0)" + required: false + type: number + default: 20 + outputs: + new_release_published: + description: "Indicates if a new release was published" + value: ${{ jobs.release.outputs.new_release_published }} + new_release_version: + description: "The version of the new release" + value: ${{ jobs.release.outputs.new_release_version }} + new_release_major_version: + description: "The major version of the new release" + value: ${{ jobs.release.outputs.new_release_major_version }} + new_release_minor_version: + description: "The minor version of the new release" + value: ${{ jobs.release.outputs.new_release_minor_version }} + new_release_patch_version: + description: "The patch version of the new release" + value: ${{ jobs.release.outputs.new_release_patch_version }} + secrets: + MY_GITHUB_TOKEN: + required: true + + +jobs: + release: + if: "!contains(github.event.head_commit.message, '[skip ci]')" + name: Release + runs-on: ubuntu-latest + permissions: # Required permissions for semantic-release action + contents: write # to be able to publish a GitHub release + # issues: write # to be able to comment on released issues + # pull-requests: write # to be able to comment on released pull requests + + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version: "lts/*" + + # - name: Install dependencies + # run: | + # if [ ! -f package.json ]; then + # cat < .releaserc.yaml + # plugins: + # - "@semantic-release/commit-analyzer" + # - "@semantic-release/release-notes-generator" + # - "@semantic-release/changelog" + # - "@semantic-release/git" + # - "@semantic-release/github" + + # branches: + # - "+([0-9])?(.{+([0-9]),x}).x" + # - main + # - fs/pl/start + # EOF + # fi + + - name: Semantic Release + uses: cycjimmy/semantic-release-action@v5 + id: semantic # Need an `id` for output variables + with: + dry_run: ${{ inputs.dry_run }} + semantic_version: ${{ inputs.semantic_version }} + env: + GITHUB_TOKEN: ${{ secrets.MY_GITHUB_TOKEN }} + + - name: Do something when a new release published + if: steps.semantic.outputs.new_release_published == 'true' + run: | + echo ${{ steps.semantic.outputs.new_release_version }} + echo ${{ steps.semantic.outputs.new_release_major_version }} + echo ${{ steps.semantic.outputs.new_release_minor_version }} + echo ${{ steps.semantic.outputs.new_release_patch_version }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e6d98ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Code coverage profiles and other test artifacts +*.out +coverage.* +*.coverprofile +profile.cov + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work +go.work.sum + +# env file +.env + +# Editor/IDE +# .idea/ +# .vscode/ +# .releaserc* \ No newline at end of file diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 0000000..2fd4f2c --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,17 @@ +{ + "branches": ["main", "+([0-9])?(.{+([0-9]),x}).x"], + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + + [ "@semantic-release/changelog", {"changelogFile": "CHANGELOG.md"} ], + [ + "@semantic-release/git", + { + "assets": ["CHANGELOG.md"], + "message": "chore(release): ${nextRelease.version} [toster]\n\n${nextRelease.notes}" + } + ], + "@semantic-release/github" + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9820e2c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +# 1.0.0 (2026-01-16) + + +### Features + +* Initial commit ([b223b7c](https://github.com/MaksymLeus/hostinfo/commit/b223b7c6f721ce887b1213c3f7e80753da930f9b)) diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..5f88e9d --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Maksym Leus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README copy.md b/README copy.md new file mode 100644 index 0000000..f6cd778 --- /dev/null +++ b/README copy.md @@ -0,0 +1,240 @@ +# Go Host Info Web App +[![Go Version](https://img.shields.io/badge/Go-1.20+-00ADD8?style=flat&logo=go)](https://golang.org) +[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) + +A lightweight **Golang web application** that displays detailed runtime information about the **host, container and cloud environment**. + +Designed to run **as-is**: +- locally +- in Docker +- on AWS / GCP / Azure + +✅ No special permissions +✅ No cloud credentials +✅ Safe for production + +## ✨ Features + +### 🖥 Host & Container Info +- Hostname +- OS / Architecture +- Go version +- Container uptime +- Environment variables + +### ☁️ Cloud Auto-Detection (no creds) +- AWS EC2 (instance ID, region, AZ, type) +- Google Cloud (project, zone, machine type) +- Azure (VM detection) +- Local / Docker fallback + +### 🌐 Network +- IP addresses +- MAC addresses + +### 🎨 UI +- Clean dark UI +- Structured sections +- Human-readable layout +- Browser-friendly dashboard + +## 📸 Screenshot (example) +![alt text](./web/image.png) + +## 📦 Prerequisites +- **Go**: Version 1.21 or higher ([Download](https://golang.org/dl/)) + +## 🚀 Installation + +### Clone the Repository + +```bash +git clone https://github.com/MaksymLeus/hostinfo.git +cd hostinfo +``` +### Install Dependencies + +#### Backend +```bash +go mod download +``` +## ⚡ Quick Start + +### Build and Run + +```bash +# Build the application +./build.sh + +# Run the server +./hostinfo +``` +Access the application at `http://localhost:8080` + +## 🌐 Deployment + +### Linux Server + +```bash +# Build for Linux +./build.sh all + +# Copy to server +scp bin/hostinfo-linux-x64 user@server:/opt/hostinfo/hostinfo + +# Run on server +ssh user@server +cd /opt/hostinfo +./hostinfo +``` +### systemd Service + +Create `/etc/systemd/system/hostinfo.service`: + +```ini +[Unit] +Description=Web Hostinfo Service +After=network.target + +[Service] +Type=simple +User=www-data +WorkingDirectory=/opt/hostinfo +ExecStart=/opt/hostinfo/hostinfo +Restart=on-failure + +[Install] +WantedBy=multi-user.target +``` + +Enable and start: + +```bash +sudo systemctl enable hostinfo +sudo systemctl start hostinfo +sudo systemctl status hostinfo +``` + +## Preparation with Docker 🐳 +Hostinfo is available as a Docker image for easy deployment. + +**Image Details:** +- **Registry:** Docker Hub ([`maximleus/hostinfo`](https://hub.docker.com/r/maximleus/hostinfo)) +- **Base Image:** `golang:1.24-alpine` +- **Platforms:** `linux/amd64`, `linux/arm64` +- **Size:** ~100MB compressed + + +**Quick Start with Docker Compose:** +```bash +# Build the image +docker compose build +# Start with default settings +docker compose up -d +# Stop +docker compose down +# View logs +docker compose logs -f +``` +**Or Quick Start directly:** +```bash +# Build the image +docker build -t hostinfo . +# Run container +docker run -p 8080:8080 hostinfo +``` +Access the application at `http://localhost:8080` + +## 💻 Development + +### Backend Development + +```bash +# Run with hot reload (use air or similar) +go run cmd/server/main.go + +# Run tests +go test ./... + +# Run tests with coverage +go test -cover ./... + +# Format code +go fmt ./... + +# Lint code +go vet ./... +``` +### Development Workflow + +1. Start backend: + ```bash + go run cmd/server/main.go + ``` +2. Open `http://localhost:3000` for hot-reload development + +### Project Structure + + +```text +hostinfo +├── .dockerignore # Files/folders to ignore when building Docker images +├── .github +│ └── workflows +│ ├── ci.yml # Main CI workflow: tests, lint, security +│ ├── template-docker.yml # Reusable Docker build & push workflow +│ └── template-semantic-release.yml # Reusable Semantic Release workflow +├── .gitignore # Git ignore rules +├── LICENSE.md # MIT license for the project +├── README.md # Project overview, usage, and instructions +├── TODO.md # TODO list for future development +├── build.sh # Optional build script for local or CI builds +├── cmd +│ └── server +│ └── hostinfo.go # Main Go server entrypoint +├── docker +│ ├── Dockerfile # Dockerfile for building container +│ └── docker-compose.yml # Docker Compose for multi-service setups +├── docs +│ └── semantic.md # Documentation for semantic release workflow +├── go.mod # Go module definition +├── internal +└── web + ├── image.png # Example image used in web UI + └── templates + ├── index.html # Main HTML template for the host info page + └── index_to_update.html # Optional template used for dynamic updates +``` +#### Notes on structure: +- `.github/workflows`: All CI/CD workflows are here. Reusable templates (`template-docker.yml` and `template-semantic-release.yml`) make it easy to trigger builds or releases from other workflows. + +- `cmd/server/hostinfo.go`: Go main entrypoint; you could add more commands in `cmd/` if needed. + +- `web/templates`: HTML templates for rendering your host info page. + +- `docker`: Contains Docker-related files. `docker-compose.yml` is optional but useful for multi-container setups. + +- `docs/semantic.md`: Full guide on how semantic release works in this project. + + +## 🤝 Contributing + +Contributions are welcome! Please follow these steps: + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/amazing-feature`) +3. Commit your changes (`git commit -m 'feat: Add some amazing feature'`) +4. Push to the branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request + +### Development Guidelines + +- Follow Go best practices and conventions +- Write tests for new features +- Update documentation as needed +- Ensure all tests pass before submitting PR +- Keep commits atomic and well-described + +## 📝 License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE.md) file for details. \ No newline at end of file diff --git a/cmd/server/hostinfo.go b/cmd/server/hostinfo.go new file mode 100644 index 0000000..3751858 --- /dev/null +++ b/cmd/server/hostinfo.go @@ -0,0 +1,263 @@ +package main + +import ( + "fmt" + "html/template" + "io" + "log" + "net" + "net/http" + "os" + "runtime" + "time" +) + +type HostInfo struct { + Hostname string + IPs []string + MACs []string + OS string + Arch string + GoVersion string + StartTime string + Now string + Env map[string]string + Cloud CloudInfo + Kubernetes KubernetesInfo +} + +type CloudInfo struct { + Provider string + Region string + Zone string + Instance string + Extra map[string]string +} + +type KubernetesInfo struct { + Enabled bool + PodName string + PodNamespace string + PodIP string + NodeName string + ServiceAccount string + Container string +} + +var startTime = time.Now() + +func main() { + tmpl := template.Must(template.ParseFiles("web/templates/index.html")) + + http.HandleFunc("/healthz", healthHandler) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + info := HostInfo{ + Hostname: getHostname(), + IPs: getIPs(), + MACs: getMACs(), + OS: runtime.GOOS, + Arch: runtime.GOARCH, + GoVersion: runtime.Version(), + StartTime: startTime.Format(time.RFC3339), + Now: time.Now().Format(time.RFC3339), + Env: getEnv(), + Cloud: detectCloud(), + } + + _ = tmpl.Execute(w, info) + }) + + port := os.Getenv("PORT") + if port == "" { + port = "8080" + } + + fmt.Printf("Server listening on :%s --> http://localhost:%s", port, port) + if err := http.ListenAndServe(":"+port, nil); err != nil { + log.Fatal(err) + } + +} +func healthHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"status":"ok"}`)) + +} + +func getHostname() string { + h, _ := os.Hostname() + return h +} + +func getIPs() []string { + var ips []string + ifaces, _ := net.Interfaces() + for _, i := range ifaces { + addrs, _ := i.Addrs() + for _, a := range addrs { + if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + ips = append(ips, ipnet.IP.String()) + } + } + } + return ips +} + +func getMACs() []string { + var macs []string + ifaces, _ := net.Interfaces() + for _, i := range ifaces { + if i.HardwareAddr != nil { + macs = append(macs, i.HardwareAddr.String()) + } + } + return macs +} + +func getEnv() map[string]string { + env := make(map[string]string) + for _, e := range os.Environ() { + kv := []rune(e) + for i, c := range kv { + if c == '=' { + env[string(kv[:i])] = string(kv[i+1:]) + break + } + } + } + return env +} + +func detectCloud() CloudInfo { + if aws := detectAWS(); aws.Provider != "" { + return aws + } + if gcp := detectGCP(); gcp.Provider != "" { + return gcp + } + if azure := detectAzure(); azure.Provider != "" { + return azure + } + return CloudInfo{Provider: "local"} +} + +func detectAWS() CloudInfo { + client := http.Client{Timeout: 500 * time.Millisecond} + + req, _ := http.NewRequest("GET", + "http://169.254.169.254/latest/meta-data/instance-id", nil) + + resp, err := client.Do(req) + if err != nil || resp.StatusCode != 200 { + return CloudInfo{} + } + defer resp.Body.Close() + + id, _ := io.ReadAll(resp.Body) + + region := awsMeta("placement/region") + zone := awsMeta("placement/availability-zone") + + return CloudInfo{ + Provider: "aws", + Region: region, + Zone: zone, + Instance: string(id), + Extra: map[string]string{ + "AMI": awsMeta("ami-id"), + "Type": awsMeta("instance-type"), + }, + } +} + +func awsMeta(path string) string { + client := http.Client{Timeout: 300 * time.Millisecond} + resp, err := client.Get("http://169.254.169.254/latest/meta-data/" + path) + if err != nil { + return "" + } + defer resp.Body.Close() + b, _ := io.ReadAll(resp.Body) + return string(b) +} + +func detectGCP() CloudInfo { + client := http.Client{Timeout: 500 * time.Millisecond} + + req, _ := http.NewRequest("GET", + "http://metadata.google.internal/computeMetadata/v1/instance/id", nil) + req.Header.Set("Metadata-Flavor", "Google") + + resp, err := client.Do(req) + if err != nil || resp.StatusCode != 200 { + return CloudInfo{} + } + defer resp.Body.Close() + + id, _ := io.ReadAll(resp.Body) + + return CloudInfo{ + Provider: "gcp", + Region: gcpMeta("instance/region"), + Zone: gcpMeta("instance/zone"), + Instance: string(id), + Extra: map[string]string{ + "Machine": gcpMeta("instance/machine-type"), + "Project": gcpMeta("project/project-id"), + }, + } +} + +func gcpMeta(path string) string { + client := http.Client{Timeout: 300 * time.Millisecond} + req, _ := http.NewRequest("GET", + "http://metadata.google.internal/computeMetadata/v1/"+path, nil) + req.Header.Set("Metadata-Flavor", "Google") + + resp, err := client.Do(req) + if err != nil { + return "" + } + defer resp.Body.Close() + b, _ := io.ReadAll(resp.Body) + return string(b) +} + +func detectAzure() CloudInfo { + client := http.Client{Timeout: 500 * time.Millisecond} + + req, _ := http.NewRequest("GET", + "http://169.254.169.254/metadata/instance?api-version=2021-02-01", nil) + req.Header.Set("Metadata", "true") + + resp, err := client.Do(req) + if err != nil || resp.StatusCode != 200 { + return CloudInfo{} + } + defer resp.Body.Close() + + return CloudInfo{ + Provider: "azure", + Extra: map[string]string{ + "VM": "Azure VM detected", + }, + } +} + +func detectKubernetes() KubernetesInfo { + // Always present in k8s + if os.Getenv("KUBERNETES_SERVICE_HOST") == "" { + return KubernetesInfo{Enabled: false} + } + + return KubernetesInfo{ + Enabled: false, //true, set to false to avoid showing k8s info in the UI until all is verified + PodName: "POD_NAME", + PodNamespace: "POD_NAMESPACE", + PodIP: "POD_IP", + NodeName: "NODE_NAME", + ServiceAccount: "SERVICE_ACCOUNT", + Container: "CONTAINER_NAME", + } +} diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..7625074 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,98 @@ +# Stage 1: Build the Go binary +FROM golang:1.24-bookworm AS go-builder + +# Install build dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + git ca-certificates tzdata \ + && rm -rf /var/lib/apt/lists/* + + +WORKDIR /app + +# Copy go mod files first for better caching +COPY go.mod ./ +# COPY go.mod go.sum ./ +RUN go mod download + +# Copy source code +COPY cmd/ ./cmd/ +# COPY internal/ ./internal/ +COPY web/ ./web/ + +# Build arguments for multi-platform support +ARG TARGETOS=linux +ARG TARGETARCH + +# Build the binary for target platform +RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build \ + -ldflags="-s -w -X main.Version=docker" \ + -o /app/hostinfo.go \ + ./cmd/server/hostinfo.go + +# Final stage: Debian-based runtime for proper bash support +FROM debian:bookworm-slim + +# Install required packages: +# - ca-certificates: for HTTPS connections +# - tzdata: for timezone support +# - bash: full bash shell for script execution +# - coreutils: standard Unix utilities +# - curl: for health checks and HTTP operations +# - openssh-client: for SSH connections to remote servers +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + tzdata \ + bash \ + zsh \ + coreutils \ + curl \ + openssh-client \ + && rm -rf /var/lib/apt/lists/* \ + && apt-get clean + +# Create non-root user for security +# Add user to tty group for PTY access (required for interactive terminal) +RUN groupadd -g 1000 hostinfo && \ + useradd -u 1000 -g hostinfo -G tty -s /bin/bash -m hostinfo + +# Create data and config directories +RUN mkdir -p /data /config && \ + chown -R hostinfo:hostinfo /data /config + +# Create .ssh directory for SSH key operations +RUN mkdir -p /home/hostinfo/.ssh && \ + chown hostinfo:hostinfo /home/hostinfo/.ssh && \ + chmod 700 /home/hostinfo/.ssh + +# Create tmp directory for session files (SSH configs, keys, wrappers) +# This ensures the non-root user can create temp files for terminal sessions +RUN mkdir -p /home/hostinfo/tmp && \ + chown hostinfo:hostinfo /home/hostinfo/tmp && \ + chmod 700 /home/hostinfo/tmp + +WORKDIR /app + +# Copy binary from builder +COPY --from=go-builder /app/hostinfo.go /app/hostinfo.go + +# Set ownership +RUN chown hostinfo:hostinfo /app/hostinfo.go + +# Switch to non-root user +USER hostinfo + +# Expose default port +EXPOSE 8080 + +# Health check using curl +HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ + CMD curl -sf http://localhost:8080/healthz || exit 1 + +# Environment variables with defaults +# TMPDIR is set to user's home for proper temp file creation in non-root context +# hadolint ignore=DL3044 +ENV TMPDIR=/home/hostinfo/tmp \ + SHELL=/bin/bash + +# Run the application +ENTRYPOINT ["/app/hostinfo.go"] \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..89b4998 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,21 @@ +version: "3.9" + +services: + hostinfo: + image: hostinfo:latest + container_name: hostinfo + build: + context: . + dockerfile: Dockerfile + ports: + - "8080:8080" + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost:8080/healthz"] + interval: 30s + timeout: 3s + retries: 3 + environment: + ENV: local + SERVICE: hostinfo + PORT: 8080 + restart: unless-stopped diff --git a/docs/00-overview.md b/docs/00-overview.md new file mode 100644 index 0000000..032661d --- /dev/null +++ b/docs/00-overview.md @@ -0,0 +1,167 @@ + +# HostInfo — Project Overview + +HostInfo is a lightweight Go-based service that exposes system information through an HTML dashboard and a JSON API. +It is designed for DevOps engineers, SREs, system administrators, and automation pipelines that need quick and portable access to machine telemetry. + +## 🎯 Project Goals + +- Provide a self-hosted tool to inspect system information +- Offer both **human-readable web UI** and **machine-readable API** +- Be lightweight, dependency-free, and easy to deploy +- Support Docker, Docker Compose, and standard Linux service management +- Integrate cleanly into CI/CD and automation environments + +## 🧩 Key Features + +- 🌐 **Web Dashboard** — view host info in a browser +- 📡 **REST API** — extract data programmatically +- 🐳 **Container-Ready** — minimal Docker image support +- 📦 **Binary or Docker Deployment** +- ⚙️ **Configurable** via env or CLI flags +- 🔐 **Zero External Dependencies** +- 📂 **Clean Codebase** with docs + CI + +## 🖥 What Information Does It Show? + +HostInfo exposes hardware and OS metrics such as: + +- Hostname +- OS & kernel details +- CPU model & core count +- Memory information +- Envierment veriabels +- Disk space (planned) +- Network details (planned) + +These can be improved or extended over time (CPU usage %, disk IO, network stats, etc). + +## 🧱 Architecture Overview + +HostInfo is structured as a simple web server with the following logical layers: + +| Layers | Description | +|:---|:---| +| Web UI | ➜ HTML Templates | +| HTTP API | ➜ JSON Responses | +| System Information | ➜ OS / CPU / Memory | +| Runtime | ➜ Go 1.22+ | + + +## 🗂 Repository Structure (High-Level) +```bash +hostinfo/ +├── cmd/server # Application entrypoint +├── internal/ # Core internal logic +├── web/ # HTML templates, static assets +├── docker/ # Docker + Compose files +├── docs/ # Documentation +├── .github/workflows/ # CI/CD pipelines +├── scripts/hooks/ # Git hooks (pre-commit, commit-msg) +└── tools/ # Helper scripts (bootstrap, dev) +``` + +More details in: `07-development.md` + +## 🛠 Tech Stack + +| Category | Choice | +|-------------|---------------| +| Language | Go (1.22+) | +| Runtime | Standard Lib | +| UI | HTML Templates| +| Packaging | Docker & Go | +| CI/CD | GitHub Actions| +| Release | semantic-release| + +No external dependencies are required for core features. + +## 📦 Deployment Models + +### HostInfo supports multiple deployment targets: + +- **Local Binary** + - For Linux/macOS/Windows workstations. + +- **Docker Container** + - For servers, homelabs, CI automation. + +- **Docker Compose** + - Part of larger observability stacks. + +- **Systemd Service (Optional)** + - For persistent Linux deployments. + +Kubernetes deployment manifests may be added later. + +## 🧑‍💻 Target Users + +HostInfo is intended for: + +- DevOps / SREs +- Platform / Infra engineers +- System administrators +- Automation pipelines +- Observability stack maintainers +- Homelab enthusiasts + +## 🪄 Use Cases + +Common usage patterns include: + +- Checking remote machine details via web browser +- Collecting telemetry in CI/CD jobs +- Integrating system info into dashboards +- Baseline validation for provisioning +- Self-hosted server inventory in homelabs +- Lightweight monitoring for edge devices + +## 🏁 Project Status + +**Current Stage:** Early Development +Core features are functional, and additional modules (disk, network, metrics, authentication) are planned. + +Upcoming enhancements include: + +- Disk usage collection +- Network stats +- Auth (optional Basic/OAuth) +- Metrics (Prometheus endpoint) +- Swagger/OpenAPI API reference +- Improved UI front-end + +--- + +## 📜 License + +This project is licensed under the **MIT License**. +See `LICENSE.md` for full details. + +--- + +## 🤝 Contributing + +Contributions are welcome! +Please see: + +- `docs/07-development.md` +- `docs/09-releasing.md` (if semantic-release involved) + +--- + +## ⭐ Summary + +HostInfo aims to streamline the collection of host-level data in a way that is: + +✔ Fast +✔ Portable +✔ Single-binary +✔ Self-hosted +✔ API-friendly +✔ DevOps-ready + +It bridges the gap between “simple Linux commands” and “full monitoring stacks” by providing a clean, lightweight, and flexible utility for machine introspection. + + + + diff --git a/docs/01-getting-started.md b/docs/01-getting-started.md new file mode 100644 index 0000000..094958b --- /dev/null +++ b/docs/01-getting-started.md @@ -0,0 +1,145 @@ + +# Getting Started + +This guide will help you get HostInfo running on your machine using the quickest and simplest methods. +Whether you prefer to build from source or use Docker, this document has you covered. + +--- + +## ✅ Prerequisites + +HostInfo supports multiple environments, but depending on your preferred setup, you may need: + +### **For local builds** +- Go **1.22+** +- Git + +### **For containerized deployments** +- Docker **20+** +- (Optional) Docker Compose **v2+** + +### **Supported Operating Systems** +| OS | Status | +|----------|------------| +| Linux | ✔ Supported | +| macOS | ✔ Supported | +| Windows | ✔ Supported | + +No external Go dependencies are required for the default build. + +--- + +## 📦 Step 1 — Clone Repository +```bash +git clone https://github.com/yourname/hostinfo.git +cd hostinfo +``` +If you plan to contribute, fork first and clone your fork instead. + +## 🧱 Step 2 — Choose an Installation Method + +### HostInfo can be run in three ways: + +#### Option A — 🏁 Local Go Build (Recommended for Dev) +This method compiles and runs the binary directly. + +**Build** +```bash +go build -o hostinfo ./cmd/server +``` +**Run** +```bash +./hostinfo +``` +Open your browser and visit: `http://localhost:8080` + +#### Option B — 🐳 Docker Container (No Go Needed) +**Build image** +```bash +docker build -t hostinfo:latest ./docker +``` +**Run container** +```bash +docker run -it --rm -p 8080:8080 hostinfo:latest +``` +Open your browser and visit: `http://localhost:8080` + +#### Option C — 🐙 Docker Compose (Services Setup) + +This method is useful for multi-service environments. + +**Start** +```bash +docker compose up -d +``` +**Stop** +```bash +docker compose down +``` +Open your browser and visit: `http://localhost:8080` + + +## ⚙️ Configuration Basics +HostInfo can be configured via: +- Environment variables +- CLI flags + +Defaults are sensible, so configuration is optional for onboarding. + +**Example CLI overrides** +```bash +./hostinfo --port 9090 --debug +``` + +## 🧪 Verify Functionality +After running HostInfo by any method, verify that: + +**✔ Web UI is up** + +Visit: `http://localhost:8080` +You should see a system information dashboard. + + +**✔ API responds** +```bash +curl http://localhost:8080/api/v1/info | jq +``` +Expected output (varies per system): +```json +{ + "hostname": "myhost", + "os": "linux", + "cpu": { + "cores": 8 + }, + "memory": "16GB" +} +``` +## 🧹 Cleaning Up (Optional) + +**Docker container cleanup** +```bash +docker ps -a +docker rm +docker rmi hostinfo:latest +``` +**Binary cleanup** +```bash +rm hostinfo +``` + +## 🙋 Need More Details? +Additional documentation is available in: +- `02-installation.md` +- `03-configuration.md` +- `04-usage.md` +- `05-api.md` + +## 🎉 You're Ready! + +You now have HostInfo running — either as a native binary or inside a Docker container. +Next steps depend on your use case: + +➡ For config options → read `03-configuration.md` +➡ For API usage → read `05-api.md` +➡ For development → read `07-development.md` diff --git a/docs/02-installation.md b/docs/02-installation.md new file mode 100644 index 0000000..2cc644b --- /dev/null +++ b/docs/02-installation.md @@ -0,0 +1,213 @@ +# Installation + +This document describes how to install and build the **hostinfo** web application across multiple environments. + +--- + +## 1. 📦 Prerequisites + +Required: +You need the following tools installed: + +| Tool | Purpose | Check | +|---|---|---| +| **Go** `>= 1.21` | Build & run hostinfo | `go version` | +| **Git** | Clone repository | `git --version` | + +Optional but recommended (for deployment / CI): + +- `make` — to use Makefile automation (if present) +- `docker` — for container builds (optional) +- `docker compose` +- `scp` (for remote deployment) +- `systemd` (Linux service) + +## 2. 🔽 Clone Repository + +```bash +git clone https://github.com/MaksymLeus/hostinfo.git +cd hostinfo +``` + +## 3. Backend Dependencies + +```bash +go mod download +``` + +## 4. 🛠️ Local Build Options + +### Option A — Using build.sh (recommended) + +```bash +./build.sh +``` + +This produces a binary based on host OS/architecture, e.g.: +```bash +./bin/hostinfo +``` + +### Option B — Manual go build + +```bash +go build -o ./bin/hostinfo ./cmd/server +``` + +Run it: +```bash +./bin/hostinfo +``` +Access in browser: `http://localhost:8080` + +## 5. Platform-Specific Builds + +### Linux (from macOS/Windows) + +```bash +GOOS=linux GOARCH=amd64 go build -o hostinfo-linux-x64 ./cmd/server +``` +Available arch targets include: + +- `amd64` +- `arm64` + +## 6. ⚙️ Install to GOPATH + +```bash +go install ./cmd/server +``` + +Binary installs into: + +``` +$(go env GOPATH)/bin/hostinfo +``` + +Add GOPATH bin to PATH (if missing): + +```bash +export PATH="$PATH:$(go env GOPATH)/bin" +``` + +## 7. Deployment Targets + +### Linux Server (manual) + +```bash +./build.sh all +scp bin/hostinfo-linux-x64 user@server:/opt/hostinfo/hostinfo +``` + +Run on server: + +```bash +ssh user@server +cd /opt/hostinfo +./hostinfo +``` + +### systemd (optional) + +Create `/etc/systemd/system/hostinfo.service`: + +```ini +[Unit] +Description=Web Hostinfo Service +After=network.target + +[Service] +Type=simple +User=www-data +WorkingDirectory=/opt/hostinfo +ExecStart=/opt/hostinfo/hostinfo +Restart=on-failure + +[Install] +WantedBy=multi-user.target +``` + +Enable + start: + +```bash +sudo systemctl enable hostinfo +sudo systemctl start hostinfo +``` + +## 8. Docker Installation + +Hostinfo can be containerized using Docker. + +### Build locally: + +```bash +docker build -t hostinfo . -f /docker/Dockerfile +``` + +Run: + +```bash +docker run -p 8080:8080 hostinfo +``` + +Access: + +``` +http://localhost:8080 +``` + +--- + +## 9. Docker Compose + +```bash +docker compose build -f ./docker/docker-compose.yml +docker compose up -d +docker compose logs -f +``` + +Stop: + +```bash +docker compose down +``` + +--- + +## 10. ❗ Troubleshooting + +| Issue | Cause | Resolution | +|---|---|---| +| `go: no such file or directory` | Wrong working directory | `cd hostinfo` | +| `exec format error` | Wrong GOARCH/GOOS | Rebuild with correct target | +| Docker build slow | No layer cache | Enable BuildKit | +| `permission denied` | Missing execute flag | `chmod +x hostinfo` or `chmod +x build.sh` | + +--- + +## 11. 🧼 Uninstallation + +Remove binary: + +```bash +rm -f hostinfo +``` + +Remove build artifacts: + +```bash +rm -rf bin/ +``` + +Remove systemd service: + +```bash +sudo systemctl disable --now hostinfo +sudo rm -f /etc/systemd/system/hostinfo.service +``` + +--- + +## 📄 License + +MIT — see `LICENSE.md` for details. diff --git a/docs/03-usage.md b/docs/03-usage.md new file mode 100644 index 0000000..9a06df4 --- /dev/null +++ b/docs/03-usage.md @@ -0,0 +1,158 @@ +# 04 — Usage + +This document describes how to run, access, and interact with the **hostinfo** application in different environments. + +--- + +## 1. Default Behavior + +When started without arguments, hostinfo: + +- runs an HTTP server on `:8080` +- renders the host information dashboard at `/` + +Example: + +```bash +./hostinfo +``` + +Access in browser: + +``` +http://localhost:8080 +``` + +No configuration files or environment variables are required. + +--- + +## 2. Supported UI Modes + +The dashboard renders the following categories (depending on environment): + +- **Host System** +- **Container Runtime** +- **Cloud Metadata** +- **Network Interfaces** +- **Environment Variables** +- **Runtime Details (Go / OS / Arch)** + +If running inside: + +- Docker → shows container metadata +- AWS EC2 → shows EC2 metadata +- GCP → shows project/zone/machine +- Azure → VM metadata +- Bare-metal → falls back to local host info + +No cloud credentials are needed for metadata detection. + +--- + +## 3. JSON API (If Enabled / Future) + +If JSON output is enabled or supported in the future, examples may include: + +``` +GET /api/info +``` + +Response might include: + +```json +{ + "hostname": "ip-10-0-1-15", + "os": "linux", + "arch": "amd64", + "cloud": "aws", + "region": "us-east-1", + "instanceType": "t3.micro" +} +``` + +> If JSON output is not currently implemented, this section remains reserved for future support. + +--- + +## 4. Environment Behavior + +hostinfo auto-detects cloud/platform context: + +| Platform | Detection | +|---|---| +| Docker | `/proc/self/cgroup`, container hostname | +| AWS EC2 | IMDS (169.254.169.254) | +| GCP | GCP metadata server | +| Azure | Azure Metadata Service | +| Local | no metadata endpoints | + +All metadata requests are **safe**: +- timeouts are short +- failures do not crash the app + +--- + +## 5. Port Configuration (If Provided) + +If port configuration is supported later, recommended format: + +```bash +./hostinfo --port 9090 +``` + +Access: + +``` +http://localhost:9090 +``` + +If not yet implemented, this section becomes future reserved. + +--- + +## 6. Logging + +hostinfo prints startup info to stdout: + +Example: + +``` +[hostinfo] starting server on :8080 +[hostinfo] environment: docker +[hostinfo] cloud: aws (us-east-1) +``` + +Logs can be collected via: + +- systemd (`journalctl`) +- Docker logs +- Kubernetes logs + +--- + +## 7. Production Notes + +When running in production: + +- run behind reverse proxy (nginx, traefik, caddy) +- restrict public access unless required +- enable TLS termination on proxy layer +- do not expose raw port `8080` to internet without TLS + +--- + +## 8. Troubleshooting + +| Issue | Cause | Resolution | +|---|---|---| +| Blank UI | Cloud metadata timeout | Wait ~1s or disable metadata calls | +| `bind: address already in use` | Port in use | Change port or stop other service | +| Docker no metadata | No cloud server | Expected inside local docker runs | +| Cloud info missing | Not running on cloud | Expected fallback | + +--- + +## License + +MIT — see `LICENSE.md`. diff --git a/docs/05-architecture.md b/docs/05-architecture.md new file mode 100644 index 0000000..abf175d --- /dev/null +++ b/docs/05-architecture.md @@ -0,0 +1,239 @@ +# 05 — Architecture + +This document outlines the architecture, execution model, core components, and cloud/container detection logic of the **hostinfo** application. + +--- + +## 1. High-Level Overview + +hostinfo is a **single-binary Go application** that: + +- collects host/container/cloud metadata +- aggregates system + network information +- renders a web dashboard using templates +- performs safe non-blocking cloud metadata probing + +No external services or databases are required. + +--- + +## 2. Process Model + +``` +┌─────────────────────────┐ +│ hostinfo (Go binary) │ +│ │ +│ ┌───────────────────┐ │ +│ │ HTTP Server │<── incoming web requests +│ └───────────────────┘ │ +│ ┌───────────────────┐ │ +│ │ Metadata Collect. │── system, runtime, env +│ └───────────────────┘ │ +│ ┌───────────────────┐ │ +│ │ Cloud Detection │── AWS/GCP/Azure/local +│ └───────────────────┘ │ +│ ┌───────────────────┐ │ +│ │ Template Engine │── render HTML dashboard +│ └───────────────────┘ │ +└─────────────────────────┘ +``` + +All components run in-process, no goroutine explosions, no IPC. + +--- + +## 3. Source Code Layout + +Relevant structure (simplified): + +``` +cmd/server/ → main entrypoint + HTTP bootstrap +internal/ → metadata collectors & helpers +web/templates/ → HTML layout for dashboard +web/image.png → UI static asset +docker/ → Docker + compose definitions +.github/ → CI/CD workflows +``` + +Key directories are kept minimal to preserve clarity. + +--- + +## 4. Component Breakdown + +### 4.1 HTTP Server + +Responsibilities: + +- listen on `:8080` (default) +- serve dashboard at `/` +- render templates with collected data + +### 4.2 Metadata Collector + +Aggregates: + +- hostname +- OS / arch / runtime +- environment variables +- uptime + process info +- network interfaces +- container markers +- cloud metadata (if available) + +All calls are **non-blocking** with short timeouts. + +### 4.3 Cloud Detection Layer + +Checks metadata endpoints safely: + +| Provider | Method | +|---|---| +| AWS | IMDSv1 (169.254.169.254) | +| GCP | metadata.google.internal | +| Azure | 169.254.169.254/metadata | +| Docker | cgroup/hostname heuristics | +| Local | fallback if no cloud metadata | + +Failures never crash — they degrade to `local`. + +### 4.4 Template Rendering + +Backend populates a structured template context: + +```go +type DashboardData struct { + Host HostInfo + Container ContainerInfo + Cloud CloudInfo + Network []NetInterface + Runtime GoRuntime + Env map[string]string +} +``` + +Rendered via `index.html` under `web/templates`. + +--- + +## 5. Runtime Behavior + +Startup sequence: + +1. initialize collectors +2. detect execution environment +3. probe cloud metadata asynchronously +4. serve HTTP dashboard +5. update metrics on each request + +### Execution Contexts + +| Context | Behavior | +|---|---| +| Local bare-metal | fallback mode | +| Docker container | container info enabled | +| AWS EC2 | IMDS metadata | +| GCP | metadata server | +| Azure | IMDS metadata | +| Unknown | fallback | + +Timeout defaults are small to avoid UI blocking. + +--- + +## 6. Cloud & Container Detection Logic + +Detection pipeline (simplified): + +``` +Container? → Check cgroup / hostname patterns + ↓ yes + Docker + +Cloud? → Probe IMDS endpoints + ↓ yes + AWS / GCP / Azure + +Else → Local fallback +``` + +Priority order: + +``` +Container → Cloud → Local +``` + +Cloud checks run in parallel to reduce latency. + +--- + +## 7. Dependencies + +### Language & Runtime + +- **Go >= 1.21** + +### External Services + +None required. + +### External Libraries + +Minimal standard library usage preferred to keep binary small and portable. + +--- + +## 8. Deployment Characteristics + +- **stateless** (no DB, no local storage) +- **horizontal scale-friendly** (idempotent) +- **portable** across bare-metal, VMs, containers +- **read-only** by design (never mutates system) + +Ideal for debugging ephemeral infra, CI workers, cloud VMs, or container clusters. + +--- + +## 9. Security Considerations + +hostinfo: + +- does **not** require cloud credentials +- does **not** modify system state +- does **not** open outbound ports besides metadata probes +- does **not** expose OS secrets beyond env vars + +Exposure risks: + +- environment variables may contain secrets in certain deployments +- dashboard should not be public-facing without TLS/proxy + +Recommendation: + +Deploy behind Nginx / Traefik / Caddy for TLS + authentication. + +--- + +## 10. Extensibility + +Possible future modules: + +- JSON API (`/api/info`) +- Prometheus `/metrics` +- CLI flags (`--port`, `--json`) +- WASM UI bundle +- Plugin architecture + +Backend design keeps boundaries flexible: + +``` +Collector → Adapter → Renderer +``` + +No global state except basic runtime caches. + +--- + +## License + +MIT — see `LICENSE.md`. diff --git a/docs/06-deployment.md b/docs/06-deployment.md new file mode 100644 index 0000000..b9132db --- /dev/null +++ b/docs/06-deployment.md @@ -0,0 +1,293 @@ +# Deployment + +This document describes available deployment strategies for the **hostinfo** web application, including bare-metal, Docker, Docker Compose, systemd, and cloud environments. + +--- + +## 1. Deployment Models + +hostinfo supports: + +- **Bare-metal binary** (Linux/Windows/macOS) +- **Docker container** +- **Docker Compose** +- **systemd** service +- **Cloud VMs** (AWS EC2, GCP Compute, Azure VM) +- (Optional) **Kubernetes** via container image + +hostinfo is stateless: +- No external dependencies +- No persistent storage required +- No configuration files required by default + +--- + +## 2. Deployment Preparation + +### Clone and build: + +```bash +git clone https://github.com/MaksymLeus/hostinfo.git +cd hostinfo +./build.sh all # or: go build -o hostinfo ./cmd/server +``` + +Produced binaries are typically placed in `bin/`. + +--- + +## 3. Bare-Metal Deployment (Linux) + +### 3.1 Upload binary to server + +```bash +scp bin/hostinfo-linux-x64 user@server:/opt/hostinfo/hostinfo +``` + +### 3.2 Start manually + +```bash +ssh user@server +cd /opt/hostinfo +chmod +x hostinfo +./hostinfo +``` + +Service runs on: + +``` +http://:8080 +``` + +--- + +## 4. systemd Service (Linux) + +### 4.1 Create unit file + +`/etc/systemd/system/hostinfo.service`: + +```ini +[Unit] +Description=Hostinfo Web Service +After=network.target + +[Service] +Type=simple +User=www-data +WorkingDirectory=/opt/hostinfo +ExecStart=/opt/hostinfo/hostinfo +Restart=on-failure +RestartSec=3 + +[Install] +WantedBy=multi-user.target +``` + +### 4.2 Enable & start + +```bash +sudo systemctl enable hostinfo +sudo systemctl start hostinfo +``` + +### 4.3 Check status and logs + +```bash +sudo systemctl status hostinfo +sudo journalctl -u hostinfo -f +``` + +--- + +## 5. Docker Deployment + +### 5.1 Build image locally + +```bash +docker build -t hostinfo . +``` + +### 5.2 Run container + +```bash +docker run -p 8080:8080 hostinfo +``` + +Access: + +``` +http://localhost:8080 +``` + +--- + +## 6. Docker Compose Deployment + +`docker-compose.yml` (existing in project): + +```bash +docker compose build +docker compose up -d +``` + +Manage lifecycle: + +```bash +docker compose logs -f +docker compose down +``` + +--- + +## 7. Cloud VM Deployment + +### Supported environments (no extra config): + +- AWS EC2 +- GCP Compute Engine +- Azure VM + +hostinfo auto-detects cloud metadata **without** credentials. + +#### Example Ubuntu VM deployment: + +```bash +# build binary locally +./build.sh all + +# copy to VM +scp bin/hostinfo-linux-x64 ubuntu@:/opt/hostinfo/hostinfo + +# run on VM +ssh ubuntu@ +cd /opt/hostinfo +./hostinfo +``` + +### Firewall considerations: + +- AWS EC2 → Security Group: allow inbound TCP 8080 +- GCP Compute → Firewall Rule: allow TCP 8080 +- Azure NSG → allow TCP 8080 + +--- + +## 8. Reverse Proxy (Optional) + +For production HTTPS, recommend: + +- **Nginx** +- **Caddy** +- **Traefik** + +Example nginx site config: + +``` +location / { + proxy_pass http://localhost:8080; +} +``` + +Enable TLS via Let's Encrypt or Traefik. + +--- + +## 9. Kubernetes (Optional) + +If deployed to Kubernetes, use the existing Docker image: + +Example minimal manifest: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: hostinfo +spec: + replicas: 1 + selector: + matchLabels: + app: hostinfo + template: + metadata: + labels: + app: hostinfo + spec: + containers: + - name: hostinfo + image: maximleus/hostinfo:latest + ports: + - containerPort: 8080 +--- +apiVersion: v1 +kind: Service +metadata: + name: hostinfo +spec: + type: ClusterIP + selector: + app: hostinfo + ports: + - port: 80 + targetPort: 8080 +``` + +Ingress optional depending on cluster. + +--- + +## 10. Health & Monitoring + +hostinfo exposes a UI at: + +``` +/ (dashboard) +``` + +If you later add JSON endpoints (e.g., `/api/info`), update this section. + +--- + +## 11. Updates & Rollbacks + +### Bare-Metal: + +```bash +systemctl stop hostinfo +# replace binary +systemctl start hostinfo +``` + +### Docker: + +```bash +docker pull maximleus/hostinfo:latest +docker compose up -d +``` + +### Kubernetes: + +```bash +kubectl rollout restart deployment/hostinfo +``` + +Rollbacks via ReplicaSet history. + +--- + +## 12. Deployment Recommendations + +For production: + +- Run behind reverse proxy with HTTPS +- Use systemd for bare-metal +- Use Docker or Kubernetes for cloud +- Prefer immutable container deployments +- Do not expose port 8080 directly to public internet without TLS + +--- + +## License + +MIT — see `LICENSE.md`. diff --git a/docs/07-cloud-detection.md b/docs/07-cloud-detection.md new file mode 100644 index 0000000..b76a91d --- /dev/null +++ b/docs/07-cloud-detection.md @@ -0,0 +1,178 @@ +# 06 — Cloud Detection + +hostinfo automatically detects cloud environments (AWS, GCP, Azure) and gathers metadata **without requiring credentials**. +This document explains the detection logic, endpoints, timeouts, and fallback behavior. + +--- + +## 1. Overview + +Cloud detection is: + +- **Safe**: read-only, short timeouts +- **Non-blocking**: asynchronous or concurrent calls +- **Fallback-aware**: defaults to local host if no cloud detected +- **Environment-agnostic**: works in containers, bare-metal, and VMs + +Detection order: + +``` +1. Container detection +2. Cloud detection +3. Local fallback +``` + +--- + +## 2. Container Detection (Precedes Cloud) + +hostinfo first determines if it is running inside a container: + +- **Docker / Podman / LXC** detection: + - Check `/proc/self/cgroup` for container patterns + - Check `/proc/1/cgroup` for docker identifiers + - Inspect hostname for container hashes + +If container detected: + +- Adds container uptime +- Marks environment as `container` +- Continues to cloud detection if available + +--- + +## 3. AWS Detection + +AWS metadata is fetched from **Instance Metadata Service (IMDS)**: + +- Endpoint: `http://169.254.169.254/latest/meta-data/` +- Information retrieved: + - Instance ID + - Region & Availability Zone + - Instance type + - Private IP +- Requests are **short timeout** (~500ms) +- Failures do **not crash** the application + +Example metadata keys: + +``` +instance-id +instance-type +placement/availability-zone +local-ipv4 +``` + +--- + +## 4. Google Cloud Detection + +GCP metadata service: + +- Endpoint: `http://metadata.google.internal/computeMetadata/v1/` +- Requires header: `Metadata-Flavor: Google` +- Retrieves: + - Project ID + - Zone + - Machine type +- Timeout is short (~500ms) +- Failure → fallback + +Example: + +```bash +curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/id +``` + +--- + +## 5. Azure Detection + +Azure Instance Metadata Service (IMDS): + +- Endpoint: `http://169.254.169.254/metadata/instance?api-version=2021-02-01` +- Header: `Metadata: true` +- Retrieves: + - VM ID + - Location + - VM size/type +- Timeout: short (~500ms) +- Failures do **not crash** app + +Example: + +```bash +curl -H "Metadata:true" "http://169.254.169.254/metadata/instance?api-version=2021-02-01" +``` + +--- + +## 6. Local / Fallback + +If none of the cloud endpoints respond: + +- Environment marked as `local` +- Only host and container info is shown +- Ensures dashboard always displays minimal info + +--- + +## 7. Metadata Caching + +- Metadata is cached in memory per process +- Avoids repeated network calls on each HTTP request +- Cache invalidated on restart +- No persistence to disk + +--- + +## 8. Parallel Probing Logic + +Detection sequence (simplified): + +``` +start -> container check + -> launch cloud probes concurrently (AWS, GCP, Azure) + -> wait max 500ms per probe + -> first successful response sets cloud type + -> others cancel + -> fallback to local if all fail +``` + +This prevents blocking the HTTP server. + +--- + +## 9. Security Considerations + +- No credentials required +- Only public metadata endpoints are accessed +- Cloud detection is **read-only** +- Sensitive values (API keys, secrets) are **never accessed** +- Failures are silent and logged optionally + +--- + +## 10. Deployment Notes + +- Works inside containers → detects container + cloud host if present +- Works in ephemeral CI/CD runners → falls back gracefully +- Works in multi-cloud hybrid setups → first responding cloud is chosen +- Safe for production → no network egress besides metadata endpoints + +--- + +## 11. Troubleshooting + +| Issue | Cause | Resolution | +|---|---|---| +| Cloud metadata not showing | Not running on cloud | Expected behavior, fallback to local | +| Dashboard slow | Cloud metadata timed out | Network latency; consider increasing timeout if desired | +| Metadata endpoint blocked | Firewall or VPC rules | Ensure access to IMDS (AWS/Azure) or metadata server (GCP) | +| Running inside container only | Container detected but cloud unavailable | Expected; container may not have cloud metadata access | + +--- + +## License + +MIT — see `LICENSE.md`. diff --git a/docs/08-api.md b/docs/08-api.md new file mode 100644 index 0000000..f38767a --- /dev/null +++ b/docs/08-api.md @@ -0,0 +1,206 @@ +# 07 — API + +This document defines the **hostinfo HTTP API** for programmatic access. +The API complements the web dashboard by exposing host, container, and cloud information as JSON. + +--- + +## 1. Overview + +- Base URL: `/` +- Current default: **dashboard HTML** +- Future JSON endpoints follow `/api/v1/...` +- Stateless and read-only +- No authentication required by default + +> All endpoints are optional; failures degrade gracefully. + +--- + +## 2. API Versioning + +Planned versioning: + +``` +/api/v1/ +``` + +- `v1` = initial stable API +- Future versions: `v2`, `v3` for extensions or breaking changes + +--- + +## 3. Endpoints + +### 3.1 `/api/v1/info` + +Returns a complete snapshot of the current host, container, and cloud metadata. + +**Method:** `GET` +**Response (JSON):** + +```json +{ + "host": { + "hostname": "my-host", + "os": "linux", + "arch": "amd64", + "uptime": "5h32m", + "goVersion": "go1.24" + }, + "container": { + "id": "a1b2c3d4", + "runtime": "docker", + "uptime": "5h30m" + }, + "cloud": { + "provider": "aws", + "region": "us-east-1", + "availabilityZone": "us-east-1a", + "instanceType": "t3.micro", + "instanceId": "i-1234567890abcdef" + }, + "network": [ + { + "name": "eth0", + "mac": "02:42:ac:11:00:02", + "ips": ["172.17.0.2"] + } + ], + "env": { + "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "HOSTINFO_ENV": "production" + } +} +``` + +--- + +### 3.2 `/api/v1/health` + +Returns server health and uptime. + +**Method:** `GET` +**Response:** + +```json +{ + "status": "ok", + "uptime": "5h32m", + "version": "1.0.0", + "env": "production" +} +``` + +- Useful for monitoring / readiness probes +- Can be extended for liveness probes + +--- + +### 3.3 Future Endpoints + +| Endpoint | Description | +|----------|------------| +| `/api/v1/metrics` | Prometheus-compatible metrics | +| `/api/v1/cloud` | Cloud metadata only | +| `/api/v1/container` | Container info only | +| `/api/v1/network` | Network interfaces and IPs | + +--- + +## 4. Request/Response Conventions + +- All requests: `GET` +- JSON only +- Response content-type: + +```http +Content-Type: application/json; charset=utf-8 +``` + +- Errors use standard HTTP codes: + +| Code | Meaning | +|------|--------| +| 200 | Success | +| 400 | Bad request | +| 404 | Endpoint not found | +| 500 | Internal server error | + +Example error: + +```json +{ + "error": "cloud metadata not available" +} +``` + +--- + +## 5. Query Parameters (Future) + +Potential optional query parameters: + +- `?format=json` → force JSON response from dashboard +- `?fields=host,cloud` → return selected sections only +- `?timeout=500ms` → adjust metadata probe timeout + +--- + +## 6. Security Considerations + +- No authentication required by default +- Exposing environment variables may leak secrets +- Recommended to run behind reverse proxy with HTTPS and optional auth +- Ensure cloud metadata endpoints are internal-only when exposing API externally + +--- + +## 7. Versioning & Deprecation + +- All endpoints must specify API version in path (`/api/v1/...`) +- Future breaking changes increment version number (`v2`, `v3`) +- Deprecated endpoints respond with HTTP `410 Gone` and redirect message + +--- + +## 8. Rate Limiting & Performance + +- Currently **no rate limiting** +- Consider implementing per-IP throttling if exposed publicly +- Metadata probes are cached in-memory for performance +- Requests are handled asynchronously to prevent blocking + +--- + +## 9. Example Usage + +### curl Example + +```bash +curl http://localhost:8080/api/v1/info +``` + +### Python Example + +```python +import requests + +resp = requests.get("http://localhost:8080/api/v1/info") +data = resp.json() +print(data["cloud"]["provider"]) +``` + +--- + +## 10. Logging + +- API access logs can be enabled +- Standard stdout logging +- No persistent storage + +--- + +## License + +MIT — see `LICENSE.md`. diff --git a/docs/09-releasing.md b/docs/09-releasing.md new file mode 100644 index 0000000..02b8bb2 --- /dev/null +++ b/docs/09-releasing.md @@ -0,0 +1,174 @@ +# 09 — Releasing + +This document describes the **release process** for the **hostinfo** web application, including versioning, Git workflow, CI/CD, and Docker publishing. + +--- + +## 1. Versioning Strategy + +hostinfo follows **Semantic Versioning** (SemVer): + +``` +MAJOR.MINOR.PATCH +``` + +- **MAJOR**: breaking changes +- **MINOR**: new features, backward-compatible +- **PATCH**: bug fixes, documentation, small improvements + +Example: + +``` +v1.3.0 → new feature added +v1.3.1 → bug fix +v2.0.0 → breaking change +``` + +--- + +## 2. Git Workflow + +1. **Main branch**: `main` + - Stable production-ready code +2. **Feature branches**: `feature/` + - For new features +3. **Hotfix branches**: `hotfix/` + - Critical bug fixes +4. **Release branches** (optional): `release/` + +Commit messages follow **Conventional Commits**: + +- `feat:` → new feature +- `fix:` → bug fix +- `chore:` → maintenance +- `docs:` → documentation update +- `refactor:` → code refactor +- `test:` → tests only +- `perf:` → performance improvement + +This ensures **automatic versioning** during release. + +--- + +## 3. Pre-Release Checklist + +Before creating a release: + +- ✅ All tests pass (`go test ./...`) +- ✅ Linting passed (`go vet ./...`, `golangci-lint`) +- ✅ Documentation updated +- ✅ Docker image builds successfully +- ✅ Changelog updated (if manual) + +--- + +## 4. Semantic Release + +The project uses **GitHub Actions + semantic release**: + +- Workflow file: `.github/workflows/template-semantic-release.yml` +- Automatically determines next version from commit messages +- Creates GitHub release with changelog +- Tags release in Git + +### 4.1 Triggering Release + +Releases are triggered automatically on: + +- `push` to `main` +- Manually via GitHub Actions `Run workflow` button + +### 4.2 Example Commands (Local Dry-Run) + +```bash +# Install semantic release CLI +npm install -g semantic-release @semantic-release/git + +# Dry run +semantic-release --dry-run +``` + +This previews the next version and changelog without committing. + +--- + +## 5. Docker Image Release + +hostinfo Docker images are published to Docker Hub: + +- **Registry**: [`maximleus/hostinfo`](https://hub.docker.com/r/maximleus/hostinfo) +- Multi-arch: `linux/amd64`, `linux/arm64` +- Base image: `golang:1.24-alpine` + +### 5.1 Automated Build + +GitHub Actions workflow: `.github/workflows/template-docker.yml` + +- Builds Docker image on each release +- Tags image with SemVer version and `latest` +- Pushes image to Docker Hub + +Example Docker tags: + +``` +maximleus/hostinfo:1.2.3 +maximleus/hostinfo:latest +``` + +### 5.2 Manual Build & Push + +```bash +# Build image +docker build -t hostinfo . + +# Tag with version +docker tag hostinfo maximleus/hostinfo:1.2.3 + +# Push to Docker Hub +docker push maximleus/hostinfo:1.2.3 +docker push maximleus/hostinfo:latest +``` + +--- + +## 6. Release Notes + +- Generated automatically from commit messages +- Included in GitHub release +- Contains: + - Features + - Bug fixes + - Chores +- Optional: additional manual notes + +--- + +## 7. Rollback Strategy + +- Docker: redeploy previous image (`docker tag ...`) +- Bare-metal: keep previous binary copy +- systemd: restart previous binary +- Kubernetes: rollback via `kubectl rollout undo deployment/hostinfo` + +--- + +## 8. Post-Release Checklist + +- ✅ Verify dashboard on production +- ✅ Confirm Docker image works +- ✅ Confirm semantic release tags pushed +- ✅ Update documentation references + +--- + +## 9. References + +- [Semantic Release](https://semantic-release.gitbook.io/) +- [Conventional Commits](https://www.conventionalcommits.org/) +- [Docker Hub](https://hub.docker.com/r/maximleus/hostinfo) + +--- + +## License + +MIT — see `LICENSE.md`. diff --git a/docs/TODO.md b/docs/TODO.md new file mode 100644 index 0000000..34e9365 --- /dev/null +++ b/docs/TODO.md @@ -0,0 +1,60 @@ +# Add Kubernetes +### ☸️ Kubernetes (NO RBAC) +Uses **Downward API only** +- Pod name +- Namespace +- Pod IP +- Node name +- Service account +- Container name + +If you later deploy to K8s, probes map directly: +```yaml +livenessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 10 + +readinessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 3 + periodSeconds: 5 +``` + +# Upgrade UI +- Make new ui on js +- favicon.ico + + +# CI +- Docker cache +- speed up CI + +Scenario B — .releaserc stored in another repo + +Not supported out of the box, because semantic-release always evaluates config against the current working directory (current repo codebase). + +BUT you can make it work by: + +Option 1 — Checkout config repo first + +In workflow: +```yaml +- uses: actions/checkout@v4 + with: + path: current + +- uses: actions/checkout@v4 + with: + repository: myorg/semantic-config + path: semantic-config + +- name: Use external .releaserc + run: cp semantic-config/.releaserc current/.releaserc +``` + +This approach is used in mono-repos and org-wide standards. \ No newline at end of file diff --git a/docs/semantic.md b/docs/semantic.md new file mode 100644 index 0000000..9628578 --- /dev/null +++ b/docs/semantic.md @@ -0,0 +1,128 @@ +# Semantic Release Guide + +This document explains how to use Semantic Release in this repository to automatically manage versioning, changelogs, and GitHub releases. + + +## Table of Contents +- [Semantic Release Guide](#semantic-release-guide) + - [Table of Contents](#table-of-contents) + - [Overview](#overview) + - [How It Works](#how-it-works) + - [Commit message examples:](#commit-message-examples) + - [Configuration](#configuration) + - [Key options:](#key-options) + - [Workflow Usage](#workflow-usage) + - [Inputs:](#inputs) + - [Secrets:](#secrets) + - [Custom Versioning](#custom-versioning) + - [Troubleshooting](#troubleshooting) + + + +### Overview + +Semantic Release automates versioning and releases based on commit messages following Conventional Commits +. + +- Version is automatically incremented according to commit type: + + - `feat`: Minor version bump + - `fix`: Patch version bump + - `BREAKING` CHANGE: Major version bump + +- Changelog is automatically generated. +- GitHub release is created automatically with the generated notes. +- Optional npm publish (if Node.js project). + + +### How It Works + +1. Analyze commits since the last release. +2. Determine the next semantic version. +3. Update `CHANGELOG.md`. +4. Commit changes (if configured). +5. Create a GitHub release with the new version. + +### Commit message examples: +``` +feat: add healthcheck endpoint +fix: resolve crash on startup +feat!: remove old API endpoint (BREAKING CHANGE) +``` + +### Configuration + +The repository uses `.releaserc.yaml `for configuration: +```yaml +branches: + - main + - "fs/pl/*" + - "+([0-9])?(.{+([0-9]),x}).x" + +plugins: + - "@semantic-release/commit-analyzer" + - "@semantic-release/release-notes-generator" + - "@semantic-release/changelog" + - "@semantic-release/git" + - "@semantic-release/github" +``` +### Key options: + +- `branches`: Defines release branches. Tags are only created on these branches. + +- `plugins`: Control which steps run (analyze commits, generate notes, update changelog, push changes, publish to GitHub). + +Note: For a pure Go project, npm publishing steps can be skipped. Only GitHub release is needed. + +### Workflow Usage + +The GitHub Actions workflow is configured as a reusable workflow: +```yaml +jobs: + release: + uses: ./.github/workflows/template-semantic-release.yml + with: + dry_run: false + semantic_version: 20 + secrets: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +``` +### Inputs: +| Input Name | Description | +| -------- | ------- | +| `dry_run` | `true` → simulate release without pushing tags. `false` → create actual release. | +| `semantic_version` | Version of semantic-release to use (optional, default managed internally). | + +### Secrets: +| Secret Name | Description | +| -------- | ------- | +| `GITHUB_TOKEN` | Required for GitHub API authentication and pushing tags. | + +### Custom Versioning + +Semantic Release automatically determines the next version based on commit messages: + +`fix`: → patch (`1.0.1`) + +`feat:` → minor (`1.1.0`) + +`BREAKING CHANGE` → major (`2.0.0`) + +You can override versioning temporarily using `inputs.semantic_version` in the workflow. + +### Troubleshooting + +1) Permission errors pushing tags: + + - Ensure `GITHUB_TOKEN` is set in workflow secrets. + + - Workflow must run on allowed branches only. + +2) Invalid release branch error: + + - Ensure your branch is listed in `branches` in `.releaserc.yaml`. + +3) Dry run vs actual run: + + - Dry run does not push or tag. Set `dry_run: false` to perform a real release. \ No newline at end of file diff --git a/docs/test.md b/docs/test.md new file mode 100644 index 0000000..e69de29 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4d231e1 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module hostinfo + +go 1.20 diff --git a/internal/favicon_io/android-chrome-192x192.png b/internal/favicon_io/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..1e59e91fed3b1c9e6c7b909120c98faf39dd25df GIT binary patch literal 3108 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 H?lJ!XrXf?S literal 0 HcmV?d00001 diff --git a/internal/favicon_io/android-chrome-512x512.png b/internal/favicon_io/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..06f6d974cb01ab435dd3362be28f3eb4cb917136 GIT binary patch literal 9026 zcmZu%cUX>Z*nZxZl$W1gic&@*DjG(M#*2zdLsFE~ucS!5Y42f16iRza!$?L_y&6WO zO=)OqFVc|S=65~M{rHaWIDX$hI69wuoY#3?*L~f0uIcF<_=hGy!!Yb0jf1NC7)FJE zQZcSY@YlF|?G%R5F%8wd2JV!h29w9#My8yDvjM>r9@Sa~ zuV~z7&INaDHqke2SEgI5rX}%dyI(Nkv8?9bnGt-V^x}49{xeCM>IUBQ3mYEXrgv?= zD`q{>=5u=Yj}w(vW^)lQ>xP|n%ttv@w$8t-`!m*Loxt3qrOZVaV)$bL4pbT==k}6A z{uqV2P+uQ=O(OsXgTf)yUahtn|E7Vur+=mYS`I$=gp1C#IT2eZn3Dtl6r!89>-%!F z-?>=07@v+I3ZrUsE{ou!`lu0OTd@d{87*RN>S9qW;EIGeK8GrwyLch>SJ>q$v=~1O zD^KSjhZ*%4VNYs*DWKJaVYSs+k>qLvGqj?w>k5Qn0Sa})LSBEm<*GaQ*6%Gj5o1Ql zF??T-f5{N?_ovd$ON3Y8yK)W~gq_sKOfDm)v^mM3*!aGul32^=R*dyvavj1>0OnoP zu^f$SfpIL>q$I+O0rPzQmc;U5oY$vGWrQ6A%suDcUkt{1HMw8HcgD4AYa+I5kMSGV zh4!o;a?28OesIUn1-*)!`}yoiz2dN3$j_l41G??+CDKBgN1l5nY_D6|_N$|&&7;hs zYHr1`Yel=L(?1?!LusAQ1~}qF{eCsJB(VjV^4{YamZoiUYuo-@2-BHv92oif!slHb zw_U}T%$qXr8b9wZ9Df^=9baR)P;0rUPEm}(`Hkljl{rSw&CMukF|~48URi?&3w~!e z&DlPxTJyO+dTrF~F`fQDAELk4otp>_8{E$^zVA=1?mBiE zR{$jD>^8!dtr(R@LC}LBIHU`cLAVG(;1u*&gfJbz?C(=a%nE{6Ch9>7atN?~z=#5<3cWKC89F-k8yq~bXIG-LSzCN*&2bt)^9|=BNW59y5D&2p#k?{Hc0B`qsqav)o&9?v5FyLA5HMFn^*xg(LCH z8wG-qDqq94Lh93~1QtrsyO8jAEuC!eSpp@Wp7G@{KeG1^zBMd>#=+}PyV_HN&q6mf zAA@A;xnhVz6zW&V->)~z1Mpdd@)+xZpfYd9UPO=w2nt4ak{RU-1ZU-qFC&5?Ku|{V zDw*6qV3?^pSxESF5HQ{5Sg{? z*{eWEt;*LYS4geSO#6z*-k-4&2(GkrJctNZyZLe?G7k^pz5{|#F=$u10ujN)`578x zKGkp&L9lp=UO3$ zj_?|}bteqk#8sjje=Q+VZ->1$>@6H&^_iw&OG zo$kXo7{62Ngt>rb_qq;$ z9o7=-U9W5UZNSUGE2G|pof=l7TPuCFSi#J3oV85Kck>_3&S@F0@|qbB(@GVk9$WkH zBg(tJeRa9IN~-SoKkpnqO;!Q3E5P@Io9yGr^3Q|=u>coP z;eV4ueL2594~MzD$yVe+3-F-DflhPMgC*d>TX}W(IYgmerio&gmwk9fCfQSv=wF`< zB0Uyf=hefmuL;^u7MLVR&izYF5(p3SiY8*b^1KJgqV=xV2)iPey?{tEQ7BBJ_M4We zA`gBj$;56Xo#RFxj06uhFxFf}9=s81M&bY1D2Y4>XI*0YdHJRIEUJ9`TMMbu;aNV& zgL}b)d*3Y*vB_s%C!J;{SY|38TspO{n#COP ztV`{6RiRL0mN<*4IEAx5`_2u&o>j{9X~{^Nonb2H*&6cCYjK!+kF~nRwm3}I72jK5 z@}jYE`;*3PenQ>jqs8%abRp?yMfYyB<&EuB_)s-htTZ~b;J~u=qhl%~&C?CObsvT6 zP^nm0#~0b5!(mQy@xm(t7H|zTy~b(oMTSEg!Xea< zll-aD8wxVWjC6)*^bh?&t;%L7Ih&JTZAa0M+yT)~Daa=bR-{U$6{-HpCM!-2l+@JU zul5jXM?&e3yws9P1|M%HYI&YQVBiOlP~Ic4mT(fA0&IMz0~Kv054N&9_aC$s#$wpY ziq!U{_@@l>Qxp#KQw~j}^L9Y>vnqchMmV7;9rlvmEV~g8LmxGz%Lvnqy5xjwhy^SM z>dT+msUhc;e#*qcb}renfv|cql-uOPO@YXHVPR$z(K7;-`v@nfuh7HPFJ%9NxQpV> zD^d(z&+4t$UBmhlqO*q88}c#d^o#K(v0g#WKno3^a6;nNSk0Gn6BFB8n!R1q8ykhyHXF6UD^D5ZsvONtG2Akl$r3`PZBD`)|w9WbHgHwRyKHKLX~m6|0i2( zIO#>>)+055$!VxN?LjT{gay{t!xnYFBpM7=K2e!Ms@}kf%_jWoX zG3g*C-7tte%DzCBrRbKh5kU+HV(H=dc>!W}Lm4SexXpun+zJF+H6#uqG0#F7sg)M7 zMn{Vh5LCKo9E=Fo-ZrD8Tk7bbqs0>ldUj{=BQeuOfMD7E*ia&^$ARE@;2ANJpe_)M zKdvA~5bV=tQ2K6u*+&vg1%lc&j)nxm>QHFXc%q zW#k*U^`&;X2a!1pZm3(_f#IZrzd^x)m?$1pm~R5Xn~Nn#1_|0rwd?TD(#OA9y<}v4+%ML%j0yX=} z6)_sl;l@_IF?$-LqQ=RU2=>!df9liCu}x&KTkv4__KqVrNx-HhWaY@x@D1GbQn#9o z%9H@ONx;S)&v$UsZ*bE#smq7GS&XZXvO7d_CuR}!XgFk(;|B0gRuP5%{4|0wh>ss=+F6S1L-og45; z0wt^QYS7ZCiO$XmXbvXsj}S?v%4eeh{o<&z6<(AnR4+Jly^1EuX4z&BU=!Xc2xEb- zSN}#mWY9zZ1 z$SxyPfPV2=q3FowHP&nE(XbEIw4?^?~)`Z_Bm@95b#1tvt*P{s0;oP9xwthk7D+qR_ zw#bhZ(gudv@I*uq!?rDSr4=<67j86VGxa0ZsrXK}WHQ;M3QDZ9GaqHyeHw{_dGUtN z%T=j|7e$8FpKX(R)|_I!R>6Cc@5*fc_8-G3k`lg7&izb2*NwBvI@Z?K>vns5JXUYn zR>CwnHPfaT6KBUZUi#~(LdyOizu66pc3Oa>RBLJGWL~juuW8VO&6Qy_W!4JwYGs9T ztBOaeO)nkxjv^2MFaP4hI2qV8ExZu)9WH;Qwp-@s$!bB%uZ z>cc`EhBU_e2W3R-@Ez*dFDVK}*o3|lg5dO1pGX)73Ww0-5{5U#2Skeo(RO>}5;lbN zpnJ}|g|DETHiPS$8?}gzB>*NXddG(szRw)Okix>cW<*)wf=mCUwfXI66)+XuYwioN ziXN=e*Io>T6CNh|Akcl87*P>~AkpZKL;HwT{AXzX^+fpTTyP33T$6|2$^s>Y{4{C) zMijy#{}+%Dnhl10OKwL;>KiCn<_q|60Y7te6>dtzPB{LOL!;nAc1hsXQ#9Q?RGRkZ zm;4BCGK64n)lb!+b7DAC*a=X%f_&L5&R$bb ztumvCyuxAqZv`v9^$OCQTP`@ilWA6dP*jPrV%_GV#@Fm;7EH34%AM6$>Yo%&j?Ak@ zSG9c`9loik>lalpH*3a{oxWujVvJRKPmOo_{SHZ+`yFsqcTS_MzC+^Yiy&L&uJ*^; ziRuB(F1a6^G97ykw;32I9BZkXtzz0U=lfF&Yv+JtRi6L?>oooF4nWAyL%_r+Nve~zb`e>%jsRl5Dw$zX1G#V0G#&> z{|!NKrbDKVnn^(vnH_r|0z!;L4>(5I%a@SYFg%J4rFM8B>^xv2l%NA>+^{5IKRZ|m zI|Eq%2<f?Ci$yqa%r_|PtG(ABYuz+q~L-a7B{M_B&XjWYO_Jk0VVtM^Pn7FNVcgM> zs_&4(xWq2-sY`b=7!1-~d=UM( ziYOB4V5xLxhhSiYTagL&KG)N0Pgqee1#ZRN4QrxNacP538w{#vVQY{U`@q6)lN5Hq zEccd_6y`!r%1v5{L;egGNGF$#PUeVUAvb)=t!7IS1Y?#i>d8R_{Z!ynm0&}37di^3 z>Cu~I|B?g?;M0Ol8RXj<_)T0OJQ+?g-21ce>FjaiYdCi<11MPrJHaY#5J5jKP>}1WEjnP|f-G-2yWB|yr9naICx-YPl|oemS=2&4 zekYSO2?!>g%0Nz|fr7Mk!O3KDzXO8r8jYg}g1oS6-aP`GWKp;Z1h3S-Bsv+oD99q} zEFBDKi4)|8PnQnbx*&pZ(&%*^TXP)|Tn`W1{KgGah#4UNrqW)1v}x>&f`r;L0(XhcPEV#X2C7`5eR;$%|^e65CsKAPtk*+Uf~2ofZ&Zm zJ2^xUdQa&h{=6{dD95RGi~bjOC)pc=PJ~EG`nmG-<>|VL&HwI5QtwSjE%|*g;*0C( zvzQ`2=xt6YN*AB-Y~K0fRo#~C!l{%cpAKXK<;>WKRbK7iLx=Z_Re$0M8Vp{Qnd$R; zCWa;HGo)|qXSP*Zm+h0_HRDuK>36+;{PkdK49j`W*w$>A%egnZx4+^@xz%H(VZXjE zFO{jNKQZ%_IbN@M{nu@m0CgzT!NobY-VjP}r#;9kpTH}h4zy!rD0v~2yaN-c_u~Pt z@NgGRkk~H3cKvikJ_?0)HFUHIe-j4ZCO`)nso7dU$Z8IWX}-_fge(L#5apW2CS;>S zkllyWnUxLc#w4=$cF}*ZbXo!uR)L(v$V;yd;pt%u*KXD zVF7?q@+3&CAFzJwcJgxL3=23r3X&-+0?n+*p*O1uhZU6opOW@$($Qs5E=waX9zhN} z46_{0wIh$m2;oF5Y{%kWR6I8V^^F!yq@$${fH$mH>FgtlpgNpa>dD#5k)zGvj->Zx zS1fv-zOe~xv(QcuaWC8k=6~!VPdu&A`PCpVE;nvQq4qliPB_DJ9^G!F;Wi~reJ(*d zdKAJmdb&du#Tpt(a|a7S($ONYH4&ZSC`7Ob2o@EElSQ`-999fv5_OZ+LD!o_KyXp=OKFneb09eC9D9?f5leyK z(uKCiNrLBq;1FAWEquyF=Z6&Hzx4GUNw6OX_VYL@#j=# z!9+Kh#Z7++sh|ufD0Ao&y5BIWV3w-Ha8kiRP;ekJ^awKcNtorNf&{5xJt$baL#!7W zTL}m%Ss0TFW`ct0)`mKogo2(x&@(xUP7;LY4ZWJISahoy2ZG}Z&#Wd1O2XEjcqm8^ z1p7e2zRRD5NrHB;H9H=slLW!)i%_~3+!G-coB{=>PWQYg2%1}hf|mQ1kP6Dc)?{=l zqKGPAbOjW=;%`qXI0y8x2+xZ9=w^DbR$(|SQ? z(?9IqxtfUHPY0OP7FSq6GV#vLjqSfFl|P)a<;%5c?$znSwvM~mqkCAf%CqqD5i#g_aV(^wGET!<@`R_%B#S$MJ<&NI zp7--)=Bq(7rl}^kUCKlpuP)#4)h+=t~%&z_G3nR+@11cd5u0E?ae~We28}b3nxb++E25g6OfG)B<&Op zMu<-99YWF8hG=Wczaoz~7pQP9RaN~c+QtxV<8$_q1o#PH3%^P`n~*q$ijxdzCR5!E zS&rvRJ@n_bD*G3sJS&B9rKjxiz5~XYfDMf!?~xC-YOtP~v@WS&6JXyWlX=lzL;w?! z)FrNGFbgh5|#enwmuyiBYIhnjJ8>NbSIo!neQ zRF_PNCcwT$r|m)*Xkjk4Sr_ML=x{q1!>Dce{|mtP45l}ZNmoSQEr#zFZ_*`C!Y07J z#XLaurX7y#c80L-U#{Rt_{pApaUTKkkP~@JlBPv}C=wdxIz`LvW`U(&)r9OqK6eA>1GnyZcK~DVl&_PWX@5 zk(eD|KiHb68FhuHPFJfDZ4z?9Mfq_}_>wNK1glUw)m-v+M;gh0Y z_){vhsq2Ak;7(f&$f&?+AyoG|1s%S8 z5Wb+LvvXfl#y^GCaPmEhw;T7Ah611_>j%GFIA&g#BGo0Sk|Y1&%COvnu4$ELvtOl5 z4=SSv;MZ9V3%C=!+oG=O#2-81mgU|u|LcKV%Y4bOFWzGC2uY_DMIF$I+cP#2%CsNJ z?{Q3>UcZ~&8sghC+neTWf1b&aSp1+Aev1{bl0#7`9%97oS%K2zKIXn0uVk+=|3C=B ze;-0XMHP6({C^%o@ZcPU;QZ$y1ixwIz-0e<2*ES)0Q9W?eF(u<$u62-fMN6Vyhrbx g!!T?SZuJG&-nge)R;3y;@Dof!O-J>~KJyFz0}_1(T>t<8 literal 0 HcmV?d00001 diff --git a/internal/favicon_io/apple-touch-icon.png b/internal/favicon_io/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..006a1d9931e188acd30b4a235ca35d84b565eff3 GIT binary patch literal 2917 zcmai0XH-+!7QPfAfItWeNYNOP&`|?JR}x`pLr_EzL_nHI69h$iF(?TNIw*urs5(+6 zLuV*H3F1&gk@n~{bTmjXw0B(V{djM!H|zbl_uPH&J^P&f?eE+B5U=2k1^FfUAqWyQ zH8HdTW95$v#slv9T|5^cNI=rm5NrL2V>QdtN7`1VEvi`)^XE#zaSCykmb>>L*#+Ku zANPSz;B71l244=WqdV47J`!&z$c`Dzd=wET4u)%q zH4}-Zzb1*c7R*! z)-|%7QmIi>K8`X^(yj=$zxSnXM-t`o+xPr!Tu)EtsY&YYh(KxXmxrU0Ik2+u#e{NS zwb4}VGt3>s*>9B-Gl2n7lRE5K%WGYys>F4yNAGm;c5YNJ4HSARhHt)ds#LSQhb~ML zD%$?2?(2xkm@&{Kt@l*zd#P1=$K>Yp!VDO_JJWt`FaysvPONS|h#>z<%XlIf6OY6` z{P_&sn9)m`^@Gc&Lj=`yt?iE25TX0H8cEy&!iif*@6MY+xJ8n9Yl0j^NswD#jy%33 zfU}h~c}B%UxbndFED}7Lew+{CS|S=vUyTDB-ouY*q` zE&GGP&HmdXOH0)q8&9T^jU(^pPD(d+hc#U^DahEr&`*n*t@nSajFw`^3DX##g5bxij#D!AJqy(QAN$ML{n)jvD~0*qsW@=16{9AhkeR(mEsF8DmSRtfMp5T9IG;NR;Tzrcpia8#y|u- z^tfeS7NT^dN!8<#5JmL;l)pBg0ptF{lQL0Qt*UyC)r0h z?cKEZ_)?dem=ngLy{vfZyOsHlZp!t2j+YiHe1607W@kexxn#@ak)CO^>G^AiWAUs} zcBkPd+#_ou(3@2{IS#2V`{o!2?ABYY^G69!xDec#Z#gJgM9$|XVB?5mS6fbS!bj8O z>G9kgFcqwbM&@bgKR)++O*v2i5h=A)y9w?RSn2Rki11)VQ&GJ7_-Fgcmp6(ANh4@= z^M#IY2W~~3RixvTz%2Pvt^WM7L`Ok|P^`;cnI@mea|g-Q^ZtuLX#V^S^DVIqS#F-(F-_agu;KvVPRhiK8@d5_!U*T6g{{8OQje>c0LZx`BwDaP!gOx7jov4i5k`aVs z<;A)do`oppQZkYo|F&BZ@G(K0zz0KxT>#YnQF;s#AOjOETm`je8)Lv5%guve_tcCE zpVHrIs;_R}J!>=kys1PZLSC!wec@&w>;0PxPM&{6z~k87h%%anjctD2C5Mf%Qw5Y5 zLZZO_I$QWs?bmMa8d;lTOS&}s^$o=~L7`fQs6zjKi8K1omG@uUGQSqAhf>ID&4+DE zx`u0>a0xB}eUtMJT%6Sa)=O-fs4uL~kDr8&}_@cy&iD6}uu5LVT4Y4;^fF8rPXxo~D3do3H$+9?Z!PjSt&d z1pg+Wop#e!73m|GG~j%G-!BO`lq>7nSgcgy|nu zaahp?oo)P_eqldcjM{22 zY;GBNG_dqb+NmNIM)0Q{3)Z?yxlC-n8nx`t3QDgQD< zvB-Wl4H@*jrF@0~uP417d?W`O$An8OD{&~{dion!-}2XPp*=>E5i6LSLz?VewuyHH zy4)z;HYiXbooxS*J)q6rn-f#3 zrF-(+Gb>pb9!9M&cuyOt%~cH!en`DJG_fDt+n}ntzj}Lkts#BLAz=KCXk(xCp41Od ziu8Lvmlj&1_KH0n+>AUEgC3KG&NC}%7pk%a6lx!QUru9vwYM$Hstb6jDs~_>DnSuH zoM=k>;pKX%AN9oEO1Nw9JwMB*xG@Y zhxowADIu+j2F0>_jQ)TVbYh6-X$R0@Dy9ZiuR4DcUjK?pS&{|JBOXmm{S_R2+FVmGCT=1_$U&v6ZB7-~k0g4+kmY34*5HR{G~U8X zWdRSS-_KOVxl2LseqWqtS&nOlNQZY?O>wQh+nWA%{gXK66*>C#B>n6{+@K7|yo@kx zP0$#^3AYK@>f)yk!5R7R@Pa~s0(jv0aZ2Wb;IR>#XBK#B!5hfb2ya-1bB_HNWLahw literal 0 HcmV?d00001 diff --git a/internal/favicon_io/favicon-16x16.png b/internal/favicon_io/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..14eb75b34a1895af9bf4b912b2211cb077361b34 GIT binary patch literal 542 zcmV+(0^$9MP)>JEwms}fRw_q-GD+#8Vo0X0}u^PkSwt2jG!`q5QLM8{6MO6iWEr^ zDftAai_#=gZZdD3U0)AIlYduMHzLNM~?mvoi?t?a5s8?F_AQ2G;-p0RR8BIh)J? g000I_L_t&o0H}S$YZqb*9RL6T07*qoM6N<$g39all>h($ literal 0 HcmV?d00001 diff --git a/internal/favicon_io/favicon-32x32.png b/internal/favicon_io/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..3537defc2718ed2a3a5d00049edb921a08294dd6 GIT binary patch literal 806 zcmV+>1KIqEP)rHN@+5Lp?5FmrvU&9tvGuY;9hSupA8y!Xzz_nmq3K1NZSuciznujG4@L%GEG zVCF*5cX~M64Gx+h7gcY{fa>I1j#5jGb7mxz)ax7p$1$1+aS;to8HmX9&s6&jq!NV} zHxv0>qenkwvQky`(r8B~0rJAEzJt5Rq?&=DXjH zy9litTx8K|ATpxGWcv;5Dkm-;XZ4!@;e&|B=nxlM=8&FMV}5?z#iOU_TfO=4q~z^; zTg0s6Dqtf0qt;%?$jroChRg02`YykMmuoYh7M7;tKJ+AfdzjvJJO|^2=$)S*_t(p_ zYtWPppkRV6Lr5g?RHBgdp_iR;&!0;Y&q%^6c*M zg8C9YXkuy*>T4K11WDz6C_yN!j7Zc>Dl!PQKlbnU?e6TEowIw+p1pUu$=w5UXLsiN zH{X2UH{Z5iTd*MLpQCBR(==@^V1NP) zza3TEpQBsz8V1{D4^;0<0;E2IAy^g0)j4xb{;y+L8`VDXPJIMpn~aM#QP)g9)z+(q zqcayU!+^C#pKGu>wYj?0AQ*xbeQNC*d^_=QO?F810so-ahCb1z)?)mCJr=;8q_KMA zHThRT+p$jL_jwclR!+)BIr! zABZs|)_@WAE8~-n9HY9ZEe(SmAsB+CAK^RY>I(nO|GTAJnGYH-lKgg=aVnUbVQQm3 z8CJy=ePrXR{ZgCg?^I)6C(1rT&(zilFH1kgnO>BoC={Q%`8DoYF=c4+;B+dEK6q<4 zxH2r^3ObD?D-aRW{oq@ETp(XH*x7Vgtx%pFh5p`*JFSq>mT}J?zC&vQ60* zqWTMI&-e2voC{n*$0_rOKSnj4{AQbch8i;(G3%5ZM+N+lUY(gtIbF|qt6118M-z-w5hnFc=sUqXIk z@^fJ^_~lOWx8onDKhPtZArJCvl7alQp|j;!E58#3I=nRM{4pj$->b{k1H?Pv)3yHE zjX#b43;bmJjkO=j{pCEswB#pPdV+s+E%8vaVFR7P*V5;?XCk&^(coZvpjj^C`R_57pJb3_x>(0NgmMS@ z2K3`%uAYh%oxMEP_5-_v4J&e$Ej!GUf_som^P9K*6!c@x-((-o*?$ZDx79z+c7LH* zpq}U0IzOz&EZiTxufhRlEn>@g6}I`F_x#GXl*e-+|Nk+6j>}Kt;(u*`--dOI{3_@;EcOkz((VQ}S96I?CI-zffMtvR-%dQY@@VJ1p=qEzPF%Mbf$GP_$_`+&@ zySM?oJ{)a3g6tpR?*AEl8@s07&s*|ic^Fyb7x-<@4@dG%iNRB!pGV|3_$k=+DU5%I zkg;7|>+73{Z$BD@(=5SvY5soUY_na4!L!LvqfhL2f0N + release + ci status + license + coverage + docker pulls + semantic-release +

+ +**HostInfo** is a lightweight Go service that exposes server/system information through both an HTML interface and a RESTful JSON API. +It’s designed for DevOps/debugging scenarios, observability dashboards, and automation. + +--- + +## ✨ Features + +- 🚀 Fast & lightweight Go server +- 🌐 Simple web UI +- 📡 JSON API support +- 🐳 Docker & Compose ready +- 🔒 Zero external dependencies +- 📦 CI/CD & Semantic Release compatible +- 📁 Clean repo & docs structure + +--- + +## 📦 Installation + +### Option A — Local Build (Go) + +```bash +git clone https://github.com/youruser/hostinfo.git +cd hostinfo +go build ./cmd/server +./server +``` + +### Option B — Docker +```bash +docker build -t hostinfo:latest ./docker +docker run -p 8080:8080 hostinfo:latest +``` + +### Option C — Docker Compose +```bash +docker compose up -d +``` + +## 🏃 Getting Started +```bash +./server +``` +Open in browser: `http://localhost:8080` + +You’ll see system info like CPU, RAM, OS, disk, hostname, etc. + +## 🧰 Usage +### Web UI + +Provides a human-friendly view of system info. + +### JSON API +```http +GET /api/v1/info +``` +### Sample response: +```http +{ + "hostname": "mylaptop", + "cpu": { + "cores": 8, + "model": "Intel(R) Core(TM) i7" + }, + "memory": "16GB", + "os": "Linux" +} +``` +## ⚙️ Configuration + +### Environment variables: + +| Variable | Default | Description | +|:---|:---:|:---| +| `PORT` | `8080` | HTTP port | +| `DEBUG` | `false` | Debug logs | + +### CLI flags: + +```bash +./server --port 9090 --debug +``` + +## 🧱 Repository Structure + +```bash +hostinfo +├── cmd/server +├── internal +├── web +├── docker +├── docs +├── .github/workflows +├── scripts/hooks +├── tools +└── ... +``` +For full breakdown, see: [`docs/00-overview.md`](docs/00-overview.md) + +## ⚙️ Development + +### Prerequisites: +- Go 1.22+ +- Docker (optional) +- Linux/macOS/Windows + +### Run Tests +```bash +go test ./... +``` +### Git Hooks (pre-commit & commit-msg) +```bash +./tools/setup-hooks.sh +``` +## 🚀 CI/CD & Releases +This project supports: + +✔ GitHub Actions CI + +✔ Docker image builds + +✔ Semantic Versioning + +✔ Automated changelog generation + +Semantic Release is used for tagging & changelog: + +- feat: → minor version +- fix: → patch version +- BREAKING CHANGE: → major version + +## 🐳 Docker Image +When published, you’ll be able to pull: +```bash +docker pull maksymleus/hostinfo:latest +``` +Or run: +```bash +docker run -p 8080:8080 youruser/hostinfo:latest +``` +## 📚 Documentation +See the docs/ folder for: +- [Overview](./docs/00-overview.md) +- [Getting Started](./docs/01-getting-started.md) +- Installation +- API Reference +- CI/CD +- Deployment +- Releasing + +## 🤝 Contributing +Contributions are welcome! + +Steps: + +1. Fork the project +2. Create your feature branch +3. Commit using Conventional Commits +4. Push & open PR + +Please follow: [`docs/07-development.md`](docs/07-development.md) + +## 📄 License +This project is licensed under the MIT License — see [`LICENSE.md`](LICENSE.md) for details. + +## ⭐ Support + +If you find this project helpful: +- Star the repo +- Open issues or feature requests +- Share with others + diff --git a/scripts/hooks/commit-msg b/scripts/hooks/commit-msg new file mode 100755 index 0000000..b24b637 --- /dev/null +++ b/scripts/hooks/commit-msg @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +commit_msg_file="$1" +commit_msg=$(head -n1 "$commit_msg_file") + +# Conventional commit regex +pattern="^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([a-zA-Z0-9_-]+\))?: .+" + +if [[ ! $commit_msg =~ $pattern ]]; then + echo "❌ Commit message does not follow Conventional Commits!" + echo "" + echo "Examples:" + echo " feat: add host info endpoint" + echo " fix(server): panic on nil pointer" + echo " docs: update README" + echo " chore: bump dependencies" + echo "" + echo "Your commit message:" + echo " $commit_msg" + echo "" + exit 1 +fi + +echo "✔ Commit message format OK." diff --git a/scripts/hooks/pre-commit b/scripts/hooks/pre-commit new file mode 100755 index 0000000..2e24371 --- /dev/null +++ b/scripts/hooks/pre-commit @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -e + +echo "➡ Running go fmt..." +if [ -n "$(gofmt -l .)" ]; then + echo "❌ go fmt failed. Fix formatting first." + gofmt -l . + exit 1 +fi + +echo "➡ Running go vet..." +go vet ./... + +if command -v staticcheck >/dev/null 2>&1; then + echo "➡ Running staticcheck..." + staticcheck ./... +fi + +# echo "➡ Running go build..." +# go build ./... + +# # Optional docker build test +# if docker version >/dev/null 2>&1; then +# echo "➡ Checking Dockerfile builds..." +# docker build -q -f docker/Dockerfile . +# fi + +echo "✔ Pre-commit checks passed." diff --git a/tools/build.sh b/tools/build.sh new file mode 100644 index 0000000..03916bb --- /dev/null +++ b/tools/build.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# Build script for hostinfo +# Builds binaries for Linux x64, Mac OS (Intel and Apple Silicon) +# Usage: +# ./build.sh - Build hostinfo binary in root directory for quick testing +# ./build.sh all - Build all platform binaries and store in bin/ directory + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +APP_NAME="hostinfo" +VERSION="1.0.0" +BUILD_DIR="bin" + +echo -e "${GREEN}Building Host Info v${VERSION}${NC}" + +# Function to build for a specific platform +build_platform() { + local GOOS=$1 + local GOARCH=$2 + local OUTPUT=$3 + + echo -e "${YELLOW}Building for ${GOOS}/${GOARCH}...${NC}" + GOOS=$GOOS GOARCH=$GOARCH go build -o "$OUTPUT" -ldflags="-s -w" ./cmd/server/hostinfo.go + echo -e "${GREEN}Built: ${OUTPUT}${NC}" +} + +# If no arguments or "quick" argument, build for current platform in root directory +if [ $# -eq 0 ]; then + echo -e "${YELLOW}Building quick test binary...${NC}" + go build -o hostinfo ./cmd/server/hostinfo.go + echo -e "${GREEN}Build complete! Run with: ./hostinfo${NC}" + exit 0 +fi + +# Build all platforms +if [ "$1" == "all" ]; then + # Create bin directory if it doesn't exist + mkdir -p "$BUILD_DIR" + + # Build for different platforms + build_platform "linux" "amd64" "${BUILD_DIR}/${APP_NAME}-linux-x64" + build_platform "darwin" "amd64" "${BUILD_DIR}/${APP_NAME}-darwin-intel" + build_platform "darwin" "arm64" "${BUILD_DIR}/${APP_NAME}-darwin-arm64" + + echo "" + echo -e "${GREEN}All builds completed successfully!${NC}" + echo -e "${GREEN}Binaries are in the ${BUILD_DIR}/ directory:${NC}" + ls -lh ${BUILD_DIR}/ + exit 0 +fi + +echo -e "${RED}Invalid argument. Use './build.sh' for quick build or './build.sh all' for all platforms${NC}" +exit 1 \ No newline at end of file diff --git a/tools/setup-hooks.sh b/tools/setup-hooks.sh new file mode 100755 index 0000000..cf3b982 --- /dev/null +++ b/tools/setup-hooks.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +git config core.hooksPath scripts/hooks +echo "✔ Git hooks installed via core.hooksPath" +echo "Current hooksPath: $(git config core.hooksPath)" + diff --git a/web/image.png b/web/image.png new file mode 100644 index 0000000000000000000000000000000000000000..06e706d2b64e2a6a97a2dd3d0f29e7ee5bc69054 GIT binary patch literal 205603 zcma&O1y~%*)-?2*8Byfi9QfCDO1m4<0 zL_|?aM1)Au(azMu+5`kdGAvO8>b=S!MwWJbZ0tA~N-V19TT#zARAD$GBDHFictPPX zCgPI$2{DMd@K8fB4KXxbIHHK~`5$@n4~9ZwnwW6mAI~Ysat=Dq+b?{ab~?>QAB&e$ zy-q-K7J3pT9Tn+79Q!;bo2rR^HGNj4*uy3Q!TA82?klr zZ$zp4YV7Pc4YzkdBoqiU=!SDgWHOw?Taa7GEYUP55T)G4lq5=}br0kNX#;=cWE%S- z)Dz`(XL4J-YN=2o5pM_(HkxGG5pcmx{%MvAkJMO+d?Z6NCVetUK_~FuXI|oDq#BZI zl;&m?v?e@5ZF7bIto}D`lbxHxkGOvK$O6gfT%Qs>5JI)8B`F4fj@}B$Ty&CiNbv9W z;INk|;t&)M5YC>uUVoN^C=y<#mXC>6n#S26K+)H915pfDB*-eMmxyv_X0IUh{srRl z%PEkQnEfNCYQ|gq!pUN@P+>0Qo|%vGnA;AhM6)68HG&wZRkaHwnW)9*L9?ck3-1!2 z@ie&CHGUrC^76zLZurrn*{eRLhzBqdV;jQuCf(%~n1`|vxyGA(mngFq$An&(2xM3G z$RP2R3Ec$Ws3sy8NXjNgh*DX=+!=zr==%ySmSK+0A4@3=s8F^3z$y?We}s>qZwCxypi~4gkQdKUEEqX3>}RiH9=>hp z!)Av&S{vlIUmv}Gt_FH|*HQA4)}s=90* z$0t-w>EPrbKa$O-Wc4sNj_|3jvz(vsI!-{#Frp@KUh@##mB6ZZALK1h^o&t*bv`U_ zeM=T%MWv9P!<#c>5rPSUM+Y^2^u*6x8a!^CH`L$H!KW_JaAWwym_=xY(0(y#o*oc= zaO~wRMv=ud!^8wZTf^BtIKVer$Nk7i?>$B5@3pM3ytcM>D~EZ9?Jr_#|Ip|&wV)GJ z1#_Fl-xyDs{EOD5X+zlG4o0qN3f>>48ssY@A?7-$w-6CDC<+lInLipMjJhWDTr-;N zo4PJ_BGpRO$9(>Nfviu*FazlFt|%^TR3(U!R^*dc36d81rU z&5kA@4MrZpoUJThnwwFyT->Ijp~0bXqfS^(ubf@1SJ|QelyOgbrJ#ym#+N9UDVV5| ztmLMgUgB1AV$PbzMi-kT{Ue_)Uu{Bul3>!4br(k-2M?j6UndGSN-L@yr;2SbO+D@1 zh*TOxnn@ZI8-tarWn4o=!@QQx7cMPyEs1(t3m2=Y1__Jr<~x=QGpk>G^d5o`rM_!X ziiT9xDVn8^N@Y?yN7@RRO?g~GTpnFcUApS#RYuWEaL6=@x>eam*d}L1XQk`KuR6EG zw=z(*R4tTQ@d{lJF8;=s-h!A3Gdcnh!1TwyvtV?cNnasagtcNuYYv$r<>+q#Pt znrVRjy@jILX%{j_JqMl@=ZJm6Pm7+>pDjH;As4}zNCJ$Hl_-|Rc4%g-Q|9RtvkcakHk|Ab>@(~)HWpt&zpB{K+Egu}9JL+R9)?F+uGsaWdF9w#gxW1xNE zDI%meWIDe;zh`QaWyC3PhmJwF>U~wA-cB3Ls?e%aJ8rwco%`+99p35PkGTnsCAY?t zhza(5i2TMF-wm_l?^jFvbHBze6)x5`)-XO`bRm?$yW^Ro3?MC|+-8`3A2K{LXh(EJ zjEBTWydStW7&GO7FzPnw?(3!_<|Ym!J`f=YP75&&Ru6FvY7QBJw}PeoBoSN%-`U6X z>y%S1qY`>7bOEK9q%`gUqDS(kulOjwAOwb^2 zjKbxzy>5!24yMlge6Ol(sk|ROeSAi|ITvY{z0RL&{%s_T{cW)VufU+a@@PSR0vn|h zC7~>70eSI}{K00wJ#F*KUdAQDo-QxD9o-4KK&<}I3GywBI!unn41w|5!y*j^qP%ZR!p6@!eLHQno?`ZtCMg7 zG55`nJI^NAKeUp(-mTStY!YtU_%-C8*s5+(Mf3j3_$pTL|WAes|qKZrZm-<%^ zI+HKnHrgJl9#7mqTxe&nzF<~%Dxs*>*ac1mljHK+ZrQc9P}{4Fm~M`crwiDj*fh7Z zSzFl@x|l4mZ+v~U8T(3UBk&M0$Dqao@AmodwpOLB&PHRoQbjFGO|3P`m7z+!e0v7p zi{h}kmc!l7C(3AK)+f@3{Gr?T?m4-$=*ja!a4XLeTr{W6GmyK(vliw zg;lvisU07xLqT(QE5;m{9QM2h`9CmAI_T@+Hzpo>BxZGBkM>?0h z>{flgJ#9c~Aq^4W@Krqa9LMguF-i8`?KBlVg3?YhSk=*F|w+HQ9o zedK#fd?Rx5eS;t{OENd72v8cQ0 zJZgoYo4Uf`8-dVs?|a>7fonmO0NmS}-qPo5BmcG|YT{(%XkqVc zVP{M9V%NaX&c&IJgyf~7|N8q^J5Agz{<|hyr@y-ebdcfY3Ih{8Bg22$28!~&oaI)u za5u5m6t%DcFazq~XJTey}-Ha zo%#QJ!2T}$KQI1WkeA`5@Bagfe*yj1SpaE%SYC$z8Z~~{#mXr+U?A}$WRamrdbh{tB z8PrzORa?1OT4RnQ9rhIDaI#lu zyv|A(n{UBI(pF;P8!v@)+~3RAt04SyVWKb4;8aae_EP04(&kKjzl|Fw`$ZTSs-d{s z1?6FUc#+}3h(P}MIzxe0^hR1oU0dEmopDmIr?uj}oHT4bvAE+d_(2JC00N5q=NBdz ze~^2&L2?0K*YbtDHLV5jW$m}tojrHNYkwq^BG{SS<= zKLQO5K#m)-J@sU3iq|Yx@rl%BZNCT-{ZK3Yic5}}4`j90E4@YDbp@mE^Tc#&WZ)PF zo3uM?FF6~319w>|CD{A6q0yR0#eNy(ZA3&^eb{x-v7hsK|Vhtl+34=(-wc z6qOEcvNN6^v4J+qkG)3|OZIF>LCtuMp%#jb zV6v_H=zUgCY*{%)sYkoU5ntML5D_-CKgb_mf(*p#dbh5GQa37d>XoH?jrWzhACSy> z3mj^enppFhjw)GB)I4K~Rt7%^z0xGUSbs03WgoH?`KfB3n}Yh*3jdi;?Iz(HQ@g@KS2Uk<%w+I*24TX6r^WR2h$JV zTbRVB9BOWiC8voMYHkvb(EJ#3L?eH`VUmg)o#CXDN~3YH@LZ2qfqIiF^rzTHBnMqk zgps+Cpo00uk~)i9(bGaK9*c@~TYgYq5eFF364)>A#FUhuF}&o~b;71L(AP|iESf9o zbySnv$y1>JK)amcx01dNoCYKoO$EDLrEn?@pT@Ym!6z-rn7Rw3pcRe{CkxJEDZ~s+ zu*2g&anO6i?!aawooXhaO%%%PO%BF`LtFgOGl*88$r_suJzRA;6L+{t0{0D7M+CTm zw$hL>u{lAEq_qV(J8crn!43^Eqml3O#Ip+Q9Lmc~QozMikxvzqS;W=_ApL=o06PRf zfDO`$E~yj|6=w3p=FkhyfRRhqCe(vT$)1xDPmy=T+FJ7#2%Twae$4yiO^WK|HhNf3 z#xCN&TOgl0Z48Qmg!zXd1cG%yC1tF8z@nZLs>%YF4}^=d+697qZ6Iqa8HGnY@*b+T zCZvmfC3KTw(<=nN~KcMH-*o2STEi%v2l32R}lRvC0W|3u!OR)D6*u#;DDi<H>JG8W^Q_T!%%&AJeB5{y9)q_GDE?TT2jPUv>xAgTTsLO_vE$=CHoujS$}5J5*t{bg=dP^5y;_+^@) zEhwbmlSxtReoLv$yYB{r)iM|6kqJ+*sSgv8Y3x+L2~dV~gynI9eezoLR0#zu{l7ta zN#Ng+PZ?N?=Ssof+kqsX?x4w~#ZO1~N!Cf`_0X9>o=B;T1NJvw>=%HSrwB<(rBW%j z2+B)o%6m&q`uGr!mSJW}ht>tt{=q{9%V6B+tX25D;xCFH2EpI!gCyIJ=fNao%>uo^ zkYa72SlQ(_hzBU7SgKZ}`nkYJwXQtPqIPr?(bCSUIQp9Xx+5?}+lo_|n3`1kEqqQKu}KBRc(>mv42z7B9Kjk$)}KgINwibI z*Ls#S{ftgd&uc4WX{t75#J#vw`lTU?4Nr?y4$4(`rLL+6Cp3(BIE)DD)N^n-(>t|Bj_`82fgD>aDTVYON9c}dp zwzqyMi5F@4yJaxF;As31$GYVIx-3`46}}Tk3__@ zb$tUj@$c_oKDGRE#(n`U+27ujEx#<4xuM~!vWm!?uc7SS0SJG{XMd&7uhL-HpEXn^ z7Zw&kNA;Y+KcNx5tB&}rhWaMt`_Vua0-E1ucPA&|P0OXA7mfBfAL^u-I~ei`SErL(`&H{B;ZQ~$l3#ORkc5#CpE`bA7bFnShcaQ!BcWCt-w_%Lg z9NBy-0~YM3naDCz_z&W%&gM3jj<~`2ueBXuGZm$PK<$kPBU&CUQNf3}?zV!8c&vBq zZ5l`xyx>-_pSXPDlq7x6j%;kE-)n^M;6Dl);Js$@t0P$E;cUp`#)f~4ZtVv#ps{-h zy|qOY%`RYZbDTIx-Edb!vIb}-uwpO?eKQ)yXz}Gv);am@nQZv`sTjQd43zzU3?qL5 z?#U7WPeVly+w`-(FBdKi$-4}Vok*CSpe1LwQZ|s|3pXesBPR**5dZQ4~V2223Ys`gD zXsk^^M|rF$gRk75>dgaW(G=)^`IP1D#eFQV(;!%4^#pS z^7cG;@H?b1S>f}Tc;5H#os38UHcN_!zUGpES0YCX%G=Vn9^?`Qw4>+Yf84r;DF8Ce z@o>f}C=QYX#!;qIv-Tmib@vL#dguUIfq$+N2Zrg|QLN~C?ktGe>m=3_%1M~XfLqW- zN`0&hX|fuf=xXYTA~RZuPnj-0LpGaOZF0xV=L@21;)LXgkQIi-qYJ99_Q3}t7g)$Q zl-DQ+fVK<`;H*NwbkfbgL31q;xB=ThIUDr;D;fFGYJZ^aRwKldwa5WULM#~NgfPy* zK}mi)pFJnu>qHamiV`$c$3RCzFYj?cf5;bd2r8?S(R!zi8t=(UP*2mw>Z2^m(@sa0 zp>B+-yk4LsNx7ujl4VDe2|f@o2O{wgfXMg@*p`+k5URV)!j7fkzzeccRVvQG7%rg7 z?oYapmz`EdggSM}+rc?dpc+ibI&3+?SlHMUC5iKzN+Ab>zZ&~usRG(Qf(?xhvK5Dw zuVbp_?5_myo_}IthM>AD`!*(qG-&XCM>iQnQ|LralQ2HEk>-YM5gUQ%&lM&GW_VXm z?NFZb0iuGg!ARn=nwky*Tp1`x*na?>2s{s07Sp0K7`C&!00MjZuhv`FymKHsXaRUK z*9h_50-P-rUKkebVE-7;yrjHv6tS3ZoYx-gwKWxBq~8QY4#U&Dqi2f3vK~fjIVD7Z z#y8F2OCmRBE!i5v8+}@U02)N z;P>GjT~XZTNa01T&Na5>#P7I^1x8bMsS(1CfFPeFFqU_@FBY&|Z?vzu2@3TGq4e+U zPnIqiXSNwIX1dFYCA)eM4*3a@g(AuXCgt6%`zif5+#MLpdX**Namf&UR*?Z-akqS> zRHP^<2q?7IX-$T5YR7vT2CwEDIOruc*m6OfiG$?ovxd=z?H(!4@@d$}nF9x(CI4() z5bVvXZ4^X>2Df@K^+Gf+Dcv>i4{p4XNa|rXdd*JLqn2>;+5Q-DLXsW1;u`rADX>vc zq!?JQdo(H;a@$!KC9^XQJjD&USf)r&6Geo{nN-q7zsRX@=aL67@{Sh`M=u5uCm>h} zoJq!Cw-_%(qywO6UUG>mdu$cK9Rlkh*I#=}CM~zwU3s?j@c^|QQnWs{?UB^z#V89p z4pNpoJMZf;e0*E^$HPn^uNg-P(0#DSx!cCK}0u{#rUxvPt^>}8~UrL`U{(}#R_7V zy=__noAn~gLGoo)r7nd1(60^=2z0MhPKg3cgmdZQ!!OoeY}sRrywO}?yuku&WRn>N zm(g3CUvhc?vKLDqQv^PtMIpxZ>{_xyz7Gl&*(3&`Z+abtPZUHrNW9aE%}Adokj)sI zi=}lhhzFyfl$zWGuy0UM=*Zmc*bSXoaQLP~p=LN)3BZT~;a&wm&>YNiGaw{nA4~EA zdjp<=S`alqI$)(xQq35=CrpyqBN%%nQhbbcLdXmc2MM&g8{*ZZ zM#KsV_-FBW*-bzl4q)1;WqSE4)_qf`bb&Vfj#sB*VgUIJQ6r?=gkr@yT4Ymb2mE!d zM}!SN90irdW&d4JI~b$5G_9_PV$nO6A|g!6nl}aRb`cv(4TYL4Ki|Z;i3>idc&uCo zk7aId_P*B;>(!QmfPZ^SMMHhI%On}}s94&-Dd(bq?gD>kD~lHKRZki!%B{2mH(xCu z-vVym9YjtPc93QG=PyS0;0U4QrWEWbC?QzX*HJ!)76k?_)2d<3rOOOOE2F3dN&^2) zEAHFAyagFUH{SUMFKtHo9g|3L$ViSB>?l2zTBc;I!N~qVBw_g1Cp^7kUADZZ=5v;v z2maqPg0TVzGl-;ObPO;0*4(QjF28=zD-Tfvn%tv%fC(PyK;|5lFsx(Um7j#S_2Knw zDVZTi>@^)fiKK|e1sFdxjxFSrV&#-BDlz)tMy(uLgXh3!IY>Xb^l<=JI9wcR_@})S z7TEA1DrqVmN>m64=TER#^8#`j2oPySsLyG`GbUXGT_=s{#8ueLq~tCup|ZlacPslv z>bvX`?+P!N!w@+_y3q<3bPNqmebAzy5C~OYkKzT$)i_A%%&iO`;ATUw_PIZBVd*ts z$+3a=%7(EMqFVCq7JA;8?30oxOSZ&(!PSiv=jo+xx|K1NGX9x)s3hYu%3A%pFZv69 z1c#Lg*MyvaL5??^HC1Y>XG*X<@uHih{zRuV#HoRL}|cbr>-YLCJ8@Se8i;eL?$nlpC5ox>yDf{|YupHC*93DzvFq;nVybYXpNmQ=M*PU%xj@fPY^ zmh$|v{;9mPbr{UR*QJ&NsyzS>iPAZRe!o&S_c3eqn0g(F#1tf*$)~Rae7fu5`yX&tHj=o@0Nq$@V<>MWyhr^ zoQk-)$+F!az*qL1DF9SAbp`sRhNsPTjXARCs_+8 zi_{LtX<6F$A{&`&`{*~UnHs_5j{lSQSsdZrjF5^be_`x|8$QvpnEJZ zpKOHm!X~JA%xi&eq&dk(aYHy<+49a`d1y0^?HERhLxj3Cal+HHyf+Q>8!m`0Nkg~s z6#M_x5kURDuM1V~q+9s7-J>5Rz95tU@Mk8&$g*03{_10YU~Vc=Dp_2B5*rJr?NLGS zqUVi4&2W39u5NQ7QgIR1hVK>wlyFQTMhH!oxs3 z|1f6|7-38K_(eHD%WPoA*V7Pl2jfHjJcxzwU`lZ6wQwki!1_dpgJddP{qf%ufd3OH zGI2px0{1Mz8Q+j>d=t3yv`7H;cmSP5JR`&D?jIxG-vf318X?SVkz`nD1S>Z3s0bGB zVk7K^E27KNybNc6DT13a*6U$2G~0jCechvN==PlV+gOsd&^26w%7x1a<<3pV0r4Kt zhYrm6HaszP9W*fqCmP7L6e)ZqHZy`Bd3Q_=xrpg9+Rmr2ucpRWUa+7(YbOhdfHedA zRK?qcc)9^%04Sot_$o4K#561yN2e=uUh_V8x-aQCQLxD6O;q99`zb4Z$*Gy7@)2Ck zTbOSr+!+%-zOsV~EJhJAfdM0-kgIx(-e&|h!;n`>K|SjtcwQqqsDb)LX{Y=_7YRr* zx`6;<{Fo7dw@u729Wr9|*Q+=xr0VN z3g`wJrkH~Q0XEZZzwCZ!Sug;{2o{P#Zev*3C@8|r!JL3U8l~hbb+sGj zT0#PNO?k7j;Ydj??K|K!uIxA_At@Di7G*3% z`$Vl_0L}aXnzcoc|J|SD;UvwAsM0dEnF`6B@-51+G9UubAep}|3I<8m0q1->fbOk! zmZAp^c+PtR#6Y$W?up*y6YE$kr7_z3z*0&6zzJhh#lku!ODSoYseI9$xg{%wWiLLG z>BoNzF+@NGT<~f>A%g%1=>LlYp@V7JMci+Aagq9^+gL)30o>w)gG%6O!%rO~>3x9+ zQ<6q1*PHWFl{gmQohqOH(L3|bx-PKF?hzAuKD_ro4U-g}@z#Y}e)1N{Lrl`X1Sqg- z3%3Y3_}5^+`naBRtcK#ep9kWzZ(#tb95 zDQIGjlYJ%GM>cS^*?qQOAoV7RS7h^>107Vjm{1*Lq7dgu9i^hfB`j~@i8vW0GC)}sM$L~e00^;F5CPel zA{7ZO6{GF~pc@o3H9!~Tg76LE^@$`1G+CISkVEdGLiGg>%|p19(jiqb@>N~CjnFmg zi#xr$lgjIkOU*-sVJFBC^D<%nUfq+^1SfV8@#06K74M|@??0r@WdkMg~% z{j6(;QS}BrA+PD3^zA(0O~imV<+NM+rj>EAB(7W`w{Ldq_`5-h(tXpG6xTO-uM~Og z>AFOXQD&;hwxoeVMks|wFQ0EBv?-+c@gEj_dBK3yEhHy|wGV2t;TyG7FZ3V^!HTLC zkxc?3=lXyw3>!fOYCD%pZ&?$S^mtB*{ySA;X&H?IOIPjq)))iJQW|pFGNT?8w;4E#Ol6?z)W6$uYDXOM|6Q&QQ!)v1_J+Z zftK1xtmuf5vm1^J9H4V*|1k&&0=8v;X?1j$rJ(hh2z{d@fl)i>z$tW~x8w(g=7Nu) ze~-;=<$JE1pFg^JF0N01H%Oq7KKS8hnZ4-HuLJ6`)6I=Lw=C)Oo?SyrOWImZ__sIlVW# zV_8g5Nrjn5v6-X++C659CqMv;CGf9vT<{!v7f9p_IzKqx|3y2-eIu{MkB7FbLx}sZ zKFcb=9u&=8cpjaqI)d*DaB|VnLyJvCI6f&#E=D*HzoV|2HoFHgF<#HGqA+M^H9Q0) zeO^l4nDH4aA6!!KAh|#j`rJiP%eKr|r8kqP0B2&eY%h}96ry{Q!1!0H$o%9!)6b@6 z$8^dW0@qmmJElJ{2NvdazIlMl0zF#M`7u|KwvR!sP74O;hEK_U1U5nacoW`(cDz)H z-ePfMf(d{3NCsJj9PpctGNqX&%(~u4z*jB9r2ELpS*>rOafY805wQlI5sE%;D9g~^J5{GT1gHB?Y6=i2FZq5Ji66kNflA)jQ1Kcs^X+>`rlH7u~b11*gJx z{40Ypf%Ww?7Iq8FC!EVbDbDQo)%%WqpPrZo;eWY~94=@k4!M1UD~bPwZdywlp9W*} zKQiyn7oAFgLA_cINR+zFX(=>HF{LMKy1!Vyz0Ual&EkvLsa0{rMNs zIV0$}yRCG0s@kjpD|@jDJ-`o@dUL#otnG?VI(0<}eRxAU?%XkE=<-Iiqe9jNmD~jK-t{8 z!l;XmoQ0c}5BENa8%Y1LG)%+^S_mwM&m#qFiOf`>jFgr3w~|zh4_L8wA=W`G3<5bB zP=e+(Zivh7$R-}d7IoPB4+I^q)xX}pknSZqN6F;zRd7(u`|tO&&iIcMLhve3^YEl2 z@)-);UtRW;MzY~p&Q%nTCYf3iFn>;xOA(c`q5C{S-*z}Gzc{^`tOa!>ObUrPk<*bn zt-2(ATZW_Dtci*Ry3M!)#F|Gt+}EV!PG@p^#8V#w~ms{@wi5l;2a3UK$D>TYle=;m8diZw?ZoLe@l(t$hbxI(`z^&j zhi99eJ>^5q0O{1zllR;1|Cr!+QLJ0@o)Rlnvt4>q-o+@ES{bkIdRzdYyQ8*Ngft%qM7LCc|&^3ZqSV^LnF} z>dTLFlvI!$aEp@ZIfKiRM&0ng3wnbyU%*)m3sH_oe}DQc9UKjvXxy_rX5M{qGqpIj ziii%ib4khgR5?e|sqCt~C-J-GV^aHb{SxCLHQD-ABXt}}<(k{+(%kR(S>JP0*fIE0 z6R=Xx>}@V(Vx)Nk4ekv>1qgXven0kh*FEZhr=S0^-L<1 zBiG{QZB6GjzqO(mf#>kqG2hg*ADdQ0?oSe(7DvA9z%Rqje#+k8$N4F(ETa1!Yp*ov zPryZmZ0V!s`#JMIsO?96j!R5bJa=*8&y9KZpk}jKj8)89#T(AHJNB$@&Epl-9`F3uTu3Af8`PX5JvsxjvPUl8p-w?@HAp!}Wh+;7suJ37I~&8Td3XBvfMh?=@^ z^TaRNF#`C>^~Duw<7C@+TZBWT|J>2`1B>o|gA|5FAbKSv>B#>tErFHL*YvVXb_|5S z$#OuOB#ESP5WwSuT5Vsye7&xoRkLqljF<|Qpo8%b|D2vKC-Bgx={(wtZ~ol*TV5aE znY97^DmSQ3pxER`(qvJ%W0}zy#+bQXz8i&&0(pM_cp7b0e%#Z&qJsg!jJn3ufRIl~ zo-KrrGE+2x*9KjhfEmBd?YYH%MV0C=*fIc1zi~VY0X?kyb;8c##FGwVoK;I3b6)44 z+S!S6Dx;lQ{RE!MlN8%?R}UP&lhl1qr6(P)lSBjEo{8}tdwMF1FSdO+CfvqWcDPsZ z-$*>~kneY{AXcjQc^@R!JFB1Sv$axu1GeAv{L;tWp`%z71?97SohN$;mDO#tCbBv7 z*IM@9ZN6{oS8X3VtE7}%KHkw4;laB|AgcU2R`FvrC)^`@f%`vZKmoll;1;~VLp5wZ z#bPeZkvvJbS2cV#-Hy(HM%z<6j}!et49wxCw}Xty74?n`v=xIh!-2#eq$WA~-ZrkE zT02_#tOBwvt{(@Y>E@D zd%g{MuxQrhEo9L~u3r&88JsuW(R*xa-fw*^4_nk)(fLZlgM*}d;kuKAm?L7BCgdX# zB{TTRt}ZI%Y?mDU%bYLFG7CN~(h|O-@|G>um24i)+UCza!V{4%Y}&Ft)rBd4)AUWJ z`|Vff<@V#4RN+nHp{EqDwLJbFi6GCnrg$UQu7(e;KmTJBOp*Z92-voaK|2#8*OxqB zhAcN!M&n|nka+R&Y8`w?D?zWQJM3i4vM>JC)38r@O8XxF8!3@VBpS?hm_ zzE*#El#@wJ?6RA|Pg-r0LXpC2dUAaOY{(Fk$nRcA&If@`=aTQ~WQJpJ+m74fD6e_C zb|22=8*-Y41EnaA!rVE8{zS!Eb_F{UAF1aFuvuK{XLsi|^Rtmx#N@53{GM@@t}jL6 z*b{4^(`CUHU(H$9-Ld!eiN(fXr>Y`{nq6*V6;BMi-%X-n$fOH++cSIaS!~)3XcUxR zr6z=!^L^c#JGc55E$OxPQ;>$~Ii+lrB_&s1P@kvukt`-G@c+Hp1g$`14otILXRk6b zW(FB6-zh~(kmdHb;X#sXQ33kx{0w;F+}y@Lr3}#U8OX`?Omb28W#S=4IG-;8PUzotqC#EYs;M|(%a~@hVQ=*C~q$f03Jat4dig*UffG#%HR7& z>|?Qg=a*v>!xdb+P_@O)tzBA~o26cOWhlEy2)!MMsy?`H{O!Jpx8VTCXoN zwv>0J({mRi?e{c(7Qc4SU$`Z^aqlJ9A8>Ul+=GavNjZa5q*Capy;c8|2gbrBD4LmaA`B2_BmfmmN5`A5RoKx%pkc8C%*_ z++21O{qJQ=!3}U7TqItXf=~vUJK@@2E?U$5ftkIq<9cG=|DK8HeD)?|Vpi5-AI*Ox z7QEPALyU8H-^+bs$vd^LUY+_vN9me1hUFeE^X#YO*ZP)gxWwqeWxAG zU%3&8pS3G3lslN4Lelg8ysA_C`B_U-;>g8idf~kU{^PaRs`Kt)&eVcY;xn5kv<*Qm z-S(;v(v=ZcTH1bT(p{2<>Uj8exy8acdcETv^S$Ts&E-`_;pd473=ZR}4Mc_eK0ZGs z#~qAhaRU7*x2^a4EY`*)2wwUfyPS))?pg76R=^|Dl1DsxEi2K^mwwNf=BUq9nNb&= zc>rlAO|Ng6eLqLfX9dfyri&le=;z?oopb_^Q+g>7t*stUC*UHrKeg|L?d)7N!bF8k zx8G0!_mwtQB5kJOZjg6u~?r=_}_ zOXQDS6fT?(AwbgEebL-j!wOC}?Lch&6;xm{3wul8>zdOYlB)XO>hmypWRpq8o7|kY zT84!*VYFl2o7?yVW;$M18kb#dGU;A?F@TMLV%jDzgk60?<$zrg!isx~@zkiUZkMA6 zay;yfbjjBoq?r{*T!a+|Ubb0PB{>^5HQUO(D_mOhqs6Ow&lwu6ug=sq^OJ&g0*_j4 zHV%?u7;e&y_zDkto^V&Ve5-EP{dDPjsdc_s&E-9F3*U*03qBu(b^FFGCyvRE4(3<1 z@miLbPjsevtJ4NgHJrt!>JL;dMAH9Gr1*V5c%mu|YZr?|(S?=w~L ze2DM)yC`D5Y7sZGqtLqNC!wD68w5O_2~JyVkBf~km75$y*EwYpiAJ5V$&Wk>USsLu zbPZ|_`b)Yi?x8w$sj1VeijCv{74oNcGS?9fuY0b{$G=-@jh^1)zzEsG4*OP*V4PoLP;*zfjS$H z@XWc12VS~fkxW4IvUQVKYl=htTFe$$qxuv>V!_X(_CBcK6LmPCFO+~S+=XEK7RiMP z_gw`W=XIq1sck}U(emVq!UemLygdFs)tPbGM14PzkYTT63bdU%@o(H(DrJ<%{~uMpRW)I6q(p5(7Qr{*~AYfP%l#<(8(cdM_j{0V+w_W5hC7UcCe8V;GO35TCyRwYhtKxOrJ`W1ND-U@V$L+XRtV#Wx zm%B8MD3LAaqnV%JN3d;`^l&j|G_tr|AFRt`)#?8I;y(Gpu5Qf0+Al@bSK$#SV?t zSv);`mn&il$&xKRT?~A}Cp2EV zTRRz%!5A+Ja@uYE{WNzmVHxuLME$^$_#MZpFEC`uL-ylns2{^f=9xl5x#7XSl~gJo z?Egw{mx5r=QBc0hdGQQncgAexc{Jg8WP?hCOH&x{0GGpw)gvd=JWP#(DzzdC+px|Q zpZUf*LnLhUM*9LEDZy#c!gCFtrumdR&HkQYw>2oe)bUE#Qu7D9|0r3R};&Q#gO??+^PKzIX@%+hqGc($EmJ*_GHjmvW1uY0AqiSgd%hm*0;b zyTAWI&{!YN|J$2hO@rbY{@ndJU*EF_-J$D~?rx}dmb!II?fq2a-pYY*ZhF)`qq^+U z_V>;!y2qjJHk7lcVana|-_N9#`nebSBFB!ib{5}Iim4%3JrEOg>Rid&{Vw`CV&czf zJw4b6igaBlqtZO9Ej*XL8mwYo-fx>JLV8&z`=08{AD<`4SS;sK261z&df#pd%-yfX zbw%`iU;7=+|K*F{)4&(j81xdv);ou}sO+`&Gcc_Y*sU|X*5^l>#l0pcUXf1E(-YUT zl_@6nl&NAue807&LtB4TjT>@1eu?bmbEr87s~pyFd0e(&Ym=~OQkJ#^MEpBPzwIW)l)Fn5t(n@g2NCRJX3 zZu@7C^ofKS?gUl@Pt&$|d;Aoc0wLZP9Gq-v2XE@K z^2Ti2CzVCoJ$1D+7JyZalUf$J-J2{Z8(!zW9ZkL8<9yd<*+CKBC*R_Ynua6Ro!(C@ zeaZEjryFbXB|K=B<^KI}`ct2Q2?3(E?}Fc}JtU5Yc5FU^`2yDBclk-q&(@>ORz!je z#MUVj7AxTt#45J5N0+6f?TFp&HN4VvoA&Z5axX*Z|D9ucU_^0|9?1$U+r8yc>agC% z_eimu?+q)F5OLdj0V?9>-&5C!K^xvC^^3wFdnI7kd<>KpbPj^^l0f;rbg3!ny1l2oIM%DIWQ(aH$geQYTxuNk)%qEg1#SC5te(@* zLk!fp8IQa}W1|Hf5Y4LUv)Pa_Skfj>($wY6S^HL2C23F6i5&Q4R+biUR=l5+HO7Mx za4kmm8|puF8{R*+-g10ST4g-HN7{D}=XucgVz8;313gDWyqnmh^k9c@7qV&0*4@(< zbtOxJM@av%*FZa()57+Hyu5MVY?_n%vPeP%{p>RGBHPQ~bj>YrNrS)xUDlnxvA?IF zj^03Qcx`uzn)`R*PDCg7@<5UR>Om|nVc5g+tvImAwboo2gxwKK6crM%>1HanWiixZ zLcZGn=C?ZcO#bfZ&U$CK-^S+^5rHf#W-DJg?wNM~VLz^iWrblk>{E}{bI)rUw2S^X zod;W|oqPtrJ(H`(PS)4nROvghC;A<|+wk)7yWQU2)Qbwqlz(PB^1QYSEO5BGJMR0v z*g3?()py}>X~oOyGpo9Ilp#>fQxwf4w`Zx%^C04R@O#uiPNb@{X2O1zi0`%q3Y7-S zZx{zmM$@S4;It*nebeC1i$UVp+3CjwE-sSpokz62Nz4fM?md*n?iRIe%9{nu&TCem z`x9Ecj`o_f(+vhB6I?+7{M{Na=GZEN`;fEaDACTHWne<3DBe! zMH~^Sn(w&flR+5Zn3*xFb=J0~)`fYj5_7|v7A-JZsXY$PFA196RvFlF(}qE4i}mV~ zo0FS9Cr_HBL_2~pw_Sz80>x2xK0XF|!=0_DFq=-O%{hxcku68zK+N&bCwyzocg9xV)VU*y@-Rg8ZYthr@v}HQDw>TAa}q^$_}8BJiUhcxQeq4xvQzEs+$tqGBJso z^fp=YXzfQjhl)s1*v+xwn;GmQ9>e3l@c8{&t5PMTN6DTM|j{lTj zs2>uxgUWv*BDavSu=P;P!HZ_GIV$lDhl4}ae^)wx_ocjQZ! z=FN?R*EZml$bjt*zW&bBxiHe9LV!Velc(-ti%9t^JgjA*7Q6bc=IbM`fl=I_+Au0n z+_~`^EUM`K?C|r76FKh)(h(jZZeI=?^L|%YhTokkN|qqm_Kt>b3@7o4C5uz8X@uP# zSN~>idM!;?QykA0bPJnbpfv6>|59MmijUKS*hZkpW;5e$3c2MDTj%f(+JWr;G1sE? z?>iwz{#g`rE?w}vj~|7YjLB&exJ$FC?x!d#@-JF?q1JL*KYF_*+^`WS^L4!+rmlsQ z<-x5dtA>VpH_B~3Ebw@<*NT5;#<)9cY;>sPAb#%iVAh1MO6Lq1N4`_(5y<%nB99c| zdMVH;R}tWR#HXI$I1Tj3l#aPizDYum-gmS`wHCuZD)Pv!Ebu$Q(M7okWq(+(R-(AY zHLqKjjwj9QXl;sGi|D+KS*8RZ8FnZL(NZZ$4iK!AnNbkx_St;^sck2>-3=)bs$cX} zs#4kN3(gbqa>DDT3WEz>DTHF zES7sS1)g^X^O(EypK#Wcw!w>Yo-fDwY6&|JKt@|p#KRFdgR1iafgslAhCg!k&z0fA z60^<94d;h_FqpFE(uk-#!~Xprn>`aM(9D_oo&9%Bwc~RX@!=hQQsRxx?u-6K{Iokx zf*Aj^X5T=mDz#ylZ~eKBQ^}p*tJ`aoH%Ds5} z;}1Mf!6P54f%$Q+lsa!8zmwM$EH(-Y3ef#u7eY0wO{$-RhQL8dw_`v7jZ4jog|dtZQ+vMC30Bv51<7#Roc@emuW6TG|9uW z(uXkLV~#>2@@$eLa#;!Ybh7*h7EJFC&it@hF3U0wgY{Fpssw0PF9SKl!3q#ua@*CD~wqL29-4lkFu2@W(i#j2&!eB31Bm0xb^C4K4ppSyUz zH-~axAm4bY(Z+>S$b*bh#yk<_t&f}C&XUwcPSjqw3x(Y_5V9{Q4QfolrQbxmlmK*@ zjB1#9M7EP)V{n~4N6=)$rYE5q!%BPE##nFu`*|ZKauEL@CCF3EzN-YSB5iJdbBtGw zFmqofbt5>;p|$BJCLzvo?#EAZ4&BZS-1!9}%n{m!`*ENiOY9+F(kp4Nvq7yr%zF$q5-&(egU zbB?TbrqKRdsLAR_H4Er`pKwzzvx|WRX7UwbSJH-*<=qNKn!vhr@E;EExGQc+^P;gm z7K$i!9v}sQVpa=|dBWk&ktgU)8GEy^Li%lU>Py&<8pNgL^ohIg;e}8y6tDS%pl|7^ zD))PanO7M0hf_zOi{18w6J++iF)iEoLdmj@d#XRrU}6^{wFj}@yR=ySgt%T{AisT< zgGoT=hA(@tOlWTOvl+=@LZlsNdLa?orN(ej&kr5^xVo!h+s`wmeum+DsOzpL-}5fx zsyh9jA5nIiNc*tc;k?6Kk)C%{X9@il^dr6By?YmSyIK=Y=knxU$e=e)cUD(aqz;qI z+EWcH>_}KG`>bL|+?2Ebe&D-py{)>TwbgEhnC+qEK*pC!zv)S42cc08w7~eC$u3~t z(OZ3T47>A_`>9U2zR|fL;dp;ON~d>K^Kvm-n_+Yd({x zD`Zs@Z;uZC0M?EUe&>xV$zgJbQZNFFE9Lt!piPL?XZ@aH%jYzS!0PK4Ko4u z^|DU^BjD5Xp&82-=Mz;glcqGk3_AXpcziyS}^>6LapY1UlCG&a#aNU_jD-F?H7=>ANvVGS-IZ=9kx0-L1U+C20Fj3H= z4r`rq`3`HW7kx|e8UY|}w4U-DU+Vp!nL zsSp~&1nDQ;9TKz!(2kV%k4jixe%*O_U&?kD>3z4@`{V^~kM7ygx=-=ndUwk}-FLIF zayDSG!m9~x4}$EWXAk`M=q7LbHhj|bJ*w9xMRRnqSzM+=-nYU(^;56A`**QHE>AkL zw4fRAOXq5etS*EjDi%o!3=>_=x=r3+hg!kXxRFqw2L|X4cG>8|M|i zKKWJ3*Q(1ynmiF?Oop(eo@9$*6lTN9XOnGf==Ip>q;@A6z*W5+Z8zE)pC-duz&TJ{lpQQFa-ObIMwNh;>)NGCI% zB<$l|c9(GD)rz(wuAHRQcV`z<+?#+rMk&Rq-rd<*V8Zj&$tyW=)|kGRPIqM$H5nwx zj1zNGM)0x7(unL0;t&=d#?sF6`~6Kjgh3}t!C=w}$_XyRuZEe4q>Jf7CJxpX?n$o* zHDd*%^x=4zTdx9G+Ja9kDgs9E;tLz;zU&XWqto(tYZmkWJ+yOrW@zx|#BmQs;rGv~#p8#vZnq~OTS{!TZQBD+dVP_-m;GcQ|Is&E?bvcm?j%-~bt@dY5}>04 zTBTif$Rr*~Ap>x+3&-ih4R!Kz8=ZI!Qj}956vi)PacAODPDEF`HU2iHA?iST)q}z{ zihnvTmshnn_F^25Q$^(!hF zCK~vVJ_ml1JSJDVBls!c2%c3Kpt%9E;YGp zMkJ1J6o7{z+@>cOn%bk)iU!Kp!30*KND=Nt;lhFs&5G@^XbFAAu#cBx)NUOn;DkhM=p29y*1fyM8$WsZ73si-AkR3hxESRjRdSZWYRyI zQ#9RB)&1H=pNTv8N?$%a=Fi_do;>e=t*U{qC5elLiTE=A!rb$BrgCHnKk@_HGLD~4ej9laKg&OeD?O$aJ%Q{l zy%E^?{$X@$lFL1G^ARKApeMonz06;9S3{BVl@mc95wZK_aguYgfZyI{CJWF&< zt1SGY9-FN%M6m5$fq*x398UGhPc24^m`5m95W) zDPr{DtE{Hfzd3i$W1&$ZKS&sc*xX~zZ$>8=Rd)<90)LYVluMym*f;*G2Dp+A~9_^ev37v=h1@bx5+s&kHK#4eG> z9awIjyuQ}cal@Be{i>!^-;atiZ>t#zKd=?rKX%htG(NW29c=JDue%#%CpfPRD7zn? zH&xHJ=Dv0+JZFWiJQ?Z0+d$xZDwXU)c8EtUP8miXu=?)4INvKxU@(D9jQpz|hLNND z4X$V5g$dnaCrO>y|>_nqtCrn%ay$kN%Zf zKj>2k|D#(mOoIowQTDCl;6wT>_?4p(3BqScJ1dpYy3jt0!foTJx5WC2n z4%Zwq*U|Pp&CLQf>f(D8V9>~t@{N=dH6e566e=@Tm~%AD4&Vvz?ubGIA9^S?*~Tp2 zcLsgoV*|DEQet;kt*B?TN4CM65uiL*C~T3JA^}nQY(C-0tVX$=q{h%6x!`UO*%#|r zy*ME`N6GC|IKr}mvCX%WX!9cspG_Ru>}1rnox4sP_f4#8F`(=&mX?OzvKK~>M8lDr z!U}SW9}wLyEq|tSCt}VUKwkIM2Ja^?^XNKh@c=JS1`C_}HqjelfpQl5;xSVJr~)D0 zKJ>@AZ(H1E_^{3H;#Xgv2dV9!&k$Mkh#Gq?W(tDfftQDG$+~zU0Ixk5BCeNoxCdZ9 zd;l=W4Y+S}J!g$I>^A1mMBwbpF2s+AO(rFPhPT#6Fsehs-9WyCKKSoi3hwN(mYd={ zz*7%v<4t!#g3{!70!|Bc6&lz?!7<~C}@9;vohTm_mMYL8`2aFYL( zwzf_gubn?Nr$!FIa0;$BAw#pGykaW^DW>y_%l#_^xTa|1r$1{iX8I!#gB?46o}L7f zueY0Rk$7to**NrP7(dQcF)?_$?R+FRT5E`uUCewL*jX+^r(vJrVBi~`+XqR3=xK`k zvXUuNIW*ihE7v_u(bdV1!`K*mNmW%q7eM;8)MHb`HZ_aPVE0ZgrRu?e#hAum zL7Z%Q0i1Z#JkRX8ee#p-TJyq26=?RS=XINAFw`!~qh#|N&r74$0hb@FaP7jyxJ#y^ zcIahS4fN%r2@}Q3u5s6ec0)=r9%k4?|K0T&4QaZ!JN12H$7KY=QS7z4?I?oYX%W= zn>fa}1WutpZVFRDNmhI|3p*qt!}>75ppHjR@RrhOHtv{sP7g-!ghi$f1d2S{}s@P;DjExnS`-_i8XD zF6%S$luU-q$sn-!$K?Gx-<|p2;93C&Y`T}Zjel2Nfr{T}!Si|9BzxO23iq z61iyZj-}7J>emF4#b$T-)qx%0PaodZ@=h!X6~ z%^K*9hPDse%Y!Yz(%5ct08Uyv0nd|@!}h!B1h&h`-U)IIo^H#$f#LcOaCsw_?W{ew zN2w13pI{8AmIhu)LW`=`q>$jsX zlZ|-I@lQtX!rbb1S<@N>ldO-zHf9pU`*9v6Tg6IeI-83z(a)Z20xM3?zl59Zz{Dk&I_Q=y|;L&V!Fig?g z4Yq59Bl$F_Mf059+cqo1aq#O7)vKOe$7B8yU(g`Hsg%!un~A`wWmh9; z9yaYhxA9%|TqinWwK+(lJ~4Sb4I6$%>aTpl1AKQ3QALqP*GykE@V;|V;mIdEF~Zr; zI)d)~g@ZCqtB0o-94)j7zF7}%FB4HH zN4S=nz3JiNy$vIUw*uDh!UAi~b*!hq1#j?{nn$>Dwz|DkihnlMPT73ENaYNT zywM7@bYmsf0k7J{RJW?cKZnm4)EIfZ!i^gqF?omL=CpZ9qKPPpe+{^hUEh-a^D|L2 z*9YF*0=Ek741qpeCqj-p zYZ8Z6vlRLLV`Fe%*0W_OX!CNfllgw(oM;R)EHBUciSl+l)m^uqAEtc3 z8qksH`GOQ5xa7naCK6q!#ZuuDu8yQ9QYHFgHWH(k|Amqfjf+`;^F31 zt}1B9RyIMd{-hqwEwEO9TL?iVY)^wGc05wbDVxaD{RuueI_i*^Y?9kx9#>l)-lZYm zAc?z=Q!mqZ0%An!w#jrj4;{^BcMmN+O+%2<@B;AY@DA((C*!SAG20{%4`fznYV!KW zZ}P>B%uJ*f|9Igk%-Ai?)Qz1nRop#E-wX=`iv*7A9oS@foL;u`yxaA&oGAz#;q{{0 zn&M7a<>{uv9CGTIgVk{-Qhn59A_D{&+!)`8j_d3R{C&21t>C6OZnpY4 zW8us$=m-c%qusC#oc#D};%4~k-aam-BVDFViZzysqKlU554`HhsR}gy3 zUazN>%Zj|F`v}?*kwMO9G3;|jTkz-h-KcglLVm?C?y`b_+-j}Ch4x~XjSA_)fi6@s zrA><08UE^Yn#LZ&1|87(#5M%?DSY~ zqaKCHNJ)K+w4(5&aqYdvpuVN`yymM?OOP^h3Q-bMW;I`XN4#kK4MAxaO>KwaVi`q0DxKfGaiqZ;_&Vv$yZ28My zAJn_nnJy)EoglvNHw;pvnkEv~pKfu4i-i|z$gk%Z0yo}EEs9ag;g~?b^o8U2|6l@( zhq8)`AGjhn+M=K2EWVT-{Gl!R$hxo7IPx8<>gkUO@cYqCTtheAx(2QkQg%lUKT^_4 z@6i`RLciDquQJCxd6rt7%}@F|B(|!A3~Dwflxnf|f&xpkj&ilq_ZOxuU$y7x22+!gts!x`Wf5j@mnlX}V~g4UL+jhe}CqUBBS z57we{Qr4$(g(BXn3C33aRB~g(jq^xP?|e5ABE2{c^&M*Ly!>*y6nm7T2EBMwzWzG0 z0dN6XhRyp2TB3w~Yt)pDBTAllt;yAt5a`CUXC?SLXWMHY?c>_qys!t>?R3n_mtZRE z>8cjqGvcOii#A&6QZdR6kpT*`ZQ)#Zzk+;H=9+d$bDvG4YxzbJ{lfXe9TyK_uM#)L zS==cjEHWB?HQ3WM+;TY3^K5S|U3Yb(4{>$>n8!sm>w3^z{-BBpmfV|41{hpJgl3+ zD&l&@e-u~>9mU$nsr>o-gz>s!Mn>eAu2h420Rx#EK^=6OKVa9D`l>)yWB=&i(YF=D zyF)MTzR)l3D)>3LrcY&uOIlXQ^247IgaYd@s&nkeV@wKrCT^-_NgLUP$sK_t)R8hJ zG#?B3TmViCRb(Djm=FI@gS7MNIOOCev*MIMMaAl^>Y9LV4czH-L7#<{`UL75uDXk5 zErj?cbiy~8VVf(in%_}2jrV4)+;Mu2^0$@pTL$J4*JfIQx9O-5j>3m$SKi;@s)n@k zD^u(iG*t+0oMA7?rHl~PE${A8v)EDWk0Y*-ivl!apOH1shy*KYdL94)b(mYjj1j@R zY-f$%DC%OiQZZrh?F64Q{bzWX+vV{08b_6VA|tqp(AHt2KiSu>`Ar5*eoRA~a_NFa zsi82%Tz*Wm-*Lwc_|O>C0?W^0=0&nha*~H?Wv zIl;hvfWnYRuG~EDheR&O08uMQv%D#6_k*7vP|Tdf2<`AuHP98!JnC=+M%ZrR@VB?5 z@54GpfAfEcQO~aJ;+WJpK^%t4_sUB!S5=qdPzk$=MCQwZFGB9%jv2W2SWUY!t6YqT zR*_A(-)+M*3vE9WcHj=};Y~OH3O6NxpI+EEY zZ?#+T?p)27D-2T;?*@nGLHY(xLc89Py7M;d9ddG6dZ-almRBX|(58&L0pf6dj-UwQ zlGif!${6YJyiCE%$eJW4qm1@V*u4b53*d`ZACQs%0dbL02^If~oU6poW5;Umatm=g zl~}6g`+~%P9Nvxm&%rcwp*G<*N}umOHrID1{92M_i>|de;`(H|=kAdv(2X#uFX-T5p0m1zkqB01&CTY}Eg@7xm`F zw)cAZA$FaEn5SswcI}0lG~`HU?~na)c~)8*O7GmdreR*Gk&z2Hw2`?q)klcJSLYH9_YCeUBJ|GK-nLw z5e0B9p~>BH%veOvJLV)PM#)o21;M8&FaU0)=)r4rHX*s zc=!iaTtaw#`6%~}CNv;vw@=U^>Y;q?$W*O1^V~aV?J=pz2KSe><~@DIC3`RJTEQOq zyY(%NeVoEG@WIL+9Yg}3uxqOB_by4A+&LiwTriWA?PchFLDN{ zn4PH3bg>zgB%Ar@$H+UGC1G-SXhQJ7FzI?fTbsq_CaKm7PmN>H`8LPYeXq;0Y^h!* zzDi6v#H&@gZMhp}r*+Ei^c79o)UD%I37#YXZDwXd=T8#;fr_e8$|` zvg>`@?MQ)Kr?egmRZSysS+bN=k0uv{3;KDVPi?wH`f0?pY=stKQ8Rp~V%k7vc|a~Y zg0dz@;ry)gc`RP$9`y=S*j#Os`Zip`b1>I9{ z15<)}Fq~KwA>a1;X$nPFp1;#QiN~eG<8oJgZ zkoe;Titm&LoLyJni?IpyML=_=Qvn?`e_`$lcq-7FD~)?bu$-9?*W#s_z^8mddF)9f zO$p~Cs#dg5iJnIj?g|@x(eK=88TV#Mr2}eGRuWxM?Wl+2HNend2`HkASY$AsA|WKf$dQGNn3mOd}fR7>>H!CZP?1 z{jhW;1Nq`we_L0qom?W?{!#<0yxYXG*o=9TTx0t}zN9*+uJuLZ&*u(KKI}r1zU_0X zpi=omH_C%%gXBtIX$%UXbj%L+~UbO|vqkD)ZHtSl*h(=XtCc;IU`TgIL>!^v9S5l{h!R~KT?KQ}F?A_4ZV z=-s!4cd4|u;fbLAg&VxlR*R5*wQ6g4w@1k~@{7l9ZkT{c)YZ zPgEUTM*N}GZtPaUjD6DYyly!(~Oiq0=)zWzGURf;1nrVrfC1ZqvQ>~EC zSOVc^>WOwwU$)d4Z zUWzWOMwh}!x?yP{R>oJKA_noqY`}VulnMX>j3l4(d?!}#kg4B zsl;YBrZ3266sB1l!p4AObQb1qYv8$$DYhed8#<#kbap(EM)fb+xdR<(^~3C-LC3CP zAz-(Q8(yWXTy#ohiidhJN);1LyOP@;`|>ylXvaut?U>m!pS({yBtqm`+54uitcZR+ zZ?J+80ei6Mx?8oqDUS_pP6n~$uiHO4){R_xuKQH{*w|b}Ex#~qSbBA~-oJWKZylQw zb0|db8(|JGhmhd@voaQQ@Z;Yu2}chs r>=}LF^r~i37n80WO$e7I?=GW`aslt| zlTEey&4jzNI%pOY)T=+Z*Pj%r2FSO*k0XSYVsf|JNvEg52f2cfC$2uQu{XVRaTV`U z-X6f1J!QY-q{vs=r)8_6-B!>^GxvB9Z^*besgb@HxW?th{gv=%T&76*iJ1E*Z*@Fb zE<-~T2UU3W>-{K(j0296r$lq%srpRZPtvp3jGozf9TdHuGVPK)6K0Z|v^_J_z(io{ zgp-55NO$$FCI_B+)-D#<_a_TIudnC1!0sZ!(sPYRH$ibTB|9}FbuswSsJaKr6xC^c z$pZXOIP%EpQqsIaSWN5_-i+B??3FGTn7BvZnv+=A-W}gA^^_DSX*AbRvl%<-Mriet z0%o7|y^X{NpyuOetlg(hV|)D;Q)hmZuGBq&L+7-Fxf?C}oi2so+tTG2kEE2=HaZIf z8~fdAlZP^qzGsbXrla2{hA%Wp5w!Bh)6`*`73D99fJL1fXsD(Av72q$kR+V(th1$M zj1YBW1+{8rPgqe1OIeXwXn`bL?PJ@qV^ehR?Y{daFHhQL*rG~-4YXbKLH09S&&boX zy|b`t)MB~r+F(TJS3Q(2fyV%QoV~XkW6>7fmpfGtMgO>V^TDrQ+S)f? z``b@h{{nNc%H&y{~W1Uv35KMRxVo8e1vsg$54CdlQo%x|M8be%S8! zFzZNEv2e*I-8*kIw+dBS>k8fDWha;zr5vJY7^w2ZzF$WvT66=2$-Ap-iPMdcWwr@REOP*ATZ<+O!j=71$TA=*bh=Fuu;v zAE=bjy=ajvsFm32CssbeKRLrZrS;~|Z+4?M>rD09@XntCa%f91rO4z!QJC}gl)3Zx z?CZjKb~LD~wrvwL8E@kEuIKyAB5Ax{f&}$Z{Kg2&f zh{Bb|-VWl>Kt01+TVdq5`%h*2L-*C=!fL~eo3Bxtor zIqg&*adVXlG)>zGjdn!&Y?Z;6>mGZe3v{Y~#irf_2V6k!RHsXpGpHi@zab&|#|MA0 zj5?2RE>&a6j5KSznq@=Kq{rbsrt10EF^-Ny-|4=nwhU57B|!&b>sW+t>SFk*V!1l9 z^~S4PyOTtS3#pR}x#))PI-KD=fv{k*CXxUUlHA&+=ri3rkary0A0>f!`^m$rIYcZv2n_3sU zNU~lPT(=8mHAESZGhSccJI5PW1*O&{>ssw zS3#f@mSgKNSL|;?PpJ%S`l4`(-F&Wq)S7klJ?rII&2zLPLotN|cE1(%=xVZl_P>p$ zv>VEuxJEDM?rd*!?j2s#zYBtpV-yrmcY1BL!hSko{vrnxZ+mz&i`}6O2D$=je0O>41BYy<5mS$b@jE^ zp_h%40Sdm08#U9?*H<4CKzY!h;teR{bqflORiVP9CtHotCzm=}f>QSrasGnOH-e5z z1XhZ>)EAuJ?_NC!|8E}iy_Gr=$1ImLjI(g|wa>#P@M`a;?3+CDv>!k5m5-8Qk_B1# z*86J}50xhm9jevKIG_s34I|RSF^B2@hKvDpf+dXL(Ok0Ir*%N!4=UBchdHDHt!=hv zyW^~8BVi`ao=*!qp-#%qsr z-qrX-`_(nclSc6YwrTDwE*PIbac*xLeVwGiuWO#%M2R+9$5(TCwcP+(^2b+e(f-2# zEh6%5Jf+<2i}A+pqG`{hDyLN+Ly|4l_44zF(jIodIcBVnNFcIWzZO{0V}HB%KzB>W zj${6WDxhb+6U&NhI?wJ3U3WeWS_vjPEUii4t6eXL&eR-MXq1?+Hv4YAeR|M`;Y5W1 z3Mp&^zs|h$ILhe9FO4rdW@MF+1q=SkNX>)9WZ+bqKFpeku{Yt)AZWaDX>H)i$* z=YFTLtJA&1^VPb-u%BCPsUgiO8gQ$`qh48$t@-}o-1^4yo$|vE6&+Z5b&t3K+-s%; z#!_vpykK?4dvijGimN6RDegH#)o4NIyuw1Ur>y0m8MMfORfYJT2)%z`BQj!4`0>iN z%|Ps~f%o%MZ;&d2n1H9*q1jPqdiJB;avFMUt@#3FVKn&X=JMt$RnZVM%60#6$#-|U z3KzH(X<5- zofHYh%v=i7A)AjZi%W)IlWaD&j8_aiv(PwmZ;y2}S=y(?CVSbmn5N-Q!CAs>GUOK& zxUivk=8wa&y*{g*FZC!j3~3QaNre`CIL8kO-@KPgpm z6Q-`nxbrMS=fGt;Kb{^#@aL0@MLuWIk~n?=1nIqzyOdLL3Umq`|3<4{7pT^Tj{ewC zm#G8ujMXzSOQJ1+bHsL%+Zv&C3y{vzTQa0QU6aU0x^r2gciI{{nR7JWjIceM4>ijf z`!2`1yo#SB;26)Hju1^wmIrsJ_r;ntr#y$Mvptcz?K)2YHgX^xHSaddKK_}OC+==g>Ym{I_kZncJYMqu?&V`#Ft!c6`3W~`66fRr@Rbwrb2=~NyMs=+iLE0ZYWx6 z3r0i9=c2M;_j=>PXan4`62khdIN@dPieSyoDF3ui4*AGKTbkG;y*lySbOWa7_ z_DS9u z)`WB&Av=4r%R#-T4zR)sS*_((0%MC9-t9hUlzu-{OI~iXJ)ZLt5Op^3@IF~G5)P7{ zi|GjMLdfdhVeG-i;UeKFqEsC>J(}~wUhEXYbDwPpM4f1k75hGl^yTq|U}#M2_J3S> z7?85-hSvIC*6>{3`BG3vspEf!p1@*WI*tNX7WQBh?J{N3EGj7Ii>1FsO8K4FUEhMs z?iuq_$EarFa|Y)$yGTyU2H*u{w|POSw1jRR|M`(d)FttX&U|j9b%nWbVt>|kf0I>Z z5}5pOjM0>L-Vv33#V+uTScc)mYNRh@chwMly27bV7O@7V~=2_J{ z8XLL?RhFuZR7bl^A&JaD^U&mHalY!W7vIt-N2EPp4NrfuxU%S%y%^I){%gzj5;RAYWlAqmB)-@n29aGBn!8UN)BPEmF>|Q(uL8(tL?%9oe zCE_AvJi3tN;Ynx5eltOMyt~pmouWw6^SIo-m+eBU3q_f*Zoim*4Z@_`3E$pU{S|xu zHwyjFCu^FQS~w?qO;zl>2A&@C+0j)nDogicw}jog?&TU>;i&b@0L;K}Jv(si*sK3d ztH)Mf9s0iU0(8z0KS71A=&LpiEzY@Y7HN#Ftv)aU=K?k>F@Zqz3 zeq%1>McfKX&ZG3crUSJyN>|AokVd22xc~L&(z-r0@`6Tc&fDql3;AFD1ox@^I)VK{ z3((mL3BJZAw1f-eVteZ~v5cS5&VlQu57s3cz3nSTEr5x(hTWgfy}#xNC!{elvmz7> z&fMy&?59M{6K9?`p-pZi8BD2|r|eo$JC_qQb+QdRSQ7pJnokE!@AF$ z+a)U2cWRJL<#Jqc`Ke0yIk+Rb9@j)SX7LpelUHZh|fXn>- zs|EU^;q=_mNqAb^*~q$#a?6?5Z>yYY?xkfzF)A-px&Hxie~Pl+T&dqP0`)R$E+t&& zoZ?m>$CJ_9h=BA{Ca0*|@K{cid)1R0T4!gHCEl`fpO$TCl{QzPs4uedcebNTd;lO= zEl{u{01Pb+<97=jp9Kec%UArPDF{A_s({N9Xo+@KNnK*U>=~7Ri~u~kwtig?Ec^P8 zmBc3Vb@H`O;ajKFkA3$)ddIrK-OKzKg?^YeKKsq2f4jkGfhcYTLgH)o`%C@NXLytb z3oXTqROaUC_!{%d2NC(PfPh;;ecp!}B(u!GMw$=5Hv3@IQ?Z3-uZEugBeF^EUBd@V z9Y*dPcLF{<^F=AX@H!e6kId?>Lpo>`U=Q zUvbQAC$a5O0$om9t7=jxzD8t;s&(x7@I#z5Nb)>7{HV#z|8})t=|>kaMAu($APkrO z6~B!HQ4a@!*Jn)|Df|PU1o8bVzx+F1f4SKY9xN&iI9Vz&wI9o{d{7cc;Z65NocQ_U zpC|uSilUm?GyDS;qNpFAO9f^0lCZR9pS9L?Kf0+Ulnr|D&#$sl{JoJuXp(mP#WhMZ z0l%Rg@`RO~$`NT!8rkQR>t1L0fc<9J9Ckw0QymgT?fzN5K0eKE3@<_V-)8~Pq`j9X zag|)G``O}`74+XQ_xDdP0eDUXfErGfZ+V8eSXCctGvzm-dgQA98gH)F^{gt67k~WN zT!wbqbt~O|EP>1_@u_ud6|>zZ6ZH;qQa=5T>d{yRT;`lhz8YO(=B1eZ*#5Pu40n2~ z-)*VK{|!cd|6~NjXPLxtW4nA@K-F9%ub!cXkTbYlVqpieQrYO}IAW&9%+>wrKgPK0 zu#QW=6c_$KFVdq1E;AI>xW{RntL%gPWrC%mf|a=I_y8iH6NE+fA9qS0GY-+sww^W(4heTpZXG{|OS;Zf;0Z!}g3OcoRtS zzY;{ldlfrOaEnUZr*=a)Dm2rbcWRey>PN|>mXt2TK8ZN1Nc+WAYFY&5U z;^Z71Ke5Uq-z1QkHO2IrBn0Vvo}~^>c_H|rRU#nb!S74?AN4~tL+NIP|Lq3L`Y~uj zDKv$JQvSxos!q4DIVVp4;(P{SKhB{YmgZ!^!Dq-TcQ3M%e;0sDUjmRRRu&yUGI6|b zo^{u+W$uBnvJ??eh(7smO!BWv&%dBUkoDc0Sx7sy$~=GSKWH;TG<|B;-`5tbh3 zgbzO5_<K`z|}ne_n5!57VT;t%t<6sEL)}+tms!ockDZx%=Qw*&xBw4qG* zLj2rfvyp71Y)RPXJb#R}Fy`65KQR%og5~d8-|vrisFdM-mA!Im_~9s<-*EI;ij}m( z#P5@ii3l+;82@H>c{`@%W!hj=k}mjbj*;oQ-_L!ICZWY={G^Sv|461eqK*qK-ofs8 ze%QW`{n)+#c0yZ$a&a>@(~`y`FQ$~Pff%V99jA19Dd#H z4o%t~3Cmi>-u%-c$^TmI_fHIgq6l0H`mjO#Pn#FW=P`0^xRvc|O!({i;{7>$Mz2~c ze%L1Q=7@_u{Qb56ZY~wl>;1aCJiTlEXzm`pmU$6&%7r&I#8=zw@QTl5Mset$PEdN$-`F&+ZGrxaS#qGEJ!t3G% z?6?b&JdYiKX6&h`xRl)^0*(T1T>B@-`n{1fu@c9dC8_r>70jq9qPp`{@RHaKHbNlx z1Mu$hoWOO#pEn-nWJzi#&j;T=RuljI)_;3%6^rW{BtToo%=d<%JlfZFjW#a=Q?4%8 z{pqv`m$H-HBY0%J18v@WKtfpr@c;i^%LBK8!1ZH>-%6n)4|DY9s-xTbaZ=GoQ}K)c zJY7Po-%nW_r3BsF{ri&t_K2>`q6^RV)e~;zQ$Jm3T0Oc7tK#_>?LB=ADo58JmBbYp zaT*W1tk5kUZGAMrU5n}ey4Fi}0su<(`1p>%a9WbcF^{~@P-$J8Q_K+l;aYHDpSkZ< z%u%rW5bqaZI^UfL*7{6Eq`&M5v8ejP|2Fg!@Bd+me4KDEJbSDDj*14ijDO%7R|VU- z+QnQ2G0-JbR0eykfSKr{G24R9zAZi=IqJ?ov5(OQqI_ll+XPSIo_rLLh?Fg66v`%o z`028qk^Nnx{oRLdDE-+g@veZ4Lq&|U)eC-Ik!C6#Xal2D{Wn6A&k)VXLD|F(Ei~5W%>l8d(vy0`i1tQa<7_?6ekivyc z0z|+@=)-^JH&+G~oH#|LI=uvNJ6lv{@51>Nk+X@XH_wa`<-cie3E#$LNH&u4aTO!E z>OTk$1Acd@e?x*O;%+QHz^$n)Qf01H0?QHGTp(C8%n?_yqcySXwBP4O*&rjhowJR) zC_c5!-*_YDruf+6Trr|3!I?4Zf0&WZ)1&n5#sn^ux?5|y5;H>R>@SIpeG}g* z!6bF#pHP%Rh_}(aX9O6~|1hu9EoSkT>(XP#k;`!15HTR97XN;oDJJlPhZ93L z`jm2cfFR&1$&+j}#pNov@8a^w@zRM$z5qKym z@Q^(zB@_^#%gXPr^N*Naew1IcXH;s<(3nB}FYZDzT;=1@cUKVqF{N%LGw>q@O&!Ii zZEgA&G-8?>C;D{N+*4%ff4*>zAp%moV*TIW7nfLZ65!H`dFx99JiI(wu8vOvUubJT zC!8ebK+2pG#;v4Y$`F}=O4~yvkWACk_qHNdy(f56Q1qBg<5Rg!$1*98Pq+7Z{ zx}|$i5s^k}Xq4`u8%997q?@6;V}>DS&NI08ckliC!d~aBv(6u9E&oH-l6mI3ulu?_ zaozVb*Z3jt9AMUoq1_KIzUX7U8LOBROs{yX$Ic@)*ZBUwguE0eLAZtR9aY7*{Om@u znA{Ema{E_q1h{ofJ@LcFmdCwf#h!od{)>R(Y1IT&{YB$idobI#-qm*I`K5Z(9DNMY z_s}o4=@YjUM+=#pUikAM1H@I(~SVk;+i$xhyn520C*t(!W{vi zr*(~IcI5;^xK1x6;GVIvu}_!MAaO_BJ>984`|{S7tf?-PlpUq7FBdqPhT0 z2h;xpOO@JQD!S1Fy44O?J7hM{f6?rN>y>|}1b4_CFerVqv1W1pER6Q5m!-u6IM+|l zf1u|llCQ`KDDI^k)~n=}6I>FQDe*s20KI2qf7&`&Q8OJjB zPMS3aQXosEw+!A2zUsRPVAmMtRr8_QNaC;!{B&**2@4PWQ&CR%G)U*L1+n1#cauh;Q{{T8rKIhub*#IZCx~ za^nn&X_LJcVq(X=_rVlLL0%k(WF$PcI{cX|q*jI1HMgpExGkn@RLi^YNZ3_e#+^mt zxNR(-!P_Vs6ssHcZ-3dUgo!S#E5AKiqiZj2dsuiIxoRXIos|O8Te@+%;$Sd$_v@}f zU&h5&i0l2It?&@P-w`8%nBW4tQj)`Q_`BMqHAC&uj93i>c-uj_g-xNbIhBuWB0UWK z>~%6|odmg)7S`2G!XebhX9eG=gy{T4@AMDjap@X1eH7^~-lBtn1a#!S?SAFG;!(a* zVA73FZVQ%2wGw6ek8S-Q`Q|gwBpOL8mtiqTJCJUn%r~FiY!W8q`ei9*+uBxlNQLx4 zq-Hc6DjjDSh$j+8L5-L}gipCm_bhk%S|nR+#pe}_3=*(WM=;BPNkHg8i93gdIc3=$A**jHZn0c>uNhP4n>DKUmIW3o66`qo4C#vw z%Sz=uueW^dn~8DRnlP<32CjfzYys;-IVM{ww}DX6>JQbc}*y*la0pJ8zaC zgloHAClN)v6lhCkAW@OD3Xz#|q~JoAXKyMb4fLY{hU>Z1+#SbZ(m8}fa=0|=u-;+T zLF{{2N|@R>d^S*NM_Ii&fNIPZKzlo#~RoELKyXjzLEf9{bLBH?GW!)DSnag zPMyra7ENBV% z^aT=p=`WYUsP}&Qm=$fiSW%Hb<4_pU0*4OUcj1wT{4cG&EhT%msK>=8+7V?RlV5hm zKF}a1V_}d?Wy7i$%hx4X+gQhAJ4GL8Gu^nn6^&PFmN3a2H~CO5N$ruy*;&w((;&IS z=oT8TV>MDncFY8s(#5M#=RKV9_K8059o6#|uW{BMQRtAOOJN4q_f=FL06~b9^xRPW zv!%nSv{g$Z=yow;-8i|?RTue{*n#=Ldma7A8qL|zFjMe20A}bikze;ZeXcD5K=MyR^hJ3 z2GWE{7EL^Dry?>qJkWoQkRvU%`7jVCj?+254n85^FnGk%F)I2gJ`c-O?7(ur3m@il zS8&p>l-VU}4tB1{>+Mr|(`R2wA`is`snf%nLQ7&oF&99GB?;kDSGOX#uCC*rq~mj5 zkXuWeq7>QGJglz&)Y}CnR*;0p*fUInM;24aEp-AwhkKVRA4P95Wo2b}tIfpR?cd4Bu zA}8fGe;|bT)f1VH!idnNW!tWqGYsKO^l{El(#C-@Eaa-1Auqkk82o z14BO){qn`2=pjf#|cF1xr>cUGR8`rR_`D9S7uq*!1L-Lc3@VZs+uVf<^#Z6UhkKz=fZZ7@p#Ojrv^ zTFyNQHddTL0HX*_gL3Mf@&fC9#VfOi9H>Y5B4<@jy1+&2yC|QodooVaF|sfWAHQ5} z)}X%*2+f1J07MUW9N;*&Uo8Q&&)ZmSqKApUT5PCa)o#(mdR`}?y6$7E!_7zJ6BR1x zCK%DZ?t|za-}tO9&eHLj_9@dGrDpME!-3N7=5#ZuE~1_c)h^Rz>Caf1&*wHx39!|Sy+=%dY{D|Zr7)D4s&Pc(!ZT7 z811N-w9Sb*+c8q;3ZP5QakwVprb?t=m)1#czZkH8cEBo8ohwWor=mM&_|wcz<`lja zG1u>jMu9uYU1t)OQb^v+`8gt`+12j19AJ?lRM*c)sPkD6# ztfV8~sNr{{f2$H+p8;*SYkI7)SVkX>z+Q`oQKUK0rFi8hx-}^=x+3cFJNeA?A1qtI+&C6ljM3hXnzU``DIMuy(dF+Fu!DY$ zfX04ksoAjk=1`88%ZTG8yh?5{T4+;~E(9;^Du`*7qK44#uUpwkLqR@4pHz-WI!ILs zDe|{~!kBz3WTq_`sXC&`bGB`km4epGOF`N09o@aR8*(KhT6r?3+{}Q}CXF9pJ5gq@VqOI1 z84EcZI3Oc^rOwOA+2>y5s>}W)b4+o)R2?S@mB;Cab6{X`V7#Weoa>LH{4SQiK*g<8hKUFWqi zdnGQ{@xr_o*k}e~BCk@%U2LsFS_&ROmlC}YDj#a31Nu>nK{ z=xT1UriE0of+o4aD3km|?^eEjq}hqfOA43<|1D`kYS-R?g`ioMEA()u*b~*U97_ z%`Ka2RG1q?3aOt5$tLnL=q~G{PqH?v+iZb444P}y+cSW8?dl6`m$`Z@iASI}BP-LJ zA8$oI^T@iAiD;~1Qb_q%3N>AoLIzMbNV#VR5-q4tv{M#aX4x;hSr9{-fZhy=+Kj#p zM{jc6Af0Fu)p07K8zcIAv$kgv%&agrFY1&>qwG|KpPtNb^GTMiQp@A4zinxG!;ZVp zlfJczO3;uNqs0k z#3IPaR;fPGGF4*+1z~rl2048O{!izpl{5ZS$A^ZNpf9kwXzG>?`e*(+UGx>Jx@m?%?NMzctw$#ox&$hsoc@$ z_=8@me*T$q37ueH7&voT8XwJNF<|U|Y7=QU@``)ht-0#Xg1FaSi+V|OsAp!t1uTy< zlN*9B5mTJNx2VCo6CxUKa;x7gPVO{-Z*NUXt+2>kKj#LN39#RjOJx(>5De{N97fdx zw9Dx-+Esi9kZ-}2E&g~sGmRo#^|<)wdI9FEt`A*&RqIQ?YB({) zy?5NpY7)A&E?j+gRt;>)hnfiPlZy8_7M=GqgJq4G@ZoB*aLqfk;o|m_M3kEaahq7D zl$k$wdwv@3pYQH$4e@ypPOH(|6$7{bci7e=B7x`UUo8aW9y)6S3VE9p!v1G91JoLi z<$@{Eo+`k5kEX+&gi*(TXy%QsOFgKg1OS%92J>jPSH*78*?w0?se;H8_&1msq_R8u z0hwJoCBNLgCkwX}tw%hUI;ER%t!c#1xE#F+Ud*gUXQ{eOmVbAh5fgKQ48>LT8 zFu4HBs=~dMB-hM`Zr_F1JFz_T(z_sa^4nDl$+Is;fkPj1GCl&&eh%rX4 zUp%8XzDx!JynX7cy{{z#K+!7ywPa$KE!m5?Mvkk;NfhOQ&sWpFK#@AXKOQ5k|Az+m zJNUm-@89?9zliAXjrY6k0I;X`chUV{TKn&o^Sj~xt5E*?Pru>DZ?J_iJpaFgn$=V{ zt!viA*)506Nw_yBudfyH5?xB@?qrJs(3eOV1AYDZFOw57qo2b&rh}PsOZBe$o#*!= z=@h<%iI6;j)a}>Q0ctRCqr}jZJ+Acr$p$M}*LD06e}}p{yp+pp2N;D2`0|$hZX` ztz1?!dn=Ay+6|Ek|{J4W|(Pn z{30&M@qf+E{KvomW@$7dfeD#UeL|;2hnwS~v~uxJ^A2@HP6pt)pOxkLl3lonBJHUit3Lif!GV zzA^E^!_OFzK9&vINgXE0Vpo5ZJe0m`K&ex|5IWgDV7mSJi%8e$j?WW}A?qNyKF$Bs zu8_zbxio4#`#sDAoKzVWS}B(>QnI3l-2qtP!^iPhc-;lX+`Q+T=5D8j)9Hh`c{1J% zfF4j*C!~8@Wb^xK*F=*q^K+-w zpD%<@%MSlok?A=g=;b|s#a8}#mCLY+vSebNER(nL48~Og2z*)g1J6XH9getoI=M!t z64Mu}S17(D=jB`D+$haBm9~-*6BC;Nb0)jJd3VTOsaA5}nO|b-q@4l8M57mV%FrKf z5~IsSLRvrhxC6IOgp`m@#s6lq=eCLRsz&B634TvJS0uWA|KrnoC-A0<^6s&2r~t~z zeIdkE1*Wdwz*?YH9zmDl(M#}#JDS@@E8~1xT}vGh;VdcM)I$^9TLbndtM*E|Y`x_k z8C&yb(uSB+%*a>N9d~*tyX``iy@g%So&|2A&QzD;P2chY>Ss@RKT^S;Gc(R++;xLkeTQ~Vt8 zZ?+pY(8dQnJo2MRKrpqKN-uWW9Ao3R)m|*xSQ65i@G(E?jw@Ykqm+=GtSB%kZXpU8 zDRtzTGABbKSCrOHq~;#k7az2SFbVk^`r@HUb@z6cW)T_A8?Rr?Q#hKmjr%~UlJq0R zZFqLbIIUQEde{`$)YE<(eX|s4)yw$VDY#*@-zUI1ZQoOlrc>8-_T^8mN6hFJR@Ie# zv*p=z<>>Q&AmF0IX^N4|vXblcwJNMhbgPR15w?A{Q8ZOpTcfDu=IQqh=E@ndf^i6} z!C~%t#w71}9$vEB{CGArd-v^OzrUC|(bx+(T)uXv!N;lPQ^=@pC3s3?ws*DEI+0oM zpKkLwOZ90o_F|$Villstwcd5IgFuoc1c$i!lQ)yLO}*6wn1I51ej0(*`5v2skkD%2 z&&)4VGabukJ9A+I#~EsLR6YNY|3Sld8tAh(sll|IgD3N&uz}9 z+M7#iBz^ZS3Tf8)2z!T;837mAk%2Y%i`xJvdw50>GpDn&)G6*Kk2Gz{RlA|PDJF$D z$9cgN1w1ud9K={tuAZEB-_OH3RM%ic*Z{v7->eegiwdeUK%#6~rp6 z0Ea=GT3T?J3ZrL;7#Re|v|k_T9I-BYeuRSa&y^&|33SU1Q?4sbs>^ZjfTzrr$KmS< zsTo=4Y%gd-C}_xIHfTfQIioc>7>&QXBy{8l+#i=dm9GuC>~P3{-Rt z45T(c-e01|Z^s6M16f1l>d?4$pP7wiPfVORZNDt3O9l~&rg+HCR2Lm8Ivo8;o83`t zwsPmv<1^tm0BiZ4WuMaZfJj90Mcu}n7QBVE;iaTfW&8r$&p#YQVM^h-DXi&Pz~vw^ zmlh@xIkU|8fzKQ1=dTY!sx~0uwN5$myN z?}xf;Q|Vuhn5p?hL+ggUY6dxalK!ET&wUAncc1S7S|C(^|;^vx@D>3dhy zc6t>4zF!rKwf->vp~qP9nwFTKKgMQd)~X24N^#c<6+Rhqs)I=u7VQG}empJt>~+#X z)?Xy-`S5|zlZh0gNq9BB9rVcS87*=~?0VcLzvI18fIlG~fuO6t5?Jl;f;5 zEfLHQHj(Wd1Nm}f$n|20#dDcn1(AkH`FfKy`eI862V;yfEL)^edydmjY#!Ge+*!VywFXKYexx?8O7x?G(= z*mG&eQkzG7rsag%fJ2puUY&gV?bKm;G+OaJs7I!Qum%>k%EjvODN@EA=e8}Lc71eB zwQLf*s@i(7E%3HJg_(%kL1pTdBK+{!=B2|ynd`yiE>m4P%)2K1!jRpcnQ~SnXDy!n zv8nT)x4h8x>c$;3#Of)-{}_hrpCaKf72k1(a$$}_A(!wD5mYQ_pAnnl@dmkP$ygp0#aNBg#?#61NrER6 z%%%CaJhvI#PwPqt_R1~Bw4ae5pI{GsH57Hv?t`;0Wu<&r8z%RB_WtinQEf6{X9Rr% z#$;xdXnKBCd>W1^&u6^b1DJ8oDTkBqy1>E5QhL!m9bjbl>=PRl6%R_g2}M7$t3OW` zJm{9eAX&~7&RizJV|C_-{}^APQy%toV50NnNj0f0jE5uf%k)Y|GxsO$pi&ObKaOD_ zFqel-VrY3tU6ZfREeQ(e2tmY@jHUd&vz;Zn9-D-koF-p1vZE?@X6Fo1?|?#r#YKre zHi@?BZM270#^&AaW{%z-H)nQU-Kv2+so(MDtYMrhgytv1x6E*`+j1)$_uH3GYhn3L=R@|&_|1n-bPBjWTY;#c?%8+M@m(RdYIdO~|` z3L>w3W}J0QhUwAWO4wQWY49ungmIZ)Rs)CB0Nz2UjNwCj-B&TPg9bh*C5!GMULKR+ zn0v=oM6Sn3qXcCG3*c)py7%R|ZTTq3R4&y9dZT^-vy8NDP`y{0VeliHYDr(a0zjLbm0bWr#MI!^((xweT zicxITCU{6Xm)!RAe;0Duc6m;YsrsKUnAVRgiI!kbLB)h@C~ETCK>aCBKlW|%laMLn z2?gPk)%$$s!9MMP0h!9i#VnX#v(DJBN(8SR zhL``qBI|G&oq5cWDI0UjtuGQgldnv-c6x|TJ$px95gu5)&8@`*AJ^vQU(ksYwojdc z4r8rNO;h^3m|z*!#1e6X+&Tj{q&^`h;DNjEF$3tRzRp?72f1AAKe$blB+xYzR)41j z=tk?ILb}11A2Q$r5dG6Y45GiSd6iR_3+r*$beyQ^x!rJz;*=)^o+g9` z6*T3#v(-y$%pT*gw&X}Pa)FLrb^VypK3=w@(Sxte zCevvO;hk|DO98%!FE{n^rA{Xbi&D!pbAy7t7KZ6ZR|1GLdLm>xer8dEU0HbPI(uw~ zBwgyxlUL*Lxs_Dh01zXAyN`Qm_5Z&g?jrt4o67Pj4;fS`p*0#Ktt8lZc2&Y|R~qi_ zL*k3b&H(Hu!^p(sYcJ-9EwH=WeA-dHk{HuloN$gunr`SMCYmKr4$ud3`?ZX76Ig@y zKx1@tY1$j9_-7#~VjWu7DSL(ZT&X$3e@vsK@xi%Y1EVdr{Lsvhq;2n4L>mnb_DMvR z|GW!;(`!CEC2f+?8#(LEzS)nRrceV`gGa0K@15=TRykHAa<2ZkL|13dhXf=8jF*9cFys+#vl` zDu6@qYCvE8iRr5mE!~~~M!e9Ax`0zpFTHzO&w+SR=&sEMjcY$3cigmS+zLBmg9rNk ziI}FynBlIpT(`0vk3#S-hMzB;9rOm*#Avz<8vqV}lC^D+6F@Zvc(?KZ5Kfou6!UNh z0?DtOnRo-S9J4+kA4H|w_qU+Kz2OtOJ>}j;ZK06|r~0%C!bod&P7eq$UmeKA5?RSLcZO z^b;AoDtrE3A`aoIIYvzzv~Sv9KL#ZZBa2f&QSj4{_=mM3$(Nt-4FCWO69%w^^SbZ+ z3hX{@OOHWOFR_@P3 zWxB>5`3|ez7E7_ROBfPvs2`vd0kPh1I2W%AdNp7nN4!BFbhOH#3_2HU#YZx_%JRov zUgeyrfb@8LF%os>(y`Rvmili?{kNt5uSxEI{`7mG;P*hm?}37gaSAwkT;SkcLM|u_ zjbkDim%&NRZk%~XOe{k&P+W{bG>VOcoi^&xs{sQFMg}?Qmn2M4kBvJuUVp?|ulL+4 zMT}c*l_M6E>{eP&#}RrfJcW+EWc#S*9@=w3` zZMgJbH}C^1ia~a~cOCC`dfMK>ds;ir2$$}r=;F9GQN(uhBX-DL+Djh(pMNwx;*TcZ zRGWMlnfwo(kNe4Fude+L!|yQs&W7Kc;>PdVa8WpahvEM}VF*>8n@f+Bk5HEH$n-|1 z@t_xqr1~6ZzrR49BVdEdq?(RRH|ZojU`!5weE4Sz{ND{eR0(LSmh-{o=FI`IBZ@PQ zzUBtwJ!-}3Z{QlqBtJieNIe8bRR%ptyE^F11{%=)adgRUsm7gA47wk_zrO};k6oe7 zJjDwnVt!l+1C;{5mhKfoJnTpoN;nXHEONH%o39OhA&fMx=9mHAfzg&SAVll;Umiyc z^Ee~HN1njr3|{d#%^MtgQGg(B4ih=gobV3kb2RNp5rNYGIWMVDZaJI*FAoV*Szb<< zajbRPnbAy?yKNzijZYCpW>=%!aONNv?~JDGC^2D;>5OK2U?&7RBLEEfbMaf)SD>Fe z@4kPz0l=4+7w}~lcS(rnGgi#BHK8YsY1mTSpEhH$KkOL=WgqhBB)gk_grA2sImH^c z$G*TJ;1w+~?r`{_i|@Rl$I3I~wR0CXi!5%=P%9vn$UneEogIh_G>GzG22Rd?Lg$^2 zci%gBPxH5$#44i^Hs0EM| z{lbL2wtoI|?ABw7z(*(LglfO8C679On_f#{Ta)u`%BjoSqMK1)HScyNTJOa7SjO(~;pRs6aPgGCUc-N{qjA)uk7wYhlzo$vt zoIWkxJlPhIxE1&4KApVG2xFS4KXyj%aC!Q75+sdwg|)bRNG+^(VJ=W>l#FjF(PbhB za7?Un)Ryk4zvDxuM=q|MpPCG2GP7GA;_jnPDYb+cpE)d2F=(YrvLiaon9_~fwjIVz zRMYJj$d(2=373A(iL%?F+SqNUR5YviaVz0Z%+6|h?G=w~tHfCvsy+(%Bvg7a`C{RL zk@C#=R|K4DcZUif2g{B6r7kN!-aa;ExGE9nGO|o&zo$*ne7$ph;$3#j39tggLbhuk zIc4nZtW#1#&xxpCbvPKw3 zedGV6PygJ(=`p~Do&Uu^tS@6A^HqctpQK<7^+`JrhMS}zlFHGao|&|VH)S#DY42NvHkuUcDfFd)M3}42=~cTL3*}VTNR%N-w&R1`n6;k~PT8G#E}P;q;Jo>z3=EZ*+@>qzMaJ}EQoEUc9J zkwj>s1&fQw&(vtnngT!AJ><#BMr6ie3k|iSCt^F&4;S1iuv5p%q3^@wK>QI_<wO35HR{c*mH(kwLu4Z)=XI`%ecEzs!Zv}k@gQDrzo-H}nZaVz!8t;tidZAXb9+2S za(6vgbD+Y;jO1YRS@nEim(E?|(gDTt=SOU@pBhk}#2XEl zF=}3UKY#ns-Jj)M)IQp7ihm4y|H+pmaA*EiFt-gwjP{OsPH{z#7jnNn)o-U3K|eN5 zl}fY=W;q?Io}A@dVfog zHEOmotSba>oz}7Q0S_q9X=|b_#KhUfB;P62f*-jU8OgPNoK~`wOLu;#$0Fnjzs2BT zIA@%~KIDD-{DfwErah9bvfINuFwx%hy>ZCF3O$Xqnv2*S!r*%pQE&I^4!psvXdQlj z2^K;|FHi}LCo1;faM#S_6rQv%xl++9M0EW;>NJ-H&Z6Hq#qfq;_J0GEfAk-QM?jHz z_Dg}dc~!i?$&`G>TZfwFjVEJ4PL0> ziPe%vUq>8=3|zTHTU|VR!LP<;3z*6spLjsstMpiWvI4)ohi5pF12_4udQ30`>5-)4 zI?cR)x&x&%IuQ0!?iJWcOowc9(K{HrkA_Y3D{ge2l(=m1^Z2YJg=8q~NJQ2*d~+eS z2JS3tINP^Et}e9fujfI6Y7`!=ONH&e1v-#%({aCe?LD@U!byW4QOf1gZ+JeXxE&OX z?|;9BUu&!3rkHZjluEcbs_(PcWzyePiWdV#gWBt>1T<$KSQ1OfuMF6r19JBq>#mfX za;SlP(KyZPXK@9PWWc3BpslId7r%n@BeuTNuHSV$`GG?QBv(Vq+>iT9WrOmO_Y>96 zm6}BR^t=~CYbl1B*Gh)B{jl%~Bl8MtUUq_+?t9)+?$c9fA1hvnG^((4hXCWBv};Uh zy<9wesg0x?=Bbp3vnRyhocMg9e&m_uT@>(FE4Hz`rd>`yG1Wx+{oSZIT<&teIgD%tFVZMR(TbSkb?7u zPqScS^)0?U>f3+2(q%VB?f!(IpI4W5^yA{6_$vuKBIUBAB2N^+9h$~IIateLN(cE) zMt%ca@%B-xZlmB&3YAiK{hwyb9x%6A-onM-JdGvHOfb8QCqh=|7vZdT$DXC zI566Z6v46D(Zvb@OznQjPJUPZCoFv}y#j+2q90OzL-> zgMoKZf;BQ!b4OzApwsKEgVcibO0NQ_PdIE&Zdt&DkGnlsdd1y7Q;R;lY8kOH%NX`c z3wF3-84b4+M;AAS^)cH4dTZ7%qwUy$Tc z7X<&)ZOx@F@E3Lco7?7!7BmF6DEIP%T|-|^R2zXOm2zIYyVu#dn$;bI+=ZX*rbV%6 z_k0$+Ng)1y;g;u#CJCo`>|K<3qX2;OC?sav7jfwQZ<9yt6SDHS=s(pIl)xcqw>#vj zs}2C*X8>8yJ>aScPvA-;P~)nS08fc&Pkb=B@R`3#LwY7~4cNA^tnN#p?F^+tpVgl` zy)+a4CiCeQ1AQb4LO1YPuu$(W2L<2lWdKGcS5P^d7;6F)t>a6JR#sE9V_Hg-a#GjV z;WSVWB$VYfq*uft^}vTN@og%xGNhqk-AiOIepRTyT1D%KMsHYbFt?YGF7bFK1>U*D zwPBHOqrCNockRgm=mvBh;P4Ln9dYDrRAW+eD?=MH3$!wow%$kKR(-K-N}bX5V}W_^ z?r}@&dgW|R*Y>qT;S*07v@@gbPF5cXknn&?9ODiv0pu)K-C#>j4M}Vg>+G@Fm2+VS+^4w24!A&*LJseY`;w;#Nh9XpGx0w8 z`?GO(t;~ynn{@OqUfjIjo5wFNb)LfX$_+xIv-gsiHnBKSi$-v$Y91@3+(r$er`j4O zb5;WDZI!?KaulHa8z%hQ7x1np%Q$e zWN-!enXSUOFPXy_toT_9{|fctaBW!1VX1S?%g0s!L#5y9~Ge$eeqlg zo?a2G{Ku&uwO*kY!bB1i?@`?opqQGz*9m`x!!_ax+lc{ikiHSQO$D&AR>!`D2&g&v zk*I?CN2`~`$4hAJD?h}^x!DUacpymVjausnh94JbO>clg+l}dYeNZo}vn-faZluFP zy0z`(dA1{eMuOF?JPO0|tlVS4PJ`aVT z?(}F=iy#ms-PL-|42sE5Y0oel_UNPF1v z(kv_HPW>!X^L<`A5n>_=l`X#(#$yYFGklZe^vBKa}t+JvrpJR~3-@F}~Md-l-)moAv zZ!F(rQW|}qdf^@vG*Sh3za@y+nbkpDao5<#`zu(xYw&~TZe=% zfan2PoY=7Ceq$ecGUJ!jl^;Y@uL$U0PlJekR|rVTy+5|m(4K-eeZJdA@ciz|76Yo=eGEcro}Xno!0oGxmRbZm-1*I z9XERA%#TiMQ>}L?Ry%`n+j@ zhiBBGLwhgK_My7*YzO2kW8aubo6$Z8o29s&61X2u?;k2S0XNIOE*>B(@kHQzJRrNG zH3Lc~6r)kZll~GFS+4qItku|OwXQo)d*Wwoz17S}GrH31jpu(@sD)nM+c3X<@56*y zH;!M8U;DDgk1>JKpaXy|gcC8xOEn0(KN*yYGe+QhPT>}v!pZ4j-j$~?^!4M9SFaQB zVk~@|PngC)$_Tslt6IYD)@ilqbz>GVpWKrH_(WK zFKfh9g66@`9{n$;{C}Q{DjGx1Y%b!s{P?ms0{RBE?9~}?N0J5zm)PV*8LYl>N!kCq z$+z{!{VSL9kH23_0X3kX`=SQ0Tvh|#?|x-#`uJbMpn?fQ*)KIU=yw<}uEg&!TxjdR z!|*!{ziR`~)qa=Ai{}1248OzhKS=%ernnfR{J$55P@97v_cV{xTMz=umjs|R zDgpxy%m6`D0G)2}AU(D`-1l0f_BTseGlv;5V;h13rrASvbv4?f^%5c^A|PqwzMFK3 zRM)T$qnIppXjhV5D-yl-J4&oZ5g4YLVF#sKI_OgugCiSHx2+z_#ZNI36EQ5px2KRv zUMFif7Uu@o$nDx`P~1_j@Is^l{0k{q)fvWP)EZo3)FzMIEbYrMTA?U0IjB?%qomiX zixoVc^>)xKms!d$#p#@D_Me`y?{)P$+iL`AZdwHq^`uWX7wE{B!V;)^5=4xLZj%ma z+giW%%}1VC0Rtc3zQ}~7xpCL!fZ{U*T(=@*SgP4emIq{*I?{9}I>d+xqwCxq=~h4^ z`L5~u(5%OvH!kxB-ao_O>K_?=u?gJ&-bCS9X&gLIz&m$gT;;Fn94!c%0q>yBJH5@~{->O9kL&^$d7L8`T# zkC8QKG9V9D)jE03yxX z4|(aMSqo^$8papFbo!-cB4RO5J@k9|5SzxbJW=fq!^1rM^KE7xW9407Rwl<8AGs*S zyGQx@l`mKzn(FXPWWRLYj)3E`F~i&qV${L-KH=+v39g2Xh6X;X_@QhiRnQr5+o{`# zm^h12FJ0HNbnzPtTvd!X`jD@$_|wMExfg5w^^x>1=-1;!&_=HEsSZCb8yR^gkD(&Z z04gG3wlw}f^m)FWt>^B;e@y_lRk-Kl;ESe^Gm)?x16SU9Ynnp&HpY@aqE{4Pg}O`( zDywy43g3tt?Q9Z{P0p6(v=>wFMH^X#Me(ksM;f=mxgvmRpvuDqzo4b^(V2)QzdrD! zZ9N}@_$`lpi?#lRy~Ved*o_B$BIHP!Q{Fklg-?BATltE8qgEpwLu-)ER=k7^mn1C* z$L5pmc|?j2ioq;VQw5OyeD>?3`b$@+2O4y(crsso3W&_?;5X}s>eXANcV-0apf?~; zY1Cj)mB78jWd*P|y;u;Ty{JB@Ts+y+HgR*ZR>8JvPAf;dvf{Ks`mjA|#Vf5+qd2w1 ze6AL_&6T#&HKUYlp@+w;Hub0T=!krv=)to0lTjVdQdA6}n{up;R@TyJoZW9~UY66B zOct!3Ca2t~J5Ao>^IFMDfp<`c9Ukf;jzR@UJF?C036KsUQrMSF;zWGDG(Q3hj-7xQ z6se%vJnrjE1Njyo>e>FdeVKCf6Eo)Y07KBtv%NTekyenzt|g%-17=qI`Ljf1qc3lh z==b>tm3JsY_ioBIw)t!p_YQnJ_uUql9<3_??&IEKg<64G@CDj7%Un;}1Q8KsHm8yf z`5b*usZtw)nc)u<}P^?Ak;o5@gP(CSZAZ_{y7A*>QcwctiY zGjMjeV5+VY3;m8f0DNc}E`o@^za1ZGiy$sPO$C@4ofE1UahzK+=8mr$kuaqoJzbUze>u z=*5mm*^_Kd72A=@iD+3uzr|#?CoO|<5lFGzoN(IrCa+Krb?B%#d{sDCu{p{b0Ws46$K^7Y1j7?N2vPA(aolO(lhMWyQb~_#~ zagSc?P3jV2T+J^H@*f;1TO((;UcGNyXOaisPQvdb5n5-cK8K3~FYa`*DX@&`Xi=B>$5 z-MAh4oK4Bjam%jKi=opVa=oL$)z{!iLSHDBMX|hCGby7 z5*VpTu>h_~e}zK?T)-VJ!B4lHrszYa)F$Qk>4zT%qyDGkIO`LONklIrxqcuKIsZ!{ zqAPaac9FYEmy@b%C=NY7ER$MTN~?A(n6lt=l&+PikuIF7E|_xdr78EWarpwcz0Ey2 zr}RT+p&nT9Et&)#-k^@&X?)*LA`3CWAdl>+@lD~5`5%W8=MmmRKO0Mg=Ru$I3hU(y zt5KiY5lSgpK7HH!wT|*ov`X%4)$OXRxC0V&)Jif53!+?h^Y-q+`cY2XX(xWItIVkI zd46AN=-e8tG{Ny?+l^SVdh{Tud-H|w+6du=c*3sJUAci5t1xy zS*wszxUV=#WD8s?R&F2qFh2RA=oaY%ZtHj10(ebfCtmBQk1au98s+s~Ux*sB%lS%c zXe74{Kb>R4H=>H+`5G-_^x|M&y2sJRR22XGDlo1P={h5|`9={zl1?LdypVCUj{8Ss z617h}7w)q4Or7TI1|3brgY?AeUgs4q!O(_{7vw@2pvUHAE zsjfiv&7@RR!4%BiuIG#=QD@(CjSlu&{=e9J&!{H5u50wRVgpnZLK$>)^Q4x?X zARsj=O+bPWLg-OZklsP*Nbk}K5TepsK&q4wdLVQ{2rcBh@P6O-`_A*=bAFsL&NyS7 z`=2toNF$r`R>%3v4}DQLF%J3Qq4L!rtI76+%E646MyS(W!DxVKtB-4zNlvDylPcG72y&vw&2}59cY$w|X<0b(^ly3R6rSm) zdvl)?7>=Z!e3u9o_~9_JQ&6+}o#|}MU74dzp&BM{up4jnodQa`O@9qBrzTM_bbhQO zl}MSBN$ixMMOp8aN(j}hTZPUgFLIsR;UP|^Z^KJbyMKQgnxlc~x<$Z@BT>`;z zE%}D0jZf?;Qib%rxT5(6o$U4y8)N~4I^TT5(gBV6AN3!@%>g~=_)7bVUXg0mnot48 zrH$FW?JK1#Y(QJhHa2+2STA%Kxkjv`V18M1nG-k`fa^`X3AHrf3se-R-%bJEe9)CP z9{sY9*Iw@@;XH|^Kt+)&>M#yn#C72)M?#zuy|uTlyVAH+8?C@8<)5rhVw3 z7g_CtYP2bQy^3ji%I&l-_g!=a!l{ABAp5T8R#@rT3E`pnk5>uWQst2;PuDIM;x_kp zr@wOE%vNS%i~81Gq--#>^ihz29Jug;aQ9shwN`k_(hWYqKd9F%1zQ}z%3)Ma7>V0| zF&o8yiQq~k+u?^71`A+{{?xmReUUN_W;TNvc?s?Wn`PGHi6io#_V~r^24i+;S&%UH z&UilECe$==_}HmTbGee9>~Fo_u5+5YOUtk;apNAK86JRgv+YM{(jAf^5Ywburn{4B zlUq3!Y1SR?=DEKmvVbmH|eDOBI}@q<=o>uD(961d35_XMeP0-k`aLA+)h zQM6R;j8vEgm^>Y0$9Q3PmJBc#bfjJ|jrC8~?!2md8pc?^af+Ux z5xtc%>(bU|fbB|Y$ZU;FiUeljw2`vVvI=oxDxOT2yV3E+=hwPK)0$LwH~rm09@V5q zYRM1DB`pAYt7uh>i5N8Gmk!)ETbUZ~cx>;GgZS5Rj)TOP=l4Opewe=hfNGgbmMp5< zIRl78zqUwLlj~>_p(~(|9U0u&XX)p2$ab4cBYdW8wo^4d>l2&HUuuoMBFZTBtu>5$ z?@GI5z|t-n*Vu`hEJf|{MO=*13%H&~wq|-G0(Q9E4^XBb_tA}BdNSwr5!)H1rO%Tu zXceudEF-Q+ZK=RW^xmH%CaHpRD_(sbkTT9u}%-1XsSv!f~!#&0-K! zUU(`k1S>^-7Z`jSb~B=yM@lWG1MrCA;ml%;?x&o#eQScfoDwpv9g4VLwxgb90qULm znTStfxfRTUon~{4qV&CUziq=_08HoT-NrmXJSZd5Y>)eFJ2fYEDwAILl4L|J!ynlst672Mld*_pTI%cyg?U%;-l zvVc%?odZqVkWZzRd0XOAjp^}&GkE6aGkUdU0i#13X5=XdEaa)X+vNKe52hbDZq$$Z z4HX89u>^AcQV-GpZG?=L8Tkd_>z(J9W!~y)$#h=0nadp-5hwjTf&Wwdc~9qw)b@J& z{FwrLklT-Pe6VnXI<7!h7 z^X*Cr$fwMdlqPm5r?62NzM$~sMefJf3RY8Ax>wOYMt2cQ<&ui5%;})Il+~Upm7Xt( z9&(ny>h1|YpL}97wwG#AsF&w4v*s-q(fpi?Ax_wCpvFlpt)~1DkT#wm;+%w%^!@d~ zg)u{=pfG5clyPD0y3>MSFxsOp!aek;waLrV*Kdz~M}FEZX7iW1tJ&`8vR?OrD0J6A zut-}25X{%3?s<+>u;z|>GLmndoBK5&|1B9A#c_i`?;E7~~?C>a7sk9lqp;Ol*vTpSabS1l6IR zjN?bAOl;$EZn$U7KGGsDTe2I9YPf6f3{t?(*O|lO)!pyKo%sUMHe2C`J83xK6r4kfi%9kgBdK<$%@w&rh+7ucK@t<#^*EFPmpPvV zkFg;gVgR8BWvmPgJga0$k|GKqNfqx~z8tl!^c@yHsNzNyU!x5WdN|$F^n{;)aP_(BB4mHV>Gmp&1?%R!~39IDr!h{v^|UR ziJBX-=5E=<(z6xAf|U_>nwD+WGAx=OT1#=cGTr8t)Sq`0&_0Gqt#mm<2XWmkQa$$G z7N&Py&3zUuN2?m>K1tZi)J#6CQX==SX)}RMbd1Vt2QTCksz&>8iW3}`=5Xj7<=6j6 z1OB({X>a9#UxzyU?nwtL{ZSH8`x#KL{u>Huaf*JYXw#3*^T9xYA*)oA-k=%xpyZ=# zsl;&<`KFzw6XRD0bWM(lNrlP!QESjP;LO+7{q~qedlRj9iiIg)A@-Y^t~_95 zi5ODSQ7jCcXV(8>ZYcH<~^i_}~}MG7!mRc4a;$wsuQrXtgu* zb>0GE`=V9^lOGZy#*I#a5VNhgEtNk+O%{yyT3)0S8Aj`19rl!i{ z0BEhM9ya$2JM>{a0km-eOi8?MTd71jS;<~JCR8;Qq9j82oMl2L4dP;&DkVs|hwIM+ zfr9$M3*eoGoZx?w;OtWfm?fYR^j@6&KdcU@wV!j-V)kbgje7#Kn#r=-vnY0x z^98*&J$v4~;Np^+H`JFk74B{2{UK`JrUJw9BGBGq{A@%#Ph!Ef%USqNW3b?_}j>-N;^6Qt;>bTVhM* z)|*RMJ{tN(_yTnqv;(1mA>LFgUndL|Q|zpnG+iV~jk!e@!)44sD;d!u+B|Rj0NA?3 z69xvZjwd-60pRjgWtK;%_xg0?>4AwgN_!%D&o=l;zN_Nhr5yy&a^NH8;9V(8vUu?I zC!!ziJWr#u_vK6B`EVgbY6}^$H4)QOGnfX^Jbf-1QP);Z>8kU_ojtu_~gA84`|4o?ly4sa***C^f6eDsm_GGBj` zP@;Kq&+b;NMV2_i(cFHd+NjUbG+qMYxKI5C#nDO1M44gkNmprq?Z3GFJdNjq0#HZX$(SUZ$_i$YQ1jP` zed+}mNajGbzO_R<8(M^Wip}Zslum@)l&vMribrus;uhhhTTxbhQ+z@XV`# zLOi=Vx1Z3mKEoGnBdg#mD@c~8X{VU&oWS*Fq2|nP$~k;*#uNr<P{Q*1!R8-J5=+ z#_?4_rmWxl3hX8?E+}C%eE|TsT6_W_0ogKHpJ|^mfuOWiqYE!;P8}vBeR~tAEkFD? zNWgApfBRcLrQbbetX~{^=fHDh%SWo{w`Yj9ZAYAVo=hx7BAP?-BKn(IjSDKs(<&dj zxl@ZK9og7Dz8|vsx{D%^{^~=(vh~`^?QYO7u71JDk%ZkFeZ;mt6%n}0WbBG0);*rs zzUqWolUSJkpxremfT0UGC-SP}sieeif#LcNpZ^J)%c~#!Lh)UZtwN}081im(F=6jx&0Y#biddIQeeXwkRPX#NL{p?D}IC zJLP(2YsM)3C7HW5!V<0VMW~qCC?l^bT}`=u!%;Zn8^kYr&wWlQ!j%-(k-@4T_l#;- zfkNw13_=>mDo99rR}CwQ;f-W~emP5@`+T&@db;VPWEF~_g9lhIWW|p1X$Ha;2aMiF zI`@&O=N_@^r;OBw;+A(Ph|6pD*6|yNuYd)k>1{p!6~Tm99#{_aRW~Cj+LHvAcpoa; z1~fJK)ekOfYXeE5t-(7X{_NQuQc>jTsbpBQ+f4uGkbA^?PU_TbE^({WAC@zI?3*{C zbra~7R0Y3>=&a`?1GQ;@I&^Api!ORDfB`}Uf6n8&d@h%*DZ)WAv(b*G>;q%>boIT8 zTTs>T*{51|=Q6$}Ln~GQia27L)0UyLl4(3TdIbL{g$?s%zX-iI`Oa2thDkyQpb+V#0xPT&p-Eyd`&y6|n-3%hYwVbY%FR74x>VLt5~15#aG~pL!doiV zZ{3p@$I9C~fuJ-|c1SGSU>Ss!R%UGM(R;3@j=PH8VyE4lgZ`RRSShp%WH~+EdkQ|4>aZ3 zC@YePWLCq6BvevZv3awa|HVdNqQcm+@Ppe@4r9tro#m1>Fm38J71wSdDgD@J^j@1f zz*e^ca$#-^iZsX<$X(2@d-jgW4BR1gCgz-zEA{FHL@ zclr6^^7M#9B^T&R^{ZX~eg6~0Wba`l!=%7xN zAIf9Ll{w(LfL>`L;QD!m*R=XQXTEc61lL8lw0hD`fSBE|^bnI}I&hDn2p5+;;wmCg z9%P`ei?#>1y$2AxFh8#_&;qWc@rfU460=GqKcNgK8eg+R{j__xljElVZRJtDQfv7oMvp6@5(h) zvTclxKD6!c*##h9d~{%sQ2gdYKqRF5GSKlKTdLae=5HT@-#EoD392#Rbo99DrLawJ zsxJR|65ngC>vQM^gQE(5h1iI*$Xr888%V+TT&v@NlrkGFEJVI1c^Fm{yFmD?JS4mS z6h5@M#JHXJ!{;ixwDcEvHGh26gHk%T2@;Ua?)u_d0U5l}+C=R~4Kt5})EK?_rY4p%TmJrICRuv=gq_JeKn$SJTG@W;yq-quwI|tz|o} zcJS+zud69n`?M7l{5Ih5OE+n{gE}$qpdSKaxrICibX16se)!z>oX-e%>J27E$Z0pl z_1k3-EMe13TBnG1mqIPo@NH7~n=aS49I1N#x9o>qTof5VvuFZ(hipD!P#*b3wLA}d z!d(u((${TXEr*ku%+^fwb?52P3|&oWI0Do1lXuv8#&Am$w*1}o5j}Df+C>Y@%7&Co zdf!{p;awVaNOe)03#|^@TdK?qd;#!n=y+ga$fH}Ds*9cSGcLWk=oTM0tws0&)O8-C94OJZ;rhN!AvwNDI}q_0pm&f5%f6^DUZrN(Shp&gO# zZ3emg8p}1fa$|A($vOgct48$4N`|cyzLQUGmwOBiCKqBRsro2A)fhp};FVfqO}V8f zoQ2+v)J+yqON@*I%i5R5K()XMe;L|7Ym>d7M>*N?=KvK4TNOGdo{s(Pr9nXKLJ)j! z<@+XuF=Tgxb#9cty8&e)y_e^W$Y+6MbJi!b-2_Or4Ta?k4}f0V=xP{Cjq>(Qh6_r| zqsRnsLy?oHoChiP$T45VC;p?3%AE7PyV}~MeZg~j*^F46H<=;^r^^Q8&}&T=$aE(~ z@)j?Z77#jU8CEg;m+$vwqai^1R7RkVC%h`V4-8G$cBhJqU=V_DG5)87#EMOsq zIyF3-i}KaG7blx2d20zsV$1vqP58r@Ak3HLkm+I_?I}wq!7V)*rM;>AFSh&f#Q~Sw zRdRG1XWE4ZtCX2@kdIc<1XBw23rc_{;QH#`>nQW|N=?e7yfsGG^835S<)6y)OS&+{ zKt{*+xknK>m9&jS%1xDX^;Bm(P9W6920xJOrRi^h0}7c(nd;1@)Z9FlmN;>{NMN;}#MY#8jLJ(6 zbPG8;;!9F819ikjI5_%?Vqk-JrJ7K|^x~$gzKB)F`R#r{il-xQ#zhro0PYm+=!8|e4iI?VpKHb1vmPR1wMPo%aj2fR>9buKQFd&{TJ zStmn7qIkjDA{LMMH}3so1f+-s{oL(NFdSbk9$AZl?ZtL^JUgA7S z&kT?hq3T3|uDfY(`1f2ZWMaS{SH~mAFCW(6w^svXg4x@@>82N(f5am;J1=IawNna4 zs=?Etp#89YIggeQd*fiC7(mh2gIbx2XqOsLk_%+=zRA!eZ{1fm|I6?U_-UWi@SMT? zPo&Sat!-;ggzBblh6j2-LflRtKbN`GlVulcArElUIytJB$zGD)L=BOomHSdI z>ArW{lZyVVoP)U6dHGM*w%1n6BXBna0Qw@2A>9Povn*^>DOPP9Be2Uq1jre;uL(j; zZanXz2dM)7DiKJYdIUSyGoBj?fK`Ha`p5f+rEv)MgJZz`_c3_-$74`8glOq?Qr%o! zW?~k04a+wxzrC);J276of9xi7wgXaU=jXECo5XHnZ0aOyu>oBU@r7HVZj@TpUX)S_ z<|~Q<1Qk+nhCSXTBiXP~@t@UR%E+(36d_%!Str}#gwR2YgFcQx6K+5jAW12KEl9Xc zeDH?^?szT@mjh&)2rzY0s11%=iZLqBZ7&}jhkI6J5>!vi>z z)p>6GaJowPw5UtqMB}e851{Bs^;x%dc+#G3H3VqV6CZrhwTX^!GVm3R z$K>0io=`6-Vvy)`L8VoDz9bF?Z^)L^sSzqHGH#wQQ}6~VyfFk-0q`eDZ0Q=O=?5UZ zq>y(fx}0aHJPu3>uQdfWG9CZX{OG`HO<$1Of)sr?4Rp!3eS2HKml{#zwc`>w;(!_= z0Z^@3-L{pvNEXXJEA#ElCR;2)UNU$!MfZpAzl#w6I!ZM=0O28j8$Tld1i}NY5*?)& z0H<4+@%AT=dFxu$$;x#KJy~6Zrb9y;e#%#Wd;JcYRo`o#1owzVl&Ck!i*j^2{%G~~ z@oUS;N}Bb>xqD^SibIvE8rG-Y`^9FVZvw171I1uP3OXAV&3Ez3{are2`hknl(E!jY z;AC-2z_>his1(0*gA4jgZjOby`L*cEM# zmRuo5wavw$QSfqX(QD-5P-(Lk`x*&A57gEs8!h^C`vLTeHuP!H8BOW=n}BGl%tQzI zzyf8oiY8T9DPgE)s((F20k%u9{uw2+rB}$Zw*D{21G4;RU!ORFx`!=DUH)|nMA${| zV)&Nj=q5%1UQY<02zQxgrN`F5W2`hB;Gs{ezrJ+SCXx)20L9l`#98*%R_EKovhd)xTr^}V_9lF3M|2qY)-<=bEmT8A2i%_WM;Ntsv! zzfhY10yXbPJ%9jX+p_?an|o+xuV+91opsfZckcA%pUg_0TsGIwKn%tieyLs@bXQi&-Z_!)_=fsTkmZT;}c z)m6Tsmt!+z^}U2BSm2O%R2D@umt= zmPdIVlUw472334cR&s|{<~~jhQcv2bEtTPnl+k?Z`iZ!&&I{O;7!piwd9_?{biTp0 z#@m3suY+TT&a?^bg{|d=z~jV(9VJ`K=0*2IQ8J;#98X|IjIfXJrdMm7{$@sY*kc`= zpBx!a9t27#p5>4)*xAr7v&EIEs`8#OjNnph5mdXm3%N1{T^7Yt+x78aOI57h@f!wr?yP+9 z9$+5|Jn-c=F;}#DFe35dU6YTM^P*pYab?(Zp`n#h12j@?aAYR0sE$&Pc-U1(N%#B^ z0Nh>r^B!HM%)?nzw22vO%o>RXW9h9+rjR=WVy{M@N}J(^yf_{t|0*`G>4>MKc}m$v zy~0)KCGXTvfrec_NjkhW>8?PndGvQ~GzMB#)4u(Q**+nO8|9RzDznhr`gDOjz_x>h z6Z;E0y_z87B(}XzVzzzXyi6bWTG>CunsjyG!3F^|}=0#SOC12W!a~1O7qm;7qfcjrFfLAhlyQ-eOL1 z*LU#!2FcO-nx*O}IzTjgaxLd6@!m{7UuJ~^?n1JzPYCNsJ10hpmcZ~zHrrR@=a>E7 zOSwwoYWn*n!&0SRN<1*{+j6*#hRuGBNlPlACRI=`a-MxZS^~vG@U1buqbsUY_jA_n z|NBeXH#{oF)<+Uq#0t0__~-75;7=)QDa@?96WqzQoV0|& z(-g&0?*zVB6DQc5iBbevPORGvt7$m2wY#$3O+ks2JRAl?3&iZlILg*iBwC{RRN~5C z@oHdR2`UCKif%sX3^$nuSj@1o2hRd*k!KpF<~ z-AZIPUxIS<_$bdDNKX{=(P$0X2&`a{9^RHw9OD$1vK|l99StAbBkw>9XxE;1H0P z#!!J91UKcyJhfGl!kRzlxr+=8iis=Z8)v7-c6VA=C)nAae~>Z-h)ZOFhe3`W@g}c+ z?bE$zCHLrR!y?FVo>u-^3VTn%VAEB41>d@xJ|{nGO>Zo~lb!3nzmlD9k2PTINS=iV zuY!CmetnhPSel&r1v@3d_Cn*V-j#Cs%{G3R=6iQ{&P~D{QbTi9adNebZsz`VR_QIO zG2Y+ruH3QX=kNW?86Icnyf<~<^!C&~>cV@ImT$F8JBA1gp@VTD(BR9TPuq`tJbjfu z?q@MOTYFQz-k_;amZV%SouJgs${g=1aa+3rj{na_h;6&K_jj_K(`vSd*cZ? zvvPQ&e{d{jpTZuvlKk`g38n--V7`31v01?mFJ_|KL$vydnU`gDnQi}Qu7Ttnc^S(9 zGTD79!A9uI;Ytp^y1Y+|g;c8nzTf#Ia&|-Il#)txZgjqZKxe#6qnAd6`*5mLboE+& zHA%xcEo9I+dZjL&psAH4rUW$8a_*AZ!`w;n(nUtYm%&O?H#EQh%XS_7b*uRl5Qk&i zst^vxZ!n4chdA84uBRrx&=(yK(E=^Px8jRhqx>1@cR%zoobyR?8z|VG*yUXxXs@R; zW>3gB9NhM*%%Ik#YSBh0o@K1!I&qdMT*7{&Z3l*KdtW>uneO9Z={EIUs%6)g1wy;e z42Uk1Tm1X;RQn>26m!$OIIF8m-^wFK9Vp4{6Mc|y}J zHzx>6#|5XOBuR}>STG>q&fG_C49c^%Hvx$J&`fMJaTGl@gg1+6W@ldv@uB*o)^Be$ z)W3HC#i~a0J?hI#?vp3_RX%8xe*G)WDnp$rQcl|9Tg zD4LbuKJS^!Q875-jY(YEW5v*}Ybk}q$ShZpUuQIp0tTwLvc%;Z-KwX z${l+H>##IA8)7+ipYraP?pe^@9W_Dpm&i4E_C193&0nsr>#c8{8fRjw?4C@`R^k@3 zpc^YX=T^%_yi_s}Cx=dygG%Qh1^4PzO2N$yMpZ*ZE2g8F$F>b@-4=SCTH(7hA`$;7r zu0|vGDFJK-#K#xn4e0vUpQ7v5;)Y2JYJ~~t2@)>r7Hef`8nk{p zLSOZK)t?4-t6qzkSyd#gqjj~fVDRBdx<$^|3cG~?Wd9OOypnOQr1rCkID(OQ+T#)w|?~<@3ujmo%cGB?P7>*(6#Ntz9NGW zifJs^F>!CTULAgIw~M+@{x}u6k8XT6^k~93&neQa6Vtcv`e}n5N#Z)W&NguGrMUj} zr&Jnj@s8FEFhap}m(P;e6pUe@7g8gd8vc9K7T0k<+VF|7;%C~Mc zSFd#gMU@+VDHc2XoedwY6e=(Abj)MLoEswuNHfAT2VFi=1->`=#JMgbxu<3cr=z}W zfrNCkG-F*fOCPX~Cj#D{aEkGY=XDhOmGiiT*T*ofcN6HKr>?*GmqC2raJp}re5wVT zC?1@rkhX8#gf>d|Tg(myKFnU_6JZ#xxWFLBCYCm3{)vxQWqBE^tQso_{UsC?QKFUP zzHwo@-{#tJ&VT4*Q?2q<-erqU`ndAa^>%3UhXf8^uG zMl`if8vvWFSOp7aBO{^hWZmZKqPl>78U)~xeP0^)OYT!hkd5rao;!!k%HFd-@lrNh z&3qIs`7Z1%(yc*WkN|q*|$0pP;3?DdNdP4YZil zRticXvOT(p8O$crVd!xD((NaYW-*_q2R5o|LOrMV+NtB#xx}_q5ldCSx~43rnSk|y zc%f>dZDO@yojd93d@uPqkXSKEk@`-4fqasbo#BU%{4&ce>>pxk?a62l29vj~R}NZ{ zMn^MZP5DHv_Fi_-`23x?YLK4`q33H0=l#bwE|`&`ET&q^<_SctJ zLq*C$I1aroHGzX#BKbRZM4$d+>_}E19e=2TFMkA}Tu$KFOR%TTeU(~|9z|W`Qmgg= z5GvysA=BG53H5o@d3IQ2wF%&>@uE=`pXqKJWXbN5Xlz4E2YV_e;BM*A zd<535{AW~7Z-P~8W(Pkgx13OJ0weTAl~`(qCC>_##fdU+Tef8ymA8pFPF`wl;mZ># z;THe*ukEj+2*i=A^n|O-s>Ajf_}M-EGeE97 z{C8wy{8MD(^!>v>|LdqBFHj!Xu>5{iM#umBs{Yx4KfJTsqW@Yi`cR< zF{yHGHL|smWJ?FRMcrNr0fHSG#`Z1%L0q--ce$SVrx2JnW$g-B2&qJh#)PN6~8P^}7>kev@sX(q7&6u%c>@g2MoI!4DbIL~)DI6~rzz3S)YF^6(JzdKZ!n8&aLqeSi64|>pKW9TbFvzpB8dG#C;LH-=(04-YRzX- z^;MlZ!2%3F@l#J)_c4L#;kQg)hebsh&vqtGOWaPN;(3NjG7PTetc87E3I9Aij#Cag zEQ)_CKB%Uhe^=8l*#C5i@8Ic^udF9{95eNb{4WgWy?2fdcU1V;u^wvqev+I=`k;N~ z`+>{+CJzA}tr)LaN%}Q)Ii3Y?Q@-9?S`krRyAKwquhw(vVq52}#UeNq&&X)e^GCyR zl*ciGrh>P@NL4PtaORySmEycGXaQXw00W$%6CsB-{pnAR^@VB}WC_r~+x1J>x%d(T zGMFq$gz^JRuxbz!MOLmi*pDmFy1MgSsIuoRI2MK`d6=KsMb}sm9?1lu*j6ygs(9i0yg~hHvc@ZKT&_94YYqm8>oxV z0l%@CF`8W@crx#2a;r6`y!;|l^;}8&%%TLoM!ZXo62+e&*_G{p=q&Fkb34nKbriO` zMzd1K71gUFy`%6nKc8J`N)Hk$f%|?VEP4Ak)5zYm z(=XVo2hWXGFtwdvsthc`0dxVz&c{jh?Yk3GNTwuae!wZfzq!?Iy^W#kTyS&fCgQQT zX{j`uGA%G9fU7sli}S;otd#;Y1OS}c@|skI0iAs~(@dwKQtMJ6!~Hs~!hnt%ZAD_M zkJ0E@*8AcV#YLW=cJg;&C^4IX*{?L>))i~x5YPA2Ju!#eUGm>GYtq>G&)DqD#)P5B z1Ov0n&dCAUh6`fzeK`ugEc82O#g2c+ja}m5$0khx$SwV^9^{q{GJk?mqr~fD1iGS( zAi3qOtX(S#T?oju6D{KD^q!N3hoG<_-S1mOyBpQ$(nh-hnVvI1~`>+Ye za{H}233}@tkyZuNWX#TFLK;vKlMg~OYLnII!Zkx5vG z@Te$GbcD@x3p~RcHN4E`P%CA^=D8Zag&WIIX-2(h45%WK#|E{0 z>9r7ToWjD|J0NGY$w)CuM$&7CAGx@$x#soGC(VnY&H-nJL0%ZFMrTqLLR$ewj&(13 zJ(;@UkJg{}#bB`p3Z@83zZe@o@5RGzvF6ePpcEGDfdqikr9YmWX769su(QmYJ))`T zY=HXXT2z16JsD;ply&2Aq34OAYFOR|3SSr&5R?H~atuv)l9ow)|Lw@UmlpjH3Yt~? z)ud4s=$iF`EVp+lwa$feP7>XS_*kW^|Xzz2TP*@I}RC zB;rd6Ho(#*r*?lB;17NhYyz9IlID0<);D@oi>4sA{mZAQ+#bH$p% z%EcUPaK{Zk;DV`2@~*oA@J@3dzefO@VX@w@wmuf`@N_r(e|S{qH4e8O|K*&dfv(4QD?2j)XUEBB z{w(YTO+L0xp5j*TM;@ub=%yfE5E=68#c`p;yU)v>ab}(KzCr1psRVl{W@;o^ETvs!l{Uq*aiFZb*ydp)0;%H6 zDug1PT?pG%TAQkpI2za*0cCa0hn^gY2>@XxzZ-vN&NBVJU-b@lu1bQ6XZ%R-vr z>kBer^I(k}bNz@?vY`NDZ?YZRTLAmb&hTqe7r6*9b=!$#``_$crux3>8Akr`eO7uY zymakFr$UAM`0MkAqPMCLT>&%bM?^-ZD+qx8_whM|cDgqXzJ74tEq|rI58pu&zWsVjM#ORS zSGQc@f=i^BY=u;AyuEcd%`Nut`jmS{5LW;Xj zO$=ElM^!K-EL?k*$f1`f{u=K?)uw%Wf|!Mz_olE%51M&!Kb>p>2x(vxmgeUTY%DOE z@ie@M$;?xO&21W;ind-IZ%)T@&3B$8m9+p_QgF3wYwMS!PmhxY;wx2$Q^mA*Q5!uA zkmX0z)`LucDcoC#cvy@)wWocM>0^Fp`i1}TQ243kWqu!L0>`|6e|!jyK%blQLcba* zky=;7dRo)xXy;YP6oGCCkNZp{tHqK;8mCiKY(DECw zxS4}KGv8T>iu7#mQeu5 z9#60g-EE2%uS_MVE9bk7Mtp1kUNIKlB2PiBv?6NVhR?yVAgn_L~lHx9l z$LBrGcNlKE2g^fxXI}wY9SZEJySzl987jB0K)0P1#3rl?)M2eHzjWK= z7exdhks^we5Q1lCJE7pTCuuGMImXmWIZu%#GHCdUnOhkGJXB(%yVoAeqi4kC`Z8sA z`n@}AcQb}B)~fT<#8``ibJM)jn&lE!svM5?@Mx2_4*iAyuFMuY$)$R?==?AzpqI4s zjic|3OPr{M%qx;t=gP_yL`b{yfTP?QUgOQ`E9q1xM_@;gy7bh}!88e2@@EByAuWlmiwRU0LC zK`i^l268!+0ZO&ma@E5A*8=S5JD_+6h)+(uIfI|f)O}Vnxuso)gu!Q)ky4tRlRwVN zG{iI_l)OS+-%7No7gPk@IOLKCM&sxKp?>mrb$rU@kGI>_m0Lg+ndHTGfN`^Jp3Ry4 zR!_ef12n`L!Su9bRhH-6ilRfg`tlM^Zo1v8fNZibx~Y1!Wm)zd1mgnMZPtq*+DwEYRRA;8_`h6HRrT3XdXIbWl1t@wN zYZlMW!x-da3})PW)12pZG#Re%?FD`tfVdI`OLMheEo*N`j3hqpaZAie_lVc6_m~o;;3n7GqeLt z`bEE4YwYRi0VqS_LslLXAnY^Oq_-U`5;8FBZ8f6q`qHiw&UUp9_vdXm!9C$Cbvng5 zmiTAWo$JqvY6`aNxo#T(GulRdV}Q9lz)VDoeuDV=HXe+m;S(~JmI1U$o4Q5p?kgyR zbTjO*QvH27IpG~(9@K#GiqvQk49_UI_1%lBmmhEAua+<<*9aIE(S~grltInhoF4-+ zD>Fv^r_-9z&U6t;@=Nhghb|z-x{)eV*X)YcpL1*WS-s^K@6y zi$1Sg1*k$_$w?iO5ga)l;(GwIy*z;0&rkg^8C;X4xDF2b6}vM-akl7=zf$^#KjhXR$nA6q)bfNH`}0tKZMw`=@8WP>+6)qW+m-I-+yIzU zZ2Rb6S%kmA`EQ`#G$-Wmq$&4L_amn-;={L~`KgSD{u|u4l@M#?vZTJ!66bj*o1XMy zK&5QyWEMZL{I(6a-Xd-LS@tg@IM9a{c)&Xe2iA4M?)SPj|Kqv z15JH<`^2ykf^x1~b1^Kju#xRLEZqWr7D9hO|49F?P^WLx{E_|GXFaYO!!HQf@<-0J z8#sMj$4$Ijl|1w~_r_ly^ezPaE&+w< z4y&2Am?YTN8TqfzXvwP(%4L2xe} z?=+tMR(o|upk)^(F^*f|&;l1AL*Ih<0Qma!-*lhkVc_*7xX<-bOOi(0U?ErVi0O^X zS~eX19|Bu__3UoC5#6Ow5WOLn2U3QT4*m(Bg#2C!=%`9$a zQkD;{dqS7a)@Q=}$0F+g9w(+oG$}Ya3Fg2t$e!_%5S?lZW?r2;vHW9Y#uY-~TZ0KM z5tlSBevW$j(A)Iu6G8rP9$vSAmV|~MC6>*Q)>2E#=E5 zq0FY4aL4G(-6B|kmoJ)HEW5uo{`4h<-?$>kb94SRC&;Pqk}tQJ=lm!A+CM{lD0I z@1Q2v_kHlF2T@cwij5*zC;|!!3IqbESSUi2DqRFbdM7}D97V)JZ;@`Hw?Jqjgor4e zM0yW3gdQM3NFd3+;e0;x+t~x(-PxI)+1>G9m|=L|_j&Ge-Pd*9=do&d(U$e6h-C@c zX4FB8rew&g7VI$W`bZs@;iWW*H(2V?;)puQQ>9K)D)iE`@zQ|?c?SCRAT3Thc^Imj zF>&2&tt(BS7in>iS5En7AF-eUACsa8<*A&w?8BgDO>U8~S8EJf4E~|(^6!j?9pE!8k6x3u|7dTMvKsX%D(&P7SQA>teJ}9;ZmtdtppVYlTH5 zmAgT+`MF-3yx(a+OxO%)E!EMqHlfRx+l=4qhlPApWp4_08a$1`wD}^MUa-9j9a=v5 z%SRqxcyWY%hif^SCeE7bfc}|c_U;6dyqTz8c3lx$Bdml7#=OAJ#fgh%G=D5rMJYj* zjmn&>Vq4?R2B)Z|LrDXd#jt2*n_Zs`P>o+IH04FY< z9kEF`YYg{k8-h9DxDz({!)?f?(Xi-m)8Q&)tbNa29dA<9FqTP>cn{&2RF6L5Agg9v z{wL*4aF<@z+~_K&2IA8BxseDGB(Cr|Cx__V#?mdN*EddMiCd-lMQbG zW_J2DXH?WveW&ga%d|}e~7Hme8nf#&7!-a@-gvBu=XmU#>Q@eNg z31HTj-_PSdS0JYmyh2F7v4D45Tfnd1I+pV4;XHY~ zsMlt`?;w_2*o;suYpeP37TY9X-HG6<8WQ2L1Dm=pv$>l<$@jxz>0%x0-#MdauVrlP zh?D*OEsZtSKD_6IP+5U4 zhtzt7kOzi&#=>-+~3h3En^$*lfNIjHhozTzGVlNbNl`*=TWgg*dQpz;aapAHm|VjUH}4F z?|(@&^-){cX9js(6WLP7rDCUrG`~L57{)^m>nij@#VUW)17n_0r8=sAvfXn8cjf46 zDxaYiS{pJyw=0v-D!2UB{2sJixxFLa`CNr11)TxOxzQtzZp>_4{3l2MMEqbkD3-Hg zNj{E_TwV&xoa^yRWImr_*jNfsnqtOyZ~d(|l;N<@S!v;CLn0{hFRY%5ERAF8XVMS`(& zh`_$6i`-ebC|nx<#X(u86)n}f0I@1tSZ<_4^=<@Xz$~S`zAh`RMq+KYhm#uSJqr&? zi(%49An193;aC#@Q%CS2ugg|bY61bX*9PZ8`|jqIHhE0#UlLoix#h)9wqhSD3KU(p z92y|&Jap*@Zz|6rVVMz|Uop7G$a7LZdvk9VEf+y?=1mLNB3B`IdAV2|(59GrH65Mk z8oj4&i5C#1bI&FZ?8x!DF^W3~6j_1wVF4EowC@N^%WLy-UFVaFk5{5;lE0rPtUQg` zhn@375wMp><{L#FPF{|j0!V{E}(QzucIL_`vn$E;-O{_{S zltGFVI&o3l!uc<2_AtLy_L*SRt6lsVL}E=6M-&YFcZmw0=o>NNv4pqDQ0G zl0_#lrBGwdq_v6H?*Z4cnp%ITquQ5y0+I8y36F*inYz29u0z*Bh(^G`z@|?Vrv~h5 z6}o!0ou24tnM;0~ql&#dD;7R0nsVMUj18URN*S(bI#U7dtZ;4cv%0DD^MiSCI$U|v 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

L`Iy8!9EXA-h5_Q3cGMN@r}!E4q%4lfEZ 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%0AMBX)AX1_6{$H+>;#zJCdPGk-*0?NaB^pJD zdt$=GqL=8HuK80veuTv|6dd$YaKBVXtDN}yS5rLn!68jKSkAt)_hclghG9r`=hjj) z@@uFPHZzF9;MA2>%H^pYGkS9@7njSG%W<-^kRf9((7c80k7?jHK#eapKr+~=^%1Co z|L9*4A9q`l=0He$aOrcE#iF&kFA8iN0?pLPr!f$Zqbv5yS%M)P{6wNv`VHs$X>?%| z<;_WDr-kfpa;`k)0hodu7@qW%)$N$QpP&=>VD#!@;kzg5qBP)xe8)vZy;vLJ27qxy zQ@eHWLt2YNrHKtP>^Dcd8gBxcpb@!AF6-G-2eWf=mAH+E((ez8d|zi9K@x!?=ugQM zd51}%YR`PxYJK7dTe5LSbuL`FbOc8z(QnBTSi?d_1Bqfi&qJzbXoCoXNTBosbBL9# zabo~WF2md=eLj-an>hv~emo51dpN4RZW~366?w-VlXQLPsNLU~D)n>faZg=!+5dWWm6r46B=GEtMya?%f;6P-2$KMvkl$gLS|X9Qs@!k7 z8;82A>>6y;IyTBtsmgVcL8{TM@lq!N2thi2H%uU5mDPt`l|D zI!%jJ)?n?1tEq{O7DruvGagi$!c-RZdEyIBl}V#~V4c{)lGddnj-U3*L2Z}Yy&gYQ=RN3LoV5NMU5`Ca~*oqi)>O7zo8$PQ{ z*J&Esv>@yp7|U4{sDTK$lGucx9&U1c8M@EhU1xO3cdf}8fi0b!s(WjkaH4C1S(D(EhUcbV1mE~wzS`(SehH6-(5z1~s+bG|-b>+BN!ymrH|uoY zijRhTYFj(vY`4tA5)@zRyph;bUeF^R%s2W@x9(Q_=|K$GyVEW%jtR=!^L-c zD!O-ZF%{zBH3#;FHqf)f-pE2>iO^*K6+H4kQ2|W`lE75^TK{${C;k_=Qk4x9OfICB z{87+W)Z#EHVTXi8z4`2G7@+Cc>B|U6Um0zAb_{5k0_h>1wu2#`=3Ubki_yO-T(&S9 z!^^6*!N@y&^p^@rbK6BH=nfV`u&6~#+~ndNI(jDOhg9q#_wXqh*RNEo_@F`sPyV7P zUZxVP&6=D|`uIJ$j|GTu4vOwjX7+xkXPhkhkvf**ODm?OM`?x&blVEQxXrHk z3iayPhY;$-l)m^>e?zdce_kNICx>G>?77@hgfN{0ACIN1D-H_xx`h7 zkSFWK?j-#4N&Lw*PMkkQa!NU=?Q}mdRMpApVl(^mWhEDf#ZFyB0Qho4g!sD$&>B`c zj3dE{1_peH(_=mc7u7FHOWK_RX`XjTSScSO-h8Rj^{HN5d7ZeLPJI(~3oV$wL zua@~cK!kyBG))Xg`N#y9@ng^WDtYoX4eY<{^TuMzO@(xU4fMtE%S7LU;}#s}m0Iui zQB-TY3fgSrfv|h$Z6Op?>-hBo{pI15dc zdqF>Z%c#!DeJ5Pz@D1S^OtT7{!c^~2#twdE;i}4f`7Bt%EgnHj`-8MA8vzIx!EI%32))TX4JE4xM+rWeV8L?zf_h>zo;O(T%WYl zN#Ip=brk2O-Er>JbQrzCgI*0r!MV!lWP%4SsJbi{69y`e)hsx@0wk55Q0QH#z-f;8 z)=VM397M1)%KJrC`F;}uz|xUWOkG;js0NXXmHkF1S1y+mme}?iy#jbq+0mYlgCP>{ z2#L@7hJ@{$AV~-r1}Dlc@Yr^%S1Mg1UJ1d$3siMlpzQ$!OjCLZK@Z)E&&eWQ$kE`W zSX`kmsTeW;4}0$!)MVRsjXt-EC@9zf>C!~H^bR7@rFUu4rT5+xga`;oml~w^j?@t8 zJ@j58HS`XlCD|9=&+~rYyf=RD-aq!to<081aU6_EuJb&~I@VeTZr10Z-9lu9o*_FB2>K%r-MT}9>lt<{JcB$u|tX;CVZlHuY97ZNv2O43r9a0+@N3KS@t@4zZgrwe}9noOsPY) zEPUMhEMNPc#c(!OmE~#vozs)WP>#L)h3e3c4W35&Ylq`;EP^V%QOhb>I5qmmU;H@*Vib<4oK|0bE?1XZNv z&)xljhI=2djM@$8Zkr7(p6;}sxv-Z{MAcpR* z69nWApRSpozWD3PrW1KdE`vX$aWlR3m)pO&Bv+BX*CJa40d#H~*E<8i20sj$cap!W zCp6WX|6n{CeI}$S(^T5r(sW&C8TJwA@&i*@3~C**NCYzR+32I1=p;n_6_<67UMNX~ z(#oXX4>Q%2(b*PivpCHC5ChPb7Aj`s;)`F#GB!TLxmVWJBn=^0y2g7FVHwvmFJ?Yh zp>OA$Ol#5pW2I-4*Jb@Nqe{(tzK}H{2U=BE4JP@fT$#TJu=apoC?;?ON{>CQ8dD1afP;EPP{B126dJU@tjS8I$LA-Ve<%s7!|zw8 z=KIt{6Cw1v)-eHsb9kbf1FLHzQWvW6Tqf;~p@VLl_2P$Qcs&q~8F;q}G3m0N#10@A zoheJo*R*@vTB@YUCB4l;LU)j>mveiMUDR;Vb<2GKs;PTx^;m;a`DE%@1ZO_2E6jOw z;0G#NKXZN*-6n*FE%mBTyz6arUbF6w;?M9Vos%D_ zhQBQnv>>8WOpO6d*f+Md=2Nf%@dhD8&-e2|)JeV5$pC1fCdaGG?=vwrpj>OMt+Af~ zl&_m?Eq6{oRi-sTvFF;FBwj;)kSyVnUl|nOp}+*uTlDhwr@^Bg~bzaPOlS#SQ(!RV{hGNa2g_57HTYQ;^w>yVBB%axw6Fr7+w&U>=iuZdS7 zrez)Ds~v=EVOE?by-bOUyxE!(m>VwPP)nS`vlqFU0`&Z5Am8SX__8jFgX4x#W8-2- zVQ2fK!^KukUM;lvp~r57Qp~m^T8JtOy8gM%VRIWZ}jS?=FZN7HH**yeW zca+<1qge=u?v4O5X&WD&XIJP0|8)bMdMQ;tH~LlB7oYyd=`mwk6vY(&Mg`31vL3U) zO6g1ewO@`>$b|Ojo>cTMfD5dDcJ?I!w5a)vGw$kGH#8hSe89yOs=-{RC-&HVcu{4k zmSxfv-AQq`gv**L^{_EXfeg;F1@uHjD16Bt^ur`xy9@!BBw19^nUtMnpHxSKQrhR~ znkfv$+E7-uIunts3+Atucc0&Bk#UWbDbp*YW zijSn1j(rO9<7dbL&|Qt^{81&`dQ!WoiQV#NZ&rC4so-)s&B80S>i{AgHfu(r@Hwu!eBwwB(LUmvSb!99A8b@vzu~JTUI|+vPO5j|2w<5&ljx73^^M4K ziT~sTdHmpTc7$}qDKa9|=W1>rhU}7N1NlYvwWs&_os-EHShBvh67$eybs$9FYEC-; z$gJOUUW}Z+RjP1zxJ^u4xQHUAQzJ0+IyOEaL|zZL8&vtKx0q`0+b0MIM#bB1o8>nnXJ%;UFY zZTE|#ToyLHM}G(u=WKcapW9QpD!&Zt4rKnj@3*b#>P$i-#I1v{UXnN`)stxY<4!mp+mDVIpW{h{Bg@=h!wQG!pMzSP|Jp;|jl8)RVX0fp zySzP9TQu{bRfL!mkz@CY<{<4`xX!Tg5d|I^dY97Anf6_RQp%iMpj&anY(~Xlk?PLq z4|v|0S4&{1Nmm~Qf3L@Ax5R!my7t+Ng9x}@m4zXPPAE`9|8T-bCI9&SIn z32;r*6^|?j#)l##3?We755CN})BFjY9e1#}G0Qoq+P7)0YNdX<0S~9-UOlw4W$u-6 z7J2h2qAb|zGhqiYx9>`99iH~rUiJueMHl1r=h@p4eEDlV&Iks&y zwUl6<`qVbHw_eHL_3Ndudf#WK7D2HWa}p8r)W)7lT8G2l6Yhf?0_ecY=erMgv2fzp zac}`rU0RuOQ0D|-p+P8RQiZDviUov|KdkboAMrANsm)VJWJN{txw^IBUrPw%;4_D(CGP zsfB~sBWTfTusvAl8jG0!4d?YkVn&Tky0vIP^R+NXcmetOO>`NvSy2Wj9s~LG16#-? zoDi*q#)m1?GaZJD=Q|GXR$1>re(*R9z4kocnwY(QwkPkY>Y*Q+R9s+XIw{Ji&de6- z*+6R4a-JlCA5~O+3qI+etEkyGcOY37XV70&%$Q)YZGN?~q8sSj=H2ZB(_d7m(13$>;ItUZBu+1B8ds|@Bk-)4C2bA1{yv&N`^g>QFLFrVr|q|L z$j_)!*szf z7cQrBaYcf}u6lf)hcyn8sJ|s86aLuKL3b&cl297?YN=DuNfn;WNeKW~cy*@gvJdw6xP^t6Q% z5u&8(>cJ2YpMZIJiDJ;)xxd@a9SV*_Bcr*8_3!q9sc=cHpki|r`mCc)FA!8~1bVGs zrW%C3^LlkAE8hAExhPsstxipOovKkY_*LYG4#9vEyC|SMhmgM@d-Mti| z!27KK+C}|Lf3|Zm9|?#`TLT%n0cpNWL(6+;#)a>=z(|?m%+DmR7bhLxiwc56?S)R< zL_E5gr|N95)xGnWU9912TT2Exf2mogw4) z^mnKBteJN^8_{1_G;z0Q`*b-_aU}%G13J%_Gc-qlFOyQeYgt$+i)18<>cA9;SGr=>mYI)09MA`I<6 zbCFwH%K_iv=((ndV6_`&9*lT0R1+JB?F(i%z8Tsq*ELYB+h`Q#j?-w9rl+`x&A%(W zGDiWu*mwFeIXVk)Xo*(ipuFIyV1KR5GQix!a;#9EmZ+t@Zx+#)vV<17lR>kqknw%1 zZYW)%tV5oGa;~iM-TdLFGF|tL!3s+G`&jRF*cTXu?3L|gdV4>Oa~;oijg%N{7L&Qi zpTX~Eg&vYrW>80oT3-o2v#dE2!K!!ph4lNTUrwaRyIQ5# zAzFV3))Mk*TH9+vdrF_BD{#NB_BSt|~90?)-N-64?ahb>S%%4IH_^~beIQf#b_ zn?we>>0>qm!>|;u3qxM!g65WNd7ZrC)x~>U?j3ELlMj~^2&xwSi7(yy0Qd$}^L`B= z?K43|Uh9)pB)_Rze{2>+aYd|t@ssURI~_n2eD8mzvcDyieh*&lk70mFtHEUe9J@aI z+KZnrdDE6mmH;Hsj_%yS1;KAB`cZ!iMC4&>(Q$le-S)SOZ)mBO+P}L9h6v>LApx+g#@^a};_nFQ{rKJb6?PzY z->0Wn|L;%Uzn})TA3n1&#?0M%SuBwT$3+#w%jtCjAXF=4OdJoV%I}i{ewj3hSHAhn z4;1_;Z8xnl4CO_0tCsN37x3o<{J8>uV!)pu_$LeezmNvIYZ78&Xk{ixYQ&>xyBwd7 z21n!d7eHCYm_(+I97F^P-yke487y? zh|Re~d7i>4HY>kbLg7=&kqjKR5-7SsccL=#qSiR4;cRMpsCoy1Nwe$JJmtN0xvG-l z6zY{FJvhAFL22G&Qw^ur`EGs6>+RA=^Ut@qTm2A7ovguSes(vgh?oa<0)5BqCZmg* z=I%L#6C~WWnf!!@gP#~V+Z#|nws>P+1fi=j$ezZjOokGbsR83mF z0NncG5Ep;Z?=kDCqR^mkz#R@==XyOeSaNhzZmSF2!yLc1!aU-HG@n#BKUr1kyB&rz zHDj-zFjpd4L!KbW z$q?zIvpe^%{bM!g2|chAP)T#}@A8H0cjZf`wML=(i*>*Pb%|}|5$S$jBQ1Xorj(Xa zA?|t}h*ll1b73%#V}Fs`aaHMKN%ayN(~692$oc1|2l5=BY-{#-P66|J&d0Ge+lFv& zR`q-}uDM-+Srdx}`&ca9LeE_r%}=4JvKoIbvXleH`$7)WV8o+6~Xu`_N4Y&34n0l?2m6a7%klbMg;$Dljes-J)kPA z^Z#{jb>T0dL}%JiQ^oH$y~KO@HB6pXn$idW{?wAb2P2${U$*-R;x8m#@f5;oWI#^R zAqVk8>((Gt9`;h2Q*l7ufuY*YT@J+bXI?7|Lm)_wwN`r0^B{|FVJ{bsd_FV7aBU<)f_1ISy%0z#``y=ZzFYw%6DZ5?!OwQT5UZM%D zEqqhe-c8xl`k4|Mf9d;*CIs_E;GRQ4sh>KuEtK3lLnxf?|MyS!6aEY!9P7FKDmQ)U zuc7o;%~5f}lO&sW*%4Zi2?KFqtH7Ng&f<1j?xrEq*M5~a7bErEc82BLjb8#}&A((n zWXqrIJ;AU0hWxpY*726eGfFrz$@)Y+<@5`BqlAz`z^zuMJpvqqUbwvtS?_j*WjF|Q z$BBOLu7DXBuxXV(Zvwxs?#X|lfs1c5{XH(;z_a`0@p>`%0bRi>ic0jwK?LBx7EkKL zV{;}BE0`PTq@#YOQJ6d9=cmYEP+WND)a>KoHYznUJDVc5I(ye%?XmWa|MvL)x`CFs z_K0Gn?eDT)lj?U@Tlr1J05Sh^KDMm%Tsn3#XJ&<5adIs!ybF{JER=?cBIWWAYO(@g zY|V!s{JKAWNJ@bo_q(@nXt__+I>kHJt~}j<9KEt!7fTRyYfMcK`=NZl({f%cCA!q; zRCi;~uzJ?FP%T-K4w?bzYhJCQk83Gi2{6d$ zY&YSgVx32~4!ypCk93YrtV(%XlZ_?J0H7c}pgF)SW{g>dN1}M$C2q+cFHS+=D>J_$ z-;+s|D3hHzwXV3$5$a=#n67}at`RXoQ;aQrWUyxGZmCP?!Ru3}h|Pq3VCi{!vfg*= zi&-;RDid<<{F7R-uCjpsqz1`Y$)fcpVGMlSE=JpFr8@v$gbH8=I&~Cu%p7`bET8C( zYLz?ZIi+n2ODbT6k&efMD9<7yF}9od`@NiOGRW<584%OdV;@bIIx%02Peu~1Z#!j5 zKpCa?E_>~K5bIE}o)v?y2K!`#N2t@iJ&AS_U@HtA5{8zU4>x2o0mUBURKygU1M_~vu7Q2Q$Tkk^43xsI3T&NjQ=bCx)y0`v8A(qQsH&1gRuG*g}#`;>j z%S4oN@(9%7_=p8(rZ{NhFlJh0@$<0h)W}Yu_Yp~$$zpq-WTRn=r{e|-uWTfBjvy5G zR6-bLwKe5ZzJxI4c>Ci?f{0HxyFONv)k%)A$0We_I#?mWh9$Ufp8lUH*S_)w(Cy0n3O~u$uZlh=nuHYPGRI^g zPi62n1(z=D(FR_z`El}7qFOH<5klH{=F^MT_qJW|h6v0R%B|t0ie}+6OEYV`Ib`TuYwZLx2^S=-irI6Zr?MDGF|U30u9YwRCp`xcdcE)JgJ*i zt~^B`ywle+#==I|A>ixDeP-&r9{YKqEV&%K4|A3FYvs$1O<3`P$9a9bB6iRRb*|RU z-LVC;^80;Bm`_doNbUI*WC%9;Hj`ZqhVJ7Z+ zkyh+}MvS%;8mQ$~NxU2-}I7Y!=t>1&^33K|?LW zO5sNn=4u!-%C)1)Ll;={&%l4eGGsQFh|0)iNpf%J%G8UN2n1cDM?|ZJkjME;q|`s& zrxkQ#r|p?b$t$Ra1QFD?oOi86dld zEWAuky%D7r2>Ue}5Ax9Fmg6ly*+%cvgu2XduiP2r5L20NQlFtYx~sg`EU_aa8@P zuc{(3x8-ZkmR0fpUON4CBU}rpov1mFlfs_d;JPaFhWm-l&+pEgpZ=b_VL@x#$P3Q!gW zeI|v^qljb0UFV2>DkZMm_|D3`uY zwXG9OtILAm-pCWkySPcKuL;cop5AeF(^Mxa<4*8K6l@W!fa4mBPHDlxAN%;JRb3PQ z1Yoy2d2uodnH(Wy2s~E$lZ;Rln-gVnW0=M{59Y*!n%WnG&)wr?TgRV-U2#Dpf$F5q z^s+j+Wp*WEiP>!ycXV{k^A+>0-4xA{Q`tA1tX)p)Y}$m)-hn;cXreWz*()XEYZ#w= z+w;`VRyww*Aexr0g9^beJGorn?;G1^g~yjvMzUyNeG?ZyL3^cs$M;JyZzi;>m}kwv z<4FE=9@OOsERn1CgCq;r``vu+38wV@`bf0Xb8<)~nIaS@YfP&ha=O@$Tcbj@(F~e(5q;EgU>hz~M`NfevuoteP=NW0Kah?JVl1sn;Cy0jzetdXQMhIU1NgC82D_(GClgjP*b^RhW zkAm>Ed_x+{o=#J}bxhH^X4`|KvmHNh3{!Fi*WoxI7h7t#t**~2waOJ3$;b!F7uD0{ zcz80Wplr7}*k^ggKm05su6f(F=AamqTp>7w~-TayD^gn##dPta?CJ%(#7iU;Gsk}nQl0#?OUXF6n@?d|0cE-bi~ z+s{AX&7p8ptz0GE^R3#``#$5b7hnp+2H?8fal+%@20HGO#Qec_{#&P`nqY26LqGW< zya-zl87JeJ0Xxa=qH1(UUmyWR_z3N$AYdpqE^JJ7++JZG=7zV;`UnU(26su;Cqy6j zqZ-_Uj^a-NqqQgkCSMl{y;vQIioGE`p__+$ge!JEQ zQNM$1sWF5lP_)guZi)h%k@6b`r$tZBDu;0du~~^?(ibOsL}P0Y>5#c?hJ3Ve=5PDQAoh zS#}Cb|4Fw2?k=74!!(Oq8w(f%nTPgF(rjPd9io z)N=$ry8)}k+WAl=<^PE(wva7-;&n13GB3!*KIhQ!zDC&qYO+B>2U#+1+@&bGW;}az zuU;@iET9gS@|Ta1_$+w6j#=+GG*VRzQdjkPX7@h4yN?$R+-cI4Gf5X~6(U61Hhs$V z`lIpANaGpAL5HJM#cmn}{E^U%fCd?07E*oD(8^qsD}S<2VENcwvsEPiEN> znHQ6$g`7%<8}}|P8wwiH3lhV#Qr2s=6M7tLz;zhgQ7wJ{;G8V~Uu8>(Wmv|XS!bG9 z%Ys{K>)GiQ(={^r%PMgAR~5K%brskY2ohjXj10A7V-`%|By(KpehO2@S4mS+sQwqg zu%$S)8M95eIJMQT-tVu8o_bU?^$g6lsU z-{UINoVaP{8Hnw&YrDpQXwUBOJ9fj;k}AGjyaKrqWfw|2%Tjo9MM#AHczyO8FU&2j_;BnO2A4kbrJ|Sou{(S&=XcE=;n9H+Bizd)GRpU z0P2Jr-rou*H8y%Oz{`TIhm~kwiuUVJ;Rx+pW(MaP;#y6QX;5ZdX5t*z5mVHwv5WF*o48|(sr`cGlv=M z{36>20>Gv4VgQv=WSZ~w$;`ateX4otg8r)jQ$p(>i0yMARI!1@JDI_A1VrhNHebM7 zWKS>lr0nYbEGW)}^7+z3Zi4X0L$Y2{S{!KN;2ZeO8sw+w&@~oYzOs3Qy)}|>lYlXM zmcX9tbu&-V(N3m_4uDQo^G86>A`TuGLiYh@-aOrrO-DCNNzxTdrF1~fA$;k@LZAFa zlY5cP1rc0Vk!}=NvB*EVdbG+ZDc0A=-*#I)lP^kBG7$8Wdt_Xpzf2+&S0V-aB`QiD z9AzsAgooWS-Lf{x@Qb1SV4v+npt#sT%J_uSgD*r8WYOVe5i#5Sji|;i*eM6E+^POK zx|UMCoLqBVPOe)8uCBA+&CfRoWG!7-;dgFtol8WK-K+w+KkH(k-Hf4o=+?6XrR9(3 zP(Rp9qEAVr)&nzBsWxh}se-l-11Uql- z!YMJQncTFi-a?tBNu>;U^5qXfAN?vkYdn!TX^4TWY{*5qQL$yaL75&tS!!qzktA!^BE*C7Q+9Vh8bu^K z30}0MkD3$jo<2c3yy@y%r+hmlM}AQhqMetAhC)g^Eb;~|H`68LOy<+uw*OkQJNE6W zIsd6eK+zoXZ2h26xAqO;MP+9#lX;v|xU3uh@*_-d1aQtD(ksLk2+rolvFkU%4My`| z?n)b3n(lmWpo(Sqs^4DnuT51|j0i*H9*I=h*Ga_ZBX};|i(0eyAN~Vtyw_O{q$7!U zzv^N8tJBdyvp7hHv8Ti;uJbG_FN)JH%IT|flr`L0i>E}G8Tn40GzZDQA&D5pL7#W!0?V32$aQqdb%kx#-GDrn zM{(q;u_tRQc$xgqd^qNGab;r}sd#6dQ=E!rtO{=JHHVgT0lS?u!m_VO1t#*qLEq;* zE)09}iubn%ebWiIyFAdZvk3)DxjU!XZ;g6l&}2QdJjiLfwJ*AyRGrKn-aH(KwhaX3 zfVb&VijVQb#(b4$vKu4wk>o`Ho^rEfD%W-^+4Q;H+%+W20YX$c`!!l&f0>hV{@|y$ zqBOh_TyGp657afKa)g+7WU_@6rt0Sx+2sz4X2|+nOs;sqs1tH@!T7VtQzlFTZwG@J!C|Pjq1B8F64M13XW2MIcAFQ6zWGdLWEzpL zE0DT$$ZxtJXREu&tKA}i!oyhE{oO!XxYMng+C22+`;w)`bI&$-z2kBShqu<$jsbHX z#_P7=!OO5=%IcWpFOyDj=;Y${+mh>Pjyr#j^LC+U{T!%lj^56Tx#Wc*4)sAPRvFv>`YLcFf@dsApG3{X+ za>tttd%mMJ$8{7Owlg<)-70bL4M|VCFSqcOTc5Hy_OKuygQ+;1fLy;qrZmUnnES?n zJgC^WIEw>bBK=PN-yz-tDjl+ZrV-TiBcJW&rP!)9{4liaXuzi6CM|@ z?@yOz-oL&xOzt*A>jrlzc*BTR!8D%v9cP_G`KwakQZC2hyU4w8!R`1a1N_kb z|I@?j{rsf$HxCOKSe&m&P4yb^X&3t}yq?^hrZ_!23M+Rr|2TV}nM}jIv-dDnnDUEQ z0Mr3Q5hm}75;ng$fh2pbGk7x2HKwDgU=^F@?TcD8Kosirm8?qj^oCs;nJh)4%$Gan z=r?nwGNGBKUdkYrj$g6oC?jqJp^i}RO%N6vNg+${DblM@fR%VDd!Gz43XSBe>oq<; zQTEn@+&^+>_Ia&UuT_@L;h{36{m2kyXdaa7U}&!Lu@8SGTw-1fBDL#AchYF+0Ki&? zPR6xc)0h}W#7G#C8AshB6>!+|ciHW>O$tiyoeLR?j+z(hW+`Y9@&9Aw0U+-zpPb(yT@MNaMyxV}>E^BV9E?J_ zoJ{Luc?6V{*11NC^-RT)wXjpVGN4|;=4J)Hw$TDGlY#GEunVEoCg-Z?DF5@VWd9|D zQ^>&hw<|EA8<#A$`tj~iA|jG~__jh)KJ61;dElHBjR_CyUe4`| zAb-&P0(2J4_|r&+?V&lkpuyAh7&XLsS&1V(PUgLq+ezzk%`Vh|LVWSZ{2as}MuTNI zwV#qSGj+Tqb-DyPV(gjfhjy?SY{*;cj+NVSobE2P!>}c@mxN3Wj2SdF2lx)STI)_2 zR|2*RRMoStcGjQHRc9Uc+ez7Eho9ap>Uex)Pf^9ydn(K~H2xnjUcP6Q702a$y=U@h zEcp}(h`7K%+LKEe6h7Y5dEekMcDlKm`+y%?0Od29j)D@~on&MC>LWf{^*eQNj~gTE zn%|=(!`9p4M}eb{Eeahx*Gu4Y$Oje!Z;U0-;S#YDs>cAMtkJzAHJZZR-Ju@Ha;)YY zN4x1VQijEOB|&0&uY=MV=9NqN=KVyOaeOWujGeO6hx^Y8hmQ~#r^e*}B1)|MmSdO< zED%tPn<`THa_|OByC^#O%&B9W`|Ngj$_v1mFY{$!MV9rX%63wrK4i=z978=>W{fEF z8piB(ud|Ky@MGU8=wK@{z|=FpseAg~wRe)R@-b_N5qHNX3FbzlH(MBtC^Hdn@i*11@9f&I`7 zPAk2l`B1p_sgDX2+dM^m1l0)achBzyLzRGu9;x5qu)%3@uh>8F1`Zkku3{iQM@rr^ zdi*C4M>+pyzR$AxHI1+O?KJ++7x3o<{4;X?xdOj#)t?yfCkXz@0)NurpE}@AA@u+G zN{}4hN5=2iS(yi~HCPCy*Z%^bD{@F&$l1D+3RK@lyUse$dDGozX=0_1?MwZ3odk?o z?k(qMJ1QD(sjMIv63wy$R#~?sLk+s&=f0mUB^UqOy*TQPTt$tbp5h5h;RG)w8=wAP}#|_`fZXBeX zDa1cF1BRM&UPzbP)%5o5N`T5NH|yC~D2UCdS^Nxshx{It6M%y#<9$&#+u^bpp|T~3 zKGt|Txw!V`0WdI4j=F|5ghOHZVz<5(2j7ysYHsh0aT;yerJ4DOnNsA{cecBJx}$q% zvnMlGo(2;Q0ECU=_%-|ie0Q%_E&xYig!e2U>BtbyoWHA7`R;xK2mh9WKd|3kdhuxy zV8q64C?_3CNF)ocHgG*Z=0otsFd5K@C|thXlfCFHr}%9Eg`G?jBVRjT6O)3 z1PQf;$efo**1-=d;^a->?P2Y1XhAGcg=w_aQ&^NZwrZ9gYTYjxts>teeQAO14>?`-l zcAU&=QCx9|e!`yuji#Q%-{gAA|2yYB9S0v^?$HC|oLQC!kJyy?t@9%bG`7f20o?*G zuXk&;<3XQWtqtnH`=ZO-Q9hn&_Ck&g8KPZw@A&FMxb*K zVFGflm_{+YooTDXHvde30YBaqXo8%oigIOkE2O_L|9(L)^L=yD6tf~ff4zY@5ajH! z`gglrpR;oBe|Z7??=5_1I&h(|{+^0mP0|2#tr@LcNjx^sgl)GO0sAUfxc%DD^8Sj> zPLGy(g)kEv5EhsML3E*hF+v{uoq!ujre|2C6UETEu8cy~&uRmr8m#LsC(b<@7H)E@ zV8kU7t{*W&kA+L{1t86Su|#lJ#*OS$zVj5bA{Mq(dWnk>+IICK;z zh)A^0jIoI9jJ$El+5O9+AB z&kFq&{GnvOk54`o+7(atw*^ZA8wbN?ZQ}pNT7sDVtEl zOzwc}L~?!9jul$?HQsi<=_}cT&t&{SEEwU;MEJ$ac1LRp2ufh7zk2LyY(6Pur?3iI z<1j52G}|cY$$AkyTG_v9c4dR~l$QieJ(x>k`Y_o%Ms^7fufp9iv zI?;>cx#?n7?k@ifC*MQnI`m&x8RQ>%1NmN}O#{N)jE#mdGRVM;i$=WeuGwpQZ(@}| znLHGQ0YzHoi@gW7z6GiSpCBqJ{g2f7g2S$&q67Jf_)?tq@)xjlg7rIbn!s^i6O1Mp z=0qX?=s1zvE49)h`a1*J7+6^WzS{q;aE_8$sPJNo9=RQ;oY5{@z3sLtPBYDE?#&j6 z3#PV8X>K{a9|*cn1C0j9_d17?0BfBhd!}8(OUBDhojfX#p@5@mK!hCB_j-k{`(w@( z?Al9`De^>+3kKO@<^mey!c)iuD@jo+RFfO=Dj5Ga-Ov=? z=%bRo>0!(I-E6l^xvVAYlI9*EbXFMV~sl19*AQ*Flc?rh4&?HFF8?!EK{Sfb-?88^}5? z*Qc}dz=f#wJ??tlDTj@q^z1oB-1T}l8K@+hoHf59 z3andkc6#{qb)-VdFpLr~3s3fO)joASeB!q0wS$c9p!CyF5Jah+I!tNt^1j=BzBYN_ zTb-gjFUICr^RWB`Z0u9x90(hi>6_k;o7F*o`2eH&GUU~UgP*az$_iCXArm-^aVqGc z^AT~R%saH0wQT=-VcH{;)MyYaurt3nr9RVmu-%}^XfP?kgXS`j)b)#HZ%DC`?%`N9U5Go@5 ziO#lMA9P^3?d+MeA;6v0K<|9>nqe>Npv`*%aM|}&>HU}_LQ<)K8r5;f^O8@J*=HJ` z%bKTM5DXZ5OID|>Hn%)NI4yTB%@3`A4x#bL^YhUROIa5|OZz#ijF6Nyw;*g1yHcyH z#6ht&nN?hyM==hX;?`-tysaHa1wHdVURh0m+^H*@C~$7{UOg#w;j@?XRN38%|9c|* zw+8QnF%?i9QxxuG{SB{zL|TO;6tSD!4let1E#s=f=)kY3F&W&2#c1 z-L5#B%qAMenh5^&4WX513;}`0fgs;L(#{VoIxnTbZ!*4Dt^%q$0g1py1qU&|WhQOe zDZIxLd9K)(&z%eCF@mi z#Y{h^7tFhMljC4eD4#|#b%p~lALC!kyx6WOxp_AhPNpI)&B45fNwB$NGh|&NU7wdN7WsJ{$u7OtWexBu61nY|bgo6*7!p-11-P$+T&DZ0C*_ph6Pgr0hm-DwB*X+1 zpPTNB<-lf5n?LThB+z7&9XW4&5YKwC7qvf(0C)FdMB?*~ICb2gd8{oD{lrkXjnAC* zI3ny_gQc#)@D>;G}q=3X@4>C(&n)`$2q6`_f;va;W`rckv#la<@kJGdUO$zBC{ zdG0f{G?B|Jd{_92w{q{x;zE*rOo28Ve;xSoaUUCU4 ze7BuVoU=b8`#`vZj8gU<82)D8?i!&6_Vq&5uA@mo3SZzj|JJ*`fDC|rfq@c-6&F#Iz!i2@;7*(9M#a=EAW{f%sugx+ZWV^0l zxlD@Ee%cbX&Q03$y=a;`{t}@c0+8y-YCCWYgO~mi-{{`K0&&!InR3a$MYs;rhM2*U z;B~*vGV^5={#npnZpZ%`ZY1IB_oGqpcu$utD>LQ=to+@5^f@@lCGrZ1^Su{96?cjC zUi(#Xuqs{2`u|T+=7`Ok-_ocJu&XvVO~JOvWtFW4r%e5nDM!i3;YH)5 z>UX42uXL#h(#%)EoQppB%Eh|Gd7cWUyu7{_CZ|3D9`UXISfR_&iXu+uN6i7U%(^vN zZ*;u_r-_ciy@V8rToz9#eNN20Z!fFV_0~ouCX)28-9;tlUer}>WVxo`9#MVU^4fuj z+d`KAFZSLusL8ea8rOpdlxhW~T0u}ir9%J}DN29`+0D+IeChGXIsRoPqo2J|> zD#87~Co(i{926dHUAu7Dxso%SU&K*F#>y*GUF*S5naN)e0SfvxViqSyKd($XV8=dJ za$8;(;uIB3;p~q+m-D7K3T*tK--I6dB->Y0u6#VH!XGbasI#=mNz)zG;D_>_Kaz7!3&o3wUP}RAE-);-9Q@Tv7!jzfCaslm zT3))lt|70dTIPrFxe^T*?Clo{qhCK8ok1jk-@M_9+euI`?}4vf59>)6CDW!a@nHI5 zG7z*!y9($_tDk>?E|*T*IA7~JvHxOG7ypY?w3`E8Ro-umt6zS;zk=7b!31YSnu(+< zmrPu9cMp5F{RFyh0oMtlO(QC?t~E~XYNhh}zbNwh-waK=z4Y%*P`hj=8ee4H+}l%> zs~x7GIRix}{{uE}A-N90ZTUYq?xTOp8XWfEy7{IH*~zw#!@{#wBY(to=gbI0tQ*{7 zjVt_!$=|N!0>f5$$SLuLK>t(;)}GI0rvwW4Epw#_BWuMa(6=C#XFopD+;u+p0OXNh zkpU@tkMeX8FfT$`o>={)x>_J_eQCYnMgA5rt#!HhWA>;%%)+q5*l1^|_r^c{0xnQ$ zUA};0$B#(C|IsBYYWOZ6I)?WV0ePNRyywn!{XAJDOSu5Cn|*VW{Ma6B8zZ&pdsn0e zF?rr^T$uTN9m(5NVV+3>=k&^?wxTk1ifG&YNv3QH9y}(tf*ud@G|F3Rct}ifk->Dm zYMQF)PT&*!j4CyME=;l;?Bx3K=b?9%;kUU;-4~<1NS^&hyR>W02SL|_odx&t-%E1aGs^8`kA0F6CsKXHp^ZnW?}J8`?@i!ihhskj1wMK;K?OcB?VeB<%&@E zse|BEW#X&2`fsZFEF4_H<6vfm?=j!THbsMfv|!j4E%zVA$J8*q>9B5c`%!0ZkJ^~A zS^T-;mn{7sI#)_0KynmD{zEodcK`n-LmJM{Kg8-`#M0zz4FDUd?ui!}XQdU>{SQ{G zhE`Uqx&SM*H#N@Yy}ja!OHuv{-CTf)f^rmc|HcP|nU5Y!9z>X%*);!ZS}9gl`zfhS zW$bHO-q;mV-I~Q9&CCH!yHnRz$D-0yoIZ88#_6_9rjZpwaP_ePFvT|}`Z7)wmcEWg zfZ58eicfmDjEM9vrm)MaM!kR!oN7$(D&E@axq*CCavan|zR=)2zKgZ*Uu|_(lGy=Pt`t3yZ z;mrmo>nz+cy+TF(i)l*EhEu{7uazrq*03x82fZ_waVW~QH2Hmhe)apmi;icLak%M9 z!1Csg@0z)3MJJ_|Kc5E3vhu;xZOur~>SpS70P4nNMSBBTE z0W*87D_caki+_JO?nfisDzXgiu%Km^A{SF+ z=<~eaW(K&+(9T1ISEG0QP?@he`I`mEA6~%vfBjglc3*-NeDt(dYrjk$$@k|fIPy`{ zpM5`TjA3W$2Xw(&_ogMVz=QHS)#B@`?Xd%h1U+1RQ>OoshByCb)b|OLN!cg(^hKUj z;C!sbL^J&mUb<1Yk;WtQBSAE!-kF$TkXFg~94i{vNN{)93XT?Vo6mYO9GP1L>*@;k z>i>Kdt8<)K|9nrj+yBusQ`v7BKYg|@$e84ra};^2aKKAxc(?Xs_wl;+dlxJu9BTZr z9koojPe#^i-@8yAo6oOrq@K#+vf-wa*yKuru9(VgGwsth%@?hWa{kF4S9H|%K27#u zit)Q9hkbn3wLf{h;$rFe*z*3^9V4G7($5P$yj8A|bXcqVTN%hUn@oW(TFHTi=Y#n^ z0K+v@HBzJLsBY4Ea0cUl28{n(1`m%Ky?cf z34fM9Ao5sMvXtWjZ~i*nrjiyfIAtc^OmyHqpwTSpJEGxZm4Z2@HLcbu`_pCLBAL+qkK%US-p{&2?` zWWe^djirJN&I=h}1kmr(YwSpJw?kHGFMngGuvX15DjC&iI_OKoU%}*=``ntnd3F5T zvshGNlH2z67pu3m&%yYGP|M|Z)7l%l+$dKZ>)WPhMZJ@9uuGs-(CBp|pl>c()GrvA zb!Tb8?(A|3VZS%t1F50sq3pQ{(u1iMszmcBZ>K7N(;PFU`;|-0MQa+j@nwLE;=R5E z`7k!(+pA5tGC4OvzBT4r7Xs}#OqG3K{gU61B=twC^j~>x_(`>?d39Vqh_hX_A56}e zr;N~(9b#?Xd-R9+6}{>hu`Z~GX=nwm$nE>K6u30+H)PaDN1G}6z1H%RE!|r3mZdu= z&N&L}RaAX(4 zYa12c>-=rDGeg{6$u;A5J}Hloz67GL=r?OCkjw%t%FKRQihhb5_|Jlbe*93Mc88mX z3`~Z6`diwVkBoVW{qz7U7cI!_-{;P+g3ABkd)H;WwlEdIp;Cf30R;d>*&lnfK-G=5 zja|O@w(#Pk$B)Z1pC)9#eRrAj>^;?17pHx5>xlA>(^6{I5uFdLRa)LXKPvp{&tt8x z#y{KUnOz^6qGFR~CX(!INlhfh&Hi?>vr=8)BuIfAI7W){^|o!MVhj{zm7jNB-PF?Z zlDd(zro5S#Ok7)AD)&J!D^arFM08#Fs|`n)ki%6!%67Md#N*-$TJT-P)n>v@yvMpO zRcL2E42_j;4`~Z+FQk0eRZi02fm$dN??-ZpXIa7=Ggf=Mmf78-`Kf}6)b}&fQl->V zxJ6mQi5(UW1B4%VLt8K0M=p&)K3wA5kb9C09zk&z;Tzqkt@_w8T2EvL#sH~>dj-tf*bu?l+Bg?}H1 zKnKC&`=Sdn{NifHvu~Fgk{QyZS5bq!iQc8@#iua9etA!0I6IH>Y(hO zS3FRKG_~(uj-Fo?PqUFuz_1k}73imDls3A=At$bjx&HFs($^h%_h{C_Z?@6b6W2uV zlPP_>s2xpiBqZ0c$>qt29;|B4BlUeEayN_jT6k`A==Yx6E?2=RLGIGc%$l5p*;XqA zyqbwR)!X=a$vk>HVI&f{()U@eAN@4hIpCMM&-ltOgCgoR_xRT9)zgDc)$5<+AkMRi zIzdFXQl*)k8&WfYP|Ll!3rEXR19*vnYnH`D%vTm&n(jF_g2p9t(#UB#WXr3O;*Ar; zxy9&gBV}r0N0RAfC!hITYlrRkE0gd*OT1SgW%!{LQh zsb$IPo19!KY8C-veSvn3yL>)m#rhU}y8?9hT^M2@zZ8}&EtLAjxsmjz#1pyTpqTKm z`o83A84+#)+if0_D>xbMb9R5V`6)z11JK>k21cG{DZYN*7x8P~dc>?WPV(XFi?X{v zq(cPR9!i%jjz+bG+B`n0uX>K360%xl-aBdG*YnG*gM1RucO}=LpG8;Yw_#i=K;G-e zR$)DIZ5f*Le{>MXEc5J+#8nkMFg?d_R^#+lCJeKb8!v9Iy?(`7%! zqVi_wS^~wi_u4uB9qM~`?@2e~4$Kg__j^c^MN-1?wCQif5;+p$SuaWmc}7M4uNYw_EllM4+MZ<_0*soTe)WB}=LA4C3)U z@GZTjdBLh8v!23v>8T-hh|0QPL$OU7YU;osyy?Limqs<%^i0`H8DFKv0x#L{IEc5~8H7<==?OhI+Q4@OllmGNO zc{@^fL|>!Y-a~2*8l?(3vw5j0TC!LcYwok>{bf>RVTXRk*}wVLeI&s(A?Wnca($0s z?UmfDvbD=$y`3F9>+c1fNtyDH`~mN90qs5oPn1?rnnVhtfXDfBNq1B4{ncK};d@z$1+PGfQ#(-5KO-n2% zBOdhL)J`UZh{?V<$DglD{s>trpTC18%8cpU0J)|nd2O$iX%fGL1N>o{A3qP&%6*!!O)pFW^^+^y#2|yD$i?eUIQ}v9i!Yr zi5UYyiB?;Ypuo5*<8XYLExQA3a19eaK z04}^U(0-TAE)x32gv9~Dr|6m$`@4h}$&@Vy!nO*>vTcDjhoS;lIKa)4iGs84p&D+eme%@~?d{Aihn*%Eb4D4jYcF5bOxBF>ScPuBHj$BS z`KpxWYnD6h;vk_3AYk1X|GkFmzkMm>K3q|XG<}a|xwa&{MJb1r8Z+}SwYt4^I%7XH zm)qx>xixo7B)B|VI>9QVtsU4_e-FtKC0&&!VaM;a>|axM8_1heg=22&YpBNVYE!=-2xC)Depz_ZcI!@C zSfqWdHaDXLf0P{Ncs&kGpF3HPqlGV*cQHCl&~HC&2}Ri^qZ-H3W24o0*fzC;+!k#=Srg@K z&o<@Rwx2CEEw$#ZnvC*Oq{dtap&SQa>G%Zs#8^+3h!6Wz!x;qf3+Kf8;*NE`iZCrY z*p6G(z0}nW)f(ct+d#gQxD+SL!KIb1P+t-hnZd$HRf`%~@n!7TF|90g2-or4YvNcX z``#DjGir2Kb@#1={mG?0-S+0h^{^VhctXU^yz;!B;9|wKd1ONr&UA^^q^@bxOLy|@!IIr8jDVv zd>a2A@`|MGPxG~rqp*+ufy^A!5%5`FwJDc|>P_S?4&{97XhpeYbH(b1gbtY6+EIUR zC3;)0=StpSruZ6_Ob=&YJ?-p73FfkT2+X61ql);yOB#)O;K45xO&wTTrD2#nK2pp zl7l31+aL6;sj%>EzINJ|vc6u$BsMrw5vOl-GGbSGt`==R-|D0R!Yv9*Qr7Kbq0PD; zTbTBO_ffki&wKjVnNfSiJ5?3ZJejKz1QB3|c%HuwA35D>=q<_DXXjhLVIw5Z#baxR zL$6mSEwnE&_VuGDK>Q0Y3azb0QKyKK_$B+5o0UnD;Px5B^KdI6{eqIO!#v{3ZY{Hh zxy;N{d{dF^dzw}7)=ZaGE3R>WXD>tD)c|T=eTgo=+sE;>dpv0Zs@0HTVW`efFHci` zyhd_f{G&$-JEEZ46qpA;t$#5+G*CKe}(?$U=I(k>?giw1?67HJSm;~ePzFxIxl=Y zitjf7e3;*Vf&DB$uNqGc0QSnD^I58q7)h6P*Cx^*27ndn1OdmypML_UmF)-Vqq@x0 z4^7ym-6yU=bF)PCQqJR=u}2{(vi;(59$xbDz1k9dqs*NIojW{_%APd7O_y7EYUxv0wyQBWajKCcRN+diMOUh(2x#38BS)Z4K?k#o zz)6A~{&H1_btLLwIel{10(ZrJ1%`I;O=*uF;(1j0T>2$u;)D=8Uui;!J9w z(nOe8qz~WmFl};*xc<&{(7s_)G3Jln1`;(^Nnk56s*B?whFAk3dNODv zgZ@ZY(PnIJObyos{NO$rHmPf_;JupvPO9>b9mw^hxPjC&%8#qPInivnJTBikdlF9l3WHtR=Z@NP4}QyZds2bn1*$E zck9LOY~58vuG?2p+2xtmkQtt=qDEm19=zMjv3@y_(S2^^VB$5PD2UG0-Q+*T+g^F1 z#=b#8bk8;)GoUe}K*~tk<9X~snN6_FW4_u|p6E2xS6-4JJ|;bje72+bIICs7uZ2Bo zU#HKh@T;wXB)Tj#in^W@LYP&~!4BU{s`e&|n0<-=U{+^;X$0+lqZs{yg|FcS4dl03 zs1k$R%T&M>lq$dmQ(n^L$xKpsYc}VaKu62-3yk&lo`fvJFX?Q&7mxh$pC6;>Bg`C> zq{wamtX$BkbYn_6AZmQW*~Y5_vZd_zMEs7u1QXBUh|;?Yq)QgO@;y z)@!Bah5qIxGELrg9(!_6uzi3Z_S^U;tMm>7&lxZH^7L@N8Z?S5!C%{#I4C~IuHaOV z;6CXE-z5M@b3Bz-B42l~@;c>yjQKB(R}#l)?`fySS7J};U&1Km%;w9dIK_Af{IRK# zmao0?mcCo?0S<{LL`I&=DQkRPUvljWrHz3}3R20#w~Y%gvTd?wWS`;;z)P_y;;))D zJh(HHPaZ@qbxz4%CC)t$n@E|vOLT$;-sQ4HxrWwpWN-w%n{C4Ozu;hA#InWugJ|z( z&$I#@(e~6;+A0GS1W`PpDVSrN;@GmEG_b^0e|rIiNs0SDX<5_hNl)ZXH|k@ z65%}|uwrNQYb&}RPK!13+Q6io3G8%icbJwku@3~S-{*E`qAt!ZKD$xv0CV;dz$D5O zRFhqYtJU(fVU7hdM2vP8b#ZMBoCBxl$A5OO#ku!fsOmDsMj*SY$j^opBy|G2zN9Ln z6J7rrcRoMFog5awai`ng;m%iykw*|&NHX6|aPuoA+MWckq~_k&trqyh9PPpl7iZL6 z{~HIZB@U9$+8VmmQ1@=J8l@i*Pe8e@IghZu5qH>MpYYu-ocfZKELzUcXV7~r&)Fi) z!D|@ohhpEFyF_`f9)ePAf5#ue|hBZKX+As?_~acC<8eE?ogim%L4E2zp6fw7~o`Ung4!Em4Ew|j{ikY_^&LX z_X&8_PbZ&KtgZ`ZgkhtN9IN%mWsgUxyhynl%P8dhh9Qh8{M3WLx`+N>{!ihEK5nO;V`TTGAb5@%D)#Aws!GYY?hbi-S=Ubm0 zJM~xK>0i&|Zsz3^nOiMY-}jpI^p#kU0wtr6yl-wNdBcB`9y*h(Ow1hYSKj{DfB#qc z^4uX3%NOm$g~jZc`p}+K8{Y`~5IQIOpyq_6gX@s3te%hS`FqFy{onu9mqLv{u7hql z{|*sk+y4uZBmXZ%{`U#`e--t&SsZOfWl0wc<; z2-rN-IQc?SZ?gw|Rj!6qvz1ykys^T&ykaI+3B-U(ak{duf`vWi`p)hnyP#+F*Y`~SWdXCXPTZg z6mWiN1O`In?MsRTr({ohy)8B=@am1%f4qcq`MmM$anlDBLh)UZ&whnS@5PPIk|BJg z-NlU)FWLTeLCukVlkkTBxQsmh<}w1p$diS`wDR}?QQ;Gyu#^wX5hzSzQXFADM&Db& zKRhI3wh6K(Ty!op8z^4%O`JI)Lh$Z}+JBwKW z$A$V5D3nYe3@1?6R(h@KNJdG&MuiA@jD5+2glFQ1Rq@K#k;%r}X_Qr-?3Bo6a{L=Y zKg~&X3tAXwgkGz*zo$S_iioFEBm!w2SMus};={cQLZNk`oB0| z&`E0&Z*I?_bDkzRGUepqM|@RjPb0@NLz*A=#(PrcOdP63%ve|TeQhu0DJfppxRgF{q%(58HLy19`%?mPOC(b7J(Nr_>J z=yXZ^UpTDgjrL*XOzHRy5G?=xo&1E|3f?P0K2DKVC=fI6@Rwob-TKJ5jcnK~P`2C+ zHVb+_C*ipvC6jQxP=t$r5Am%Ts+Db4Y%j439UK0c0KWdo#OB1@(daYj(eYz`tA(Ks zOO(PNhPkstIM*eq`jix>;djaFlkw7VZ7Qb3rnNwN4u(3+qc!G*b3^tSu_Mwn}VK++DaR6coKlK9)2g0D z?sM{gZPsBXF6Cc|Q3=?%98wcMo`iv!!_E^8r4PLYU8;l}M4zsz?jo;b4?ys`>9Ttvh zNl4}M2@xTVj-E#s0YVyB7piTSI%|UNWG^LUf4-3;g)J%aS}r(j#h?mxa7{8venh3u zV$FMvFGf|Ot#$H+4V3zQ$)8KNwr1cNnDsKZo%FK`xQd`&)x_Xq2!Hxao>fd2p(}M? z6mtNH4m-!fIk5T%$*6GF^-QrToV_$rVJufuactVtU{&Gymt93P_WXet<~^$RK9htm zbx9te40>(*dK!+mlNvs8uY=|Nz5AO^zFQrQT)_OyS~%BhKwjf!*=TU^e`&_=pir$f zl**x;`3akD`?EF4bZp8=!b~12R%(EZ3EpFXH`)eKF?|l*F!WMiCo)MB8}hDRn>(Ri zFXn~}c_+~JQO_cIyOI3hG8G;KWS0D3vF7SKiiqzcj)k~H+bUyf5csR@w{t~#ew21f z{~b-y5vMBQQUfVI??*nWr@pEKw-t%gZfiLfV5SYu|nRcW0>ne~+ z)}VO-wJ9J3>Gx}nqpjT2{MS7hf>tLatH_YefKin?U>ob$`*iA}n4Dm(> z<$>w7rr6Ns0R>!+6vXw}-Gto*fpzosvB&!~Rpr3_KR@iX3ulWrfiGx#Nl&~^S1xA~ zH3{9o*azvEOww0*s*vM8AF#O2=sX$PSRm&8&@_Ba-?><`>B zgO{cY9w*Rld$8f6#%8oiqc16&bY#Z7E#7M?m4 zuVYgZ5jK-)c=gE*pv5l;&AuQIX@|^qld;rP)(O%zul?vYfX3yQ?#_lou21kbcJ8S| zaACRvAL5Toc#~40=o#q|85a$z+J~IWMpu2dtIjygb7j=Ih{6?c_aXj07Yv(hOFOzW zqXSU%XM+L?u4F0EM5WI)ihFu#2@?VI{wYb!PGpBfdv}fE&XZLi8v;7#A+4VeKKL_B z<$ZE$G`DObxAWAy*+v?&3R}mef2Eq0Ss*pIEe$?Y)v7C0{&irn`%+c8nQdfrHwQK4 zkPBYxkMmHJ@V9dRb7N@j!NHRy;3KJ?CJIMsR({cgYH%pkm)u^HNQY(jP=I80ZBj2C z%~)EK6AC3494Nh|HQ%CVZJ+inD?}?hrv6cLp?2x$T+J#>gO8Zj#e|&nry>)E8cBl5 znC?0(zD&QVPezwuqfEcAf^xb=pOd2O!!FZj$+Qi4;mnF%$$cM1zE1xcRt1vhAlGoS zDOSvLKokb?>f1R-AUq@UnDl#m#W~XWhH<5^5w7QM-vm_cq$10fj#}C;Si*-1fbwT- zt}lQ?KMWh6CNCt-8Gj>)qNo5JI`+&i54pU(%D*<`(G-=*0Nqn?9`uG48|S=(Y)BH7N39bo!yOVY|rSa@#u zTl!tw7;4}M+<6fUK|z=xgU{%>NC_(*9qY0Nk!t&DBtY3mbrFX4F~w~GqgoH?uj**V z3*+}|O+I7c6-o>=I~Klw;kqNodS2cA+NL5IR~Dppzh~(V@1#?p@f~51X13s~6Q2E_ z#3MA0ojHBJ3Ls9}h2-$cV{i3*G+usz50p-|5)k3E&&ge^y9M(2TNneM+W9xi!%238 z;fYS�NrYp2jgrOe^rIAag~AS9bRktx-R?>f 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 + + + Docker Host Info + + + + +

+

🚀 Docker Container Host Info

+ +
+

☁️ Cloud

+
    +
  • Provider: {{.Cloud.Provider}}
  • + {{if .Cloud.Instance}}
  • Instance: {{.Cloud.Instance}}
  • {{end}} + {{if .Cloud.Region}}
  • Region: {{.Cloud.Region}}
  • {{end}} + {{if .Cloud.Zone}}
  • Zone: {{.Cloud.Zone}}
  • {{end}} + + {{range $k, $v := .Cloud.Extra}} +
  • {{$k}}: {{$v}}
  • + {{end}} +
+
+ +
+

🖥 System

+
    +
  • Hostname: {{.Hostname}}
  • +
  • OS: {{.OS}} / {{.Arch}}
  • +
  • Go: {{.GoVersion}}
  • +
+
+ +
+

🌐 Network

+
    + {{range .IPs}}
  • IP: {{.}}
  • {{end}} + {{range .MACs}}
  • MAC: {{.}}
  • {{end}} +
+
+ +
+

⏱ Runtime

+
    +
  • Started: {{.StartTime}}
  • +
  • Now: {{.Now}}
  • +
+
+ +
+

🌱 Environment Variables

+
    + {{range $k, $v := .Env}} +
  • {{$k}} = {{$v}}
  • + {{end}} +
+
+
+ + + diff --git a/web/templates/index_to_update.html b/web/templates/index_to_update.html new file mode 100644 index 0000000..db9886c --- /dev/null +++ b/web/templates/index_to_update.html @@ -0,0 +1,215 @@ + + + + +Host Info + + + + + + +
+ +
+

🚀 Host Info Dashboard

+

Runtime, container, cloud and Kubernetes metadata

+
+ +
+ + +
+

🖥 System

+
    +
  • Hostname{{.Hostname}}
  • +
  • OS{{.OS}} / {{.Arch}}
  • +
  • Go{{.GoVersion}}
  • +
  • Started{{.StartTime}}
  • +
  • Now{{.Now}}
  • +
+
+ + +
+

🌐 Network

+
    + {{range .IPs}} +
  • IP{{.}}
  • + {{end}} + {{range .MACs}} +
  • MAC{{.}}
  • + {{end}} +
+
+ + +
+

☁️ Cloud

+
    +
  • + Provider + + {{if .Cloud.Provider}} + {{.Cloud.Provider}} + {{else}} + local + {{end}} + +
  • + {{if .Cloud.Instance}}
  • Instance{{.Cloud.Instance}}
  • {{end}} + {{if .Cloud.Region}}
  • Region{{.Cloud.Region}}
  • {{end}} + {{if .Cloud.Zone}}
  • Zone{{.Cloud.Zone}}
  • {{end}} + {{range $k, $v := .Cloud.Extra}} +
  • {{$k}}{{$v}}
  • + {{end}} +
+
+ + + {{if .Kubernetes.Enabled}} +
+

☸️ Kubernetes

+
    +
  • Pod{{.Kubernetes.PodName}}
  • +
  • Namespace{{.Kubernetes.PodNamespace}}
  • +
  • Pod IP{{.Kubernetes.PodIP}}
  • +
  • Node{{.Kubernetes.NodeName}}
  • +
  • ServiceAccount{{.Kubernetes.ServiceAccount}}
  • +
  • Container{{.Kubernetes.Container}}
  • +
+
+ {{end}} + + +
+

🌱 Environment

+
    + {{range $k, $v := .Env}} +
  • {{$k}}{{$v}}
  • + {{end}} +
+
+ +
+ +
+ Built with Go · Docker · Kubernetes · Zero Permissions +
+ +
+ + From 19944ca23b34b0bb40c184f9fe4ade0c7814006b Mon Sep 17 00:00:00 2001 From: MaksymLeus Date: Thu, 22 Jan 2026 20:08:48 +0200 Subject: [PATCH 2/2] feat: Initial commit --- .github/workflows/ci.yml | 120 +++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f33e95f..366e7c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,74 +22,74 @@ env: jobs: - Verification: - if: github.ref_name != 'main' - name: Verification of all checks - runs-on: ubuntu-latest + # Verification: + # if: github.ref_name != 'main' + # name: Verification of all checks + # runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 + # steps: + # - name: Checkout code + # uses: actions/checkout@v4 - - name: Set up Go ${{ env.GO_VERSION }} - uses: actions/setup-go@v5 - with: - go-version: ${{ env.GO_VERSION }} + # - name: Set up Go ${{ env.GO_VERSION }} + # uses: actions/setup-go@v5 + # with: + # go-version: ${{ env.GO_VERSION }} - # - name: Cache Go modules - # uses: actions/cache@v3 - # with: - # path: | - # ~/go/pkg/mod - # ~/.cache/go-build - # key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - # restore-keys: | - # ${{ runner.os }}-go- + # # - name: Cache Go modules + # # uses: actions/cache@v3 + # # with: + # # path: | + # # ~/go/pkg/mod + # # ~/.cache/go-build + # # key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + # # restore-keys: | + # # ${{ runner.os }}-go- - - name: Run Lint - run: | - echo "Running lint..." - fmt_out=$(gofmt -l .) - if [ -n "$fmt_out" ]; then - echo "Go code is not formatted:" - echo "$fmt_out" - gofmt -d . - exit 1 - fi - go vet ./... + # - name: Run Lint + # run: | + # echo "Running lint..." + # fmt_out=$(gofmt -l .) + # if [ -n "$fmt_out" ]; then + # echo "Go code is not formatted:" + # echo "$fmt_out" + # gofmt -d . + # exit 1 + # fi + # go vet ./... - - name: Run tests - run: | - echo "Running tests..." - go mod download - go test -v -race ./... - go build -v ${{ env.MAIN_FILE }} + # - name: Run tests + # run: | + # echo "Running tests..." + # go mod download + # go test -v -race ./... + # go build -v ${{ env.MAIN_FILE }} - - name: Run Security - run: | - echo "Running security checks..." + # - name: Run Security + # run: | + # echo "Running security checks..." - # Hardcoded credentials check - # Exclude common directories and files - EXCLUDES=(".git" "frontend" "node_modules" "build" "docs") - # Exclude file patterns - FILE_EXCLUDES=("*.yml" "*.yaml" "*.md" "*.js" "*.jsx" "*.json" "*_test.go") - # Build grep exclude params - EXCLUDE_PARAMS=() - for dir in "${EXCLUDES[@]}"; do EXCLUDE_PARAMS+=(--exclude-dir="$dir"); done - for file in "${FILE_EXCLUDES[@]}"; do EXCLUDE_PARAMS+=(--exclude="$file"); done - # Run grep safely - if grep -rI "password.*=" . "${EXCLUDE_PARAMS[@]}" | \ - grep -v "^[[:space:]]*//" | \ - grep -v 'json:' | \ - grep -v '`json:' | \ - grep -v "Password.*string" | \ - grep -v "r.BasicAuth()" | \ - grep -v "subtle.ConstantTimeCompare"; then - echo "Potential hardcoded credentials found" - exit 1 - fi + # # Hardcoded credentials check + # # Exclude common directories and files + # EXCLUDES=(".git" "frontend" "node_modules" "build" "docs") + # # Exclude file patterns + # FILE_EXCLUDES=("*.yml" "*.yaml" "*.md" "*.js" "*.jsx" "*.json" "*_test.go") + # # Build grep exclude params + # EXCLUDE_PARAMS=() + # for dir in "${EXCLUDES[@]}"; do EXCLUDE_PARAMS+=(--exclude-dir="$dir"); done + # for file in "${FILE_EXCLUDES[@]}"; do EXCLUDE_PARAMS+=(--exclude="$file"); done + # # Run grep safely + # if grep -rI "password.*=" . "${EXCLUDE_PARAMS[@]}" | \ + # grep -v "^[[:space:]]*//" | \ + # grep -v 'json:' | \ + # grep -v '`json:' | \ + # grep -v "Password.*string" | \ + # grep -v "r.BasicAuth()" | \ + # grep -v "subtle.ConstantTimeCompare"; then + # echo "Potential hardcoded credentials found" + # exit 1 + # fi # build-and-publish-docker: # if: github.ref_name != 'main'