33 Commits

Author SHA1 Message Date
pezkuwichain 83d66feacc fix(ci): unblock deploy pipeline (audit gate + orphan submodule) (#16)
The Quality Gate & Deploy pipeline was failing at security-audit
(npm audit --audit-level=high), which blocks telegram-gate and the
whole deploy chain — that is why production was serving a stale bundle.

- npm audit fix (no --force, lockfile only): clears the critical vitest
  advisory (GHSA-5xrq-8626-4rwp) and the high elliptic one; only low-
  severity items remain, so 'npm audit --audit-level=high' now exits 0.
- Remove the orphaned 'exchange' gitlink: it is an empty submodule
  pointer with no .gitmodules mapping, which made git print
  'fatal: no submodule mapping found' during checkout.

Verified: lint, test (32 passed), and vite build all pass; audit gate
is green. No package.json changes.
2026-06-11 18:42:45 -07:00
pezkuwichain d6ace14e70 fix(web): live collator/nominator counts after AHM + reliable B2B redirect (#15)
Staking migrated to Asset Hub (AHM), but the landing page still read
nominators from the relay (api.query.staking.counterForNominators),
which is now empty there — so the count showed '—'. Collators were read
from collatorSelection.candidates (empty; collators are invulnerables)
and only on Asset Hub, missing the People chain set.

- Nominators: query Asset Hub staking.counterForNominators (verified 30).
- Collators: count collatorSelection.invulnerables on both Asset Hub and
  People chain (2 + 2), tracked per-chain and summed.
- NetworkStats.tsx already used the correct sources; this aligns the
  landing page with it.

B2B button (/bereketli SSO interstitial): if there is no Supabase session
or the token exchange fails, redirect to https://bereketli.pezkiwi.app
instead of stranding the user on app.pezkuwichain.io/bereketli. (The
backend CORS allowlist was also missing app.pezkuwichain.io; fixed
server-side so the SSO exchange itself now succeeds.)
2026-06-11 16:41:14 -07:00
pezkuwichain 2cbfd21539 fix(cosign): explicit GHCR login before sign + verify
docker/login-action writes ~/.docker/config.json but cosign on self-
hosted runner does not always read it. Add 'cosign login ghcr.io'
before sign (build-image) and verify (deploy-app, deploy-pex) so the
registry blob upload/download authenticates correctly.

The previous run signed via Sigstore (Fulcio cert + Rekor tlog entry
created) but failed at the final 'push signature blob to GHCR' step
with UNAUTHORIZED. Explicit cosign login solves this.
2026-05-09 13:41:29 +03:00
pezkuwichain f7c070e45b fix(deps): drop invalid create-ecdh override (max version is 4.x not 5.x)
The earlier npm override 'create-ecdh: ^5.0.1' resolved to no version on
the registry. CI install failed with ETARGET. Removing the override —
elliptic override alone covers the high-severity transitive vulns.
Remaining 6 lows in vite-plugin-node-polyfills chain accepted.
2026-05-09 12:27:07 +03:00
pezkuwichain 06ed9734c6 ci(security): Faz 3 + ekstra — runner consolidation, auto-rollback, cosign, SRI, dep cleanup
* Faz 3.1 — All CI jobs moved to self-hosted pwap-runner (DEV VPS).
  No more dependency on GitHub-hosted runners — supply-chain attack
  surface from GHA runner image compromise eliminated.
* Faz 3.3 — Automatic rollback on health-check fail. Each deploy stamps
  /.deploy-sha into the artifact. On health-check failure, the deploy
  job reads the previous SHA from the live site, pulls that image, and
  redeploys. Telegram notification differentiates: rolled-back-OK,
  rollback-also-failed, no-prev-available, manual-rollback-needed.
* E.3 — cosign keyless image signing. build-image signs the GHCR
  manifest via Sigstore Fulcio (OIDC, no long-lived keys). deploy-app
  and deploy-pex verify the signature before extracting /dist —
  unsigned or tampered images cannot deploy. Identity-pinned to this
  workflow file.
* E.5 — Subresource Integrity (SRI). vite-plugin-subresource-integrity
  injects sha384 integrity= into <script>/<link> tags at build time.
  CDN/proxy compromise cannot inject tampered JS — browser blocks on
  hash mismatch.
* E.2 — Dependabot triage. 14 alerts: 7 high + 4 moderate cleared via
  npm audit fix + npm overrides (elliptic, create-ecdh). 6 low
  (transitive in vite-plugin-node-polyfills chain) accepted; the
  upstream fix proposes a semver-major DOWNGRADE which makes no sense.
* E.1 — Branch protection on main: CI Gate  required, 1 review
  required, force-push and deletion blocked.
2026-05-09 12:08:49 +03:00
pezkuwichain d93d4c6cd0 fix(docker): correct dist path after WORKDIR=/build/web
Stage 2 was looking for /build/dist but vite emits to /build/web/dist
(WORKDIR is /build/web in stage 1). Fix the COPY --from=builder path.
2026-05-08 21:39:07 +03:00
pezkuwichain faba2dee5d fix(docker): build context = pwap root so shared/ is reachable
Vite aliases @pezkuwi/utils → ../shared/utils, so the Docker build context
must include both web/ and shared/. Previous context: ./web missed shared/
which caused 'Could not load /shared/utils/formatting' at module resolution.

Changes:
- Dockerfile WORKDIR=/build/web; COPY web/* and shared/* explicitly
- Workflow context: ./ + file: ./web/Dockerfile
- Move .dockerignore from web/ to pwap root (matches new context)
2026-05-08 20:44:19 +03:00
pezkuwichain ca3976fe62 ci(security): Faz 1+2 — Telegram CEO gate, image-based deploy, hardened audits
Faz 1 — State-actor threat-model defenses:
* Telegram approval gate via PEXSEC_BOT — CEO must approve every deploy in Telegram (30-min timeout). Runs on new self-hosted pwap-runner on DEV VPS, shares /tmp/pexsec-gates/ with pexsec-bot.service.
* DEV VPS app-deploy user privilege drop — deploys no longer run as root. CI key restricted with no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-user-rc. Privilege drop verified (cannot read /etc/shadow, /root/, sudo blocked).
* Image-based deploy — Dockerfile (node 20 build → busybox:musl dist) pushed to GHCR with SHA tag. Deploys pull image, extract /dist, scp to VPS. Immutable artifacts, full provenance.
* Health check + Telegram failure alert post-deploy.
* Rollback path: workflow_dispatch with rollback_to=<sha> — skips build, redeploys old image. CEO gate still required.

Faz 2 — Higher-tier defenses:
* TruffleHog secret scan — PR diff (fast) + push full-repo (verified secrets only).
* CodeQL SAST workflow — javascript-typescript, security-extended + security-and-quality queries. PR + push + weekly cron.
* npm audit raised from --audit-level=critical to --audit-level=high (caught more CVEs).
* CI Gate  explicit merge-block job — fails if any required check is not success/skipped.
2026-05-08 20:32:48 +03:00
pezkuwichain 7fea37eb5d ci(deploy): allow workflow_dispatch to trigger deploy jobs
Enables manual re-deploy via 'gh workflow run quality-gate.yml' without
needing a code push. Useful for: redeploy after secret rotation, post-
incident recovery, deploy verification.
2026-05-08 15:06:19 +03:00
pezkuwichain 68379dcf3a ci(deploy): mirror web build to pex.mom for geo-redundancy
Split monolithic deploy job into bump-version + deploy-app + deploy-pex.
Both deploys run in parallel from same build artifact, independent
secrets per VPS. If one country blocks a domain, the other VPS keeps
serving the same version.

- bump-version: single source of version bump, runs before both deploys
- deploy-app: existing target /var/www/subdomains/app on DEV VPS
- deploy-pex: new target /var/www/pex.mom on VPS3 (217.77.6.126)

Requires secrets: VPS_PEX_HOST, VPS_PEX_USER, VPS_PEX_SSH_KEY, VPS_PEX_SSH_PORT
2026-05-08 14:07:35 +03:00
pezkuwichain 56f276af1b fix(wallet): add 20s timeout to web3Enable to prevent indefinite hang
- Wrap web3Enable() with Promise.race against a 20-second timeout
- On timeout: show descriptive error explaining the popup may be blocked
- Surface actual error messages (incl. timeout) instead of generic 'Failed to connect wallet'
- Both auto-restore and manual connect button now fail fast instead of hanging
2026-05-05 13:12:36 +03:00
pezkuwichain f024d21cf5 fix(wallet-modal): add loading state for extension connect, fix Play Store link
- Extension button now shows 'Approve in extension...' spinner while web3Enable waits
- Add generic error fallback for errors not matching 'authorize'/'not found' patterns
- Replace 'Coming soon on Play Store' with real Play Store download link (io.pezkuwichain.wallet)
- WalletConnectModal mobile hint now links directly to Play Store
- Updated in all 6 locales: en, tr, ar, fa, kmr, ckb
2026-05-05 08:28:52 +03:00
pezkuwichain 67bc28cff4 docs(readme): fix exchange URL to pex.network, add pex.mom as alt website 2026-05-04 00:36:26 +03:00
pezkuwichain d7fa9dd570 docs(readme): update URLs to app.pezkuwichain.io, pex.mom, docs.pezkuwichain.io 2026-05-04 00:34:22 +03:00
pezkuwichain 428b058cbc chore: add res/ to .gitignore (internal-only resources) 2026-05-04 00:28:44 +03:00
pezkuwichain 0b5e318538 fix(deps): npm audit fix — patch 14 high/moderate vulnerabilities in web/
Fixes: vite, rollup, dompurify, lodash, postcss, ajv, bn.js, defu,
flatted, h3, minimatch, picomatch, brace-expansion, qs
Remaining 7 (low/moderate): uuid + vite-plugin-node-polyfills require
--force (major breaking changes, deferred)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 00:16:27 +03:00
pezkuwichain 568507ab98 chore: remove leftover dev artifacts (screenshots, Zone.Identifier, PS1 script, PDFs)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 00:08:25 +03:00
pezkuwichain 198f53b96f fix(config): point production WS endpoint to rpc.pezkuwichain.io
- App.tsx fallback: localhost:9944 → wss://rpc.pezkuwichain.io
- All locales: remove hardcoded ws://127.0.0.1:9944 from error message
2026-05-03 02:00:40 +03:00
pezkuwichain 9babb94e07 fix(auth): add pexsecBot for Telegram login on app.pezkuwichain.io
- pex.mom uses @PexMomBOT (8690398980)
- app.pezkuwichain.io uses @pexsecBot (8754021997)
- Edge function selects token based on bot_id from request
2026-05-01 23:32:25 +03:00
pezkuwichain ef6a7b2583 feat(i18n): add landing page translations for Sorani, Arabic, and Farsi
All 187 landing.* keys were missing from ckb/ar/fa locales, causing fallback to English.
2026-05-01 19:32:29 +03:00
pezkuwichain d446d711ba fix(web): replace AppLayout footer with identical LandingPageDesktop footer
Footer now uses lp-footer CSS classes and identical markup to pre-login landing page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 16:49:12 +03:00
pezkuwichain d1af76f444 fix(web): remove ArrowRightLeft icon from trading button + match bottom tab bar to pre-login design
- Remove ArrowRightLeft icon from desktop nav Trading dropdown button
- Bottom tab bar: add max-w-md mx-auto (centered) and bump z-index to z-50 to match MobileHomeLayout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 16:16:09 +03:00
pezkuwichain 914d914b74 fix(lint): remove unused bodyOnly prop from LandingPageDesktop
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 15:38:04 +03:00
pezkuwichain 8f57224700 feat(web): restore authenticated desktop home layout with modern section cards
- Add body content sections (HeroSection, NetworkStats, TrustScoreCalculator, ChainSpecs, RewardDistribution) after section grid
- Update section cards with distinct gradient header colors per category (Finance/green, Governance/purple, Social/blue, Education/orange)
- Fix bottom tab bar to be full-width (removed max-w-md mx-auto)
- Adjust role/score cards background to bg-gray-800/70 for contrast against main bg
- Add bodyOnly prop to LandingPageDesktop (non-breaking, unused)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 15:36:31 +03:00
pezkuwichain 1e047eba91 fix(ci): set SSH port 2222 for DEV VPS deploy 2026-05-01 14:09:27 +03:00
SatoshiQaziMuhammed 14d6da24db Merge pull request #14 from pezkuwichain/redesign
feat(web): authenticated home redesign + Telegram OAuth + SMTP
2026-05-01 10:22:36 +03:00
pezkuwichain 346a30fcbb fix(lint): remove unused keyMaterial variable + stale eslint-disable in LandingPageDesktop 2026-05-01 10:16:37 +03:00
pezkuwichain bac4148020 feat(web): authenticated home redesign + Telegram OAuth + SMTP
- AppLayout: logged-in desktop home redesigned — score cards, 4 section
  cards (Finance/Governance/Social/Education), governance extras, fixed
  bottom tab bar (Home/Citizen/Referral) matching mobile layout
- AppLayout: Trading dropdown in header (Presale/Staking/MultiSig),
  Logout button; removed 8-button grid
- Removed PalletsGrid and TokenomicsSection components
- Login: Telegram OAuth via oauth.telegram.org popup + postMessage +
  custom Edge Function (hash verification, find-or-create user,
  magic link token exchange)
- Login: X (Twitter) OAuth 2.0 wired to Supabase
- supabase/functions/telegram-auth: new Edge Function — verifies
  Telegram Login Widget hash, issues Supabase magic link token
- vite.config.ts: process-shim alias to fix TDZ with node polyfills
- i18n: updated locales (en/tr/kmr) for new UI sections
- SDK docs search index regenerated
2026-05-01 10:12:03 +03:00
pezkuwichain 709d408983 feat(landing): desktop landing page redesign
- New LandingPageDesktop component with full redesign
- Kurdish flag design tokens, animated sun, ticker band
- Three hero variants (V1 editorial split, V2 terminal, V3 mosaic)
- Live chain data: blocks, validators, nominators, citizens, proposals
- Features grid, architecture section, tokenomics tabs, referral steps
- Pallet grid with SVG sprite icons (16 pallets, 4 pillars)
- Dev-only hero variant switcher (auto-removed in prod)
- Mobile unchanged (MobileHomeLayout), logged-in unchanged
2026-04-30 23:30:09 +03:00
pezkuwichain 69789548e7 fix: prevent 'API not ready' on mobile by blocking wallet connect until blockchain initializes
- Add isApiInitializing state (true during WS connect, false on ready/fail)
- Add isApiReadyRef for closure-safe polling in connectWalletConnect
- connectWalletConnect now waits up to 30s for API instead of throwing immediately
- WalletModal connect buttons disabled + show spinner while blockchain is initializing
2026-04-27 15:00:58 +03:00
pezkuwichain 86ff43e206 feat: write p2p_user_id to tg_users on Telegram wallet link
TelegramConnect: query tg_users instead of users, resolve visa UUID
from p2p_visa table and store as p2p_user_id for cross-platform P2P.

P2PIdentityContext: when citizen resolves their UUID, backfill
tg_users.p2p_user_id if their wallet is linked to a Telegram account.
2026-04-27 13:31:22 +03:00
pezkuwichain 18d41743e8 chore: remove mobile/ from monorepo, suspend CI mobile job
* chore: update exchange submodule to pex.network release + add shared images

Exchange submodule advanced to include:
- sweeper.js: TRC-20 JWT Bearer auth, DOT transferAll, PEZ-AH pre-fund
- docker-compose.yml: pex.network defaults for VITE_API_BASE_URL and SMTP_FROM
- .github/workflows/build-deploy.yml: pex.network build arg for web service

Shared images added: keziyakurd, kiwi_perwerde, kurdistan_assembly, pezkuwi, satoshi_qazi_muh

* chore: remove mobile/ from monorepo, suspend CI mobile job

Mobile app moved to /home/mamostehp/pwap-mobile (local, suspended).
Will be re-integrated when mobile development resumes.

- Removed mobile/ directory entirely
- Removed Mobile App job from quality-gate.yml so CI no longer blocks
2026-04-27 03:10:41 +03:00
pezkuwichain 0d71433cc1 chore: exchange submodule pex.network release + shared images
Exchange submodule advanced to include:
- sweeper.js: TRC-20 JWT Bearer auth, DOT transferAll, PEZ-AH pre-fund
- docker-compose.yml: pex.network defaults for VITE_API_BASE_URL and SMTP_FROM
- .github/workflows/build-deploy.yml: pex.network build arg for web service

Shared images added: keziyakurd, kiwi_perwerde, kurdistan_assembly, pezkuwi, satoshi_qazi_muh
2026-04-27 02:34:50 +03:00
335 changed files with 5737 additions and 4260 deletions
+41
View File
@@ -0,0 +1,41 @@
# pwap/web Docker build context (root) — exclude everything not needed
# for `web/` build. Other monorepo subprojects stay out of the image.
# Other monorepo dirs (we only need web/ + shared/)
exchange/
mobile/
pwap-mobile/
docs/
res/
# All node_modules everywhere
**/node_modules/
**/dist/
**/build/
# Git, GitHub
.git/
.github/
# Env files (built-in vars are passed as build-args from CI)
**/.env
**/.env.*
!**/.env.example
# Editor / OS
.vscode/
.idea/
*.swp
*.swo
.DS_Store
# Logs
*.log
# Cache
**/.eslintcache
**/coverage/
# Already-built artifacts (we rebuild fresh inside container)
web/dist/
shared/**/dist/
+58
View File
@@ -0,0 +1,58 @@
name: CodeQL (SAST)
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# Every Sunday at 02:00 UTC — catches CVEs disclosed during the week
- cron: '0 2 * * 0'
permissions:
contents: read
security-events: write
actions: read
concurrency:
group: codeql-${{ github.ref }}
cancel-in-progress: true
jobs:
analyze:
name: Analyze ${{ matrix.language }}
runs-on: pwap-runner
strategy:
fail-fast: false
matrix:
language: [javascript-typescript]
steps:
- uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# OWASP top-10 + injection + auth flaws + prototype pollution
queries: security-extended,security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform analysis
uses: github/codeql-action/analyze@v3
with:
category: /language:${{ matrix.language }}
# GitHub Advanced Security dashboard upload requires paid plan.
# SARIF saved as a downloadable artifact instead.
upload: false
output: /tmp/codeql-results
- name: Upload SARIF as artifact
uses: actions/upload-artifact@v4
continue-on-error: true
with:
name: codeql-sarif-${{ matrix.language }}
path: /tmp/codeql-results/*.sarif
retention-days: 7
+533 -19
View File
@@ -6,14 +6,25 @@ on:
pull_request:
branches: [ main, develop ]
workflow_dispatch:
inputs:
rollback_to:
description: 'Rollback to git SHA (skips build, redeploys old image). Empty = normal deploy.'
required: false
default: ''
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: write # version bump commit
packages: write # GHCR push
env:
VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL }}
VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }}
REGISTRY: ghcr.io
IMAGE_NAME: pezkuwichain/pwap-web
jobs:
# ========================================
@@ -21,7 +32,7 @@ jobs:
# ========================================
web:
name: Web App
runs-on: ubuntu-latest
runs-on: pwap-runner
steps:
- name: Checkout code
@@ -75,13 +86,164 @@ jobs:
path: web/dist/
# ========================================
# DEPLOY WEB APP TO VPS
# BUILD & PUSH DOCKER IMAGE TO GHCR
# Immutable artifact for audit + rollback (vs ephemeral GHA artifact).
# Tagged with git SHA so any commit can be redeployed by SHA.
# ========================================
deploy:
name: Deploy Web
runs-on: ubuntu-latest
build-image:
name: Build & Push Image
runs-on: pwap-runner
needs: [web, telegram-gate]
if: |
github.ref == 'refs/heads/main' &&
(github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.rollback_to == ''))
permissions:
contents: read
packages: write
id-token: write # cosign keyless signing via Sigstore OIDC
outputs:
image_sha: ${{ steps.meta.outputs.image_sha }}
image_digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Install cosign
uses: sigstore/cosign-installer@v3
with:
cosign-release: 'v2.4.1'
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract image metadata
id: meta
run: |
SHORT_SHA="${GITHUB_SHA:0:7}"
echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
echo "image_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
echo "image=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" >> $GITHUB_OUTPUT
- name: Build and push
id: build
uses: docker/build-push-action@v6
with:
context: ./
file: ./web/Dockerfile
push: true
tags: |
${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.short_sha }}
${{ steps.meta.outputs.image }}:latest
build-args: |
VITE_NETWORK=MAINNET
VITE_WS_ENDPOINT=wss://rpc.pezkuwichain.io
VITE_WS_ENDPOINT_FALLBACK_1=wss://mainnet.pezkuwichain.io
VITE_ASSET_HUB_ENDPOINT=wss://asset-hub-rpc.pezkuwichain.io
VITE_PEOPLE_CHAIN_ENDPOINT=wss://people-rpc.pezkuwichain.io
VITE_WALLETCONNECT_PROJECT_ID=8292a793b7640e8364c378e331e76d04
VITE_SUPABASE_URL=${{ secrets.VITE_SUPABASE_URL }}
VITE_SUPABASE_ANON_KEY=${{ secrets.VITE_SUPABASE_ANON_KEY }}
cache-from: type=registry,ref=${{ steps.meta.outputs.image }}:cache
cache-to: type=registry,ref=${{ steps.meta.outputs.image }}:cache,mode=max
provenance: false
- name: Sign image with cosign (keyless, Sigstore Fulcio)
env:
COSIGN_EXPERIMENTAL: '1'
run: |
IMAGE_DIGEST="${{ steps.meta.outputs.image }}@${{ steps.build.outputs.digest }}"
# cosign needs its own registry auth — docker/login-action only writes
# ~/.docker/config.json which cosign on self-hosted runner can't read
echo "${{ secrets.GITHUB_TOKEN }}" | cosign login ghcr.io -u "${{ github.actor }}" --password-stdin
echo "Signing $IMAGE_DIGEST"
cosign sign --yes "$IMAGE_DIGEST"
echo "✅ Image signed (transparency log: rekor.sigstore.dev)"
# ========================================
# TELEGRAM CEO APPROVAL GATE
# Runs on self-hosted pwap-runner (DEV VPS) where pexsec-bot.service
# writes the gate file to /tmp/pexsec-gates/<sha> when CEO clicks
# Approve/Cancel in Telegram. 30-minute timeout = deploy cancelled.
# ========================================
telegram-gate:
name: Telegram deploy approval
runs-on: pwap-runner
needs: [web, security-audit]
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
steps:
- name: Send approval request and wait
env:
BOT_TOKEN: ${{ secrets.PEXSEC_BOT_TOKEN }}
CEO_CHAT_ID: ${{ secrets.TELEGRAM_CEO_CHAT_ID }}
SHA: ${{ github.sha }}
ACTOR: ${{ github.actor }}
MESSAGE: ${{ github.event.head_commit.message }}
run: |
SHORT="${SHA:0:7}"
GATE_DIR="/tmp/pexsec-gates"
mkdir -p "$GATE_DIR" 2>/dev/null || true
rm -f "$GATE_DIR/$SHORT" 2>/dev/null || true
# Strip Markdown special chars to prevent Telegram parse errors
SAFE_MSG=$(echo "${MESSAGE}" | head -1 | tr -d '_*`[]()#|{}!' | cut -c1-120)
curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
-H "Content-Type: application/json" \
-d "{
\"chat_id\": \"${CEO_CHAT_ID}\",
\"parse_mode\": \"Markdown\",
\"text\": \"🚀 *pwap/web Deploy Approval*\\n\\n\`${SHORT}\` — ${ACTOR}\\n\\n_${SAFE_MSG}_\\n\\nTargets: app.pezkuwichain.io + pex.mom\",
\"reply_markup\": {
\"inline_keyboard\": [[
{\"text\": \"✅ Approve\", \"callback_data\": \"deploy_approve:${SHORT}\"},
{\"text\": \"❌ Cancel\", \"callback_data\": \"deploy_cancel:${SHORT}\"}
]]
}
}"
echo "Waiting for Telegram approval (max 30 min)..."
TIMEOUT=1800
ELAPSED=0
while [ $ELAPSED -lt $TIMEOUT ]; do
if [ -f "$GATE_DIR/$SHORT" ]; then
DECISION=$(cat "$GATE_DIR/$SHORT")
rm -f "$GATE_DIR/$SHORT" 2>/dev/null || true
if [ "$DECISION" = "approved" ]; then
echo "Deploy approved."
exit 0
else
echo "Deploy cancelled."
exit 1
fi
fi
sleep 10
ELAPSED=$((ELAPSED + 10))
done
echo "No approval received within 30 minutes — deploy cancelled."
exit 1
# ========================================
# VERSION BUMP (RUNS BEFORE BOTH DEPLOYS)
# ========================================
bump-version:
name: Bump Version
runs-on: pwap-runner
needs: [web, security-audit, telegram-gate, build-image]
# Skip on rollback (workflow_dispatch with rollback_to set)
if: |
github.ref == 'refs/heads/main' &&
(github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.rollback_to == ''))
outputs:
new_version: ${{ steps.bump.outputs.version }}
steps:
- name: Checkout code
@@ -101,61 +263,413 @@ jobs:
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Bump version
id: bump
working-directory: ./web
run: |
npm version patch --no-git-tag-version
VERSION=$(node -p "require('./package.json').version")
echo "NEW_VERSION=$VERSION" >> $GITHUB_ENV
echo "version=$VERSION" >> $GITHUB_OUTPUT
cd ..
git add web/package.json
git commit -m "chore(web): bump version to $VERSION [skip ci]" || echo "No version change"
git push || echo "Nothing to push"
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: web-dist
path: dist/
# ========================================
# DEPLOY TO app.pezkuwichain.io (DEV VPS)
# Pulls SHA-tagged image from GHCR, extracts /dist, scp to VPS.
# Health check + auto-rollback to .deploy-tag-prev on failure.
# ========================================
deploy-app:
name: Deploy app.pezkuwichain.io
runs-on: pwap-runner
needs: [telegram-gate, bump-version, build-image]
if: |
always() &&
needs.telegram-gate.result == 'success' &&
((github.event_name == 'push' && needs.build-image.result == 'success' && needs.bump-version.result == 'success') ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.rollback_to != ''))
permissions:
contents: read
packages: read
env:
DOMAIN: app.pezkuwichain.io
TARGET_PATH: /var/www/subdomains/app
- name: Deploy to VPS
steps:
- name: Determine image SHA
id: sha
run: |
if [ -n "${{ github.event.inputs.rollback_to }}" ]; then
echo "sha=${{ github.event.inputs.rollback_to }}" >> $GITHUB_OUTPUT
echo "Rolling back to: ${{ github.event.inputs.rollback_to }}"
else
echo "sha=${{ needs.build-image.outputs.image_sha }}" >> $GITHUB_OUTPUT
fi
- name: Capture currently-live SHA (for auto-rollback)
id: prev
run: |
# /.deploy-sha is written into every deploy; read what's live now
PREV=$(curl -sf --max-time 5 "https://${{ env.DOMAIN }}/.deploy-sha" | head -c 40 | tr -dc 'a-f0-9' || echo "")
echo "Previous live SHA: ${PREV:-unknown}"
echo "prev=$PREV" >> $GITHUB_OUTPUT
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Install cosign (for verify)
uses: sigstore/cosign-installer@v3
with:
cosign-release: 'v2.4.1'
- name: Verify image signature (cosign keyless)
env:
COSIGN_EXPERIMENTAL: '1'
run: |
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.sha.outputs.sha }}"
echo "${{ secrets.GITHUB_TOKEN }}" | cosign login ghcr.io -u "${{ github.actor }}" --password-stdin
echo "Verifying signature for $IMAGE"
cosign verify "$IMAGE" \
--certificate-identity-regexp "^https://github.com/pezkuwichain/pwap/.github/workflows/quality-gate.yml@" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
> /dev/null
echo "✅ Signature valid — image was built by trusted CI"
- name: Extract /dist from image
run: |
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.sha.outputs.sha }}"
docker pull "$IMAGE"
CID=$(docker create "$IMAGE")
mkdir -p dist
docker cp "$CID:/dist/." dist/
docker rm "$CID" >/dev/null
# Stamp this build's SHA into dist so future deploys can read PREV
echo "${{ steps.sha.outputs.sha }}" > dist/.deploy-sha
ls -la dist/ | head -10
- name: Deploy to DEV VPS
uses: appleboy/scp-action@v1.0.0
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
port: ${{ secrets.VPS_SSH_PORT || 2222 }}
source: 'dist/*'
target: '/var/www/subdomains/app'
strip_components: 1
- name: Post-deploy notification
- name: Health check (60s window)
id: healthcheck
run: |
echo "✅ Deployed web app v${{ env.NEW_VERSION }} to app.pezkuwichain.io"
for i in 1 2 3 4 5 6; do
if curl -sf --max-time 10 "https://${{ env.DOMAIN }}/" >/dev/null; then
echo "✅ ${{ env.DOMAIN }} healthy"
exit 0
fi
echo "Attempt $i/6 failed, retrying in 10s..."
sleep 10
done
echo "❌ Health check failed for ${{ env.DOMAIN }}"
exit 1
# ── Automatic rollback: pull PREV SHA image, redeploy, recheck ──
- name: Auto-rollback to previous SHA
id: rollback
if: failure() && steps.healthcheck.conclusion == 'failure' && steps.prev.outputs.prev != ''
run: |
PREV="${{ steps.prev.outputs.prev }}"
echo "🔄 Rolling back ${{ env.DOMAIN }} to $PREV"
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:$PREV"
docker pull "$IMAGE"
CID=$(docker create "$IMAGE")
rm -rf dist && mkdir dist
docker cp "$CID:/dist/." dist/
docker rm "$CID" >/dev/null
echo "$PREV" > dist/.deploy-sha
echo "rollback_sha=$PREV" >> $GITHUB_OUTPUT
- name: SCP rollback artifact
if: steps.rollback.outcome == 'success'
uses: appleboy/scp-action@v1.0.0
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
port: ${{ secrets.VPS_SSH_PORT || 2222 }}
source: 'dist/*'
target: '/var/www/subdomains/app'
strip_components: 1
- name: Re-health-check after rollback
if: steps.rollback.outcome == 'success'
id: healthcheck_rb
run: |
for i in 1 2 3 4 5 6; do
if curl -sf --max-time 10 "https://${{ env.DOMAIN }}/" >/dev/null; then
echo "✅ Rolled back successfully — ${{ env.DOMAIN }} healthy on ${{ steps.rollback.outputs.rollback_sha }}"
exit 0
fi
sleep 10
done
echo "❌ Rollback also failed!"
exit 1
- name: Post-deploy notification
if: success()
run: |
echo "✅ Deployed image ${{ steps.sha.outputs.sha }} to ${{ env.DOMAIN }}"
- name: Notify failure (Telegram)
if: failure()
env:
BOT_TOKEN: ${{ secrets.PEXSEC_BOT_TOKEN }}
CEO_CHAT_ID: ${{ secrets.TELEGRAM_CEO_CHAT_ID }}
NEW_SHA: ${{ steps.sha.outputs.sha }}
PREV_SHA: ${{ steps.prev.outputs.prev }}
ROLLBACK_OUTCOME: ${{ steps.rollback.outcome }}
RECHECK_OUTCOME: ${{ steps.healthcheck_rb.outcome }}
run: |
if [ "$RECHECK_OUTCOME" = "success" ]; then
MSG="⚠️ pwap/web ${{ env.DOMAIN }}: deploy ($NEW_SHA) failed health check, AUTO-ROLLED-BACK to $PREV_SHA. Site healthy."
elif [ "$ROLLBACK_OUTCOME" = "success" ]; then
MSG="🚨 pwap/web ${{ env.DOMAIN }}: deploy ($NEW_SHA) failed AND rollback to $PREV_SHA also failed. Manual intervention needed."
elif [ -z "$PREV_SHA" ]; then
MSG="❌ pwap/web ${{ env.DOMAIN }}: deploy ($NEW_SHA) failed. No previous SHA available (first deploy?). Manual rollback: gh workflow run quality-gate.yml -f rollback_to=<sha>"
else
MSG="❌ pwap/web ${{ env.DOMAIN }}: deploy ($NEW_SHA) failed. Auto-rollback was not attempted. Manual: gh workflow run quality-gate.yml -f rollback_to=$PREV_SHA"
fi
curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
-d "chat_id=${CEO_CHAT_ID}" --data-urlencode "text=$MSG"
# ========================================
# DEPLOY TO pex.mom (VPS3 — geo-redundant mirror)
# ========================================
deploy-pex:
name: Deploy pex.mom
runs-on: pwap-runner
needs: [telegram-gate, bump-version, build-image]
if: |
always() &&
needs.telegram-gate.result == 'success' &&
((github.event_name == 'push' && needs.build-image.result == 'success' && needs.bump-version.result == 'success') ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.rollback_to != ''))
permissions:
contents: read
packages: read
env:
DOMAIN: pex.mom
TARGET_PATH: /var/www/pex.mom
steps:
- name: Determine image SHA
id: sha
run: |
if [ -n "${{ github.event.inputs.rollback_to }}" ]; then
echo "sha=${{ github.event.inputs.rollback_to }}" >> $GITHUB_OUTPUT
else
echo "sha=${{ needs.build-image.outputs.image_sha }}" >> $GITHUB_OUTPUT
fi
- name: Capture currently-live SHA (for auto-rollback)
id: prev
run: |
PREV=$(curl -sf --max-time 5 "https://${{ env.DOMAIN }}/.deploy-sha" | head -c 40 | tr -dc 'a-f0-9' || echo "")
echo "Previous live SHA: ${PREV:-unknown}"
echo "prev=$PREV" >> $GITHUB_OUTPUT
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Install cosign (for verify)
uses: sigstore/cosign-installer@v3
with:
cosign-release: 'v2.4.1'
- name: Verify image signature (cosign keyless)
env:
COSIGN_EXPERIMENTAL: '1'
run: |
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.sha.outputs.sha }}"
echo "Verifying signature for $IMAGE"
cosign verify "$IMAGE" \
--certificate-identity-regexp "^https://github.com/pezkuwichain/pwap/.github/workflows/quality-gate.yml@" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
> /dev/null
echo "✅ Signature valid — image was built by trusted CI"
- name: Extract /dist from image
run: |
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.sha.outputs.sha }}"
docker pull "$IMAGE"
CID=$(docker create "$IMAGE")
mkdir -p dist
docker cp "$CID:/dist/." dist/
docker rm "$CID" >/dev/null
echo "${{ steps.sha.outputs.sha }}" > dist/.deploy-sha
- name: Deploy to VPS3
uses: appleboy/scp-action@v1.0.0
with:
host: ${{ secrets.VPS_PEX_HOST }}
username: ${{ secrets.VPS_PEX_USER }}
key: ${{ secrets.VPS_PEX_SSH_KEY }}
port: ${{ secrets.VPS_PEX_SSH_PORT || 22 }}
source: 'dist/*'
target: '/var/www/pex.mom'
strip_components: 1
- name: Health check (60s window)
id: healthcheck
run: |
for i in 1 2 3 4 5 6; do
if curl -sf --max-time 10 "https://${{ env.DOMAIN }}/" >/dev/null; then
echo "✅ ${{ env.DOMAIN }} healthy"
exit 0
fi
echo "Attempt $i/6 failed, retrying in 10s..."
sleep 10
done
echo "❌ Health check failed for ${{ env.DOMAIN }}"
exit 1
- name: Auto-rollback to previous SHA
id: rollback
if: failure() && steps.healthcheck.conclusion == 'failure' && steps.prev.outputs.prev != ''
run: |
PREV="${{ steps.prev.outputs.prev }}"
echo "🔄 Rolling back ${{ env.DOMAIN }} to $PREV"
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:$PREV"
docker pull "$IMAGE"
CID=$(docker create "$IMAGE")
rm -rf dist && mkdir dist
docker cp "$CID:/dist/." dist/
docker rm "$CID" >/dev/null
echo "$PREV" > dist/.deploy-sha
echo "rollback_sha=$PREV" >> $GITHUB_OUTPUT
- name: SCP rollback artifact
if: steps.rollback.outcome == 'success'
uses: appleboy/scp-action@v1.0.0
with:
host: ${{ secrets.VPS_PEX_HOST }}
username: ${{ secrets.VPS_PEX_USER }}
key: ${{ secrets.VPS_PEX_SSH_KEY }}
port: ${{ secrets.VPS_PEX_SSH_PORT || 22 }}
source: 'dist/*'
target: '/var/www/pex.mom'
strip_components: 1
- name: Re-health-check after rollback
if: steps.rollback.outcome == 'success'
id: healthcheck_rb
run: |
for i in 1 2 3 4 5 6; do
if curl -sf --max-time 10 "https://${{ env.DOMAIN }}/" >/dev/null; then
echo "✅ Rolled back successfully — ${{ env.DOMAIN }} healthy on ${{ steps.rollback.outputs.rollback_sha }}"
exit 0
fi
sleep 10
done
echo "❌ Rollback also failed!"
exit 1
- name: Post-deploy notification
if: success()
run: |
echo "✅ Deployed image ${{ steps.sha.outputs.sha }} to ${{ env.DOMAIN }}"
- name: Notify failure (Telegram)
if: failure()
env:
BOT_TOKEN: ${{ secrets.PEXSEC_BOT_TOKEN }}
CEO_CHAT_ID: ${{ secrets.TELEGRAM_CEO_CHAT_ID }}
NEW_SHA: ${{ steps.sha.outputs.sha }}
PREV_SHA: ${{ steps.prev.outputs.prev }}
ROLLBACK_OUTCOME: ${{ steps.rollback.outcome }}
RECHECK_OUTCOME: ${{ steps.healthcheck_rb.outcome }}
run: |
if [ "$RECHECK_OUTCOME" = "success" ]; then
MSG="⚠️ pwap/web ${{ env.DOMAIN }}: deploy ($NEW_SHA) failed health check, AUTO-ROLLED-BACK to $PREV_SHA. Site healthy."
elif [ "$ROLLBACK_OUTCOME" = "success" ]; then
MSG="🚨 pwap/web ${{ env.DOMAIN }}: deploy ($NEW_SHA) failed AND rollback to $PREV_SHA also failed. Manual intervention needed."
elif [ -z "$PREV_SHA" ]; then
MSG="❌ pwap/web ${{ env.DOMAIN }}: deploy ($NEW_SHA) failed. No previous SHA available (first deploy?). Manual rollback: gh workflow run quality-gate.yml -f rollback_to=<sha>"
else
MSG="❌ pwap/web ${{ env.DOMAIN }}: deploy ($NEW_SHA) failed. Auto-rollback was not attempted. Manual: gh workflow run quality-gate.yml -f rollback_to=$PREV_SHA"
fi
curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
-d "chat_id=${CEO_CHAT_ID}" --data-urlencode "text=$MSG"
# ========================================
# SECURITY CHECKS (BLOCKING)
# npm audit (high + critical) + TruffleHog secret scan
# ========================================
security-audit:
name: Security Audit
runs-on: ubuntu-latest
runs-on: pwap-runner
needs: [web]
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: ${{ github.event_name == 'pull_request' && 0 || 1 }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Web - npm audit (critical only)
- name: Web npm audit (high + critical)
working-directory: ./web
run: |
npm install
npm audit --audit-level=critical
npm audit --audit-level=high
- name: TruffleHog Secret Scan
- name: TruffleHog — PR diff (verified secrets only)
if: github.event_name == 'pull_request'
uses: trufflesecurity/trufflehog@main
with:
base: ${{ github.event.pull_request.base.sha }}
head: ${{ github.event.pull_request.head.sha }}
extra_args: --only-verified
- name: TruffleHog — full repo scan (verified secrets only)
if: github.event_name != 'pull_request'
uses: trufflesecurity/trufflehog@main
with:
path: ./
extra_args: --only-verified
# ========================================
# CI GATE — explicit merge-block
# All required checks must succeed (or be skipped, e.g. for rollback path).
# Branch protection on main should require this job's success.
# ========================================
ci-gate:
name: CI Gate ✅
runs-on: pwap-runner
needs: [web, security-audit]
if: always()
steps:
- name: Verify all required jobs succeeded or were intentionally skipped
run: |
results='${{ toJSON(needs) }}'
echo "$results" | python3 -c "
import json, sys
needs = json.load(sys.stdin)
failed = [name for name, job in needs.items() if job['result'] not in ('success', 'skipped')]
if failed:
print('❌ Required jobs failed: ' + ', '.join(failed))
sys.exit(1)
print('✅ All required CI jobs passed or skipped')
"
+3
View File
@@ -1,3 +1,6 @@
# Internal resources (never commit)
res/
# Logs
logs
*.log
-2002
View File
File diff suppressed because it is too large Load Diff
+5 -4
View File
@@ -26,7 +26,7 @@ pwap/
**Status:** ✅ Production Ready
The primary web interface for Pezkuwi blockchain at [pezkuwichain.app](https://pezkuwichain.app)
The primary web interface for Pezkuwi blockchain at [app.pezkuwichain.io](https://app.pezkuwichain.io)
**Tech Stack:**
- React 18 + TypeScript
@@ -166,9 +166,10 @@ RTL support for CKB, AR, FA.
## Links
- **Website:** https://pezkuwichain.app
- **SDK UI:** https://pezkuwichain.app/sdk
- **Documentation:** https://docs.pezkuwichain.app
- **Website:** https://app.pezkuwichain.io
- **Website (alt):** https://pex.mom
- **Exchange:** https://pex.network
- **Documentation:** https://docs.pezkuwichain.io
## License
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 802 KiB

Submodule exchange deleted from bb3bc812ed
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 677 KiB

-31
View File
@@ -1,31 +0,0 @@
# PWAP WSL Dev Environment Setup
# Run in PowerShell as Administrator
Write-Host "=== PWAP Dev Setup ===" -ForegroundColor Cyan
# 1. Fix .wslconfig - enable mirrored networking
$wslconfig = @"
[wsl2]
memory=48GB
swap=16GB
networkingMode=mirrored
"@
Set-Content -Path "$env:USERPROFILE\.wslconfig" -Value $wslconfig
Write-Host "[OK] .wslconfig updated (mirrored networking)" -ForegroundColor Green
# 2. Restart ADB on default port
$adbPath = "C:\Users\satos\Desktop\platform-tools\adb.exe"
& $adbPath kill-server 2>$null
Start-Sleep -Seconds 1
& $adbPath start-server
Write-Host "[OK] ADB server restarted on port 5037" -ForegroundColor Green
& $adbPath devices
# 3. Shutdown WSL so new config takes effect
Write-Host "`nShutting down WSL..." -ForegroundColor Yellow
wsl --shutdown
Start-Sleep -Seconds 3
Write-Host "[OK] WSL shutdown complete" -ForegroundColor Green
Write-Host "`n=== Done! ===" -ForegroundColor Cyan
Write-Host "Now open WSL again and run: cd pwap && claude" -ForegroundColor White
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -42,7 +42,7 @@ VITE_ENABLE_DEMO_MODE=true
# 1. Project URL: Copy from "Project URL" section
# 2. Anon key: Copy from "Project API keys" → "anon" → "public"
VITE_SUPABASE_URL=https://vbhftvdayqfmcgmzdxfv.supabase.co
VITE_SUPABASE_URL=https://supabase.pezkuwichain.io
VITE_SUPABASE_ANON_KEY=your_supabase_anon_key_here
# ========================================
+53
View File
@@ -0,0 +1,53 @@
# pwap/web — Static SPA build for distribution.
# Build context is the pwap repo ROOT (not web/) because vite aliases like
# @pezkuwi/utils, @shared/* resolve to ../shared/* — both web/ and shared/
# must be in the build context.
# Stage 1: build with Node. Stage 2: pure dist/ in busybox (smallest possible
# attacker surface — no shell, no package manager, no node runtime).
# Tag the resulting image with the git SHA in CI so rollback is just
# "pull pwap-web:<old-sha>".
# ─── Stage 1: Build ────────────────────────────────────────────
FROM node:20-alpine AS builder
WORKDIR /build/web
# Copy package files first to leverage Docker layer cache when only src changes
COPY web/package.json web/package-lock.json ./
RUN npm ci
# Copy shared/ first (less frequently changed), then web/ source
COPY shared/ /build/shared/
COPY web/ /build/web/
# Build args for environment-specific values (passed from CI)
ARG VITE_NETWORK=MAINNET
ARG VITE_WS_ENDPOINT=wss://rpc.pezkuwichain.io
ARG VITE_WS_ENDPOINT_FALLBACK_1=wss://mainnet.pezkuwichain.io
ARG VITE_ASSET_HUB_ENDPOINT=wss://asset-hub-rpc.pezkuwichain.io
ARG VITE_PEOPLE_CHAIN_ENDPOINT=wss://people-rpc.pezkuwichain.io
ARG VITE_WALLETCONNECT_PROJECT_ID=8292a793b7640e8364c378e331e76d04
ARG VITE_SUPABASE_URL
ARG VITE_SUPABASE_ANON_KEY
ENV VITE_NETWORK=$VITE_NETWORK
ENV VITE_WS_ENDPOINT=$VITE_WS_ENDPOINT
ENV VITE_WS_ENDPOINT_FALLBACK_1=$VITE_WS_ENDPOINT_FALLBACK_1
ENV VITE_ASSET_HUB_ENDPOINT=$VITE_ASSET_HUB_ENDPOINT
ENV VITE_PEOPLE_CHAIN_ENDPOINT=$VITE_PEOPLE_CHAIN_ENDPOINT
ENV VITE_WALLETCONNECT_PROJECT_ID=$VITE_WALLETCONNECT_PROJECT_ID
ENV VITE_SUPABASE_URL=$VITE_SUPABASE_URL
ENV VITE_SUPABASE_ANON_KEY=$VITE_SUPABASE_ANON_KEY
RUN npm run build
# ─── Stage 2: Distribution image ───────────────────────────────
# busybox:musl gives us a tiny base (~1.5MB) with a shell for `cp` operations
# during deploy extraction, but no npm/curl/wget/ssh — minimal attack surface
# if the image were ever exposed.
FROM busybox:musl
WORKDIR /dist
COPY --from=builder /build/web/dist /dist
LABEL org.opencontainers.image.source="https://github.com/pezkuwichain/pwap"
LABEL org.opencontainers.image.description="pwap/web static SPA — Pezkuwi wallet/exchange frontend"
LABEL org.opencontainers.image.licenses="proprietary"
CMD ["sh", "-c", "echo 'pwap-web image — extract /dist via: docker create + docker cp'; sleep infinity"]
+297 -268
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -120,7 +120,8 @@
"@pezkuwi/x-textdecoder": "^14.0.25",
"@pezkuwi/x-textencoder": "^14.0.25",
"@pezkuwi/x-ws": "^14.0.25",
"@pezkuwi/networks": "^14.0.25"
"@pezkuwi/networks": "^14.0.25",
"elliptic": "^6.6.1"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
@@ -147,6 +148,7 @@
"typescript-eslint": "^8.0.1",
"vite": "^7.3.1",
"vite-plugin-node-polyfills": "^0.25.0",
"vite-plugin-subresource-integrity": "^0.0.12",
"vitest": "^4.0.10"
}
}
+1 -2
View File
@@ -10,8 +10,7 @@
"General Docs": {
"AUDIT": "AUDIT.md",
"BACKPORT": "BACKPORT.md",
"RELEASE": "RELEASE.md",
"Workflow Rebranding": "workflow_rebranding.md"
"RELEASE": "RELEASE.md"
},
"Contributor": {
"CODE OF CONDUCT": "contributor/CODE_OF_CONDUCT.md",
-107
View File
@@ -1,107 +0,0 @@
#!/usr/bin/env python3
"""
Slow Crate Publisher - 6 dakikada bir 1 crate publish eder
Rate limit'e takilmamak icin yavas yavas publish yapar.
Kullanim:
nohup python3 publish_crates_slow.py > publish_log.txt 2>&1 &
"""
import subprocess
import os
import time
from datetime import datetime
PLACEHOLDER_DIR = '/home/mamostehp/kurdistan-sdk/crate_placeholders'
LOG_FILE = '/home/mamostehp/kurdistan-sdk/publish_log.txt'
INTERVAL_SECONDS = 360 # 6 dakika
def log(msg):
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
line = f"[{timestamp}] {msg}"
print(line, flush=True)
with open(LOG_FILE, 'a') as f:
f.write(line + '\n')
def is_published(name):
"""crates.io'da mevcut mu kontrol et"""
result = subprocess.run(
['cargo', 'search', name, '--limit', '1'],
capture_output=True, text=True, timeout=30
)
return f'{name} = ' in result.stdout
def publish_crate(name):
"""Tek bir crate publish et"""
crate_dir = os.path.join(PLACEHOLDER_DIR, name)
manifest = os.path.join(crate_dir, 'Cargo.toml')
if not os.path.exists(manifest):
return False, "Cargo.toml not found"
result = subprocess.run(
['cargo', 'publish', '--manifest-path', manifest],
capture_output=True, text=True, cwd=crate_dir, timeout=180
)
if result.returncode == 0:
return True, "Success"
elif 'already uploaded' in result.stderr or 'already exists' in result.stderr:
return True, "Already exists"
elif '429' in result.stderr or 'Too Many Requests' in result.stderr:
return False, "Rate limited"
else:
return False, result.stderr[:200]
def get_unpublished_crates():
"""Henuz publish edilmemis crate'leri bul"""
crates = sorted([d for d in os.listdir(PLACEHOLDER_DIR)
if os.path.isdir(os.path.join(PLACEHOLDER_DIR, d))])
unpublished = []
for crate in crates:
if not is_published(crate):
unpublished.append(crate)
return unpublished
def main():
log("=" * 60)
log("Slow Crate Publisher baslatildi")
log(f"Interval: {INTERVAL_SECONDS} saniye (6 dakika)")
log("=" * 60)
unpublished = get_unpublished_crates()
total = len(unpublished)
log(f"Toplam {total} crate publish edilecek")
success_count = 0
fail_count = 0
for i, crate in enumerate(unpublished, 1):
log(f"[{i}/{total}] Publishing: {crate}")
success, msg = publish_crate(crate)
if success:
log(f"{msg}")
success_count += 1
else:
log(f"{msg}")
fail_count += 1
# Rate limit durumunda ekstra bekle
if "Rate limited" in msg:
log(" Rate limited! 10 dakika bekleniyor...")
time.sleep(600)
# Sonraki crate icin bekle
if i < total:
log(f" Sonraki crate icin {INTERVAL_SECONDS}s bekleniyor...")
time.sleep(INTERVAL_SECONDS)
log("=" * 60)
log(f"Tamamlandi! Basarili: {success_count}, Basarisiz: {fail_count}")
log("=" * 60)
if __name__ == "__main__":
main()
-214
View File
@@ -1,214 +0,0 @@
import os
import sys
# Rebranding haritası
REBRAND_MAP = [
("asset-test-utils", "asset-test-pezutils"),
("chain-spec-guide-runtime", "pez-chain-spec-guide-runtime"),
("equivocation-detector", "pez-equivocation-detector"),
("erasure-coding-fuzzer", "pez-erasure-coding-fuzzer"),
("ethereum-standards", "pez-ethereum-standards"),
("finality-relay", "pez-finality-relay"),
("fork-tree", "pez-fork-tree"),
("generate-bags", "pez-generate-bags"),
("kitchensink-runtime", "pez-kitchensink-runtime"),
("messages-relay", "pez-messages-relay"),
("minimal-template-node", "pez-minimal-template-node"),
("minimal-template-runtime", "pez-minimal-template-runtime"),
("node-bench", "pez-node-bench"),
("node-primitives", "pez-node-primitives"),
("node-rpc", "pez-node-rpc"),
("node-runtime-generate-bags", "pez-node-runtime-generate-bags"),
("node-template-release", "pez-node-template-release"),
("node-testing", "pez-node-testing"),
("penpal-emulated-chain", "pez-penpal-emulated-chain"),
("penpal-runtime", "pez-penpal-runtime"),
("remote-ext-tests-bags-list", "pez-remote-ext-tests-bags-list"),
("revive-dev-node", "pez-revive-dev-node"),
("revive-dev-runtime", "pez-revive-dev-runtime"),
("slot-range-helper", "pez-slot-range-helper"),
("solochain-template-node", "pez-solochain-template-node"),
("solochain-template-runtime", "pez-solochain-template-runtime"),
("subkey", "pez-subkey"),
("template-zombienet-tests", "pez-template-zombienet-tests"),
("test-runtime-constants", "peztest-runtime-constants"),
("tracing-gum", "pez-tracing-gum"),
("tracing-gum-proc-macro", "pez-tracing-gum-proc-macro"),
("bp-header-chain", "bp-header-pez-chain"),
("bp-runtime", "pezbp-runtime"),
("bridge-hub-pezkuwichain-emulated-chain", "pezbridge-hub-pezkuwichain-emulated-chain"),
("bridge-hub-pezkuwichain-integration-tests", "pezbridge-hub-pezkuwichain-integration-tests"),
("bridge-hub-pezkuwichain-runtime", "pezbridge-hub-pezkuwichain-runtime"),
("bridge-hub-test-utils", "pezbridge-hub-test-utils"),
("bridge-hub-zagros-emulated-chain", "pezbridge-hub-zagros-emulated-chain"),
("bridge-hub-zagros-integration-tests", "pezbridge-hub-zagros-integration-tests"),
("bridge-hub-zagros-runtime", "pezbridge-hub-zagros-runtime"),
("bridge-runtime-common", "pezbridge-runtime-common"),
("mmr-gadget", "pezmmr-gadget"),
("mmr-rpc", "pezmmr-rpc"),
("snowbridge-beacon-primitives", "pezsnowbridge-beacon-primitives"),
("snowbridge-core", "pezsnowbridge-core"),
("snowbridge-ethereum", "pezsnowbridge-ethereum"),
("snowbridge-inbound-queue-primitives", "pezsnowbridge-inbound-queue-primitives"),
("snowbridge-merkle-tree", "pezsnowbridge-merkle-tree"),
("snowbridge-outbound-queue-primitives", "pezsnowbridge-outbound-queue-primitives"),
("snowbridge-outbound-queue-runtime-api", "pezsnowbridge-outbound-queue-runtime-api"),
("snowbridge-outbound-queue-v2-runtime-api", "pezsnowbridge-outbound-queue-v2-runtime-api"),
("snowbridge-pezpallet-ethereum-client", "snowbridge-pezpallet-ethereum-client"),
("snowbridge-pezpallet-ethereum-client-fixtures", "snowbridge-pezpallet-ethereum-client-fixtures"),
("snowbridge-pezpallet-inbound-queue", "snowbridge-pezpallet-inbound-queue"),
("snowbridge-pezpallet-inbound-queue-fixtures", "snowbridge-pezpallet-inbound-queue-fixtures"),
("snowbridge-pezpallet-inbound-queue-v2", "snowbridge-pezpallet-inbound-queue-v2"),
("snowbridge-pezpallet-inbound-queue-v2-fixtures", "snowbridge-pezpallet-inbound-queue-v2-fixtures"),
("snowbridge-pezpallet-outbound-queue", "snowbridge-pezpallet-outbound-queue"),
("snowbridge-pezpallet-outbound-queue-v2", "snowbridge-pezpallet-outbound-queue-v2"),
("snowbridge-pezpallet-system", "snowbridge-pezpallet-system"),
("snowbridge-pezpallet-system-frontend", "snowbridge-pezpallet-system-frontend"),
("snowbridge-pezpallet-system-v2", "snowbridge-pezpallet-system-v2"),
("snowbridge-runtime-common", "pezsnowbridge-runtime-common"),
("snowbridge-runtime-test-common", "pezsnowbridge-runtime-test-common"),
("snowbridge-system-runtime-api", "pezsnowbridge-system-runtime-api"),
("snowbridge-system-v2-runtime-api", "pezsnowbridge-system-v2-runtime-api"),
("snowbridge-test-utils", "pezsnowbridge-test-utils"),
("snowbridge-verification-primitives", "pezsnowbridge-verification-primitives"),
("xcm-docs", "xcm-pez-docs"),
("xcm-emulator", "xcm-pez-emulator"),
("xcm-executor-integration-tests", "xcm-pez-executor-integration-tests"),
("xcm-procedural", "xcm-pez-procedural"),
("xcm-runtime-apis", "xcm-runtime-pezapis"),
("xcm-simulator", "xcm-pez-simulator"),
("xcm-simulator-example", "xcm-pez-simulator-example"),
("xcm-simulator-fuzzer", "xcm-pez-simulator-fuzzer"),
]
# Hedef dosya uzantıları
TARGET_EXTENSIONS = ('.rs', '.toml', '.md', '.txt', '.yml', '.yaml', '.json', '.py')
# HARİÇ TUTULACAK KLASÖRLER (KESİN LİSTE)
EXCLUDE_DIRS = {'crate_placeholders', '.git', 'target', 'node_modules', '__pycache__'}
def is_path_excluded(path):
"""Verilen yolun yasaklı bir klasörün içinde olup olmadığını kontrol eder."""
parts = path.split(os.sep)
# Eğer path'in herhangi bir parçası EXCLUDE_DIRS içindeyse True döner
return any(excluded in parts for excluded in EXCLUDE_DIRS)
def replace_in_file(filepath):
# Kendi kendimizi değiştirmeyelim
if os.path.basename(filepath) == os.path.basename(__file__):
return
try:
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
original_content = content
for old_name, new_name in REBRAND_MAP:
# 1. Normal (tireli)
content = content.replace(old_name, new_name)
# 2. Snake case (alt çizgili)
old_snake = old_name.replace('-', '_')
new_snake = new_name.replace('-', '_')
content = content.replace(old_snake, new_snake)
if content != original_content:
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
print(f" [GÜNCELLENDİ] Dosya içeriği: {filepath}")
except Exception as e:
print(f" [HATA] Dosya okunamadı: {filepath} -> {e}")
def rename_directories_and_files(root_dir):
# topdown=True kullanarak yukarıdan aşağıya iniyoruz, böylece dirs listesini modifiye edebiliriz
for dirpath, dirnames, filenames in os.walk(root_dir, topdown=True):
# GÜVENLİK: Yasaklı klasörleri yerinde (in-place) listeden silerek os.walk'un oraya girmesini engelle
# Bu en güvenli yöntemdir.
dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIRS]
# Eğer şu anki dizin zaten yasaklı bir yolun altındaysa (üstteki koruma kaçırdıysa) atla
if is_path_excluded(dirpath):
continue
# 1. Dosya isimlerini değiştir
for filename in filenames:
if filename == os.path.basename(__file__):
continue
for old_name, new_name in REBRAND_MAP:
if old_name in filename:
old_file_path = os.path.join(dirpath, filename)
new_filename = filename.replace(old_name, new_name)
new_file_path = os.path.join(dirpath, new_filename)
if os.path.exists(old_file_path):
try:
os.rename(old_file_path, new_file_path)
print(f" [RENAME] Dosya: {filename} -> {new_filename}")
except OSError as e:
print(f" [HATA] Dosya adlandırılamadı {filename}: {e}")
# 2. Klasör isimlerini değiştir
# Not: dirnames listesi üzerinde iterasyon yapıyoruz ama rename işlemi riskli olabilir
# O yüzden sadece şu anki seviyedeki klasörleri kontrol ediyoruz
# Ancak os.walk çalışırken klasör adı değişirse alt dizin taraması sapıtabilir.
# Bu yüzden klasör yeniden adlandırmayı en sona, ayrı bir "bottom-up" geçişe bırakmak daha iyidir
# ama basitlik adına burada dikkatli yapıyoruz.
# İkinci Geçiş: Sadece Klasör İsimleri (Bottom-Up)
# Klasör isimlerini değiştirirken path bozulmasın diye en alttan başlıyoruz
for dirpath, dirnames, filenames in os.walk(root_dir, topdown=False):
if is_path_excluded(dirpath):
continue
for dirname in dirnames:
if dirname in EXCLUDE_DIRS:
continue
for old_name, new_name in REBRAND_MAP:
if old_name == dirname:
old_dir_path = os.path.join(dirpath, dirname)
new_dir_path = os.path.join(dirpath, new_name)
if os.path.exists(old_dir_path):
try:
os.rename(old_dir_path, new_dir_path)
print(f" [RENAME] Klasör: {dirname} -> {new_name}")
except OSError as e:
print(f" [HATA] Klasör adlandırılamadı {dirname}: {e}")
def process_content_updates(root_dir):
for dirpath, dirnames, filenames in os.walk(root_dir, topdown=True):
# Yasaklı klasörlere girme
dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIRS]
if is_path_excluded(dirpath):
continue
for filename in filenames:
if filename.endswith(TARGET_EXTENSIONS) or filename == 'Cargo.lock':
filepath = os.path.join(dirpath, filename)
replace_in_file(filepath)
def main():
root_dir = os.getcwd()
print("==================================================")
print(f"⚠️ DİKKAT: Çalışma dizini: {root_dir}")
print(f"⚠️ HARİÇ TUTULANLAR: {EXCLUDE_DIRS}")
print("==================================================")
# Otomatik onay veya soru
# confirm = input("Emin misin? (evet/hayir): ")
# if confirm.lower() != "evet": return
print("İşlem başlatılıyor...")
print("\n--- Adım 1: Dosya İçeriklerinin Güncellenmesi ---")
process_content_updates(root_dir)
print("\n--- Adım 2: Klasör ve Dosya İsimlerinin Değiştirilmesi ---")
rename_directories_and_files(root_dir)
print("\n✅ Rebranding işlemi tamamlandı.")
if __name__ == "__main__":
main()
-320
View File
@@ -1,320 +0,0 @@
#!/usr/bin/env python3
"""
crates.io İsim Rezervasyon Script'i (Gelişmiş Versiyon)
Özellikler:
- Kaldığı yerden devam etme (--start-from)
- Ayarlanabilir bekleme süresi (--interval)
- Workspace izolasyonu (üst dizindeki Cargo.toml ile çakışmaz)
- "Already exists" durumunu akıllıca yönetir (bekleme yapmaz)
"""
import subprocess
import os
import sys
import json
import time
from pathlib import Path
import argparse
WORKSPACE_ROOT = Path(__file__).parent.resolve()
PLACEHOLDER_DIR = WORKSPACE_ROOT / "crate_placeholders"
# Yeni isim listesi
NEW_CRATE_NAMES = [
"asset-hub-pezkuwichain-emulated-chain",
"asset-hub-pezkuwichain-integration-tests",
"asset-hub-pezkuwichain-runtime",
"asset-hub-zagros-emulated-chain",
"asset-hub-zagros-integration-tests",
"asset-hub-zagros-runtime",
"asset-test-pezutils",
"pez-binary-merkle-tree",
"pez-chain-spec-guide-runtime",
"collectives-zagros-emulated-chain",
"collectives-zagros-integration-tests",
"collectives-zagros-runtime",
"coretime-pezkuwichain-emulated-chain",
"coretime-pezkuwichain-integration-tests",
"coretime-pezkuwichain-runtime",
"coretime-zagros-emulated-chain",
"coretime-zagros-integration-tests",
"coretime-zagros-runtime",
"emulated-integration-tests-common",
"pez-equivocation-detector",
"pez-erasure-coding-fuzzer",
"pez-ethereum-standards",
"pez-finality-relay",
"pez-fork-tree",
"pezframe-election-solution-type-fuzzer",
"pezframe-omni-bencher",
"pezframe-remote-externalities",
"pezframe-storage-access-test-runtime",
"pez-generate-bags",
"glutton-zagros-runtime",
"governance-zagros-integration-tests",
"pez-kitchensink-runtime",
"pez-messages-relay",
"pez-minimal-template-node",
"pez-minimal-template-runtime",
"pez-node-bench",
"pez-node-primitives",
"pez-node-rpc",
"pez-node-runtime-pez-generate-bags",
"pez-node-template-release",
"pez-node-testing",
"pez-penpal-emulated-chain",
"pez-penpal-runtime",
"people-pezkuwichain-emulated-chain",
"people-pezkuwichain-integration-tests",
"people-pezkuwichain-runtime",
"people-zagros-emulated-chain",
"people-zagros-integration-tests",
"people-zagros-runtime",
"pezkuwi",
"pezkuwichain-emulated-chain",
"pezkuwichain-runtime",
"pezkuwichain-runtime-constants",
"pezkuwichain-system-emulated-network",
"pezkuwichain-teyrchain-runtime",
"pezkuwichain-zagros-system-emulated-network",
"relay-bizinikiwi-client",
"relay-pezutils",
"pez-remote-ext-tests-bags-list",
"pez-revive-dev-node",
"pez-revive-dev-runtime",
"pez-slot-range-helper",
"pez-solochain-template-node",
"pez-solochain-template-runtime",
"pez-pez_subkey",
"pez-template-zombienet-tests",
"peztest-runtime-constants",
"test-teyrchain-adder",
"test-teyrchain-adder-collator",
"test-teyrchain-halt",
"test-teyrchain-undying",
"test-teyrchain-undying-collator",
"testnet-teyrchains-constants",
"teyrchain-template",
"teyrchain-template-node",
"teyrchain-template-runtime",
"teyrchains-common",
"teyrchains-relay",
"teyrchains-runtimes-test-utils",
"pez-tracing-gum",
"pez-pez-tracing-gum-proc-macro",
"yet-another-teyrchain-runtime",
"zagros-emulated-chain",
"zagros-runtime",
"zagros-runtime-constants",
"zagros-system-emulated-network",
"pez-zombienet-backchannel",
"pezassets-common",
"bp-asset-hub-pezkuwichain",
"bp-asset-hub-zagros",
"bp-pezbeefy",
"bp-bridge-hub-pezcumulus",
"bp-bridge-hub-pezkuwichain",
"bp-bridge-hub-zagros",
"bp-header-pez-chain",
"bp-pez-messages",
"bp-pezkuwi-bulletin",
"bp-pezkuwi-core",
"bp-pezkuwichain",
"bp-pez-relayers",
"pezbp-runtime",
"bp-test-pezutils",
"bp-teyrchains",
"bp-xcm-pezbridge-hub",
"bp-xcm-pezbridge-hub-router",
"bp-zagros",
"pezbridge-hub-common",
"pezbridge-hub-pezkuwichain-emulated-chain",
"pezbridge-hub-pezkuwichain-integration-tests",
"pezbridge-hub-pezkuwichain-runtime",
"pezbridge-hub-test-utils",
"pezbridge-hub-zagros-emulated-chain",
"pezbridge-hub-zagros-integration-tests",
"pezbridge-hub-zagros-runtime",
"pezbridge-runtime-common",
"pezmmr-gadget",
"pezmmr-rpc",
"pezsnowbridge-beacon-primitives",
"pezsnowbridge-core",
"pezsnowbridge-ethereum",
"pezsnowbridge-inbound-queue-primitives",
"pezsnowbridge-merkle-tree",
"pezsnowbridge-outbound-queue-primitives",
"pezsnowbridge-outbound-queue-runtime-api",
"pezsnowbridge-outbound-queue-v2-runtime-api",
"pezsnowbridge-pezpallet-ethereum-client",
"pezsnowbridge-pezpallet-ethereum-client-fixtures",
"pezsnowbridge-pezpallet-inbound-queue",
"pezsnowbridge-pezpallet-inbound-queue-fixtures",
"pezsnowbridge-pezpallet-inbound-queue-v2",
"pezsnowbridge-pezpallet-inbound-queue-v2-fixtures",
"pezsnowbridge-pezpallet-outbound-queue",
"pezsnowbridge-pezpallet-outbound-queue-v2",
"pezsnowbridge-pezpallet-system",
"pezsnowbridge-pezpallet-system-frontend",
"pezsnowbridge-pezpallet-system-v2",
"pezsnowpezbridge-runtime-common",
"pezsnowbridge-runtime-test-common",
"pezsnowbridge-system-runtime-api",
"pezsnowbridge-system-v2-runtime-api",
"pezsnowbridge-test-utils",
"pezsnowbridge-verification-primitives",
"xcm-pez-docs",
"xcm-pez-emulator",
"xcm-pez-executor-integration-tests",
"xcm-pez-procedural",
"xcm-runtime-pezapis",
"xcm-pez-simulator",
"xcm-pez-simulator-example",
"xcm-pez-simulator-fuzzer",
]
def check_crate_available(name: str) -> bool:
"""crates.io'da isim müsait mi kontrol et"""
result = subprocess.run(
["cargo", "search", name, "--limit", "1"],
capture_output=True, text=True
)
return f'{name} = "' not in result.stdout
def create_placeholder(name: str) -> Path:
"""Placeholder crate oluştur"""
crate_dir = PLACEHOLDER_DIR / name
crate_dir.mkdir(parents=True, exist_ok=True)
# [workspace] ekleyerek parent workspace ile ilişkisini kesiyoruz
cargo_toml = f'''[package]
name = "{name}"
version = "0.1.0"
edition = "2021"
description = "PezkuwiChain SDK component - placeholder for name reservation"
license = "Apache-2.0"
repository = "https://github.com/pezkuwichain/pezkuwi-sdk"
homepage = "https://pezkuwichain.io"
documentation = "https://docs.pezkuwichain.io/sdk/"
authors = ["Kurdistan Tech Institute <info@pezkuwichain.io>"]
keywords = ["pezkuwichain", "blockchain", "sdk"]
categories = ["cryptography::cryptocurrencies"]
[workspace]
[dependencies]
'''
(crate_dir / "Cargo.toml").write_text(cargo_toml)
src_dir = crate_dir / "src"
src_dir.mkdir(exist_ok=True)
lib_rs = f'''//! {name}
//! This crate is part of the PezkuwiChain SDK.
//! Full implementation coming soon.
#![doc = include_str!("../README.md")]
'''
(src_dir / "lib.rs").write_text(lib_rs)
readme = f'''# {name}
Part of [PezkuwiChain SDK](https://github.com/pezkuwichain/pezkuwi-sdk).
## About
This crate is a component of the PezkuwiChain blockchain SDK.
'''
(crate_dir / "README.md").write_text(readme)
return crate_dir
def publish_placeholder(crate_dir: Path, dry_run: bool = True):
"""Placeholder'ı crates.io'ya publish et.
Dönüş: (başarılı_mı, bekleme_gerekli_mi)
"""
args = ["cargo", "publish"]
if dry_run:
args.append("--dry-run")
args.extend(["--manifest-path", str(crate_dir / "Cargo.toml")])
result = subprocess.run(args, capture_output=True, text=True, cwd=crate_dir)
if result.returncode == 0:
return True, True # Başarılı, bekleme yap
# "already exists" hatasını kontrol et
if "already exists" in result.stderr:
return True, False # Zaten var, bekleme yapma
print(f"\n[HATA] {crate_dir.name} publish edilemedi:\n{result.stderr}")
return False, False
def main():
parser = argparse.ArgumentParser(description="crates.io isim rezervasyonu")
parser.add_argument("--list", action="store_true", help="İsimleri listele")
parser.add_argument("--check", action="store_true", help="crates.io'da müsaitlik kontrol et")
parser.add_argument("--create", action="store_true", help="Placeholder crate'leri oluştur")
parser.add_argument("--publish", action="store_true", help="crates.io'ya publish et")
parser.add_argument("--dry-run", action="store_true", help="Publish dry-run")
parser.add_argument("--start-from", type=str, help="İşleme bu crate isminden başla (öncekileri atlar)")
parser.add_argument("--interval", type=int, default=360, help="Publish arası bekleme süresi (saniye). Varsayılan: 360")
args = parser.parse_args()
if args.list:
for name in sorted(NEW_CRATE_NAMES):
print(f" {name}")
return
# Create/Publish işlemleri
if args.create or args.publish:
# Placeholder klasörünü oluştur
PLACEHOLDER_DIR.mkdir(exist_ok=True)
start_processing = False
if not args.start_from:
start_processing = True
print(f"Toplam Crate Sayısı: {len(NEW_CRATE_NAMES)}")
print(f"Bekleme Süresi: {args.interval} saniye")
if args.start_from:
print(f"Başlangıç: {args.start_from} (Öncekiler atlanacak)")
success = 0
failed = 0
skipped = 0
for i, name in enumerate(NEW_CRATE_NAMES, 1):
# Resume mantığı
if not start_processing:
if name == args.start_from:
start_processing = True
else:
skipped += 1
continue
print(f"[{i}/{len(NEW_CRATE_NAMES)}] {name}...", end=" ", flush=True)
# 1. Create
crate_dir = create_placeholder(name)
# 2. Publish (Eğer istenmişse)
if args.publish:
success_status, needs_wait = publish_placeholder(crate_dir, args.dry_run)
if success_status:
if needs_wait:
print("✓ PUBLISHED")
success += 1
if not args.dry_run:
print(f" -> Bekleniyor {args.interval}sn...")
time.sleep(args.interval)
else:
print("✓ ZATEN VAR (Atlandı)")
success += 1
else:
print("✗ FAILED")
failed += 1
else:
print("✓ CREATED")
print(f"\nSonuç: {success} başarılı, {failed} başarısız, {skipped} atlandı.")
if __name__ == "__main__":
main()
-192
View File
@@ -1,192 +0,0 @@
#!/usr/bin/env python3
"""
Eski (rebrand edilmemiş) kelimeleri tarayan script.
Her crate için çalıştırılır ve kalan eski kelimeleri tespit eder.
Kullanım:
python3 scan_old_words.py <crate_path>
python3 scan_old_words.py /home/mamostehp/kurdistan-sdk/bizinikiwi/primitives/core
"""
import os
import sys
import re
from pathlib import Path
# Rebrand kuralları: (eski_pattern, yeni_kelime, açıklama)
# Sıralama önemli - daha spesifik olanlar önce
REBRAND_RULES = [
# Terminoloji
(r'\bparachain\b', 'teyrchain', 'parachain → teyrchain'),
(r'\bParachain\b', 'Teyrchain', 'Parachain → Teyrchain'),
(r'\bPARACHAIN\b', 'TEYRCHAIN', 'PARACHAIN → TEYRCHAIN'),
(r'\brococo\b', 'pezkuwichain', 'rococo → pezkuwichain'),
(r'\bRococo\b', 'Pezkuwichain', 'Rococo → Pezkuwichain'),
(r'\bROCOCO\b', 'PEZKUWICHAIN', 'ROCOCO → PEZKUWICHAIN'),
(r'\bwestend\b', 'zagros', 'westend → zagros'),
(r'\bWestend\b', 'Zagros', 'Westend → Zagros'),
(r'\bWESTEND\b', 'ZAGROS', 'WESTEND → ZAGROS'),
(r'\bkusama\b', 'zagros', 'kusama → zagros'),
(r'\bKusama\b', 'Zagros', 'Kusama → Zagros'),
(r'\bKUSAMA\b', 'ZAGROS', 'KUSAMA → ZAGROS'),
# Crate prefix'leri (Cargo.toml name ve use statement'larda)
# Dikkat: Bunlar sadece crate isimlerinde geçerli, rastgele "sp_" değil
(r'\bsp-core\b', 'pezsp-core', 'sp-core → pezsp-core'),
(r'\bsp-runtime\b', 'pezsp-runtime', 'sp-runtime → pezsp-runtime'),
(r'\bsp-io\b', 'pezsp-io', 'sp-io → pezsp-io'),
(r'\bsp-std\b', 'pezsp-std', 'sp-std → pezsp-std'),
(r'\bsp-api\b', 'pezsp-api', 'sp-api → pezsp-api'),
(r'\bsc-client\b', 'pezsc-client', 'sc-client → pezsc-client'),
(r'\bsc-service\b', 'pezsc-service', 'sc-service → pezsc-service'),
(r'\bframe-support\b', 'pezframe-support', 'frame-support → pezframe-support'),
(r'\bframe-system\b', 'pezframe-system', 'frame-system → pezframe-system'),
(r'\bpallet-balances\b', 'pezpallet-balances', 'pallet-balances → pezpallet-balances'),
(r'\bcumulus-client\b', 'pezcumulus-client', 'cumulus-client → pezcumulus-client'),
(r'\bcumulus-primitives\b', 'pezcumulus-primitives', 'cumulus-primitives → pezcumulus-primitives'),
# Snowbridge (pezsnowbridge-pezpallet önce, sonra genel snowbridge)
(r'\bsnowbridge-pezpallet-', 'pezsnowbridge-pezpallet-', 'snowbridge-pezpallet- → pezsnowbridge-pezpallet-'),
(r'\bsnowbridge-pallet-', 'pezsnowbridge-pezpallet-', 'snowbridge-pallet- → pezsnowbridge-pezpallet-'),
(r'\bsnowbridge-', 'pezsnowbridge-', 'snowbridge- → pezsnowbridge-'),
(r'\bsnowbridge_pallet_', 'pezsnowbridge_pezpallet_', 'snowbridge_pallet_ → pezsnowbridge_pezpallet_'),
(r'\bsnowbridge_pezpallet_', 'pezsnowbridge_pezpallet_', 'snowbridge_pezpallet_ → pezsnowbridge_pezpallet_'),
# Bridge
(r'\bbridge-hub-rococo\b', 'pezbridge-hub-pezkuwichain', 'bridge-hub-rococo → pezbridge-hub-pezkuwichain'),
(r'\bbridge-hub-westend\b', 'pezbridge-hub-zagros', 'bridge-hub-westend → pezbridge-hub-zagros'),
(r'\bbridge-runtime-common\b', 'pezbridge-runtime-common', 'bridge-runtime-common → pezbridge-runtime-common'),
# MMR
(r'\bmmr-gadget\b', 'pezmmr-gadget', 'mmr-gadget → pezmmr-gadget'),
(r'\bmmr-rpc\b', 'pezmmr-rpc', 'mmr-rpc → pezmmr-rpc'),
# Substrate (dikkatli - sadece proje referanslarında)
(r'\bsubstrate-wasm-builder\b', 'bizinikiwi-wasm-builder', 'substrate-wasm-builder → bizinikiwi-wasm-builder'),
(r'\bsubstrate-build-script-utils\b', 'bizinikiwi-build-script-utils', 'substrate-build-script-utils → bizinikiwi-build-script-utils'),
# Polkadot referansları
(r'\bpolkadot-sdk\b', 'pezkuwi-sdk', 'polkadot-sdk → pezkuwi-sdk'),
(r'\bpolkadot-runtime\b', 'pezkuwichain-runtime', 'polkadot-runtime → pezkuwichain-runtime'),
(r'\bpolkadot-primitives\b', 'pezkuwi-primitives', 'polkadot-primitives → pezkuwi-primitives'),
# Rust module isimleri (underscore versiyonları)
(r'\bsp_core\b', 'pezsp_core', 'sp_core → pezsp_core'),
(r'\bsp_runtime\b', 'pezsp_runtime', 'sp_runtime → pezsp_runtime'),
(r'\bsp_io\b', 'pezsp_io', 'sp_io → pezsp_io'),
(r'\bsc_client\b', 'pezsc_client', 'sc_client → pezsc_client'),
(r'\bframe_support\b', 'pezframe_support', 'frame_support → pezframe_support'),
(r'\bframe_system\b', 'pezframe_system', 'frame_system → pezframe_system'),
(r'\bpallet_balances\b', 'pezpallet_balances', 'pallet_balances → pezpallet_balances'),
(r'\bcumulus_client\b', 'pezcumulus_client', 'cumulus_client → pezcumulus_client'),
(r'\bcumulus_primitives\b', 'pezcumulus_primitives', 'cumulus_primitives → pezcumulus_primitives'),
]
# Taranacak dosya uzantıları
SCAN_EXTENSIONS = {'.rs', '.toml', '.md', '.json', '.yaml', '.yml'}
# Atlanacak dizinler
SKIP_DIRS = {'target', '.git', 'node_modules', 'crate_placeholders'}
def scan_file(file_path: Path) -> list:
"""Tek bir dosyayı tarar ve bulunan eski kelimeleri döndürür."""
findings = []
try:
content = file_path.read_text(encoding='utf-8', errors='ignore')
except Exception as e:
return [(str(file_path), 0, f"OKUMA HATASI: {e}", "", "")]
lines = content.split('\n')
for line_num, line in enumerate(lines, 1):
for pattern, replacement, description in REBRAND_RULES:
matches = re.finditer(pattern, line)
for match in matches:
findings.append({
'file': str(file_path),
'line': line_num,
'column': match.start() + 1,
'found': match.group(),
'replacement': replacement,
'description': description,
'context': line.strip()[:100]
})
return findings
def scan_crate(crate_path: str) -> list:
"""Bir crate dizinini tarar."""
crate_dir = Path(crate_path)
if not crate_dir.exists():
print(f"HATA: Dizin bulunamadı: {crate_path}")
return []
all_findings = []
for root, dirs, files in os.walk(crate_dir):
# Skip directories
dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
for file in files:
file_path = Path(root) / file
if file_path.suffix not in SCAN_EXTENSIONS:
continue
findings = scan_file(file_path)
all_findings.extend(findings)
return all_findings
def print_report(findings: list, crate_path: str):
"""Bulunan eski kelimelerin raporunu yazdırır."""
print(f"\n{'='*60}")
print(f"TARAMA RAPORU: {crate_path}")
print(f"{'='*60}\n")
if not findings:
print("✅ ESKİ KELİME BULUNAMADI - Crate temiz!")
return
print(f"{len(findings)} adet eski kelime bulundu:\n")
# Dosyaya göre grupla
by_file = {}
for f in findings:
if f['file'] not in by_file:
by_file[f['file']] = []
by_file[f['file']].append(f)
for file_path, file_findings in sorted(by_file.items()):
rel_path = file_path.replace(crate_path, '.')
print(f"\n📄 {rel_path}")
print(f" {'-'*50}")
for finding in file_findings:
print(f" Satır {finding['line']}: {finding['found']}{finding['replacement']}")
print(f" Bağlam: {finding['context']}")
print()
def main():
if len(sys.argv) < 2:
print("Kullanım: python3 scan_old_words.py <crate_path>")
print("Örnek: python3 scan_old_words.py ./bizinikiwi/primitives/core")
sys.exit(1)
crate_path = sys.argv[1]
findings = scan_crate(crate_path)
print_report(findings, crate_path)
# Çıkış kodu: bulgu varsa 1, yoksa 0
sys.exit(1 if findings else 0)
if __name__ == "__main__":
main()
+64
View File
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- Nevroz Fire Gradient - Kurdish Colors -->
<linearGradient id="fireGradient" x1="0%" y1="100%" x2="0%" y2="0%">
<stop offset="0%" style="stop-color:#2E7D32;stop-opacity:1" /> <!-- Green base -->
<stop offset="25%" style="stop-color:#FF6F00;stop-opacity:1" /> <!-- Orange -->
<stop offset="55%" style="stop-color:#FFD600;stop-opacity:1" /> <!-- Yellow/Gold -->
<stop offset="100%" style="stop-color:#C62828;stop-opacity:1" /> <!-- Red tip -->
</linearGradient>
<!-- Inner flame gradient -->
<linearGradient id="innerFlame" x1="0%" y1="100%" x2="0%" y2="0%">
<stop offset="0%" style="stop-color:#FFD600;stop-opacity:1" />
<stop offset="50%" style="stop-color:#FF8F00;stop-opacity:1" />
<stop offset="100%" style="stop-color:#FF5722;stop-opacity:1" />
</linearGradient>
<!-- Glow filter -->
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="6" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<!-- Main Nevroz Fire - Outer flame -->
<path d="M256 50
C285 115 350 155 375 225
C400 295 375 365 350 405
C325 445 285 475 256 485
C227 475 187 445 162 405
C137 365 112 295 137 225
C162 155 227 115 256 50Z"
fill="url(#fireGradient)"
filter="url(#glow)"/>
<!-- Inner flame (yellow/orange core) -->
<path d="M256 130
C275 175 320 210 335 270
C350 330 335 380 310 415
C285 450 270 470 256 475
C242 470 227 450 202 415
C177 380 162 330 177 270
C192 210 237 175 256 130Z"
fill="url(#innerFlame)"
opacity="0.95"/>
<!-- Brightest core -->
<ellipse cx="256" cy="340" rx="55" ry="90" fill="#FFF8E1" opacity="0.85"/>
<!-- Core highlight -->
<ellipse cx="256" cy="360" rx="35" ry="60" fill="#FFFFFF" opacity="0.6"/>
<!-- Green sparks (Kurdistan green accent) -->
<circle cx="195" cy="170" r="10" fill="#43A047" opacity="0.85"/>
<circle cx="317" cy="190" r="8" fill="#43A047" opacity="0.8"/>
<circle cx="225" cy="140" r="6" fill="#66BB6A" opacity="0.7"/>
<circle cx="287" cy="155" r="7" fill="#4CAF50" opacity="0.75"/>
<circle cx="170" cy="220" r="5" fill="#81C784" opacity="0.6"/>
<circle cx="342" cy="240" r="5" fill="#81C784" opacity="0.6"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB

@@ -1 +0,0 @@
rn_("0UkAAD1RPlE/UUBRQVFCUUNRRFFFUUZRR1FIUUlRSlEBAgA7MAAAAQAAIQEBAChGIQHzAwFrOzAAAAEAAEMBAQA7I0MBAQMAOzAAAAEAAC0BAQANIi0BAQQAOzAAAAEAACEBAQCfSyEBAQYAOzAAAAEAAJAAAQCSUZAAAQQAOzAAAAEAACEBAQB9SiEBAQMAOzAAAAEAACEBAQAxSCEBAQUAOzAAAAEAACEBAQAbUCEB8wOFJSAEOzAAAAEAACEBAQDrICEB8wCEJAEQ8mcZAAABAA==")
@@ -0,0 +1 @@
rn_("sUABAPXe9t733vje+d763vve/N793v7e/94A3wUARwEABD7sZSDpBQBJAQAG5OxlKuthSQEAYuxj7GTsZexm7GfsaOyzAgSgUAAB6FdhZXN28zvmAQC1AwEA8wACaXMBa+wBALFDAQBvz3DPcc9yz3PPdM91z3bPd894z3nPes8FAcABAAtW6Ach6WVpAQQBoAAAAexPczswAAABAQAYAAIAxNsXAPHoAACxQgEAZFtlW2ZbZ1toW2lbaltrW2xbbVtuW29bvwaHoEAAAexhoKAAAejUhUQF")
@@ -0,0 +1 @@
rn_("AQQAOzAAAAEBAJcNAgB0LcsGL8TLBgEGADswAAABAQBcBwIA5oLLBr7nkAABBAA7MAAAAQEAlw0CAKgmywZjvcsGAQMAOzABAAMAAMsGAQDLBgEABeXLBgEAw6/LBgEFADswAAABAQCXDQIAsGHLBg3fywZ3AwCGoIAAAeskJSEEOzAAAAEAAJcNAgB6GssGecTLBg==")
@@ -1 +0,0 @@
rn_("BQBHAAAGuFZl/lQrAqDAAABSxWl0AQcAOzAAAAEAAOcAAQCmTucAAQMAOzAAAAEAAOcAAQCSM+cAAQIAOzAAAAEAAOcAAQBAOucA8wCEAgEkOzAAAAEAAAMBAgDmChsAZQznAA==")
@@ -0,0 +1 @@
rn_("AQIAOzAAAAEBAJcNAgB0LcsGL8TLBisCsFHsaQABYXUBAQGgYAAB7DtjOzABAAIAAAAAAQALAPLiAQAB3wsA+wJlcg==")
@@ -0,0 +1 @@
rn_("AQoAOzAAAAEBABUAAQDM6RUAAQMAOzAAAAEBABcAAQCwDxcAUwCEoEAAAVw/sJHp8AABAFEBOzABAAMAAGwAAQANAAIAi1sAAOBwawADAEmBCwA66QAAzuwAAA==")
@@ -1 +0,0 @@
rn_("YUcAAOZS51LoUulS6lLrUuxSAQMAOzAAAAEAABEAAQCUQBEA0UMAAIZAh0CIQIlAikCLQIxAjUCOQI9AkECRQJJAk0ABAgGwYFFiAAFyOzAAAAEAACsAAQCqOSsAKwKgsAAAVPhhZfsEbHJ2eg==")
@@ -1 +0,0 @@
rn_("YUAAAKlSqlKrUqxSrVKuUq9S8wIBczswAAABAABSAAYAckAAAINHKwAMSgAABlMGAP9VDgAUVg4A8wABafNKRwAAAQCxDQ==")
@@ -0,0 +1 @@
rn_("sUABAPXe9t733vje+d763vve/N793v7e/94A3wUARwEABD7sZSDpBQBAAQAEV+hv+ukFAEkBAAbk7GUq62FJAQBi7GPsZOxl7GbsZ+xo7FcDAsagwAAB7GCgEAAButpARgAJOzAAAAEBACYABQA75gAAGekGAPDpAQBZ6gYADusVAPMAAmlzAWvsAQA=")
@@ -0,0 +1 @@
rn_("BQLAAQCwsFqxWrJas1q0WrVatlq3WrhauVq6WrtaahnpGukb6RzpHeke6R/pDWvsMmlz5QBGAQAN7Oxz/uj/6ADpAekC6QPpBOkF6QbpB+kI6QnpCukL6QzpAQIAOzAAAAEBAFMAAQDdW1MAGwKgMAAB6SpheWFKAQDy6PPo9Oj16Pbo9+j46AEFADswAAABAQBfAAEAy6pfAAUBwAEADjPr6NLs0+zU7NXs1uzX7Njs2eza7Nvs3Ozd7N7s3+zg7GVpAQUAOzAAAAEBAFMAAQB3qlMAYUkBAE/oUOhR6FLoU+hU6FXoYUwBAOnp6unr6ezp7enu6e/pYUoBABLpE+kU6RXpFukX6RjpBQHBAQANzOwFDellb/sCb3IBAwA7MAAAAQEAawABABqCawABAwA7MAAAAQEAUwABAMaBUwD7AnJ2AQMAOzAAAAEBAGMAAwB9W18A0OwBAOrsAQABAM6gUAAB6vc3KMc191Ig5ZQq98dGPGKbU05SQzGHsv4NxF+QL15cJvDNfFtbvIJGt91DQfgHSVRGtoodB4hnFm76NoRfUIvfrYsjcdjsQLChYAAB7OpAPRYfOzABAAMAAHcAAQAYAAIAlXFTACt0IwADAHetCwCo5gsAD+kAAA==")
@@ -0,0 +1 @@
rn_("AQYAOzAAAAEBAL0AAgA7614AbexeAAEAADswAQABAAAXAAEAAQABABN0FwD36fjpwUUBAM66z7rQutG60rrTutS61brWute62LrZuivr+wJhZSsDoLAAAepgoEAAAen5bHJ5m4SggAAB6PugkAAB6lcBQAU=")
@@ -1 +0,0 @@
rn_("BQHAAAAhDkoPSv9UFOBB4UFlbwEAAqDAAABUNaDAAABWI3R5OzAAAAEAADkAAgAvSisAglENAAEGAqBAAABS/qFAAABWwHR18s9HAAABAAUBwAAADfxUFN5B30FmcgEDADswAAABAAArAAEAuykrAKcOAIihEAAAVqKwYEfNAAGg0AAAVDSCyAc7MAAAAQAAlwEEALoKKwBILCEBIUArANFHHQA=")
@@ -1 +0,0 @@
rn_("hQBAAAAGuFZlqVKqUqtSrFKtUq5Sr1KwUv5U8wEBczswAAABAABSAAYAckAAAINHKwAMSgAABlMGAP9VDgAUVg4A")
@@ -0,0 +1 @@
rn_("AQcAOzAAAAEBAHMEAQD7ynMEAQIAOzAAAAEAAHMEAQBl3nMEAQIAOzAAAAEAAHMEAQDx2XME+wMxMjYBAgGgAAAB6yhzOzAAAAEBALMAAQCY3bMAKwKgMAAA4/Bpa/sCcng=")
@@ -0,0 +1 @@
rn_("AQAAOzAAAAEBAFMAAQBgflMAsUMBAFe9WL1ZvVq9W71cvV29Xr1fvWC9Yb1ivfMBAWQBffoAAPsCY3QBAgA7MAAAAQAAywYBANHrywbzAAFtOzAAAAEAAMsGAQDdT8sGYUMBABLpE+kU6RXpFukX6Rjpe4SgMAABus0hIAI=")
@@ -1 +0,0 @@
rn_("AQYAOzAAAAEAAL0AAgAPVV4AQVZeAAUBwAAAJQ5KD0r/VFCDE4QThROGE8tTzFNhZSsDoLAAAFQ0oEAAAFPNbHJ5m4SggAAAUs+gkAAAVCsBQAU=")
@@ -1 +0,0 @@
rn_("AQMAOzAAAAEAABsAAQD+PxsAAQgAOzAAAAEAAJAAAQAPU5AA8wIBdTswAAABAABlAwMAYCohAW48IQEKRCEB+wJzdA==")
@@ -1 +0,0 @@
rn_("MUgAAPlU+lT7VMFWAQQAOzAAAAEAACEBAQCfSyEBAQYAOzAAAAEAAJAAAQCSUZAAAQQAOzAAAAEAACEBAQB9SiEBAQMAOzAAAAEAACEBAQAxSCEBAQUAOzAAAAEAACEBAQAbUCEB8wKFJSAEOzAAAAEAACEBAQDrICEB+wJhaA==")
@@ -1 +0,0 @@
rn_("BQHAAADQT0dQR1FHUkdTR1RHVUdWR1dHWEdZR1pHW0dcRwb1UmRuAQMAOzAAAAEAACEBAQCBTSEBMUIAAPlU+lT7VMFWMwEDoJAAAFIqZWlzOzAAAAEAAJEAAgAPU5AAD1YAAGuEoRAAAFY1oIAAAEoFFCEA")
@@ -1 +0,0 @@
rn_("YUoAAOZS51LoUulS6lLrUuxS4VMAAKZWp1aoVqlWqlarVqxWrVauVq9WsFaxVrJWs1a0VhsCoMAAAFagYXgTgQKgUAAAUuFlbwECADswAAABAAAhAQEAfSchAQEBAaAQAABVB2U7MAAAAQAAHQABAKISHQAFAEQAABN7SnxKZO8pAQsAOzAAAAEAAJAAAQBuVZAAI4QCoKAAAFQrcHT7Am5y8UMAADQTNRM2EzcTOBM5EzoTOxM8Ez0TPhM/E0ATQRNCE0MT8wCGEVEBOzAAAAEAAJUEBABrDSEBIxdDArEeIQF1Rw0A")
@@ -1 +0,0 @@
rn_("BQLAAAAQ+jn7OWrtUu5S71LwUvFS8lLzUg0/VjJpc+UARgAADcBWc9JS01LUUtVS1lLXUthS2VLaUttS3FLdUt5S31LgUgUBwAAA0i06LjovOjA6MToyOjM6NDo1OjY6Nzo4Ojk6OjoD/lJheWFKAADGUsdSyFLJUspSy1LMUvFFAABdR15HX0dgR2FHYkdjR2RHZUdmR2dHaEdpR2pHa0dsRwUBwAAADgdV6KZWp1aoVqlWqlarVqxWrVauVq9WsFaxVrJWs1a0VmVp0UUAAE9HUEdRR1JHU0dUR1VHVkdXR1hHWUdaR1tHXEdhSQAAI1IkUiVSJlInUihSKVJhTAAAvVO+U79TwFPBU8JTw1NhSgAA5lLnUuhS6VLqUutS7FIFAcEAAA2gVgXhUmVv+wJvcgEDADswAAABAAARAAEAlEARANFDAACGQIdAiECJQIpAi0CMQI1AjkCPQJBAkUCSQJNA+wJydgEDADswAAABAAATAAMAHToPAKRWAQC+VgEAAQDOoFAAAFTLIZyQWJmCSPTrhva0HOkXdac+G75gQGUrMQUuToKiUdUMQzkpKf/PyjGQYKWG8J3jSTB/ZuVUd2wioohTeq41TfioWGWZ7N0KoWAAAFa+QD0WHzswAAABAAAYAAUAFxMNAIcTBQDPRwEAYlEBAONSAAA=")
@@ -1 +0,0 @@
rn_("AQMAOzAAAAEAAB4AAgATSB0A/lIAACFSAACkVqVWv1YhTQAAEFYRVrdWMUsAAMhUyVTKVKNW84MDY2VzAQgAOzAAAAEAAJAAAQAPU5AAFQBCAAAGD1ZjGVAaUAUBxAAAaABVAVUCVQNVBFUFVQZVZbZTt1O4U7lTulO7U7xTb3MBCQA7MAAAAQAAkAABAG5VkACVAUUAAECxUrJSs1K0UrtWQLVStlK3UrhSvFYyMw5QD1AQUBFQElATUBRQFVAWULpW+wJhdQEFADswAAABAADnAAEApk7nACMAArAwQeAAAWt1OzAAAAEAAF0AAQBdKV0A0UoAAIJRg1GEUYVRhlGHUYhRiVGKUYtRjFGNUY5Rj1HVAEMAAAMLSmRqLWstbC1tLW4tby1wLXEtci1zLXQtdS12LXctYUMAANhB2UHaQdtB3EHdQbVWtQFAAAAEDVMEDlNsdAYmByYIJgkmCiYLJgwmDSYPSBBIEUgSSBsCoGAAAFPKb3LRRAAAPEU9RT5FP0VARUFFQkVDRURFRUVGRUdFSEVJRfsCaW8xQQAAPzoOSg9K/1QBCwA7MAAAAQAAFQABAJJSFQABAAA7MAAAAQAAKwABAC9KKwDzAwFzOzAAAAEAAB0AAQDRRx0AYUcAAItSjFKNUo5Sj1KQUpFSAQMAOzAAAAEAAB0AAQBkUR0AAQEAOzAAAAEAAB0AAQARSh0A84ICZWnVAEEAANMAUAFQAlADUARQBVAGUAdQCFAJUApQC1AMUA1QZvw5/Tn+Of85ADoBOgI6AzoEOgU6BjoHOgg6CTrzgoQQMQAbAqBgAABUNGFvdQBBAAAVpE6lTmMCJgMmBCYFJvY59zkNSA5IEwACsDA6GgABYWPyFBMAAAEAIUQAAIRAhUCRUdFBAAAtOi46LzowOjE6MjozOjQ6NTo2Ojc6ODo5Ojo6YUcAAKlSqlKrUqxSrVKuUq9SYUgAAAZTB1MIUwlTClMLUwxTkwMEoDAAAFPGoHAAAFUHYWhwdjswAAABAAAYAAQA4R8PAHFAAAAKSgAAi1IGAAUBwAAAEhg6GToKqFJhb7cBAIWgcAAAUuIMgAryEhMAAAEAAQDRJ8UsFVs8dJye4DmbC2vLN33VZqXzlKD2d8fUe+vZbhfV/isxO6oEDgb7PgTQYmWWIHO+8qa4EJFq/fKaD2wCsTsgV4Y65bl7P4z1wJHwdCuOr9ilsBBFLAABJhcVFgYqUagZuL4TwG+UXzswAAABAACOAwgADiYdAOkpBQBgKiEBbjwhAbRAAQAKRCEBSkUBAMRSAAA=")
@@ -1 +0,0 @@
rn_("IUIAADw6PTo+OhUARQAABg9WYxlQGlBBQAAAtVK2UrdSuFK8VkFAAACxUrJSs1K0UrtW8wIEMjNicjswAAABAAAZAAQA3SANAA5QCAA9VgEAulYAAAUARwAABBJWZfRSBQBJAAAGuFZl/lRhSQAANlY3VjhWOVY6VjtWPFazBQSgUAAAUithZXN2809RAAB1AgEA+wJhZg==")
@@ -1 +0,0 @@
rn_("IUAAAHgteS1zQAEDADswAAABAABdAAEAACpdAHFAAABPDDZWN1Y4VjlWOlY7VjxW8wADZGhyOzAAAAEAALYACQDlDgAALSYrAPApDwBKPA8ATUAdAO9HHQBSUQ8A9lIHAKBTFQAbArBgLq4AAWVp")
@@ -0,0 +1 @@
rn_("sUEBANqC24Lcgt2C3oLfguCC4YLiguOC5ILlgvMCAWU7MAAAAQEAJAACAEzeIwDl7AAAYUcBAOno6ujr6Ozo7eju6O/oAQMAOzAAAAEBABcAAQCLuhcAowCEsJHp9wABoFAAAbrNAUAJOzAAAAEAABcAAQDZ4hcA+wJlbw==")
@@ -0,0 +1 @@
rn_("BQFMAQAA5+wA6OwyM+bsGwKg4AAB7OlpbwUBwAEADOXsAynrbnQHBoSIoCAAAenzoKAAAezhoTAAAeztsCHp9AABoJAAAezNsHHsaQABOlAG")
@@ -0,0 +1 @@
rn_("AQAAOzAAAAEAAC8AAQAbey8A8wIBczswAQABAAALAAEAAAABAExxCwAQ6QEFADswAAABAQAXAAEAxNsXAPsCY24=")
@@ -0,0 +1 @@
rn_("YUcBACzrLesu6y/rMOsx6zLrAQcAOzAAAAEBABgAAgCjuhcAEekAAGFAAQBi7GPsZOxl7GbsZ+xo7PMBAWQ7MAEAAwAABwEBABUAAQDx4wcBAQDM6RUAKwKgQAAB5jlnevMAAmlwOzABAAMAAC8AAQAMAAEArGIvAAIAuoELAL3nAAAFAcEBALJeql+qYKphqmKqY6pkqmWqZqpnqmiqaaplT+hQ6FHoUuhT6FToVehlcruEoKAAAen6AAgL")
@@ -1 +0,0 @@
rn_("AQUAOzAAAAEAACEBAQBuPCEBAQIAOzAAAAEAAEMCAQAjF0MCAQIAOzAAAAEAAEMCAQDfFEMCYUwAAP9SAFMBUwJTA1MEUwVT8wCECBADOzAAAAEAAN0CAwDGBqEBDSItAQBQDQA=")
@@ -1 +0,0 @@
rn_("0UMAAGA8YTxiPGM8ZDxlPGY8ZzxoPGk8ajxrPGw8bTzzAgFjOzAAAAEAADEAAwB+LQ0APEUNAMxUFQDxQAAAVQxWDFcMWAxZDFoMWwxcDF0MXgxfDGAMYQxiDGMMZAxjAAOgkAAAUs1hZHQ7MAAAAQAAQwICAFsmIQExSCEB")
@@ -0,0 +1 @@
rn_("YUQBACzrLesu6y/rMOsx6zLrBQHDAQBoLOst6y7rL+sw6zHrMutl4unj6eTp5enm6efp6OlvcwUBTAEAAOfsAOjsMjPm7NMAhKDgAAHs6QBBUDswAQADAAALAAEADAABAKJYCwACAPXeCwBP7AAA")
@@ -0,0 +1 @@
rn_("sUMBAHBbcVtyW3NbdFt1W3Zbd1t4W3lbelt7W7FFAQBI1knWStZL1kzWTdZO1k/WUNZR1lLWU9bzAAFjOzABAAMAABcAAQAYAAEA9OIXAAMAmFoLAOuuCwDz6QAAKwOgQAAB6RCg0AAB7DpscnPzAAJhYzswAAABAAALAAEAiHELAA==")
@@ -0,0 +1 @@
rn_("AQMAOzAAAAEAAG8FAQC/vm8FAQABoDAAAek6aTswAAABAACnAAEAnkGnAAEAADswAAABAQCXDQIAqCbLBmO9ywYBAAGgMAAB6TlvAXkaAABnAwCGoAAAAbq8oBAAAek5IAgXOzABAAMAAIMAAQAZAAMA/wsLALQYRwAM4y8AAgD3rhcA9OkBAA==")
@@ -0,0 +1 @@
rn_("BQBCAQC2POY95j7mP+ZA5kHmQuZD5kTmReZG5kfmZf3oAQcAOzAAAAEBAFsAAQD76VsAAQkAOzAAAAEBAF4AAQA7614A84MCZG0BAQA7MAAAAQAAMwIBABH3MwLhTQEAK+ws7C3sLuwv7DDsMewy7DPsNOw17DbsN+w47DnsAQIAOzAAAAEBALsBAQDc27sBIwACoLAAAepYZWw7MAAAAQEA+wMBAI+2+wMBAAA7MAAAAQAAcwQBAJ3ycwQBDQA7MAAAAQEAXgABAG3sXgDzAQF3OzAAAAEBAHMEAQCwOXME+wJsc/MAhQkAFjswAAABAACABAIAZFkAAFBqfwT7AmVp")
@@ -0,0 +1 @@
rn_("AQEAOzAAAAEAAHMEAQB3l3MECQEuZbA49F9uAQEBoDAAAbrLZDswAAABAQBtAAQAWg9TALAPFwBf7AAAbOwAAAEEADswAAABAQAjAAEA3GgjADsDoAAAAejUYWZnBQLAAQCwsFqxWrJas1q0WrVatlq3WrhauVq6WrtaahnpGukb6RzpHeke6R/pDWvsMmlz5QBGAQAN7Oxz/uj/6ADpAekC6QPpBOkF6QbpB+kI6QnpCukL6QzpAQIAOzAAAAEBAFMAAQDdW1MAGwKgMAAB6SpheWFKAQDy6PPo9Oj16Pbo9+j46AEFADswAAABAQBfAAEAy6pfAAUBwAEADjPr6NLs0+zU7NXs1uzX7Njs2eza7Nvs3Ozd7N7s3+zg7GVpAQUAOzAAAAEBAFMAAQB3qlMAYUkBAE/oUOhR6FLoU+hU6FXoYUwBAOnp6unr6ezp7enu6e/pYUoBABLpE+kU6RXpFukX6RjpBQHBAQANzOwFDellb/sCb3IBAwA7MAAAAQEAawABABqCawABAwA7MAAAAQEAUwABAMaBUwD7AnJ2AQMAOzAAAAEBAGMAAwB9W18A0OwBAOrsAQABAc6gUAAB6vc3KMc191Ig5ZQq98dGPGKbU05SQzGHsv4NxF+QL15cJvDNfFtbvIJGt91DQfgHSVRGtoodB4hnFm76NoRfUIvfrYsjcdjsQLChYAAB7OpAPRYfOzABAAMAAHcAAQAYAAIAlXFTACt0IwADAHetCwCo5gsAD+kAAAECADswAAABAQCzAAEAN66zAAEGADswAAABAQAXAAEAxNsXAPOBAmFpnQeHAKBwAAHrJKAgAAHpOgMVGBlFcAQ=")
@@ -1 +0,0 @@
rn_("BQHBAAAGzlI+pFalVr5Wv1ZnaQELADswAAABAAAVAAEA4lQVAAUBwwAAA8dUB0BWaXDzBQF3AQdKAAABAwGgEAAASgZvOzAAAAEAABcAAwB0QAAATlEAAJJSFQCFAEQAAAszVndMR/9SAFMBUwJTA1MEUwVTNFYBCwA7MAAAAQAAFQABAMxUFQABAAA7MAAAAQAAHQABAK9HHQDzAwFz9nVAAABEEgEAAQABAAEE9woDyKBwAABRTbCgU8QAAcBEkAo7MAAAAQAALQACAL8UHwAuRQ0A+wJhZQ==")
@@ -0,0 +1 @@
rn_("BQBFAQAH/ehlWOoFAsMBADsl6ybrJ+vt7PW+ur+6wLrBusK6w7rEusW6xrrHusi6ybo87D3szOzj7Aj86GxvchUARQEABuTsZdzoKuv7Am5y8wACZXL0JesBAAEAAQDGAQ==")
@@ -1 +0,0 @@
rn_("AYMDoLAAAFajoNAAAFa3oSAAAFa/Y2VzNQFAAAAEDVMEDlNsdA9IEEgRSBJIE4ECoGAAAFPKb3JHAQXFoFAAAFGRsCBIDQABsBBFLAABwAgAGDswAAABAAAjAAIADiYdAOkpBQAVAEIAAAOQUWRrQGxA8wMBc/KAEwAAAQABhAKhEAAAVqKg0AAAViNic2FGAADmUudS6FLpUupS61LsUu8BhbBgOfgAATSEAA==")
@@ -0,0 +1 @@
rn_("AQIAOzAAAAEBAFMAAQBbq1MAYUsBADTrNes26zfrOOs56zrrAQUAOzAAAAEBAFMAAQCGglMA4wKEoHAAAexPBUABOzABAAMAAPcBAQBhAAIAOlpPAThwpwADAAx+UwDi6QYALOsGAPMAAXQ7MAAAAQEADgABAFDsDgA=")
@@ -1 +0,0 @@
rn_("AQYAOzAAAAEAAB4AAwAMSgAA/1UOABRWDgABBAA7MAAAAQAAHQABABNIHQAFAEIAAOAkViVWJlYnVihWKVYqVitWLFYtVi5WL1YwVjFWMlZzKlIBAwA7MAAAAQAAHgACADs6AABMRR0A5wEAxaBgAABUNQBCAQryJRMAAKg/")
@@ -1 +0,0 @@
rn_("AQIAOzAAAAEAAL0AAQBpGb0ABQHAAAAFxVIEtlZjZJMAhKBgAABS5KEAAABWthMBADswAAABAAB1AwQASAwFADg1TwPwPw0AlEARAA==")
@@ -1 +0,0 @@
rn_("cUUAABpAG0AcQB1AHkAfQCBAoVYFAUwAAAC7VgC8VjIzulYTAAKg4AAAVr1pbwEjVgAABQBCAAAQF1AYUHM/VhsCoGAAAFKoYWkBAwA7MAAAAQAAIQEBAIg4IQErAqAgAABU/Wh10UUAAOI/4z/kP+U/5j/nP+g/6T/qP+s/7D/tP+4/7z8BAgA7MAAAAQAAIQEBALEeIQEBAQA7MAAAAQAAIQEBAI8dIQErAqAwAAA6HGR1AQIAOzAAAAEAAEMCAgBtHCEBKEYhAdFFAADUP9U/1j/XP9g/2T/aP9s/3D/dP94/3z/gP+E/AQEAOzAAAAEAAEMCAQCQPUMCAQIAOzAAAAEAACcCAQDiQScC8wICYXI7MAAAAQAAQwIBACkaQwL3DgCIoMAAAFa5BTQHOzAAAAEAAJIFBwBrBAAAigkhATsjQwGSM+cAgU0hATZUkABuVZAAAQEAOzAAAAEAAL0AAQBpGb0ABQLAAAAQ+jn7OWrtUu5S71LwUvFS8lLzUg0/VjJpc+UARgAADcBWc9JS01LUUtVS1lLXUthS2VLaUttS3FLdUt5S31LgUgUBwAAA0i06LjovOjA6MToyOjM6NDo1OjY6Nzo4Ojk6OjoD/lJheWFKAADGUsdSyFLJUspSy1LMUvFFAABdR15HX0dgR2FHYkdjR2RHZUdmR2dHaEdpR2pHa0dsRwUBwAAADgdV6KZWp1aoVqlWqlarVqxWrVauVq9WsFaxVrJWs1a0VmVp0UUAAE9HUEdRR1JHU0dUR1VHVkdXR1hHWUdaR1tHXEdhSQAAI1IkUiVSJlInUihSKVJhTAAAvVO+U79TwFPBU8JTw1NhSgAA5lLnUuhS6VLqUutS7FIFAcEAAA2gVgXhUmVv+wJvcgEDADswAAABAAARAAEAlEARANFDAACGQIdAiECJQIpAi0CMQI1AjkCPQJBAkUCSQJNA+wJydgEDADswAAABAAATAAMAHToPAKRWAQC+VgEAAQHOoFAAAFTLIZyQWJmCSPTrhva0HOkXdac+G75gQGUrMQUuToKiUdUMQzkpKf/PyjGQYKWG8J3jSTB/ZuVUd2wioohTeq41TfioWGWZ7N0KoWAAAFa+QD0WHzswAAABAAAYAAUAFxMNAIcTBQDPRwEAYlEBAONSAAD5AgARbXQBCAA7MAAAAQAAHwABAFtKHwAbhKDwAABWtqAAAAATgqEgAABWwUAgIQUBwQAABORSAMdTbHIFAcAAAAXFUgS2VmNktboAjAABoKAAAFa1ArAgU8gAAQSgkAAAVqEXIyWwcFY9AAEnO1EPOzAAAAEAAAQECQBIDAUATQ0dAGoPDQAAKl0AODVPA/A/DQCUQBEAjk8DAMVSAAA=")
@@ -1 +0,0 @@
rn_("BQLCAAA7+VT6VPtUwVZVCEoJShBWEVagVrdWCNBSbG9yBQHAAADSYDxhPGI8YzxkPGU8ZjxnPGg8aTxqPGs8bDxtPAUPVmFoUwADoEAAAEoNY2VuOzAAAAEAAEcACgCsCg0AghMAAAAmAAB+LQ0APzoAADxFDQAOSgEAGVABAMxUFQD/VAAA")
@@ -0,0 +1 @@
rn_("AQMAOzAAAAEBAFMAAQAAaVMA8wIBYzswAAABAQC9AAMAyA9TAAqeUwD46hUAAQAAOzAAAAEAAF8AAQBZSV8AYwADoJAAAej5YWR0OzABAAMAAMsGAQDLBgEABeXLBgEAw6/LBg==")
@@ -1 +0,0 @@
rn_("BQBHAAAEElZl9FIFAEkAAAa4VmX+VGFJAAA2VjdWOFY5VjpWO1Y8VrMCBKBQAABSK2Flc3bzT1EAAHUCAQATAAKwAFAXAAFpcwE/VgAABQHAAAALKlIH9VJlaUUARAAAACNWc45Pj0+QT5FPxVKvBIewIDoYAAGgQAAAVjWgoAAAUqiwME1/AAGFRAU=")
@@ -0,0 +1 @@
rn_("AQIAOzAAAAEBAFMAAQBbq1MAsUUAABL9E/0U/RX9Fv0X/Rj9Gf0a/Rv9HP0d/QUBwAEACFjqajTrNes26zfrOOs56zrrZXIBBAA7MAAAAQEAWwABAPvpWwDhSAEA0uzT7NTs1ezW7Nfs2OzZ7Nrs2+zc7N3s3uzf7ODsBQFGAQAD8ukHM+tocMq6AQUAOzAAAAEBAFMAAQCGglMABQHBAQAFT+wEYexjaPMAiBXJATswAQADAAD3AQEAYQACADpaTwE4cKcAAwAMflMA4ukGACzrBgA=")
@@ -1 +0,0 @@
rn_("0UEAAE9HUEdRR1JHU0dUR1VHVkdXR1hHWUdaR1tHXEcrA6EQAABWNaCAAABKBWNpbtFEAADUP9U/1j/XP9g/2T/aP9s/3D/dP94/3z/gP+E/O4SgEAAAUuKASnsAFxQBRAI=")
@@ -0,0 +1 @@
rn_("sUUBADzmPeY+5j/mQOZB5kLmQ+ZE5kXmRuZH5gEDADswAAABAQCzAAEA2FmzAAEEADswAAABAQD7AwEAj7b7A/sCY2X7Am54EwCEoEAAAekhoNAAAew/oFAAAezOUAAUOzABAAIAAAAAAQA2ADlaCQCyiSMAa60LANToAAD96AAAV+oAAGDqAAAk6wAAOuwAAOHsAAA=")
@@ -0,0 +1 @@
rn_("tQBCAQADvOdkPYE+gT+BQIFBgUKBQ4FEgUWBRoFHgUiB8wIBczswAAABAAALAAEABnQLALFBAQDoFukW6hbrFuwW7RbuFu8W8BbxFvIW8xYFAcABAAQh6QXO7Gd3AQIAOzAAAAEBAFMAAQAQW1MA+wJkZwEAADswAAABAQBTAAEAd6pTAPsDZG534UABAFDsUexS7FPsVOxV7FbsV+xY7FnsWuxb7FzsXexe7AEEADswAAABAQBzBAEAHp9zBPMAAmVzOzABAAMAAMsGAQDMBgEAj6nLBgIAkqPLBlboAAABAAGgAAAB6yhzOzAAAAEBALMAAQCY3bMAsUEBAOKJ44nkieWJ5onnieiJ6YnqieuJ7IntiQEDADswAAABAQDLBgEAe8/LBjFCAQAl6ybrJ+vt7DMAA6CQAAHoVmVpczswAAABAQCRAAIAO+mQADvsAAABAgA7MAAAAQEAcwQBAGgbcwQBBQA7MAAAAQEAXgABAFjoXgDzAgFjOzAAAAEBAHMEAQD0FnME+wIxMvvFAgKIQMcEAIehEAAB7GGgMAAB57yggAABurugAAAB7D8cIQo7MAAAAQEAXwACAN1bUwA9gQsA")
@@ -1 +0,0 @@
rn_("0UIAAHVHdkd3R3hHeUd6R3tHfEd9R35Hf0eAR4FHgkdhSwAACFUJVQpVC1UMVQ1VDlXRRQAApkCnQKhAqUCqQKtArECtQK5Ar0CwQLFAskCzQOMAhKBwAABWIwVAATswAAABAABvAAUAMA83ANwSGwDiPw0AtlMGAABVBgDhSwAAJFYlViZWJ1YoVilWKlYrVixWLVYuVi9WMFYxVjJWOwOggAAAUsVkbW4=")
@@ -0,0 +1 @@
rn_("AQQAOzAAAAEBAFMAAQAMflMAAQgAOzAAAAEBAJAAAQA76ZAA8wQBdTswAQADAADhAgEA6QMBAB794QIBAAAA6QMFAsABAAs65gj66RNf7GzsZGZpsUMAAGuXbJdtl26Xb5dwl3GXcpdzl3SXdZd2lwEHADswAAABAQAkAAIAsokjAOHsAADHBwCHoAAAAek5oOAAAen5EEFOOzAAAAEAALMAAQCxV7MA")
@@ -0,0 +1 @@
rn_("AQYAOzAAAAEBAJAAAQCa65AA8wABYTswAAABAQCXDQIAdC3LBi/EywYTAQKgMAAB6Q9jdDswAAABAAAXAAEAp74XAA==")
@@ -1 +0,0 @@
rn_("YUQAALZTt1O4U7lTulO7U7xTMVEAAKRWpVa+Vr9W+wJweWFHAAAAVQFVAlUDVQRVBVUGVUFHAAABSgJKA0oESuVSYUAAADZWN1Y4VjlWOlY7VjxW8wIBZDswAAABAABBAAIALSYrAKBTFQDzAAJpcDswAAABAAAKAAMAmhAHAIRAAQCRUQAAawOwQEdKAAFscnUBBwA7MAAAAQAAWwABAM9TWwABBAA7MAAAAQAAvQACAA9VXgBBVl4ABQHBAAABx1Rg2EHZQdpB20HcQd1BtVZmZyFMAAAQVhFWt1ZlAkAAAA3lUhP0OfU5Df9UYXB21w7YDtkO2g7bDtwO/VT7AnJ4AQkAOzAAAAEAAL0AAgAPVV4AQVZeAAUBwAAA1D1RPlE/UUBRQVFCUUNRRFFFUUZRR1FIUUlRSlE6yFTJVMpUo1Zhdf8XirBwVjMADbBQQG0AARzZEg==")
@@ -1 +0,0 @@
rn_("BQBIAADgJFYlViZWJ1YoVilWKlYrVixWLVYuVi9WMFYxVjJWcypSAQcAOzAAAAEAABcAAgBSUQ8A9lIHAPsCZXQ=")
@@ -0,0 +1 @@
rn_("AQAAOzABAAIAAAAAAQDLBq5YAQAEUssGGwKgIAAB6mBlaeUARAEADezsc/7o/+gA6QHpAukD6QTpBekG6QfpCOkJ6QrpC+kM6fsCZ2w=")
@@ -0,0 +1 @@
rn_("sUQBAGutbK1trW6tb61wrXGtcq1zrXStda12rQEDADswAAABAQBTAAEAd6pTADsDoVAAAeziY2dwBQHAAQC1b89wz3HPcs9zz3TPdc92z3fPeM95z3rPtV6eX55gnmGeYp5jnmSeZZ5mnmeeaJ5pnmFw4U8BAFDsUexS7FPsVOxV7FbsV+xY7FnsWuxb7FzsXexe7LFGAQCo5qnmquar5qzmreau5q/msOax5rLms+bzAgFyOzAAAAEBAAcBAQDQWAcB7wGFoLAAAeskEUkA")
@@ -1 +0,0 @@
rn_("YUcAAABVAVUCVQNVBFUFVQZVQUcAAAFKAkoDSgRK5VJhQAAANlY3VjhWOVY6VjtWPFbzAQFkOzAAAAEAAEEAAgAtJisAoFMVACsCoEAAAFFNZ3rzAAJpcDswAAABAAAKAAMAmhAHAIRAAQCRUQAABQHBAAASSkdLR2UjUiRSJVImUidSKFIpUmVyu4SgoAAAU84ACAs=")
@@ -0,0 +1 @@
rn_("BQLAAQCzLeYu5i/mMOYx5jLmM+Y05jXmNuY35jjmBfDoBg/pbnByYUABANXo1ujX6Njo2eja6Nvo8wIBczswAAABAQAuAQYAYoEAAK+rBwHMugAAMukGACvsDgBA7A4AsUEBANqC24Lcgt2C3oLfguCC4YLiguOC5ILlgvMAA2VpdDswAAABAQAxAAQAXqoLAEzeIwAo6wAA5ewAAA==")
@@ -0,0 +1 @@
rn_("AQMAOzAAAAEBAKcAAQC0fqcAAQgAOzAAAAEBAJAAAQA76ZAA8wIBdTswAQADAADhAgEAgREBAB794QIDAAAA6QNUacsG3pbLBvsCc3Q=")
@@ -0,0 +1 @@
rn_("wUABAFe9WL1ZvVq9W71cvV29Xr1fvWC9Yb1ivbznAQEAOzAAAAEBAFMAAQB3qlMAKwOhEAAB7GGggAABurtjaW4BBAA7MAAAAQEAUwABALh9UwC7hKAQAAHpDgFEAg==")
@@ -1 +0,0 @@
rn_("AYMDoLAAAFajoNAAAFa3oSAAAFa/Y2VzNQFAAAAEDVMEDlNsdA9IEEgRSBJIE4ECoGAAAFPKb3JHAQXFoFAAAFGRsCBIDQABsBBFLAABwAgAGDswAAABAAAjAAIADiYdAOkpBQAVAEIAAAOQUWRrQGxA8wMBc/KAEwAAAQABAAA7MAAAAQAAIQEBAB8LIQErArBQLq4AAW5wAYQCoRAAAFaioNAAAFYjYnMlAEAAAGTmUudS6FLpUupS61LsUmV4LXktc0ABAwA7MAAAAQAAXQABAAAqXQBxQAAATww2VjdWOFY5VjpWO1Y8VvMAA2RocjswAAABAAC2AAkA5Q4AAC0mKwDwKQ8ASjwPAE1AHQDvRx0AUlEPAPZSBwCgUxUA7wOGsGA5+AABNIUA")
@@ -0,0 +1 @@
rn_("YUcBANXo1ujX6Njo2eja6NvoYUgBADLpM+k06TXpNuk36TjpkwIEoDAAAenyoHAAAeszYWhwdjswAQADAABfAAEACAABAEe+XwADAGGBAADKugAAt+gGAA==")
@@ -1 +0,0 @@
rn_("NQFAAAA5+VT6VPtUwVZp+k/7T/xP/U/+T/9PuVZjdv1J/kn/SQBKAQ0AOzAAAAEAAF4AAQBBVl4ABQFMAAAAu1YAvFYyM7pWGwKg4AAAVr1pbwUBwAAADLlWA/1UbnQHBoaIoCAAAFPHoKAAAFa1oTAAAFbBsCBTyAABoJAAAFahsHBWPQABOlAGNQBAAAAoDkoPSv9UcFAMUQxSDFMMGwKwADn4AAFlcwUBwwAAA8dUB0BWaXDRQwAAYDxhPGI8YzxkPGU8ZjxnPGg8aTxqPGs8bDxtPPMBAWM7MAAAAQAAMQADAH4tDQA8RQ0AzFQVAPMAAmV3OzAAAAEAABMABAAtDwEANBMPAAdKAAANSgAABQHAAAAH0FLvplanVqhWqVaqVqtWrFatVq5Wr1awVrFWslazVrRWZXUFAcQAAGgAVQFVAlUDVQRVBVUGVWW2U7dTuFO5U7pTu1O8U29z8wUBc/JKRQAAAQBRRAAACEoJShBWEVagVrdWAQAAOzAAAAEAAEMCAgBOMSEBfUohARsCoBAAAFMNcnMBBwA7MAAAAQAA5wABAKZO5wABAwA7MAAAAQAA5wABAJIz5wABAgA7MAAAAQAA5wABAEA65wDzAQNiaXQ7MAAAAQAA5wABAGUM5wDRQQAAT0dQR1FHUkdTR1RHVUdWR1dHWEdZR1pHW0dcRyOBA6EQAABWNaCAAABKBWNpbiFBAABvQHBAzVO/BYiwQEfNAAGhUAAAVrawQBlnAAFF4AMBAAA7MAAAAQAAKwABAC9KKwDzAgFzOzAAAAEAAB0AAQDRRx0AGwKwgEdKAAFhZnFHAABxQItSjFKNUo5Sj1KQUpFSMUoAAPlU+lT7VMFW4VAAAKZWp1aoVqlWqlarVqxWrVauVq9WsFaxVrJWs1a0VgEDADswAAABAAAdAAEAZFEdAAEBADswAAABAAAdAAEAEUodAPOCAmVpIVIAAKRWpVa/ViFNAAAQVhFWt1YxSwAAyFTJVMpUo1bzgwNjZXPVAEMAAAMLSmRqLWstbC1tLW4tby1wLXEtci1zLXQtdS12LXcttQFAAAAEDVMEDlNsdAYmByYIJgkmCiYLJgwmDSYPSBBIEUgSSBOBAqBgAABTym9ydQBBAAAVpE6lTmMCJgMmBCYFJvY59zkNSA5IEwACsDA6GgABYWPyFBMAAAEAFQFAAAATGDoZOiSEQIVAkVFjdRITExO1CwDIAAGgcAAAVDQDBR+wEEUsAAEGwEkAGTswAAABAAAmAAQADiYdAOkpBQBKRQEAxFIAANFDAAAAUAFQAlADUARQBVAGUAdQCFAJUApQC1AMUA1Q8wABZjswAAABAABTAAUAxBEbAPw5DQDUPw0AdkANAD1RDQD7AnJ0BQHAAAAJo04Uf02ATWJjVf8AzACwMCZZAAEBoCAAAE6jDxQVIxIsMBNATqxYOzAAAAEAACUBCABHBCMAYhMdAHo0vQAYOgMAHToPAGA8DQCkVgEAvlYBACFAAAB4LXktc0DzAQFyOzAAAAEAAHMABgDwKQ8ASjwPAE1AHQDvRx0AUlEPAPZSBwAFAcAAAHVtR25Hb0dwR3FHckdzR3RH7SRWJVYmVidWKFYpVipWK1YsVi1WLlYvVjBWMVYyVnN0FQBAAAANE1Z0NFT4VPkCFRZlafsCZGcBAgA7MAAAAQAAGQEDAM9TWwAPVV4AQVZeAAUAQgAABBJWZfRSZQBCAAARtEC1QGX6T/tP/E/9T/5P/0+5VjUDQAAAGctTzFMz/Un+Sf9JAEoFDUpnvVK+Ur9SwFLBUsJSw1Jhb3J1+yX8Jf0l/iXzgQJlbzMAA6CgAABRT2FpdDswAAABAAANAAEA0x8NAAUCwAAAAN8OEScaKBoALA9laXnzAIYMMILyNAMAAAEAlQFGAABAsVKyUrNStFK7VkC1UrZSt1K4UrxWMjMOUA9QEFARUBJQE1AUUBVQFlC6VrUXAMkVFqBwAABUNBgaADSwEEUsAAEbwEmAGTswAAABAAAmAAQADiYdAOkpBQBKRQEAxFIAAAEAAbBgUWIAAXI7MAAAAQAAKwABAKo5KwDzAAFzOzAAAAEAAB0AAQCvRx0AAQAAOzAAAAEAAE8DAQA4NU8D0UAAAIJRg1GEUYVRhlGHUYhRiVGKUYtRjFGNUY5Rj1HzAgFzOzAAAAEAACsAAQAhQCsA8wACYXQ7MAAAAQAASQABAJJPSQCFDgCIoGAAAE6joNAAAFT4oLAAAFPGAKCQAABSzxkEBhSwBzswAAABAABtAQoA5A4AAAYPHQAMEwUARBMdAIg4IQF1QAAAUFEBALlSAwC4VgAAvVYAAPUCQAAAElkmWiYSSkdLR2UjUiRSJVImUidSKFIpUmFlclUMVgxXDFgMWQxaDFsMXAxdDF4MXwxgDGEMYgxjDGQMFQBCAAAFzVJ0XipfKgUBwAAAZsZSx1LIUslSylLLUsxSCfhUaHQlAEIAAHCvFLAUsRSyFLMUtBS1FLYUcwoTCxPkUgECADswAAABAAC9AAEAbi+9AAEFADswAAABAABeAAEALFJeAPMCAWM7MAAAAQAAvQABALAuvQDzggIxMgUBwAAANI5Pj0+QT5FPE39NgE1jcq8Hh6CQAABOo6AQAABSzwYlBgEA0GrlO5/Qd2ZIJtk6wV/BlN+xr6AgAABOowD20rXo4AERgVKqHWzBoJZq+V+JOfkrD3YNtDmHSAOwM8I3BwhRrWI3PCJYpRw/Yj9Hn1gGjl7wuf1+1Gp3+vb7bSHdoQX2S0BOvl47MAAAAQAAbAMNAEcEIwB7BAAAYhMdAP8lAABbJiEBejS9ABg6AwAdOg8AYDwNADFIIQHiUgAApFYBAL5WAQA=")
@@ -0,0 +1 @@
rn_("AQEAOzAAAAEBABQAAwBP6AYA8ugGABLpBgDzAAFvOzAAAAEAAHMEAQCd8nMEAQMAOzAAAAEBAFwHAgDmgssGvueQAAENADswAAABAQBeAAEAbexeAOFQAQDS7NPs1OzV7Nbs1+zY7Nns2uzb7Nzs3eze7N/s4OzzAQJydzswAAABAQBzBAEAsDlzBJ8BhbCB6fcAAaBAAAHq9wCoAw==")
@@ -1 +0,0 @@
rn_("BQBFAAAH0VJlLFQFAsMAADv5VPpU+1TBVlUISglKEFYRVqBWt1YI0FJsb3IVAEUAAAa4VmWwUv5U+wJucvMAAmVy9PlUAAABAAEAxgE=")
@@ -0,0 +1 @@
rn_("YUoBABLpE+kU6RXpFukX6Rjp4VMBANLs0+zU7NXs1uzX7Njs2eza7Nvs3Ozd7N7s3+zg7BsCoMAAAezMYXgTgQKgUAAB6Q1lbwECADswAAABAADLBgEA0evLBgEBAaAQAAHrM2U7MAAAAQAAswABANxuswCxQwEAV71YvVm9Wr1bvVy9Xb1evV+9YL1hvWK98wQBZAF9+gAAAQsAOzAAAAEBAJAAAQCa65AAI4QCoKAAAepXcHT7Am5yAQMAOzAAAAEAAF8AAQA+cl8A8wCGEVEBOzABAAMAAC8bAQBTAAMA3U/LBtOJlw0nt8sGAQBbq1MA")
@@ -0,0 +1 @@
rn_("BQFMAQAA5+wA6OwyM+bsGwKg4AAB7OlpbwUBwAEADOXsAynrbnQHBoqIoCAAAenzoKAAAezhoTAAAeztsCHp9AABoJAAAezNsHHsaQABOlAG")
@@ -1 +0,0 @@
rn_("AQMAOzAAAAEAAOcAAQD1H+cAAQABoDAAAFMOaTswAAABAAAbAAEAAgsbAAEAADswAAABAABDAgIATjEhAX1KIQEFAEAAAAMNU297BGcDAIagAAAASgagEAAAUw0gCBc7MAAAAQAAGwAFAAkCAQAoBAsABiYHAA9IAwDIUwEA")
@@ -1 +0,0 @@
rn_("AQAAOzAAAAEAAB0AAQBiEx0ABQHAAAASSkdLR2UjUiRSJVImUidSKFIpUmVyUwCEoCAAAA8soDAAAFMNCEkA8nsEAABnTg==")
@@ -1 +0,0 @@
rn_("YU4AAC1ULlQvVDBUMVQyVDNUAQcAOzAAAAEAAFsAAQDPU1sAAQkAOzAAAAEAAF4AAQAPVV4A84MCZG0BDQA7MAAAAQAAXgABAEFWXgDzAgF3OzAAAAEAAL0AAQB6NL0A8wICYXc7MAAAAQAAvQABAOARvQABAwA7MAAAAQAAIQEBAIFNIQExQgAA+VT6VPtUwVbzAgJlaTswAAABAACRAAIAD1OQAA9WAAAxTgAApFalVr5Wv1YBAgGgIAAATqNmOzAAAAEAABMAAwAdOg8ApFYBAL5WAQDXBgCHoGAAAFLOoDAAAFLPQU0IOzAAAAEAAL4AAgALAgAAakW9AA==")

Some files were not shown because too many files have changed in this diff Show More