├── pnpm-workspace.yaml ├── apps ├── frontend │ ├── src │ │ ├── components │ │ │ ├── create-product │ │ │ │ ├── create-product.css │ │ │ │ └── create-product.tsx │ │ │ ├── products-list │ │ │ │ ├── products-list.css │ │ │ │ └── products-list.tsx │ │ │ ├── create-store │ │ │ │ ├── create-store.css │ │ │ │ └── create-store.tsx │ │ │ ├── store-actions │ │ │ │ └── store-actions.tsx │ │ │ ├── welcome │ │ │ │ ├── welcome.css │ │ │ │ └── welcome.tsx │ │ │ ├── lottie-animation.tsx │ │ │ ├── dashboard-store-layout.tsx │ │ │ ├── twa-display-gate.tsx │ │ │ └── success │ │ │ │ └── success.tsx │ │ ├── vite-env.d.ts │ │ ├── App.css │ │ ├── pages │ │ │ ├── market │ │ │ │ ├── product │ │ │ │ │ └── product-id │ │ │ │ │ │ ├── styles.css │ │ │ │ │ │ └── index.tsx │ │ │ │ ├── order │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── order-id.tsx │ │ │ │ ├── store │ │ │ │ │ └── store-id │ │ │ │ │ │ └── index.tsx │ │ │ │ ├── index.module.css │ │ │ │ ├── cart.module.css │ │ │ │ ├── index.tsx │ │ │ │ └── cart.tsx │ │ │ └── dashboard │ │ │ │ ├── order │ │ │ │ ├── index.tsx │ │ │ │ └── order-id.tsx │ │ │ │ ├── store │ │ │ │ ├── index.tsx │ │ │ │ ├── store-id │ │ │ │ │ ├── settings.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── create-product.tsx │ │ │ │ │ └── products.tsx │ │ │ │ └── create.tsx │ │ │ │ ├── product │ │ │ │ └── product-id.tsx │ │ │ │ └── index.tsx │ │ ├── index.tsx │ │ ├── styles │ │ │ ├── image.css │ │ │ ├── button.css │ │ │ └── text-field.css │ │ ├── utils │ │ │ ├── useBackButton.ts │ │ │ ├── useMainButton.ts │ │ │ ├── useDebounce.ts │ │ │ └── createAggListResult.ts │ │ ├── index.css │ │ ├── contexts │ │ │ └── pocketbase.tsx │ │ ├── assets │ │ │ └── solid.svg │ │ ├── types │ │ │ └── pb-types.ts │ │ └── App.tsx │ ├── postcss.config.js │ ├── tsconfig.node.json │ ├── vite.config.ts │ ├── .gitignore │ ├── index.html │ ├── tailwind.config.js │ ├── package.json │ ├── tsconfig.json │ ├── README.md │ └── public │ │ ├── vite.svg │ │ └── lottie │ │ └── check_mark.json ├── backend │ ├── install.js │ ├── package.json │ ├── migrations │ │ ├── 1695891282_updated_stores.go │ │ ├── 1695812825_updated_stores.go │ │ ├── 1695891299_updated_stores.go │ │ ├── 1695891325_updated_stores.go │ │ ├── 1695812624_updated_stores.go │ │ ├── 1695891527_updated_stores.go │ │ ├── 1696959425_updated_products.go │ │ ├── 1696959521_updated_products.go │ │ ├── 1696959706_updated_products.go │ │ ├── 1696964534_updated_products.go │ │ ├── 1696683749_updated_products.go │ │ ├── 1695982812_updated_categories.go │ │ ├── 1695812539_updated_stores.go │ │ ├── 1696926905_updated_products.go │ │ ├── 1696948469_updated_orders.go │ │ ├── 1695983165_updated_stores.go │ │ ├── 1695812759_updated_stores.go │ │ ├── 1695985525_updated_products.go │ │ ├── 1695813016_updated_stores.go │ │ ├── 1695988414_updated_orders.go │ │ ├── 1695811078_created_stores.go │ │ ├── 1695988550_updated_order_items.go │ │ ├── 1695982759_created_categories.go │ │ ├── 1695986040_updated_stores.go │ │ ├── 1695988302_updated_products.go │ │ ├── 1696946280_updated_order_items.go │ │ ├── 1696959689_updated_products.go │ │ ├── 1696959755_updated_order_items.go │ │ ├── 1695983753_updated_products.go │ │ ├── 1695985633_updated_products.go │ │ ├── 1695812931_updated_stores.go │ │ ├── 1695894851_updated_stores.go │ │ ├── 1695983767_updated_stores.go │ │ ├── 1695985930_updated_stores.go │ │ ├── 1695986131_created_orders.go │ │ ├── 1696257575_updated_stores.go │ │ ├── 1696683955_updated_products.go │ │ ├── 1696684136_updated_products.go │ │ ├── 1695986371_created_order_items.go │ │ ├── 1695983036_created_products.go │ │ └── 1695983101_updated_products.go │ ├── utils.go │ ├── go.mod │ ├── main.go │ └── go.sum └── watcher │ ├── package.json │ └── watcher.js ├── .dockerignore ├── .prettierrc.cjs ├── .vscode └── extensions.json ├── .env.example ├── TODO.md ├── .prettierignore ├── docker-compose.yml ├── Dockerfile ├── .gitignore ├── package.json ├── LICENSE └── README.md /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "apps/*" 3 | -------------------------------------------------------------------------------- /apps/frontend/src/components/create-product/create-product.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/frontend/src/components/products-list/products-list.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .git 3 | .gitignore 4 | *.md 5 | dist -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = require('@crashmax/prettier-config') 2 | -------------------------------------------------------------------------------- /apps/frontend/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["astro-build.astro-vscode"] 3 | } 4 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | TELEGRAM_MARKET_BOT_TOKEN="" 2 | TELEGRAM_STORE_BOT_TOKEN="" 3 | PORT=3000 4 | PUBLIC_POCKETBASE_URL="" -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | - Закончить форму добавления товара в магазин 2 | - Список товаров в каталоге магазина 3 | - Закончить корзину -------------------------------------------------------------------------------- /apps/frontend/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | text-align: center; 5 | } 6 | -------------------------------------------------------------------------------- /apps/frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/frontend/src/pages/market/product/product-id/styles.css: -------------------------------------------------------------------------------- 1 | .name { 2 | font-size: 18px; 3 | } 4 | 5 | .description{ 6 | font-size: 15px; 7 | } -------------------------------------------------------------------------------- /apps/frontend/src/pages/dashboard/order/index.tsx: -------------------------------------------------------------------------------- 1 | export function OrdersPage() { 2 | return ( 3 |
4 |

Orders

5 |
6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /apps/frontend/src/pages/dashboard/store/index.tsx: -------------------------------------------------------------------------------- 1 | export function StoresPage() { 2 | return ( 3 |
4 |

Stores

5 |
6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /apps/frontend/src/components/create-store/create-store.css: -------------------------------------------------------------------------------- 1 | .create-store__form { 2 | display: flex; 3 | flex-direction: column; 4 | gap: 1rem; 5 | width: 100%; 6 | } 7 | -------------------------------------------------------------------------------- /apps/frontend/src/pages/dashboard/order/order-id.tsx: -------------------------------------------------------------------------------- 1 | export function OrderIdPage() { 2 | return ( 3 |
4 |

Order ID

5 |
6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /apps/frontend/src/pages/market/order/index.tsx: -------------------------------------------------------------------------------- 1 | export function MarketOrdersPage() { 2 | return ( 3 |
4 |

Market Orders

5 |
6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /apps/frontend/src/pages/dashboard/product/product-id.tsx: -------------------------------------------------------------------------------- 1 | export function ProductIdPage() { 2 | return ( 3 |
4 |

Product ID

5 |
6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /apps/frontend/src/pages/market/order/order-id.tsx: -------------------------------------------------------------------------------- 1 | export function MarketOrderIdPage() { 2 | return ( 3 |
4 |

Market Order ID

5 |
6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /apps/frontend/src/pages/dashboard/store/store-id/settings.tsx: -------------------------------------------------------------------------------- 1 | export function StoreSettingsPage() { 2 | return ( 3 |
4 |

Settings

5 |
6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /apps/frontend/src/pages/market/store/store-id/index.tsx: -------------------------------------------------------------------------------- 1 | export function MarketStoreIdPage() { 2 | return ( 3 |
4 |

Market Store ID

5 |
6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /apps/frontend/src/pages/dashboard/store/create.tsx: -------------------------------------------------------------------------------- 1 | import { CreateStore } from '@/components/create-store/create-store' 2 | 3 | export function CreateStorePage() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /apps/backend/install.js: -------------------------------------------------------------------------------- 1 | const { exec } = require("child_process"); 2 | 3 | if (process.env.NODE_ENV === 'production'){ 4 | process.exit(1) 5 | } else { 6 | exec("go mod tidy && go mod download") 7 | } 8 | 9 | -------------------------------------------------------------------------------- /apps/backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "dev": "go run . serve", 7 | "prepare": "node install.js" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/frontend/src/index.tsx: -------------------------------------------------------------------------------- 1 | /* @refresh reload */ 2 | import { render } from 'solid-js/web' 3 | 4 | import './index.css' 5 | import App from './App' 6 | 7 | const root = document.getElementById('root') 8 | 9 | render(() => , root!) 10 | -------------------------------------------------------------------------------- /apps/frontend/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | public 4 | build 5 | logs 6 | coverage 7 | .next 8 | .turbo 9 | .github 10 | .angular 11 | .svelte-kit 12 | .vscode 13 | *.log* 14 | *.log 15 | *.lock 16 | *.yaml 17 | *.yml 18 | *.sh 19 | *.svg 20 | *rc.* 21 | *.htm 22 | *.html 23 | *.md 24 | .*ignore 25 | pb-types.ts 26 | -------------------------------------------------------------------------------- /apps/frontend/vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { defineConfig } from 'vite' 3 | import solid from 'vite-plugin-solid' 4 | 5 | export default defineConfig({ 6 | plugins: [solid()], 7 | resolve: { 8 | alias: [ 9 | { find: '@', replacement: path.resolve(__dirname, 'src') }, 10 | ] 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /apps/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/watcher/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "watcher", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "private": true, 6 | "scripts": { 7 | "dev": "node watcher.js", 8 | "pocketbase-typegen": "pocketbase-typegen --db ../backend/pb_data/data.db --out ../frontend/src/types/pb-types.ts" 9 | }, 10 | "devDependencies": { 11 | "pocketbase-typegen": "^1.1.13" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/frontend/src/styles/image.css: -------------------------------------------------------------------------------- 1 | .image__img { 2 | width: 100%; 3 | height: 100%; 4 | object-fit: cover; 5 | border-radius: inherit; 6 | } 7 | 8 | .image__fallback { 9 | width: 100%; 10 | height: 100%; 11 | display: flex; 12 | align-items: center; 13 | justify-content: center; 14 | font-size: 16px; 15 | line-height: 1; 16 | font-weight: 500; 17 | } 18 | 19 | .image__fallback > svg { 20 | fill: currentColor; 21 | color: var(--tg-theme-hint-color); 22 | } -------------------------------------------------------------------------------- /apps/frontend/src/components/store-actions/store-actions.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '@kobalte/core' 2 | 3 | import '@/styles/button.css' 4 | 5 | export function StoreActions() { 6 | return ( 7 |
8 | Редактировать 9 | Изменить каталог 10 | Статистика продаж ?????? 11 | Удалить 12 |
13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /apps/frontend/src/components/welcome/welcome.css: -------------------------------------------------------------------------------- 1 | .image { 2 | display: inline-flex; 3 | align-items: center; 4 | justify-content: center; 5 | vertical-align: middle; 6 | overflow: hidden; 7 | user-select: none; 8 | width: 64px; 9 | height: 64px; 10 | border-radius: 100%; 11 | background-color: var(--tg-theme-secondary-bg-color); 12 | } 13 | 14 | .store__item { 15 | border-radius: 4px; 16 | } 17 | 18 | .store__item:hover { 19 | cursor: pointer; 20 | background-color: var(--tg-theme-secondary-bg-color); 21 | } 22 | -------------------------------------------------------------------------------- /apps/frontend/src/styles/button.css: -------------------------------------------------------------------------------- 1 | .button { 2 | appearance: none; 3 | display: inline-flex; 4 | justify-content: center; 5 | align-items: center; 6 | height: 40px; 7 | width: auto; 8 | outline: none; 9 | border-radius: 6px; 10 | padding: 0 16px; 11 | background-color: var(--tg-theme-button-color); 12 | color: var(--tg-theme-button-text-color); 13 | font-size: 16px; 14 | line-height: 0; 15 | transition: 250ms background-color; 16 | } 17 | 18 | .button.button__status-bar{ 19 | height: 2rem; 20 | padding: 0 10px; 21 | } 22 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | services: 4 | app: 5 | build: . 6 | container_name: app 7 | restart: unless-stopped 8 | ports: 9 | - "${PORT}:${PORT}" 10 | volumes: 11 | - pocketbase-data:/dist/pb_data 12 | healthcheck: #optional (recommended) since v0.10.0 13 | test: wget --no-verbose --tries=1 --spider http://localhost:${PORT}/api/health || exit 1 14 | interval: 5s 15 | timeout: 5s 16 | retries: 5 17 | env_file: 18 | - .env 19 | 20 | volumes: 21 | pocketbase-data: 22 | -------------------------------------------------------------------------------- /apps/frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + Solid + TS 8 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /apps/frontend/src/pages/market/index.module.css: -------------------------------------------------------------------------------- 1 | .product:hover{ 2 | background-color: var(--tg-theme-secondary-bg-color); 3 | } 4 | 5 | .image { 6 | display: inline-flex; 7 | align-items: center; 8 | justify-content: center; 9 | vertical-align: middle; 10 | overflow: hidden; 11 | user-select: none; 12 | width: 64px; 13 | height: 64px; 14 | border-radius: 1rem; 15 | background-color: var(--tg-theme-secondary-bg-color); 16 | } 17 | 18 | .observer{ 19 | position: absolute; 20 | width: 100%; 21 | height: 6rem; 22 | pointer-events: none; 23 | bottom: 0px; 24 | } -------------------------------------------------------------------------------- /apps/frontend/src/utils/useBackButton.ts: -------------------------------------------------------------------------------- 1 | import { useSDK } from "@tma.js/sdk-solid"; 2 | import { onCleanup, onMount } from "solid-js"; 3 | 4 | export function useBackButton(handler: () => void) { 5 | const { backButton } = useSDK(); 6 | const bb = backButton(); 7 | 8 | const clickHandler = () => { 9 | handler(); 10 | bb.hide() 11 | } 12 | 13 | onMount(() => { 14 | bb.on('click', clickHandler); 15 | if (!bb.isVisible) bb.show() 16 | 17 | onCleanup(() => { 18 | bb.off('click', clickHandler); 19 | console.log("cleanup") 20 | }); 21 | }); 22 | 23 | return bb 24 | } -------------------------------------------------------------------------------- /apps/frontend/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], 4 | theme: { 5 | extend: { 6 | colors: { 7 | 'tg-bg': 'var(--tg-theme-bg-color)', 8 | 'tg-bg-secondary': 'var(--tg-theme-secondary-bg-color)', 9 | 'tg-text': 'var(--tg-theme-text-color)', 10 | 'tg-hint': 'var(--tg-theme-hint-color)', 11 | 'tg-link': 'var(--tg-theme-link-color)', 12 | 'tg-btn': 'var(--tg-theme-button-color)', 13 | 'tg-btn-txt': 'var(--tg-theme-button-text-color)' 14 | } 15 | } 16 | }, 17 | plugins: [] 18 | } 19 | -------------------------------------------------------------------------------- /apps/watcher/watcher.js: -------------------------------------------------------------------------------- 1 | import { exec } from 'node:child_process' 2 | import fs from 'node:fs' 3 | import path from 'node:path' 4 | import { fileURLToPath } from 'node:url' 5 | 6 | let isLock = false 7 | const __dirname = fileURLToPath(new URL('.', import.meta.url)) 8 | 9 | fs.watch(path.join(__dirname, '..', 'backend', 'migrations'), () => { 10 | if (isLock) return 11 | isLock = true 12 | 13 | exec('pnpm pocketbase-typegen', (error) => { 14 | if (isLock) { 15 | isLock = false 16 | } 17 | 18 | if (error) { 19 | throw new Error(error) 20 | } 21 | 22 | console.log('[apps/watcher] Pocketbase types generated.') 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /apps/frontend/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | html { 6 | padding: 1rem; 7 | } 8 | 9 | :root { 10 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 11 | line-height: 1.5; 12 | font-weight: 400; 13 | 14 | color: var(--tg-theme-text-color, rgba(255, 255, 255, 0.87)); 15 | background-color: var(--tg-theme-bg-color, #121212); 16 | 17 | font-synthesis: none; 18 | text-rendering: optimizeLegibility; 19 | -webkit-font-smoothing: antialiased; 20 | -moz-osx-font-smoothing: grayscale; 21 | -webkit-text-size-adjust: 100%; 22 | } 23 | 24 | a { 25 | color: var(--tg-theme-link-color, #2196f3); 26 | text-decoration: none; 27 | } 28 | -------------------------------------------------------------------------------- /apps/frontend/src/utils/useMainButton.ts: -------------------------------------------------------------------------------- 1 | import { MainButton } from "@tma.js/sdk"; 2 | import { useSDK } from "@tma.js/sdk-solid"; 3 | import { onCleanup, onMount } from "solid-js"; 4 | 5 | export function useMainButton(handler?: (mb: MainButton) => void) { 6 | const { mainButton } = useSDK(); 7 | const mb = mainButton(); 8 | 9 | const clickHandler = () => { 10 | if (handler) handler(mb); 11 | } 12 | 13 | onMount(() => { 14 | if (handler) mb.on('click', clickHandler); 15 | if (!mb.isVisible) mb.show() 16 | if (!mb.isEnabled) mb.enable() 17 | 18 | onCleanup(() => { 19 | mb.disable().hide().off('click', clickHandler); 20 | }); 21 | }); 22 | 23 | return mb 24 | } -------------------------------------------------------------------------------- /apps/frontend/src/utils/useDebounce.ts: -------------------------------------------------------------------------------- 1 | interface Scheduled { 2 | (...args: Args): void; 3 | clear: VoidFunction; 4 | } 5 | 6 | 7 | type DebounceCallback = ( 8 | callback: (...args: Args) => void, 9 | wait?: number, 10 | ) => Scheduled; 11 | 12 | export const useDebounce: DebounceCallback = (callback, wait = 1000) => { 13 | let timeoutId: NodeJS.Timeout; 14 | const clear = () => clearTimeout(timeoutId); 15 | const debounced: typeof callback = (...args) => { 16 | if (timeoutId) clear(); 17 | timeoutId = setTimeout(() => { 18 | callback(...args) 19 | }, wait); 20 | }; 21 | return Object.assign(debounced, { clear }); 22 | }; 23 | 24 | 25 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine as base 2 | ENV PNPM_HOME="/pnpm" 3 | ENV PATH="$PNPM_HOME:$PATH" 4 | RUN corepack enable 5 | COPY . /app 6 | WORKDIR /app 7 | 8 | FROM base AS build-frontend 9 | RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm run ci 10 | RUN pnpm run -r build 11 | 12 | FROM base AS frontend 13 | COPY --from=build-frontend /app/apps/frontend/dist /app/apps/frontend/dist 14 | 15 | FROM golang:1.21.1-alpine AS build-backend 16 | RUN mkdir /app 17 | ADD ./apps/backend /app 18 | COPY --from=frontend /app/apps/frontend/dist /app/pb_public 19 | WORKDIR /app 20 | RUN CGO_ENABLED=0 GOOS=linux go build -o dist/main.bin . 21 | 22 | 23 | FROM alpine:latest AS production 24 | COPY --from=build-backend /app . 25 | EXPOSE ${PORT} 26 | CMD ["sh", "-c", "./dist/main.bin serve --http=0.0.0.0:$PORT"] 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | 4 | # generated types 5 | .astro/ 6 | 7 | # dependencies 8 | node_modules/ 9 | 10 | # logs 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | 23 | # Pocketbase 24 | pb_data 25 | 26 | # If you prefer the allow list template instead of the deny list, see community template: 27 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 28 | # 29 | # Binaries for programs and plugins 30 | *.exe 31 | *.exe~ 32 | *.dll 33 | *.so 34 | *.dylib 35 | 36 | # Test binary, built with `go test -c` 37 | *.test 38 | 39 | # Output of the go coverage tool, specifically when used with LiteIDE 40 | *.out 41 | 42 | # Go workspace file 43 | go.work 44 | -------------------------------------------------------------------------------- /apps/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite --port 4321", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@kobalte/core": "^0.11.0", 13 | "@solid-devtools/overlay": "^0.27.7", 14 | "@solidjs/router": "^0.8.3", 15 | "@tma.js/sdk": "^0.11.5", 16 | "@tma.js/sdk-solid": "^0.1.5", 17 | "lottie-web": "^5.12.2", 18 | "pocketbase": "^0.18.0", 19 | "solid-icons": "^1.0.12", 20 | "solid-js": "^1.7.8" 21 | }, 22 | "devDependencies": { 23 | "autoprefixer": "^10.4.16", 24 | "postcss": "^8.4.31", 25 | "tailwindcss": "^3.3.3", 26 | "typescript": "^5.0.2", 27 | "vite": "^4.4.5", 28 | "vite-plugin-solid": "^2.7.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /apps/frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "preserve", 16 | "jsxImportSource": "solid-js", 17 | 18 | /* Linting */ 19 | "strict": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "noFallthroughCasesInSwitch": true, 23 | "baseUrl": "./", 24 | "paths": { 25 | "@/*": ["./src/*"], 26 | }, 27 | }, 28 | "include": ["src"], 29 | "references": [{ "path": "./tsconfig.node.json" }] 30 | } 31 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695891282_updated_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.ListRule = types.Pointer("") 20 | 21 | return dao.SaveCollection(collection) 22 | }, func(db dbx.Builder) error { 23 | dao := daos.New(db); 24 | 25 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 26 | if err != nil { 27 | return err 28 | } 29 | 30 | collection.ListRule = nil 31 | 32 | return dao.SaveCollection(collection) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /apps/frontend/src/utils/createAggListResult.ts: -------------------------------------------------------------------------------- 1 | import { ListResult } from "pocketbase"; 2 | import { Accessor, Resource, createMemo } from "solid-js"; 3 | 4 | function toListResult(item: ListResult | undefined): ListResult { 5 | return item ? item : { 6 | page: 0, 7 | perPage: 0, 8 | totalItems: 0, 9 | totalPages: 0, 10 | items: [], 11 | } 12 | } 13 | 14 | export function createAggListResult(res: Resource>, initialValue?: ListResult): Accessor> { 15 | return createMemo((previous) => { 16 | const current = res.latest; 17 | const prevResult = toListResult(previous) 18 | 19 | if (!current) return prevResult 20 | if (current.page === 1) 21 | return current 22 | 23 | return { ...current, items: [...prevResult.items, ...current.items] } 24 | }, initialValue) 25 | } -------------------------------------------------------------------------------- /apps/backend/migrations/1695812825_updated_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.ViewRule = types.Pointer("@request.auth.id != '' ") 20 | 21 | return dao.SaveCollection(collection) 22 | }, func(db dbx.Builder) error { 23 | dao := daos.New(db); 24 | 25 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 26 | if err != nil { 27 | return err 28 | } 29 | 30 | collection.ViewRule = nil 31 | 32 | return dao.SaveCollection(collection) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695891299_updated_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.ListRule = types.Pointer("@request.auth.id != '' ") 20 | 21 | return dao.SaveCollection(collection) 22 | }, func(db dbx.Builder) error { 23 | dao := daos.New(db); 24 | 25 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 26 | if err != nil { 27 | return err 28 | } 29 | 30 | collection.ListRule = nil 31 | 32 | return dao.SaveCollection(collection) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695891325_updated_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.ListRule = types.Pointer("@request.data.user.id != '' ") 20 | 21 | return dao.SaveCollection(collection) 22 | }, func(db dbx.Builder) error { 23 | dao := daos.New(db); 24 | 25 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 26 | if err != nil { 27 | return err 28 | } 29 | 30 | collection.ListRule = nil 31 | 32 | return dao.SaveCollection(collection) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /apps/frontend/src/components/lottie-animation.tsx: -------------------------------------------------------------------------------- 1 | import lottie from 'lottie-web' 2 | import { createEffect, onCleanup } from 'solid-js' 3 | import type { Component } from 'solid-js' 4 | 5 | interface LottieAnimationProps { 6 | animationData: string 7 | loop?: boolean 8 | autoplay?: boolean 9 | } 10 | 11 | export const LottieAnimation: Component = (props) => { 12 | let containerRef!: HTMLDivElement 13 | 14 | createEffect(() => { 15 | const anim = lottie.loadAnimation({ 16 | container: containerRef, 17 | renderer: 'svg', 18 | loop: props.loop || false, 19 | autoplay: props.autoplay || false, 20 | path: props.animationData 21 | }) 22 | 23 | onCleanup(() => { 24 | anim.destroy() 25 | }) 26 | }) 27 | 28 | return ( 29 |
33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /apps/frontend/src/pages/market/cart.module.css: -------------------------------------------------------------------------------- 1 | :root{ 2 | --padding: 0.3rem; 3 | --image-border-radius: 6px; 4 | } 5 | 6 | .order-item{ 7 | display: grid; 8 | grid-template-columns: auto minmax(0, 1fr) auto; 9 | text-align: left; 10 | align-items: center; 11 | background-color: var(--tg-theme-secondary-bg-color); 12 | border-radius: calc(var(--image-border-radius) + var(--padding)); 13 | padding: var(--padding); 14 | } 15 | 16 | .image{ 17 | border-radius: var(--image-border-radius); 18 | } 19 | 20 | .button{ 21 | width: 2.5rem; 22 | height: 2.5rem; 23 | } 24 | 25 | footer{ 26 | position: fixed; 27 | bottom: 0; 28 | left: 0; 29 | right: 0; 30 | padding: 0.5rem 0; 31 | display: flex; 32 | justify-content: center; 33 | align-items: center; 34 | gap: 0.5rem; 35 | background: linear-gradient(0deg, var(--tg-theme-bg-color), 90%, transparent); 36 | } -------------------------------------------------------------------------------- /apps/backend/migrations/1695812624_updated_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.ListRule = types.Pointer("@request.auth.id != '' && @request.auth.id = 1") 20 | 21 | return dao.SaveCollection(collection) 22 | }, func(db dbx.Builder) error { 23 | dao := daos.New(db); 24 | 25 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 26 | if err != nil { 27 | return err 28 | } 29 | 30 | collection.ListRule = nil 31 | 32 | return dao.SaveCollection(collection) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /apps/frontend/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ```bash 4 | $ npm install # or pnpm install or yarn install 5 | ``` 6 | 7 | ### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs) 8 | 9 | ## Available Scripts 10 | 11 | In the project directory, you can run: 12 | 13 | ### `npm run dev` 14 | 15 | Runs the app in the development mode.
16 | Open [http://localhost:5173](http://localhost:5173) to view it in the browser. 17 | 18 | ### `npm run build` 19 | 20 | Builds the app for production to the `dist` folder.
21 | It correctly bundles Solid in production mode and optimizes the build for the best performance. 22 | 23 | The build is minified and the filenames include the hashes.
24 | Your app is ready to be deployed! 25 | 26 | ## Deployment 27 | 28 | Learn more about deploying your application with the [documentations](https://vitejs.dev/guide/static-deploy.html) 29 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695891527_updated_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.ListRule = types.Pointer("@request.auth.id != '' && @request.auth.id = user.id") 20 | 21 | return dao.SaveCollection(collection) 22 | }, func(db dbx.Builder) error { 23 | dao := daos.New(db); 24 | 25 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 26 | if err != nil { 27 | return err 28 | } 29 | 30 | collection.ListRule = nil 31 | 32 | return dao.SaveCollection(collection) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /apps/backend/migrations/1696959425_updated_products.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.DeleteRule = types.Pointer("@request.auth.id != '' && @request.auth.id = store.user.id") 20 | 21 | return dao.SaveCollection(collection) 22 | }, func(db dbx.Builder) error { 23 | dao := daos.New(db); 24 | 25 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 26 | if err != nil { 27 | return err 28 | } 29 | 30 | collection.DeleteRule = nil 31 | 32 | return dao.SaveCollection(collection) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /apps/backend/migrations/1696959521_updated_products.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.DeleteRule = types.Pointer("@request.auth.id != '' && @request.auth.id = store.user") 20 | 21 | return dao.SaveCollection(collection) 22 | }, func(db dbx.Builder) error { 23 | dao := daos.New(db); 24 | 25 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 26 | if err != nil { 27 | return err 28 | } 29 | 30 | collection.DeleteRule = nil 31 | 32 | return dao.SaveCollection(collection) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /apps/backend/migrations/1696959706_updated_products.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.DeleteRule = types.Pointer("@request.auth.id != '' && @request.auth.id = store.user.id") 20 | 21 | return dao.SaveCollection(collection) 22 | }, func(db dbx.Builder) error { 23 | dao := daos.New(db); 24 | 25 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 26 | if err != nil { 27 | return err 28 | } 29 | 30 | collection.DeleteRule = nil 31 | 32 | return dao.SaveCollection(collection) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /apps/backend/migrations/1696964534_updated_products.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.CreateRule = types.Pointer("@request.auth.id != '' && @request.auth.id = store.user.id") 20 | 21 | return dao.SaveCollection(collection) 22 | }, func(db dbx.Builder) error { 23 | dao := daos.New(db); 24 | 25 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 26 | if err != nil { 27 | return err 28 | } 29 | 30 | collection.CreateRule = nil 31 | 32 | return dao.SaveCollection(collection) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "r3-mvp-stack", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "repository": "https://github.com/r3-dev/r3-mvp-stack.git", 6 | "author": "milanjrodd ", 7 | "license": "MIT", 8 | "scripts": { 9 | "ci": "pnpm install --frozen-lockfile", 10 | "ci:prod": "pnpm install --frozen-lockfile --prod", 11 | "dev": "pnpm --filter=./apps/* run dev", 12 | "dev:fe": "pnpm --filter=./apps/frontend run dev", 13 | "build": "pnpm --filter=./apps/* run build", 14 | "ngrok": "npx ngrok http 3000", 15 | "format": "prettier --write \"**/*.{js,ts,tsx,json}\"", 16 | "install-sqlite-mac-m1": "npm install -d sqlite3 --build-from-source --target_arch=arm64 --fallback-to-build && rm -rf package-lock.json" 17 | }, 18 | "devDependencies": { 19 | "@crashmax/prettier-config": "^4.1.0", 20 | "@types/node": "18", 21 | "ngrok": "^5.0.0-beta.2", 22 | "sqlite3": "^5.1.6" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /apps/frontend/src/pages/dashboard/index.tsx: -------------------------------------------------------------------------------- 1 | import { useNavigate } from '@solidjs/router' 2 | import { useSDK } from '@tma.js/sdk-solid' 3 | import { onCleanup, onMount } from 'solid-js' 4 | 5 | import { Welcome } from '@/components/welcome/welcome' 6 | 7 | export function DashboardPage() { 8 | const sdk = useSDK() 9 | const navigate = useNavigate() 10 | 11 | onMount(() => { 12 | sdk.mainButton().on('click', handleCreateStore) 13 | sdk.mainButton().setText('Create store') 14 | 15 | if (!sdk.mainButton().isVisible) sdk.mainButton().show() 16 | if (sdk.backButton().isVisible) sdk.backButton().hide() 17 | 18 | if (!sdk.mainButton().isEnabled) sdk.mainButton().enable() 19 | }) 20 | 21 | onCleanup(() => { 22 | sdk.mainButton().off('click', handleCreateStore) 23 | }) 24 | 25 | function handleCreateStore() { 26 | sdk.mainButton().disable() 27 | 28 | navigate('/dashboard/store/create') 29 | } 30 | return 31 | } 32 | -------------------------------------------------------------------------------- /apps/backend/migrations/1696683749_updated_products.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.ListRule = types.Pointer("@request.auth.id != ''") 20 | 21 | collection.ViewRule = types.Pointer("@request.auth.id != ''") 22 | 23 | return dao.SaveCollection(collection) 24 | }, func(db dbx.Builder) error { 25 | dao := daos.New(db); 26 | 27 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 28 | if err != nil { 29 | return err 30 | } 31 | 32 | collection.ListRule = nil 33 | 34 | collection.ViewRule = nil 35 | 36 | return dao.SaveCollection(collection) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /apps/frontend/src/components/dashboard-store-layout.tsx: -------------------------------------------------------------------------------- 1 | import { useParams } from '@solidjs/router' 2 | import { createEffect, createSignal, ParentProps } from 'solid-js' 3 | 4 | import { usePocketBase } from '@/contexts/pocketbase' 5 | import { Collections, StoresResponse } from '@/types/pb-types' 6 | 7 | export function DashboardStoreLayout(props: ParentProps) { 8 | const [store, setStore] = createSignal({} as StoresResponse) 9 | 10 | const params = useParams() 11 | const pb = usePocketBase() 12 | 13 | createEffect(() => { 14 | //Fetch store 15 | pb.collection(Collections.Stores) 16 | .getOne(params.storeId) 17 | .then((_store) => { 18 | setStore(_store) 19 | }) 20 | .catch((err) => { 21 | console.error(err) 22 | }) 23 | }) 24 | 25 | return ( 26 | <> 27 |

28 | Store 29 | {store().name} 30 |

31 | {props.children} 32 | 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695982812_updated_categories.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("czwjh0mysgfejiu") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.CreateRule = types.Pointer("") 20 | 21 | collection.UpdateRule = types.Pointer("") 22 | 23 | collection.DeleteRule = types.Pointer("") 24 | 25 | return dao.SaveCollection(collection) 26 | }, func(db dbx.Builder) error { 27 | dao := daos.New(db); 28 | 29 | collection, err := dao.FindCollectionByNameOrId("czwjh0mysgfejiu") 30 | if err != nil { 31 | return err 32 | } 33 | 34 | collection.CreateRule = nil 35 | 36 | collection.UpdateRule = nil 37 | 38 | collection.DeleteRule = nil 39 | 40 | return dao.SaveCollection(collection) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /apps/frontend/src/components/products-list/products-list.tsx: -------------------------------------------------------------------------------- 1 | import { useNavigate } from '@solidjs/router' 2 | 3 | import './products-list.css' 4 | 5 | import { useSDK } from '@tma.js/sdk-solid' 6 | import { onCleanup, onMount } from 'solid-js' 7 | 8 | export function ProductsList() { 9 | const { mainButton, backButton } = useSDK() 10 | const navigate = useNavigate() 11 | 12 | function goToNext() { 13 | mainButton().disable() 14 | 15 | navigate('/dashboard/success') 16 | } 17 | 18 | function onBack() { 19 | navigate('/dashboard/product-list') 20 | } 21 | 22 | onMount(() => { 23 | mainButton().setText('Next') 24 | 25 | mainButton().on('click', goToNext) 26 | backButton().on('click', onBack) 27 | 28 | if (!mainButton().isVisible) mainButton().show() 29 | if (!backButton().isVisible) backButton().show() 30 | 31 | if (!mainButton().isEnabled) mainButton().enable() 32 | }) 33 | 34 | onCleanup(() => { 35 | mainButton().off('click', goToNext) 36 | backButton().off('click', onBack) 37 | }) 38 | return
PRODUCT LIST
39 | } 40 | -------------------------------------------------------------------------------- /apps/frontend/src/styles/text-field.css: -------------------------------------------------------------------------------- 1 | .text-field { 2 | display: flex; 3 | flex-direction: column; 4 | gap: 4px; 5 | } 6 | .text-field__label { 7 | color: var(--tg-theme-text-color); 8 | font-size: 14px; 9 | font-weight: 500; 10 | user-select: none; 11 | } 12 | .text-field__input { 13 | display: inline-flex; 14 | border-radius: 6px; 15 | padding: 6px 12px; 16 | font-size: 16px; 17 | outline: none; 18 | background-color: var(--tg-theme-secondary-bg-color); 19 | color: var(--tg-theme-text-color); 20 | transition: 21 | border-color 250ms, 22 | color 250ms; 23 | } 24 | 25 | .text-field__input[data-invalid] { 26 | color: hsl(0 72% 51%); 27 | } 28 | 29 | .text-field__input::placeholder { 30 | color: var(--tg-theme-hint-color); 31 | } 32 | 33 | .text-field__input.padded_left{ 34 | padding-left: 2rem; 35 | } 36 | 37 | .text-field__description { 38 | color: var(--tg-theme-hint-color); 39 | font-size: 12px; 40 | user-select: none; 41 | } 42 | 43 | .text-field__error-message { 44 | color: hsl(0 72% 51%); 45 | font-size: 12px; 46 | user-select: none; 47 | } 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Shoubhit Dash 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /apps/frontend/src/components/twa-display-gate.tsx: -------------------------------------------------------------------------------- 1 | import { useSDKContext } from '@tma.js/sdk-solid' 2 | import { createMemo, Match, Switch } from 'solid-js' 3 | import type { ParentProps } from 'solid-js' 4 | 5 | /** 6 | * Component responsible for controlling the process of application display. 7 | */ 8 | export function DisplayGate(props: ParentProps) { 9 | const { loading, error } = useSDKContext() 10 | const errorMessage = createMemo(() => { 11 | const err = error() 12 | 13 | if (!err) { 14 | return null 15 | } 16 | 17 | return err instanceof Error ? err.message : 'Unknown error' 18 | }) 19 | 20 | return ( 21 | 22 | 23 |

24 | SDK was unable to initialize. Probably, current application is being 25 | used not in Telegram Web Apps environment. 26 |

27 |
28 |

{errorMessage()}

29 |
30 |
31 | 32 |
Loading..
33 |
34 |
35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695812539_updated_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.ListRule = types.Pointer("@request.auth.id != ''") 20 | 21 | collection.ViewRule = types.Pointer("@request.auth.id != ''") 22 | 23 | collection.CreateRule = types.Pointer("@request.auth.id != ''") 24 | 25 | collection.UpdateRule = types.Pointer("@request.auth.id != ''") 26 | 27 | collection.DeleteRule = types.Pointer("@request.auth.id != ''") 28 | 29 | return dao.SaveCollection(collection) 30 | }, func(db dbx.Builder) error { 31 | dao := daos.New(db); 32 | 33 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 34 | if err != nil { 35 | return err 36 | } 37 | 38 | collection.ListRule = nil 39 | 40 | collection.ViewRule = nil 41 | 42 | collection.CreateRule = nil 43 | 44 | collection.UpdateRule = nil 45 | 46 | collection.DeleteRule = nil 47 | 48 | return dao.SaveCollection(collection) 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /apps/backend/migrations/1696926905_updated_products.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db) 15 | 16 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // add 22 | new_store := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "mcgotk5u", 26 | "name": "store", 27 | "type": "relation", 28 | "required": false, 29 | "unique": false, 30 | "options": { 31 | "collectionId": "ls1eoeu2j4mxjuu", 32 | "cascadeDelete": false, 33 | "minSelect": null, 34 | "maxSelect": 1, 35 | "displayFields": [] 36 | } 37 | }`), new_store) 38 | collection.Schema.AddField(new_store) 39 | 40 | return dao.SaveCollection(collection) 41 | }, func(db dbx.Builder) error { 42 | dao := daos.New(db) 43 | 44 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 45 | if err != nil { 46 | return err 47 | } 48 | 49 | // remove 50 | collection.Schema.RemoveField("mcgotk5u") 51 | 52 | return dao.SaveCollection(collection) 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /apps/backend/migrations/1696948469_updated_orders.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("h30palxkh5zdmcp") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // add 22 | new_status := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "cfgonmj0", 26 | "name": "status", 27 | "type": "select", 28 | "required": true, 29 | "unique": false, 30 | "options": { 31 | "maxSelect": 1, 32 | "values": [ 33 | "BuyerInProcess", 34 | "WaitForVendor", 35 | "VendorInProcess", 36 | "Done" 37 | ] 38 | } 39 | }`), new_status) 40 | collection.Schema.AddField(new_status) 41 | 42 | return dao.SaveCollection(collection) 43 | }, func(db dbx.Builder) error { 44 | dao := daos.New(db); 45 | 46 | collection, err := dao.FindCollectionByNameOrId("h30palxkh5zdmcp") 47 | if err != nil { 48 | return err 49 | } 50 | 51 | // remove 52 | collection.Schema.RemoveField("cfgonmj0") 53 | 54 | return dao.SaveCollection(collection) 55 | }) 56 | } 57 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695983165_updated_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // add 22 | new_products := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "qz2vqjoq", 26 | "name": "products", 27 | "type": "relation", 28 | "required": false, 29 | "unique": false, 30 | "options": { 31 | "collectionId": "9463u947e9svq7z", 32 | "cascadeDelete": false, 33 | "minSelect": null, 34 | "maxSelect": null, 35 | "displayFields": [] 36 | } 37 | }`), new_products) 38 | collection.Schema.AddField(new_products) 39 | 40 | return dao.SaveCollection(collection) 41 | }, func(db dbx.Builder) error { 42 | dao := daos.New(db); 43 | 44 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 45 | if err != nil { 46 | return err 47 | } 48 | 49 | // remove 50 | collection.Schema.RemoveField("qz2vqjoq") 51 | 52 | return dao.SaveCollection(collection) 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695812759_updated_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // add 22 | new_field := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "o3ax0bqh", 26 | "name": "field", 27 | "type": "relation", 28 | "required": false, 29 | "unique": false, 30 | "options": { 31 | "collectionId": "_pb_users_auth_", 32 | "cascadeDelete": false, 33 | "minSelect": null, 34 | "maxSelect": 1, 35 | "displayFields": [ 36 | "id" 37 | ] 38 | } 39 | }`), new_field) 40 | collection.Schema.AddField(new_field) 41 | 42 | return dao.SaveCollection(collection) 43 | }, func(db dbx.Builder) error { 44 | dao := daos.New(db); 45 | 46 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 47 | if err != nil { 48 | return err 49 | } 50 | 51 | // remove 52 | collection.Schema.RemoveField("o3ax0bqh") 53 | 54 | return dao.SaveCollection(collection) 55 | }) 56 | } 57 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695985525_updated_products.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // remove 22 | collection.Schema.RemoveField("ajeynuar") 23 | 24 | return dao.SaveCollection(collection) 25 | }, func(db dbx.Builder) error { 26 | dao := daos.New(db); 27 | 28 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 29 | if err != nil { 30 | return err 31 | } 32 | 33 | // add 34 | del_store := &schema.SchemaField{} 35 | json.Unmarshal([]byte(`{ 36 | "system": false, 37 | "id": "ajeynuar", 38 | "name": "store", 39 | "type": "relation", 40 | "required": false, 41 | "unique": false, 42 | "options": { 43 | "collectionId": "ls1eoeu2j4mxjuu", 44 | "cascadeDelete": false, 45 | "minSelect": null, 46 | "maxSelect": 1, 47 | "displayFields": [ 48 | "name" 49 | ] 50 | } 51 | }`), del_store) 52 | collection.Schema.AddField(del_store) 53 | 54 | return dao.SaveCollection(collection) 55 | }) 56 | } 57 | -------------------------------------------------------------------------------- /apps/frontend/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/frontend/src/contexts/pocketbase.tsx: -------------------------------------------------------------------------------- 1 | import { useSDK } from '@tma.js/sdk-solid' 2 | import PocketBase from 'pocketbase' 3 | import { createContext, createEffect, useContext } from 'solid-js' 4 | import type { ParentComponent } from 'solid-js' 5 | 6 | const pb_url = import.meta.env.PUBLIC_POCKETBASE_URL 7 | 8 | const PocketBaseContext = createContext(undefined, { 9 | name: 'PocketBaseContext' 10 | }) 11 | 12 | export function usePocketBase() { 13 | const context = useContext(PocketBaseContext) 14 | 15 | if (context === undefined) { 16 | throw new Error( 17 | `${usePocketBase.name} hook was used outside of ${PocketbaseProvider.name}.` 18 | ) 19 | } 20 | 21 | return context 22 | } 23 | 24 | export const PocketbaseProvider: ParentComponent = (props) => { 25 | const sdk = useSDK() 26 | const pb = new PocketBase(pb_url) 27 | 28 | pb.beforeSend = function (url, options) { 29 | options.headers = Object.assign({}, options.headers, { 30 | 'X-Init-Data': sdk.initDataRaw() 31 | }) 32 | 33 | return { url, options } 34 | } 35 | 36 | createEffect(async () => { 37 | await pb 38 | .collection('users') 39 | .authWithPassword('USERNAMELESS', 'PASSWORDLESS', { 40 | headers: { 41 | 'X-Init-Data': sdk.initDataRaw() 42 | } 43 | }) 44 | }) 45 | 46 | return ( 47 | 48 | {props.children} 49 | 50 | ) 51 | } 52 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695813016_updated_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.ListRule = types.Pointer("@request.auth.id != '' && @request.auth.id = user.id") 20 | 21 | collection.ViewRule = types.Pointer("@request.auth.id != '' && @request.auth.id = user.id") 22 | 23 | collection.CreateRule = types.Pointer("@request.auth.id != '' && @request.auth.id = user.id") 24 | 25 | collection.UpdateRule = types.Pointer("@request.auth.id != '' && @request.auth.id = user.id") 26 | 27 | collection.DeleteRule = types.Pointer("@request.auth.id != '' && @request.auth.id = user.id") 28 | 29 | return dao.SaveCollection(collection) 30 | }, func(db dbx.Builder) error { 31 | dao := daos.New(db); 32 | 33 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 34 | if err != nil { 35 | return err 36 | } 37 | 38 | collection.ListRule = nil 39 | 40 | collection.ViewRule = nil 41 | 42 | collection.CreateRule = nil 43 | 44 | collection.UpdateRule = nil 45 | 46 | collection.DeleteRule = nil 47 | 48 | return dao.SaveCollection(collection) 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695988414_updated_orders.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("h30palxkh5zdmcp") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.ListRule = types.Pointer("@request.auth.id != '' && @request.auth.id = user.id") 20 | 21 | collection.ViewRule = types.Pointer("@request.auth.id != '' && @request.auth.id = user.id") 22 | 23 | collection.CreateRule = types.Pointer("@request.auth.id != '' && @request.auth.id = user.id") 24 | 25 | collection.UpdateRule = types.Pointer("@request.auth.id != '' && @request.auth.id = user.id") 26 | 27 | collection.DeleteRule = types.Pointer("@request.auth.id != '' && @request.auth.id = user.id") 28 | 29 | return dao.SaveCollection(collection) 30 | }, func(db dbx.Builder) error { 31 | dao := daos.New(db); 32 | 33 | collection, err := dao.FindCollectionByNameOrId("h30palxkh5zdmcp") 34 | if err != nil { 35 | return err 36 | } 37 | 38 | collection.ListRule = nil 39 | 40 | collection.ViewRule = nil 41 | 42 | collection.CreateRule = nil 43 | 44 | collection.UpdateRule = nil 45 | 46 | collection.DeleteRule = nil 47 | 48 | return dao.SaveCollection(collection) 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /apps/backend/utils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | "strings" 8 | "time" 9 | 10 | initdata "github.com/Telegram-Web-Apps/init-data-golang" 11 | "github.com/labstack/echo/v5" 12 | ) 13 | 14 | func getUsername(username string, id int64) string { 15 | if username != "" { 16 | return username 17 | } else { 18 | return strconv.FormatInt(id, 10) 19 | } 20 | } 21 | 22 | func initDataCheck(c echo.Context, identity string, password string) (*initdata.User, error) { 23 | var token string 24 | referer := c.Request().Header.Get("Referer") 25 | if strings.Contains(referer, "market") { 26 | token = os.Getenv("TELEGRAM_MARKET_BOT_TOKEN") 27 | } else { 28 | token = os.Getenv("TELEGRAM_STORE_BOT_TOKEN") 29 | } 30 | 31 | expIn := 24 * time.Hour 32 | 33 | // read the header values 34 | initDataRaw := c.Request().Header.Get("X-Init-Data") 35 | if initDataRaw == "" { 36 | return nil, nil 37 | } 38 | 39 | if identity != "USERNAMELESS" || password != "PASSWORDLESS" { 40 | return nil, fmt.Errorf("wrong credentials") 41 | } 42 | 43 | initData, errParse := initdata.Parse(initDataRaw) 44 | if errParse != nil { 45 | err := fmt.Errorf("init data parse: %w", errParse) 46 | return nil, err 47 | } 48 | 49 | if initData.User == nil { 50 | return initData.User, fmt.Errorf("no user") 51 | } 52 | 53 | if err := initdata.Validate(initDataRaw, token, expIn); err != nil { 54 | return initData.User, fmt.Errorf("not valid: %w", err) 55 | } 56 | return initData.User, nil 57 | } 58 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695811078_created_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | jsonData := `{ 15 | "id": "ls1eoeu2j4mxjuu", 16 | "created": "2023-09-27 10:37:58.076Z", 17 | "updated": "2023-09-27 10:37:58.076Z", 18 | "name": "stores", 19 | "type": "base", 20 | "system": false, 21 | "schema": [ 22 | { 23 | "system": false, 24 | "id": "b8ib9byi", 25 | "name": "name", 26 | "type": "text", 27 | "required": true, 28 | "unique": false, 29 | "options": { 30 | "min": null, 31 | "max": null, 32 | "pattern": "" 33 | } 34 | } 35 | ], 36 | "indexes": [], 37 | "listRule": null, 38 | "viewRule": null, 39 | "createRule": null, 40 | "updateRule": null, 41 | "deleteRule": null, 42 | "options": {} 43 | }` 44 | 45 | collection := &models.Collection{} 46 | if err := json.Unmarshal([]byte(jsonData), &collection); err != nil { 47 | return err 48 | } 49 | 50 | return daos.New(db).SaveCollection(collection) 51 | }, func(db dbx.Builder) error { 52 | dao := daos.New(db); 53 | 54 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 55 | if err != nil { 56 | return err 57 | } 58 | 59 | return dao.DeleteCollection(collection) 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695988550_updated_order_items.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("imm7z4sxmlmjlen") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.ListRule = types.Pointer("@request.auth.id != '' && order.user.id = @request.auth.id") 20 | 21 | collection.ViewRule = types.Pointer("@request.auth.id != '' && order.user.id = @request.auth.id") 22 | 23 | collection.CreateRule = types.Pointer("@request.auth.id != '' && order.user.id = @request.auth.id") 24 | 25 | collection.UpdateRule = types.Pointer("@request.auth.id != '' && order.user.id = @request.auth.id") 26 | 27 | collection.DeleteRule = types.Pointer("@request.auth.id != '' && order.user.id = @request.auth.id") 28 | 29 | return dao.SaveCollection(collection) 30 | }, func(db dbx.Builder) error { 31 | dao := daos.New(db); 32 | 33 | collection, err := dao.FindCollectionByNameOrId("imm7z4sxmlmjlen") 34 | if err != nil { 35 | return err 36 | } 37 | 38 | collection.ListRule = nil 39 | 40 | collection.ViewRule = nil 41 | 42 | collection.CreateRule = nil 43 | 44 | collection.UpdateRule = nil 45 | 46 | collection.DeleteRule = nil 47 | 48 | return dao.SaveCollection(collection) 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695982759_created_categories.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | jsonData := `{ 15 | "id": "czwjh0mysgfejiu", 16 | "created": "2023-09-29 10:19:19.262Z", 17 | "updated": "2023-09-29 10:19:19.262Z", 18 | "name": "categories", 19 | "type": "base", 20 | "system": false, 21 | "schema": [ 22 | { 23 | "system": false, 24 | "id": "wukpyraw", 25 | "name": "name", 26 | "type": "text", 27 | "required": false, 28 | "unique": false, 29 | "options": { 30 | "min": null, 31 | "max": null, 32 | "pattern": "" 33 | } 34 | } 35 | ], 36 | "indexes": [], 37 | "listRule": null, 38 | "viewRule": null, 39 | "createRule": null, 40 | "updateRule": null, 41 | "deleteRule": null, 42 | "options": {} 43 | }` 44 | 45 | collection := &models.Collection{} 46 | if err := json.Unmarshal([]byte(jsonData), &collection); err != nil { 47 | return err 48 | } 49 | 50 | return daos.New(db).SaveCollection(collection) 51 | }, func(db dbx.Builder) error { 52 | dao := daos.New(db); 53 | 54 | collection, err := dao.FindCollectionByNameOrId("czwjh0mysgfejiu") 55 | if err != nil { 56 | return err 57 | } 58 | 59 | return dao.DeleteCollection(collection) 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /apps/frontend/src/assets/solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/frontend/src/components/success/success.tsx: -------------------------------------------------------------------------------- 1 | import { useNavigate } from '@solidjs/router' 2 | import { useSDK } from '@tma.js/sdk-solid' 3 | import { onCleanup, onMount } from 'solid-js' 4 | import type { Component } from 'solid-js' 5 | 6 | import { LottieAnimation } from '../lottie-animation' 7 | 8 | interface SuccessProps { 9 | text: string 10 | nextButtonLink: string 11 | nextButtonText: string 12 | } 13 | 14 | export const Success: Component = (props) => { 15 | const sdk = useSDK() 16 | const navigate = useNavigate() 17 | 18 | function goToNext() { 19 | sdk.mainButton().disable() 20 | 21 | navigate(props.nextButtonLink) 22 | } 23 | 24 | onMount(() => { 25 | sdk.mainButton().on('click', goToNext) 26 | sdk.mainButton().setText(props.nextButtonText) 27 | 28 | if (!sdk.mainButton().isVisible) sdk.mainButton().show() 29 | if (sdk.backButton().isVisible) sdk.backButton().hide() 30 | 31 | if (!sdk.mainButton().isEnabled) sdk.mainButton().enable() 32 | }) 33 | 34 | onCleanup(() => { 35 | sdk.mainButton().off('click', goToNext) 36 | }) 37 | 38 | return ( 39 |
40 | 45 |

{props.text}

46 |
47 | ) 48 | } 49 | 50 | export function SuccessMock() { 51 | return ( 52 | 57 | ) 58 | } 59 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695986040_updated_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // remove 22 | collection.Schema.RemoveField("ciu99tye") 23 | 24 | // add 25 | new_description := &schema.SchemaField{} 26 | json.Unmarshal([]byte(`{ 27 | "system": false, 28 | "id": "fghcuq0l", 29 | "name": "description", 30 | "type": "text", 31 | "required": false, 32 | "unique": false, 33 | "options": { 34 | "min": null, 35 | "max": null, 36 | "pattern": "" 37 | } 38 | }`), new_description) 39 | collection.Schema.AddField(new_description) 40 | 41 | return dao.SaveCollection(collection) 42 | }, func(db dbx.Builder) error { 43 | dao := daos.New(db); 44 | 45 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 46 | if err != nil { 47 | return err 48 | } 49 | 50 | // add 51 | del_description := &schema.SchemaField{} 52 | json.Unmarshal([]byte(`{ 53 | "system": false, 54 | "id": "ciu99tye", 55 | "name": "description", 56 | "type": "editor", 57 | "required": false, 58 | "unique": false, 59 | "options": {} 60 | }`), del_description) 61 | collection.Schema.AddField(del_description) 62 | 63 | // remove 64 | collection.Schema.RemoveField("fghcuq0l") 65 | 66 | return dao.SaveCollection(collection) 67 | }) 68 | } 69 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695988302_updated_products.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "github.com/pocketbase/dbx" 5 | "github.com/pocketbase/pocketbase/daos" 6 | m "github.com/pocketbase/pocketbase/migrations" 7 | "github.com/pocketbase/pocketbase/tools/types" 8 | ) 9 | 10 | func init() { 11 | m.Register(func(db dbx.Builder) error { 12 | dao := daos.New(db); 13 | 14 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | collection.ListRule = types.Pointer("@request.auth.id != '' && @collection.stores.products.id = id && @request.auth.id = @collection.stores.user.id") 20 | 21 | collection.ViewRule = types.Pointer("@request.auth.id != '' && @collection.stores.products.id = id && @request.auth.id = @collection.stores.user.id") 22 | 23 | collection.CreateRule = types.Pointer("@request.auth.id != '' && @collection.stores.products.id = id && @request.auth.id = @collection.stores.user.id") 24 | 25 | collection.UpdateRule = types.Pointer("@request.auth.id != '' && @collection.stores.products.id = id && @request.auth.id = @collection.stores.user.id") 26 | 27 | collection.DeleteRule = types.Pointer("@request.auth.id != '' && @collection.stores.products.id = id && @request.auth.id = @collection.stores.user.id") 28 | 29 | return dao.SaveCollection(collection) 30 | }, func(db dbx.Builder) error { 31 | dao := daos.New(db); 32 | 33 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 34 | if err != nil { 35 | return err 36 | } 37 | 38 | collection.ListRule = nil 39 | 40 | collection.ViewRule = nil 41 | 42 | collection.CreateRule = nil 43 | 44 | collection.UpdateRule = nil 45 | 46 | collection.DeleteRule = nil 47 | 48 | return dao.SaveCollection(collection) 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /apps/backend/migrations/1696946280_updated_order_items.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("imm7z4sxmlmjlen") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // update 22 | edit_order := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "durxyq7z", 26 | "name": "order", 27 | "type": "relation", 28 | "required": true, 29 | "unique": false, 30 | "options": { 31 | "collectionId": "h30palxkh5zdmcp", 32 | "cascadeDelete": true, 33 | "minSelect": null, 34 | "maxSelect": 1, 35 | "displayFields": [] 36 | } 37 | }`), edit_order) 38 | collection.Schema.AddField(edit_order) 39 | 40 | return dao.SaveCollection(collection) 41 | }, func(db dbx.Builder) error { 42 | dao := daos.New(db); 43 | 44 | collection, err := dao.FindCollectionByNameOrId("imm7z4sxmlmjlen") 45 | if err != nil { 46 | return err 47 | } 48 | 49 | // update 50 | edit_order := &schema.SchemaField{} 51 | json.Unmarshal([]byte(`{ 52 | "system": false, 53 | "id": "durxyq7z", 54 | "name": "order", 55 | "type": "relation", 56 | "required": true, 57 | "unique": false, 58 | "options": { 59 | "collectionId": "h30palxkh5zdmcp", 60 | "cascadeDelete": false, 61 | "minSelect": null, 62 | "maxSelect": 1, 63 | "displayFields": [] 64 | } 65 | }`), edit_order) 66 | collection.Schema.AddField(edit_order) 67 | 68 | return dao.SaveCollection(collection) 69 | }) 70 | } 71 | -------------------------------------------------------------------------------- /apps/backend/migrations/1696959689_updated_products.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // update 22 | edit_store := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "mcgotk5u", 26 | "name": "store", 27 | "type": "relation", 28 | "required": false, 29 | "unique": false, 30 | "options": { 31 | "collectionId": "ls1eoeu2j4mxjuu", 32 | "cascadeDelete": true, 33 | "minSelect": null, 34 | "maxSelect": 1, 35 | "displayFields": [] 36 | } 37 | }`), edit_store) 38 | collection.Schema.AddField(edit_store) 39 | 40 | return dao.SaveCollection(collection) 41 | }, func(db dbx.Builder) error { 42 | dao := daos.New(db); 43 | 44 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 45 | if err != nil { 46 | return err 47 | } 48 | 49 | // update 50 | edit_store := &schema.SchemaField{} 51 | json.Unmarshal([]byte(`{ 52 | "system": false, 53 | "id": "mcgotk5u", 54 | "name": "store", 55 | "type": "relation", 56 | "required": false, 57 | "unique": false, 58 | "options": { 59 | "collectionId": "ls1eoeu2j4mxjuu", 60 | "cascadeDelete": false, 61 | "minSelect": null, 62 | "maxSelect": 1, 63 | "displayFields": [] 64 | } 65 | }`), edit_store) 66 | collection.Schema.AddField(edit_store) 67 | 68 | return dao.SaveCollection(collection) 69 | }) 70 | } 71 | -------------------------------------------------------------------------------- /apps/backend/migrations/1696959755_updated_order_items.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("imm7z4sxmlmjlen") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // update 22 | edit_product := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "vmwjpjlm", 26 | "name": "product", 27 | "type": "relation", 28 | "required": true, 29 | "unique": false, 30 | "options": { 31 | "collectionId": "9463u947e9svq7z", 32 | "cascadeDelete": true, 33 | "minSelect": null, 34 | "maxSelect": 1, 35 | "displayFields": [] 36 | } 37 | }`), edit_product) 38 | collection.Schema.AddField(edit_product) 39 | 40 | return dao.SaveCollection(collection) 41 | }, func(db dbx.Builder) error { 42 | dao := daos.New(db); 43 | 44 | collection, err := dao.FindCollectionByNameOrId("imm7z4sxmlmjlen") 45 | if err != nil { 46 | return err 47 | } 48 | 49 | // update 50 | edit_product := &schema.SchemaField{} 51 | json.Unmarshal([]byte(`{ 52 | "system": false, 53 | "id": "vmwjpjlm", 54 | "name": "product", 55 | "type": "relation", 56 | "required": true, 57 | "unique": false, 58 | "options": { 59 | "collectionId": "9463u947e9svq7z", 60 | "cascadeDelete": false, 61 | "minSelect": null, 62 | "maxSelect": 1, 63 | "displayFields": [] 64 | } 65 | }`), edit_product) 66 | collection.Schema.AddField(edit_product) 67 | 68 | return dao.SaveCollection(collection) 69 | }) 70 | } 71 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695983753_updated_products.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // update 22 | edit_store := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "ajeynuar", 26 | "name": "store", 27 | "type": "relation", 28 | "required": false, 29 | "unique": false, 30 | "options": { 31 | "collectionId": "ls1eoeu2j4mxjuu", 32 | "cascadeDelete": false, 33 | "minSelect": null, 34 | "maxSelect": 1, 35 | "displayFields": [ 36 | "name" 37 | ] 38 | } 39 | }`), edit_store) 40 | collection.Schema.AddField(edit_store) 41 | 42 | return dao.SaveCollection(collection) 43 | }, func(db dbx.Builder) error { 44 | dao := daos.New(db); 45 | 46 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 47 | if err != nil { 48 | return err 49 | } 50 | 51 | // update 52 | edit_store := &schema.SchemaField{} 53 | json.Unmarshal([]byte(`{ 54 | "system": false, 55 | "id": "ajeynuar", 56 | "name": "store", 57 | "type": "relation", 58 | "required": false, 59 | "unique": false, 60 | "options": { 61 | "collectionId": "ls1eoeu2j4mxjuu", 62 | "cascadeDelete": false, 63 | "minSelect": null, 64 | "maxSelect": 1, 65 | "displayFields": [] 66 | } 67 | }`), edit_store) 68 | collection.Schema.AddField(edit_store) 69 | 70 | return dao.SaveCollection(collection) 71 | }) 72 | } 73 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695985633_updated_products.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // add 22 | new_category := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "api8gxpz", 26 | "name": "category", 27 | "type": "relation", 28 | "required": false, 29 | "unique": false, 30 | "options": { 31 | "collectionId": "czwjh0mysgfejiu", 32 | "cascadeDelete": false, 33 | "minSelect": null, 34 | "maxSelect": 1, 35 | "displayFields": [] 36 | } 37 | }`), new_category) 38 | collection.Schema.AddField(new_category) 39 | 40 | // add 41 | new_price := &schema.SchemaField{} 42 | json.Unmarshal([]byte(`{ 43 | "system": false, 44 | "id": "ycvczbun", 45 | "name": "price", 46 | "type": "number", 47 | "required": false, 48 | "unique": false, 49 | "options": { 50 | "min": null, 51 | "max": null 52 | } 53 | }`), new_price) 54 | collection.Schema.AddField(new_price) 55 | 56 | return dao.SaveCollection(collection) 57 | }, func(db dbx.Builder) error { 58 | dao := daos.New(db); 59 | 60 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 61 | if err != nil { 62 | return err 63 | } 64 | 65 | // remove 66 | collection.Schema.RemoveField("api8gxpz") 67 | 68 | // remove 69 | collection.Schema.RemoveField("ycvczbun") 70 | 71 | return dao.SaveCollection(collection) 72 | }) 73 | } 74 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695812931_updated_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // update 22 | edit_user := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "o3ax0bqh", 26 | "name": "user", 27 | "type": "relation", 28 | "required": false, 29 | "unique": false, 30 | "options": { 31 | "collectionId": "_pb_users_auth_", 32 | "cascadeDelete": false, 33 | "minSelect": null, 34 | "maxSelect": 1, 35 | "displayFields": [ 36 | "id" 37 | ] 38 | } 39 | }`), edit_user) 40 | collection.Schema.AddField(edit_user) 41 | 42 | return dao.SaveCollection(collection) 43 | }, func(db dbx.Builder) error { 44 | dao := daos.New(db); 45 | 46 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 47 | if err != nil { 48 | return err 49 | } 50 | 51 | // update 52 | edit_user := &schema.SchemaField{} 53 | json.Unmarshal([]byte(`{ 54 | "system": false, 55 | "id": "o3ax0bqh", 56 | "name": "field", 57 | "type": "relation", 58 | "required": false, 59 | "unique": false, 60 | "options": { 61 | "collectionId": "_pb_users_auth_", 62 | "cascadeDelete": false, 63 | "minSelect": null, 64 | "maxSelect": 1, 65 | "displayFields": [ 66 | "id" 67 | ] 68 | } 69 | }`), edit_user) 70 | collection.Schema.AddField(edit_user) 71 | 72 | return dao.SaveCollection(collection) 73 | }) 74 | } 75 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695894851_updated_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // update 22 | edit_user := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "o3ax0bqh", 26 | "name": "user", 27 | "type": "relation", 28 | "required": false, 29 | "unique": false, 30 | "options": { 31 | "collectionId": "_pb_users_auth_", 32 | "cascadeDelete": true, 33 | "minSelect": null, 34 | "maxSelect": 1, 35 | "displayFields": [ 36 | "id" 37 | ] 38 | } 39 | }`), edit_user) 40 | collection.Schema.AddField(edit_user) 41 | 42 | return dao.SaveCollection(collection) 43 | }, func(db dbx.Builder) error { 44 | dao := daos.New(db); 45 | 46 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 47 | if err != nil { 48 | return err 49 | } 50 | 51 | // update 52 | edit_user := &schema.SchemaField{} 53 | json.Unmarshal([]byte(`{ 54 | "system": false, 55 | "id": "o3ax0bqh", 56 | "name": "user", 57 | "type": "relation", 58 | "required": false, 59 | "unique": false, 60 | "options": { 61 | "collectionId": "_pb_users_auth_", 62 | "cascadeDelete": false, 63 | "minSelect": null, 64 | "maxSelect": 1, 65 | "displayFields": [ 66 | "id" 67 | ] 68 | } 69 | }`), edit_user) 70 | collection.Schema.AddField(edit_user) 71 | 72 | return dao.SaveCollection(collection) 73 | }) 74 | } 75 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695983767_updated_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // update 22 | edit_products := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "qz2vqjoq", 26 | "name": "products", 27 | "type": "relation", 28 | "required": false, 29 | "unique": false, 30 | "options": { 31 | "collectionId": "9463u947e9svq7z", 32 | "cascadeDelete": false, 33 | "minSelect": null, 34 | "maxSelect": null, 35 | "displayFields": [ 36 | "name" 37 | ] 38 | } 39 | }`), edit_products) 40 | collection.Schema.AddField(edit_products) 41 | 42 | return dao.SaveCollection(collection) 43 | }, func(db dbx.Builder) error { 44 | dao := daos.New(db); 45 | 46 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 47 | if err != nil { 48 | return err 49 | } 50 | 51 | // update 52 | edit_products := &schema.SchemaField{} 53 | json.Unmarshal([]byte(`{ 54 | "system": false, 55 | "id": "qz2vqjoq", 56 | "name": "products", 57 | "type": "relation", 58 | "required": false, 59 | "unique": false, 60 | "options": { 61 | "collectionId": "9463u947e9svq7z", 62 | "cascadeDelete": false, 63 | "minSelect": null, 64 | "maxSelect": null, 65 | "displayFields": [] 66 | } 67 | }`), edit_products) 68 | collection.Schema.AddField(edit_products) 69 | 70 | return dao.SaveCollection(collection) 71 | }) 72 | } 73 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695985930_updated_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // add 22 | new_description := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "ciu99tye", 26 | "name": "description", 27 | "type": "editor", 28 | "required": false, 29 | "unique": false, 30 | "options": {} 31 | }`), new_description) 32 | collection.Schema.AddField(new_description) 33 | 34 | // add 35 | new_field := &schema.SchemaField{} 36 | json.Unmarshal([]byte(`{ 37 | "system": false, 38 | "id": "10macb6f", 39 | "name": "field", 40 | "type": "file", 41 | "required": false, 42 | "unique": false, 43 | "options": { 44 | "maxSelect": 1, 45 | "maxSize": 5242880, 46 | "mimeTypes": [ 47 | "image/jpeg", 48 | "image/png", 49 | "image/svg+xml", 50 | "image/gif", 51 | "image/webp" 52 | ], 53 | "thumbs": [], 54 | "protected": false 55 | } 56 | }`), new_field) 57 | collection.Schema.AddField(new_field) 58 | 59 | return dao.SaveCollection(collection) 60 | }, func(db dbx.Builder) error { 61 | dao := daos.New(db); 62 | 63 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 64 | if err != nil { 65 | return err 66 | } 67 | 68 | // remove 69 | collection.Schema.RemoveField("ciu99tye") 70 | 71 | // remove 72 | collection.Schema.RemoveField("10macb6f") 73 | 74 | return dao.SaveCollection(collection) 75 | }) 76 | } 77 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695986131_created_orders.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | jsonData := `{ 15 | "id": "h30palxkh5zdmcp", 16 | "created": "2023-09-29 11:15:31.174Z", 17 | "updated": "2023-09-29 11:15:31.174Z", 18 | "name": "orders", 19 | "type": "base", 20 | "system": false, 21 | "schema": [ 22 | { 23 | "system": false, 24 | "id": "1guoprpf", 25 | "name": "user", 26 | "type": "relation", 27 | "required": false, 28 | "unique": false, 29 | "options": { 30 | "collectionId": "_pb_users_auth_", 31 | "cascadeDelete": false, 32 | "minSelect": null, 33 | "maxSelect": 1, 34 | "displayFields": [] 35 | } 36 | }, 37 | { 38 | "system": false, 39 | "id": "4rgmm8qp", 40 | "name": "store", 41 | "type": "relation", 42 | "required": false, 43 | "unique": false, 44 | "options": { 45 | "collectionId": "ls1eoeu2j4mxjuu", 46 | "cascadeDelete": false, 47 | "minSelect": null, 48 | "maxSelect": 1, 49 | "displayFields": [] 50 | } 51 | } 52 | ], 53 | "indexes": [], 54 | "listRule": null, 55 | "viewRule": null, 56 | "createRule": null, 57 | "updateRule": null, 58 | "deleteRule": null, 59 | "options": {} 60 | }` 61 | 62 | collection := &models.Collection{} 63 | if err := json.Unmarshal([]byte(jsonData), &collection); err != nil { 64 | return err 65 | } 66 | 67 | return daos.New(db).SaveCollection(collection) 68 | }, func(db dbx.Builder) error { 69 | dao := daos.New(db); 70 | 71 | collection, err := dao.FindCollectionByNameOrId("h30palxkh5zdmcp") 72 | if err != nil { 73 | return err 74 | } 75 | 76 | return dao.DeleteCollection(collection) 77 | }) 78 | } 79 | -------------------------------------------------------------------------------- /apps/backend/migrations/1696257575_updated_stores.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // update 22 | edit_avatar := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "10macb6f", 26 | "name": "avatar", 27 | "type": "file", 28 | "required": false, 29 | "unique": false, 30 | "options": { 31 | "maxSelect": 1, 32 | "maxSize": 5242880, 33 | "mimeTypes": [ 34 | "image/jpeg", 35 | "image/png", 36 | "image/svg+xml", 37 | "image/gif", 38 | "image/webp" 39 | ], 40 | "thumbs": [], 41 | "protected": false 42 | } 43 | }`), edit_avatar) 44 | collection.Schema.AddField(edit_avatar) 45 | 46 | return dao.SaveCollection(collection) 47 | }, func(db dbx.Builder) error { 48 | dao := daos.New(db); 49 | 50 | collection, err := dao.FindCollectionByNameOrId("ls1eoeu2j4mxjuu") 51 | if err != nil { 52 | return err 53 | } 54 | 55 | // update 56 | edit_avatar := &schema.SchemaField{} 57 | json.Unmarshal([]byte(`{ 58 | "system": false, 59 | "id": "10macb6f", 60 | "name": "field", 61 | "type": "file", 62 | "required": false, 63 | "unique": false, 64 | "options": { 65 | "maxSelect": 1, 66 | "maxSize": 5242880, 67 | "mimeTypes": [ 68 | "image/jpeg", 69 | "image/png", 70 | "image/svg+xml", 71 | "image/gif", 72 | "image/webp" 73 | ], 74 | "thumbs": [], 75 | "protected": false 76 | } 77 | }`), edit_avatar) 78 | collection.Schema.AddField(edit_avatar) 79 | 80 | return dao.SaveCollection(collection) 81 | }) 82 | } 83 | -------------------------------------------------------------------------------- /apps/backend/migrations/1696683955_updated_products.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // update 22 | edit_images := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "idmoxmg6", 26 | "name": "images", 27 | "type": "file", 28 | "required": false, 29 | "unique": false, 30 | "options": { 31 | "maxSelect": 99, 32 | "maxSize": 5242880, 33 | "mimeTypes": [ 34 | "image/jpeg", 35 | "image/vnd.mozilla.apng", 36 | "image/png", 37 | "image/gif", 38 | "image/webp" 39 | ], 40 | "thumbs": [ 41 | "128x128" 42 | ], 43 | "protected": false 44 | } 45 | }`), edit_images) 46 | collection.Schema.AddField(edit_images) 47 | 48 | return dao.SaveCollection(collection) 49 | }, func(db dbx.Builder) error { 50 | dao := daos.New(db); 51 | 52 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 53 | if err != nil { 54 | return err 55 | } 56 | 57 | // update 58 | edit_images := &schema.SchemaField{} 59 | json.Unmarshal([]byte(`{ 60 | "system": false, 61 | "id": "idmoxmg6", 62 | "name": "images", 63 | "type": "file", 64 | "required": false, 65 | "unique": false, 66 | "options": { 67 | "maxSelect": 99, 68 | "maxSize": 5242880, 69 | "mimeTypes": [ 70 | "image/jpeg", 71 | "image/vnd.mozilla.apng", 72 | "image/png", 73 | "image/gif", 74 | "image/webp" 75 | ], 76 | "thumbs": [], 77 | "protected": false 78 | } 79 | }`), edit_images) 80 | collection.Schema.AddField(edit_images) 81 | 82 | return dao.SaveCollection(collection) 83 | }) 84 | } 85 | -------------------------------------------------------------------------------- /apps/backend/migrations/1696684136_updated_products.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // update 22 | edit_images := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "idmoxmg6", 26 | "name": "images", 27 | "type": "file", 28 | "required": false, 29 | "unique": false, 30 | "options": { 31 | "maxSelect": 99, 32 | "maxSize": 5242880, 33 | "mimeTypes": [ 34 | "image/jpeg", 35 | "image/vnd.mozilla.apng", 36 | "image/png", 37 | "image/gif", 38 | "image/webp" 39 | ], 40 | "thumbs": [ 41 | "0x128" 42 | ], 43 | "protected": false 44 | } 45 | }`), edit_images) 46 | collection.Schema.AddField(edit_images) 47 | 48 | return dao.SaveCollection(collection) 49 | }, func(db dbx.Builder) error { 50 | dao := daos.New(db); 51 | 52 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 53 | if err != nil { 54 | return err 55 | } 56 | 57 | // update 58 | edit_images := &schema.SchemaField{} 59 | json.Unmarshal([]byte(`{ 60 | "system": false, 61 | "id": "idmoxmg6", 62 | "name": "images", 63 | "type": "file", 64 | "required": false, 65 | "unique": false, 66 | "options": { 67 | "maxSelect": 99, 68 | "maxSize": 5242880, 69 | "mimeTypes": [ 70 | "image/jpeg", 71 | "image/vnd.mozilla.apng", 72 | "image/png", 73 | "image/gif", 74 | "image/webp" 75 | ], 76 | "thumbs": [ 77 | "128x128" 78 | ], 79 | "protected": false 80 | } 81 | }`), edit_images) 82 | collection.Schema.AddField(edit_images) 83 | 84 | return dao.SaveCollection(collection) 85 | }) 86 | } 87 | -------------------------------------------------------------------------------- /apps/frontend/src/components/create-product/create-product.tsx: -------------------------------------------------------------------------------- 1 | import { TextField } from '@kobalte/core' 2 | import { createSignal } from 'solid-js' 3 | 4 | import './create-product.css' 5 | 6 | import { StoresResponse } from '@/types/pb-types' 7 | 8 | export interface CreateProductProps { 9 | store?: StoresResponse 10 | } 11 | 12 | export function CreateProduct(props: CreateProductProps) { 13 | const [productName, setProductName] = createSignal('') 14 | const [productDescription, setProductDescription] = createSignal('') 15 | const [productPrice, setProductPrice] = createSignal('') 16 | 17 | return ( 18 |
19 |

20 | Add new product to store 21 | {props.store?.name} 22 |

23 | 27 | Title 28 | setProductName(e.currentTarget.value)} 33 | /> 34 | 35 | 36 | 40 | Description 41 | setProductDescription(e.currentTarget.value)} 46 | /> 47 | 48 | 49 | 53 | Price 54 | setProductPrice(e.currentTarget.value)} 59 | /> 60 | 61 |
62 | ) 63 | } 64 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695986371_created_order_items.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | jsonData := `{ 15 | "id": "imm7z4sxmlmjlen", 16 | "created": "2023-09-29 11:19:31.141Z", 17 | "updated": "2023-09-29 11:19:31.141Z", 18 | "name": "order_items", 19 | "type": "base", 20 | "system": false, 21 | "schema": [ 22 | { 23 | "system": false, 24 | "id": "durxyq7z", 25 | "name": "order", 26 | "type": "relation", 27 | "required": true, 28 | "unique": false, 29 | "options": { 30 | "collectionId": "h30palxkh5zdmcp", 31 | "cascadeDelete": false, 32 | "minSelect": null, 33 | "maxSelect": 1, 34 | "displayFields": [] 35 | } 36 | }, 37 | { 38 | "system": false, 39 | "id": "vmwjpjlm", 40 | "name": "product", 41 | "type": "relation", 42 | "required": true, 43 | "unique": false, 44 | "options": { 45 | "collectionId": "9463u947e9svq7z", 46 | "cascadeDelete": false, 47 | "minSelect": null, 48 | "maxSelect": 1, 49 | "displayFields": [] 50 | } 51 | }, 52 | { 53 | "system": false, 54 | "id": "s4zyx2dq", 55 | "name": "quantity", 56 | "type": "number", 57 | "required": true, 58 | "unique": false, 59 | "options": { 60 | "min": 0, 61 | "max": null 62 | } 63 | } 64 | ], 65 | "indexes": [], 66 | "listRule": null, 67 | "viewRule": null, 68 | "createRule": null, 69 | "updateRule": null, 70 | "deleteRule": null, 71 | "options": {} 72 | }` 73 | 74 | collection := &models.Collection{} 75 | if err := json.Unmarshal([]byte(jsonData), &collection); err != nil { 76 | return err 77 | } 78 | 79 | return daos.New(db).SaveCollection(collection) 80 | }, func(db dbx.Builder) error { 81 | dao := daos.New(db); 82 | 83 | collection, err := dao.FindCollectionByNameOrId("imm7z4sxmlmjlen") 84 | if err != nil { 85 | return err 86 | } 87 | 88 | return dao.DeleteCollection(collection) 89 | }) 90 | } 91 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695983036_created_products.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | jsonData := `{ 15 | "id": "9463u947e9svq7z", 16 | "created": "2023-09-29 10:23:56.568Z", 17 | "updated": "2023-09-29 10:23:56.568Z", 18 | "name": "products", 19 | "type": "base", 20 | "system": false, 21 | "schema": [ 22 | { 23 | "system": false, 24 | "id": "3ho0t2r5", 25 | "name": "name", 26 | "type": "text", 27 | "required": true, 28 | "unique": false, 29 | "options": { 30 | "min": null, 31 | "max": null, 32 | "pattern": "" 33 | } 34 | }, 35 | { 36 | "system": false, 37 | "id": "0tkzwpy2", 38 | "name": "description", 39 | "type": "text", 40 | "required": false, 41 | "unique": false, 42 | "options": { 43 | "min": null, 44 | "max": null, 45 | "pattern": "" 46 | } 47 | }, 48 | { 49 | "system": false, 50 | "id": "idmoxmg6", 51 | "name": "field", 52 | "type": "file", 53 | "required": false, 54 | "unique": false, 55 | "options": { 56 | "maxSelect": 99, 57 | "maxSize": 5242880, 58 | "mimeTypes": [ 59 | "image/jpeg", 60 | "image/vnd.mozilla.apng", 61 | "image/png", 62 | "image/gif", 63 | "image/webp" 64 | ], 65 | "thumbs": [], 66 | "protected": false 67 | } 68 | } 69 | ], 70 | "indexes": [], 71 | "listRule": null, 72 | "viewRule": null, 73 | "createRule": null, 74 | "updateRule": null, 75 | "deleteRule": null, 76 | "options": {} 77 | }` 78 | 79 | collection := &models.Collection{} 80 | if err := json.Unmarshal([]byte(jsonData), &collection); err != nil { 81 | return err 82 | } 83 | 84 | return daos.New(db).SaveCollection(collection) 85 | }, func(db dbx.Builder) error { 86 | dao := daos.New(db); 87 | 88 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 89 | if err != nil { 90 | return err 91 | } 92 | 93 | return dao.DeleteCollection(collection) 94 | }) 95 | } 96 | -------------------------------------------------------------------------------- /apps/frontend/src/pages/dashboard/store/store-id/index.tsx: -------------------------------------------------------------------------------- 1 | import { useNavigate, useParams } from '@solidjs/router' 2 | import { useSDK } from '@tma.js/sdk-solid' 3 | import { onCleanup, onMount } from 'solid-js' 4 | 5 | import { DashboardStoreLayout } from '@/components/dashboard-store-layout' 6 | import { usePocketBase } from '@/contexts/pocketbase' 7 | import { Collections } from '@/types/pb-types' 8 | 9 | export function StoreIdPage() { 10 | const sdk = useSDK() 11 | const navigate = useNavigate() 12 | const params = useParams() 13 | const pb = usePocketBase() 14 | 15 | onMount(() => { 16 | sdk.webApp() 17 | if (sdk.mainButton().isVisible) sdk.mainButton().hide() 18 | if (!sdk.backButton().isVisible) sdk.backButton().show() 19 | 20 | sdk.backButton().on('click', handleGoBack) 21 | }) 22 | 23 | onCleanup(() => { 24 | sdk.backButton().off('click', handleGoBack) 25 | }) 26 | 27 | function handleGoBack() { 28 | navigate('/dashboard') 29 | } 30 | 31 | function handleCatalogClick() { 32 | navigate(`/dashboard/store/${params.storeId}/products`) 33 | } 34 | 35 | async function handleDeleteStoreClick() { 36 | const deleteConfirm = await sdk.popup().open({ 37 | message: 'Are you sure you want to delete this store?', 38 | buttons: [ 39 | { 40 | type: 'destructive', 41 | text: 'Delete', 42 | id: 'delete' 43 | }, 44 | { 45 | type: 'cancel', 46 | id: 'cancel' 47 | } 48 | ] 49 | }) 50 | 51 | if (deleteConfirm === 'cancel') return 52 | 53 | try { 54 | await pb.collection(Collections.Stores).delete(params.storeId) 55 | navigate('/dashboard') 56 | } catch (error) { 57 | console.error(error) 58 | } 59 | } 60 | 61 | return ( 62 | <> 63 | 64 |
65 |
69 |
70 |

Catalog

71 |
72 |
73 |
77 |
78 |

Delete store

79 |
80 |
81 |
82 |
83 | 84 | ) 85 | } 86 | -------------------------------------------------------------------------------- /apps/backend/migrations/1695983101_updated_products.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | m "github.com/pocketbase/pocketbase/migrations" 9 | "github.com/pocketbase/pocketbase/models/schema" 10 | ) 11 | 12 | func init() { 13 | m.Register(func(db dbx.Builder) error { 14 | dao := daos.New(db); 15 | 16 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 17 | if err != nil { 18 | return err 19 | } 20 | 21 | // add 22 | new_store := &schema.SchemaField{} 23 | json.Unmarshal([]byte(`{ 24 | "system": false, 25 | "id": "ajeynuar", 26 | "name": "store", 27 | "type": "relation", 28 | "required": false, 29 | "unique": false, 30 | "options": { 31 | "collectionId": "ls1eoeu2j4mxjuu", 32 | "cascadeDelete": false, 33 | "minSelect": null, 34 | "maxSelect": 1, 35 | "displayFields": [] 36 | } 37 | }`), new_store) 38 | collection.Schema.AddField(new_store) 39 | 40 | // update 41 | edit_images := &schema.SchemaField{} 42 | json.Unmarshal([]byte(`{ 43 | "system": false, 44 | "id": "idmoxmg6", 45 | "name": "images", 46 | "type": "file", 47 | "required": false, 48 | "unique": false, 49 | "options": { 50 | "maxSelect": 99, 51 | "maxSize": 5242880, 52 | "mimeTypes": [ 53 | "image/jpeg", 54 | "image/vnd.mozilla.apng", 55 | "image/png", 56 | "image/gif", 57 | "image/webp" 58 | ], 59 | "thumbs": [], 60 | "protected": false 61 | } 62 | }`), edit_images) 63 | collection.Schema.AddField(edit_images) 64 | 65 | return dao.SaveCollection(collection) 66 | }, func(db dbx.Builder) error { 67 | dao := daos.New(db); 68 | 69 | collection, err := dao.FindCollectionByNameOrId("9463u947e9svq7z") 70 | if err != nil { 71 | return err 72 | } 73 | 74 | // remove 75 | collection.Schema.RemoveField("ajeynuar") 76 | 77 | // update 78 | edit_images := &schema.SchemaField{} 79 | json.Unmarshal([]byte(`{ 80 | "system": false, 81 | "id": "idmoxmg6", 82 | "name": "field", 83 | "type": "file", 84 | "required": false, 85 | "unique": false, 86 | "options": { 87 | "maxSelect": 99, 88 | "maxSize": 5242880, 89 | "mimeTypes": [ 90 | "image/jpeg", 91 | "image/vnd.mozilla.apng", 92 | "image/png", 93 | "image/gif", 94 | "image/webp" 95 | ], 96 | "thumbs": [], 97 | "protected": false 98 | } 99 | }`), edit_images) 100 | collection.Schema.AddField(edit_images) 101 | 102 | return dao.SaveCollection(collection) 103 | }) 104 | } 105 | -------------------------------------------------------------------------------- /apps/frontend/src/components/welcome/welcome.tsx: -------------------------------------------------------------------------------- 1 | import { Image } from '@kobalte/core' 2 | import { useSDK } from '@tma.js/sdk-solid' 3 | import { createEffect, createSignal, For, Show } from 'solid-js' 4 | 5 | import '@/styles/image.css' 6 | import './welcome.css' 7 | 8 | import { useNavigate } from '@solidjs/router' 9 | import type { ListResult } from 'pocketbase' 10 | 11 | import { usePocketBase } from '@/contexts/pocketbase' 12 | import { Collections, StoresResponse } from '@/types/pb-types' 13 | 14 | const storesDefaultValue = { 15 | items: [] as StoresResponse[] 16 | } as ListResult 17 | 18 | export function Welcome() { 19 | const [stores, setStores] = 20 | createSignal>(storesDefaultValue) 21 | 22 | const sdk = useSDK() 23 | const navigate = useNavigate() 24 | const pb = usePocketBase() 25 | 26 | createEffect(async () => { 27 | const user = sdk.initData()?.user 28 | if (!user) return 29 | 30 | const storesResponse = await pb 31 | .collection(Collections.Stores) 32 | .getList() 33 | .catch(() => { 34 | throw new Error('Error while fetching stores') 35 | }) 36 | 37 | setStores(storesResponse) 38 | }) 39 | 40 | function handleStoreClick(store: StoresResponse) { 41 | navigate(`/dashboard/store/${store.id}`) 42 | } 43 | 44 | return ( 45 |
46 |

47 | Hi, {sdk.initData()?.user?.firstName}! 48 |

49 |

50 | 54 | Choose a store to edit or create a new one 55 |

56 | } 57 | > 58 | You have no stores yet 59 | 60 |

61 | Go to marketplace 62 |
63 | 64 | {(store) => ( 65 |
handleStoreClick(store)} 67 | class="store__item flex items-center p-2 space-x-4 cursor-pointer" 68 | > 69 |
70 | 71 | 75 | 76 | {store.name.charAt(0).toUpperCase()} 77 | 78 | 79 |
80 |
81 |

{store.name}

82 |
83 |
84 | )} 85 |
86 |
87 | 88 | ) 89 | } 90 | -------------------------------------------------------------------------------- /apps/frontend/src/types/pb-types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was @generated using pocketbase-typegen 3 | */ 4 | 5 | export enum Collections { 6 | Categories = "categories", 7 | OrderItems = "order_items", 8 | Orders = "orders", 9 | Products = "products", 10 | Stores = "stores", 11 | Users = "users", 12 | } 13 | 14 | // Alias types for improved usability 15 | export type IsoDateString = string 16 | export type RecordIdString = string 17 | export type HTMLString = string 18 | 19 | // System fields 20 | export type BaseSystemFields = { 21 | id: RecordIdString 22 | created: IsoDateString 23 | updated: IsoDateString 24 | collectionId: string 25 | collectionName: Collections 26 | expand?: T 27 | } 28 | 29 | export type AuthSystemFields = { 30 | email: string 31 | emailVisibility: boolean 32 | username: string 33 | verified: boolean 34 | } & BaseSystemFields 35 | 36 | // Record types for each collection 37 | 38 | export type CategoriesRecord = { 39 | name?: string 40 | } 41 | 42 | export type OrderItemsRecord = { 43 | order: RecordIdString 44 | product: RecordIdString 45 | quantity: number 46 | } 47 | 48 | export enum OrdersStatusOptions { 49 | "BuyerInProcess" = "BuyerInProcess", 50 | "WaitForVendor" = "WaitForVendor", 51 | "VendorInProcess" = "VendorInProcess", 52 | "Done" = "Done", 53 | } 54 | export type OrdersRecord = { 55 | status: OrdersStatusOptions 56 | store?: RecordIdString 57 | user?: RecordIdString 58 | } 59 | 60 | export type ProductsRecord = { 61 | category?: RecordIdString 62 | description?: string 63 | images?: string[] 64 | name: string 65 | price?: number 66 | store?: RecordIdString 67 | } 68 | 69 | export type StoresRecord = { 70 | avatar?: string 71 | description?: string 72 | name: string 73 | products?: RecordIdString[] 74 | user?: RecordIdString 75 | } 76 | 77 | export type UsersRecord = { 78 | avatar?: string 79 | name?: string 80 | } 81 | 82 | // Response types include system fields and match responses from the PocketBase API 83 | export type CategoriesResponse = Required & BaseSystemFields 84 | export type OrderItemsResponse = Required & BaseSystemFields 85 | export type OrdersResponse = Required & BaseSystemFields 86 | export type ProductsResponse = Required & BaseSystemFields 87 | export type StoresResponse = Required & BaseSystemFields 88 | export type UsersResponse = Required & AuthSystemFields 89 | 90 | // Types containing all Records and Responses, useful for creating typing helper functions 91 | 92 | export type CollectionRecords = { 93 | categories: CategoriesRecord 94 | order_items: OrderItemsRecord 95 | orders: OrdersRecord 96 | products: ProductsRecord 97 | stores: StoresRecord 98 | users: UsersRecord 99 | } 100 | 101 | export type CollectionResponses = { 102 | categories: CategoriesResponse 103 | order_items: OrderItemsResponse 104 | orders: OrdersResponse 105 | products: ProductsResponse 106 | stores: StoresResponse 107 | users: UsersResponse 108 | } -------------------------------------------------------------------------------- /apps/frontend/src/pages/dashboard/store/store-id/create-product.tsx: -------------------------------------------------------------------------------- 1 | import { TextField } from '@kobalte/core' 2 | import { useNavigate, useParams } from '@solidjs/router' 3 | import { useSDK } from '@tma.js/sdk-solid' 4 | import { createEffect, createSignal, onCleanup, onMount } from 'solid-js' 5 | 6 | import { usePocketBase } from '@/contexts/pocketbase' 7 | import { Collections, ProductsRecord, StoresResponse } from '@/types/pb-types' 8 | 9 | export function CreateProductPage() { 10 | const navigate = useNavigate() 11 | const params = useParams() 12 | const { mainButton, backButton } = useSDK() 13 | const pb = usePocketBase() 14 | const [store, setStore] = createSignal() 15 | 16 | console.log(params) 17 | console.log() 18 | 19 | async function goToNext() { 20 | mainButton().disable() 21 | 22 | const newProduct: ProductsRecord = { 23 | name: productName(), 24 | description: productDescription(), 25 | price: parseFloat(productPrice()), 26 | store: params.storeId 27 | } 28 | 29 | await pb 30 | .collection(Collections.Products) 31 | .create(newProduct) 32 | .catch((err) => { 33 | console.error(err) 34 | }) 35 | 36 | navigate(`/dashboard/store/${params.storeId}/products`) 37 | } 38 | 39 | function onBack() { 40 | navigate(`/dashboard/store/${params.storeId}`) 41 | } 42 | 43 | onMount(() => { 44 | mainButton().setText('Add Product') 45 | 46 | mainButton().on('click', goToNext) 47 | backButton().on('click', onBack) 48 | 49 | if (!mainButton().isVisible) mainButton().show() 50 | if (!backButton().isVisible) backButton().show() 51 | 52 | if (!mainButton().isEnabled) mainButton().enable() 53 | }) 54 | 55 | onCleanup(() => { 56 | mainButton().off('click', goToNext) 57 | backButton().off('click', onBack) 58 | }) 59 | 60 | createEffect(() => { 61 | pb.collection(Collections.Stores) 62 | .getOne(params.storeId) 63 | .then((_store) => { 64 | setStore(_store) 65 | }) 66 | .catch((err) => { 67 | console.error(err) 68 | }) 69 | }) 70 | 71 | const [productName, setProductName] = createSignal('') 72 | const [productDescription, setProductDescription] = createSignal('') 73 | const [productPrice, setProductPrice] = createSignal('') 74 | 75 | return ( 76 |
77 |

78 | Add new product to store 79 | {store()?.name} 80 |

81 | 85 | Title 86 | setProductName(e.currentTarget.value)} 91 | /> 92 | 93 | 94 | 98 | Description 99 | setProductDescription(e.currentTarget.value)} 104 | /> 105 | 106 | 107 | 111 | Price 112 | setProductPrice(e.currentTarget.value)} 117 | /> 118 | 119 |
120 | ) 121 | } 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Telegram Marketplace (Miniapp contest) 3 | 4 | This project aims to create a versatile and user-friendly marketplace and store creation platform. With this platform, users will be able to easily set up their own online stores and marketplaces, allowing them to sell products or services to a wide range of customers. 5 | 6 | ## Key Features 7 | 8 | 🏪 **Store Creation**: Users can effortlessly create their own online stores by following a simple setup process. They can add product categories. *WIP: customize their store's appearance, and set up payment methods.* 9 | 10 | 🛒 **Product Management**: Store owners can easily manage their product inventory, including adding new products, updating existing ones, and tracking stock levels. They can also set pricing. *WIP: discounts and manage product variants.* 11 | 12 | 📈 **Order Management**: The platform provides a comprehensive order management system, allowing store owners to view and process orders. *WIP: Handle returns and refunds, and track shipping status.* 13 | 14 | 🔎 **Search and Filtering** (filtering WIP): Customers can easily search for products across all stores on the platform. Advanced filtering options enable them to refine their search based on price, category, brand, and more. 15 | 16 | 🔒 **Secure Payment Integration (WIP)**: The platform integrates with popular payment gateways, ensuring secure and seamless transactions for both store owners and customers. Multiple payment options are supported, including credit cards, digital wallets, and more. 17 | 18 | ⭐ **User Reviews and Ratings (WIP)**: Customers can leave reviews and ratings for products and stores, helping other users make informed purchasing decisions. Store owners can respond to reviews and engage with their customers. 19 | 20 | 21 | ## Tech Stack 22 | 23 | **Client:** Vite, SolidJS, Typescript, TailwindCSS 24 | **Server:** Golang, Pocketbase 25 | **Database:** SQLite in WAL mode (Pocketbase implemented) 26 | 27 | ## Getting Started 28 | ### Requirements: 29 | - [Node](https://nodejs.org/ru) 30 | - [PNPM](https://pnpm.io/installation) 31 | - [GoLang](https://go.dev/doc/install) 32 | 33 | 34 | ### Steps: 35 | 36 | #### Create bots 37 | 38 | Because in this project at least two bots should be used, one for market and one for dashboard, these steps should be followed two times: 39 | 1. Search for [@BotFather](https://t.me/BotFather) on Telegram. 40 | 2. Start a chat with BotFather. 41 | 3. Use the `/newbot` command to create a new bot. 42 | 4. Following the instructions, choose a bot name and get a Bot Token. 43 | 5. Enter and execute the /setmenubutton command. 44 | 6. Select the new bot. 45 | 7. Set the application URL. For dashboard bot the url should be `/dashboard`, for market - `/market` (`127.0.0.1:4321` for `` if it is local development, or replace it with the URL of your deployed bot). 46 | 8. Set title of the menu button. 47 | 48 | The provided bot tokens will be used in the `.env` file, see **Step 4** of [Start and application](#start-an-application) 49 | 50 | #### Start an application 51 | 52 | 1. Install dependencies with pnpm 53 | ```bash 54 | pnpm install --frozen-lockfile 55 | ``` 56 | 2. Create bot for market and store 57 | 2. (Local development only) Run webhook proxy server. **[ngrok](https://ngrok.com/download) required.** 58 | ```bash 59 | pnpm ngrok 60 | ``` 61 | 3. Fill the `.env` file with values. Command to create the file: 62 | ```bash 63 | cp .env.example .env 64 | ``` 65 | **PUBLIC_POCKETBASE_URL** should be `127.0.0.1:3000`. `TELEGRAM_STORE_BOT_TOKEN` and `TELEGRAM_MARKET_BOT_TOKEN` are filled with tokens from [@BotFather](https://t.me/BotFather) for both bots 66 | 67 | 68 | 4. Run backend, frontend and reverse proxy with one command. 69 | ```bash 70 | pnpm dev 71 | ``` 72 | We use reverse proxy only in dev mode to exclude cors errors on the client. 73 | Backend port: 8090 74 | Frontend port: 4321 75 | Reverse proxy port: 3000 76 | 77 | ## Deploy 78 | There are ready to go docker files in the project. 79 | 80 | Build the image and compose up the container 81 | ```bash 82 | docker compose up --build 83 | ``` 84 | 85 | ## Telegram settings 86 | 87 | Use telegram test server for the developing purpose: 88 | - @intgmarketbot for customers 89 | - @intgstorebot for sellers -------------------------------------------------------------------------------- /apps/frontend/src/pages/market/index.tsx: -------------------------------------------------------------------------------- 1 | import { Image, TextField } from '@kobalte/core' 2 | import { HiSolidMagnifyingGlass } from 'solid-icons/hi' 3 | import { createResource, createSignal, For, onCleanup, onMount } from 'solid-js' 4 | 5 | import { usePocketBase } from '@/contexts/pocketbase' 6 | import { Collections, ProductsResponse } from '@/types/pb-types' 7 | import { createAggListResult } from '@/utils/createAggListResult' 8 | import { useDebounce } from '@/utils/useDebounce' 9 | 10 | import '@/styles/text-field.css' 11 | import '@/styles/image.css' 12 | 13 | import { useNavigate } from '@solidjs/router' 14 | 15 | import { useMainButton } from '@/utils/useMainButton' 16 | import indexStyles from './index.module.css' 17 | 18 | export function MarketPage() { 19 | let intersectionObserver: IntersectionObserver | undefined 20 | let observer: HTMLDivElement | undefined 21 | 22 | const pb = usePocketBase() 23 | 24 | const fetchMarket = async (props: { name: string; page: number }) => { 25 | const options = props.name ? { filter: `name~"${props.name}"` } : undefined 26 | return pb 27 | .collection(Collections.Products) 28 | .getList(props.page, 8, options) 29 | } 30 | 31 | const [name, setName] = createSignal('') 32 | const [fetchName, setFetchName] = createSignal('') 33 | const [page, setPage] = createSignal(1) 34 | const [products] = createResource( 35 | () => ({ name: fetchName(), page: page() }), 36 | fetchMarket 37 | ) 38 | const aggregatedProducts = createAggListResult(products) 39 | const navigate = useNavigate() 40 | const mb = useMainButton(() => navigate('/market/cart')) 41 | mb.setText('🛒 Cart') 42 | 43 | const onSearch = (e: string) => { 44 | setPage(1) 45 | setFetchName(e) 46 | } 47 | 48 | const debounced = useDebounce(onSearch, 300) 49 | 50 | onMount(() => { 51 | if (!observer) return 52 | intersectionObserver = new IntersectionObserver(function (entries) { 53 | if ( 54 | entries[0].intersectionRatio <= 0 || 55 | aggregatedProducts().page >= aggregatedProducts().totalPages 56 | ) 57 | return 58 | setPage(page() + 1) 59 | }) 60 | intersectionObserver.observe(observer) 61 | }) 62 | 63 | onCleanup(() => { 64 | if (intersectionObserver) intersectionObserver.disconnect() 65 | debounced.clear() 66 | }) 67 | 68 | const onChange = (e: string) => { 69 | setName(e) 70 | debounced(e) 71 | } 72 | 73 | function handleProductClick(product: ProductsResponse) { 74 | navigate(`/market/product/${product.id}`) 75 | } 76 | 77 | return ( 78 | <> 79 |
80 | 84 | 89 | 93 | 94 |
95 | 96 |
97 | Loading...

} 100 | > 101 | {(product) => ( 102 |
handleProductClick(product)} 105 | > 106 | 107 | 113 | 114 | {product.name.charAt(0).toUpperCase()} 115 | 116 | 117 |
${product.price}
118 |
{product.name}
119 |
120 | )} 121 |
122 |
126 |
127 | 128 | ) 129 | } 130 | -------------------------------------------------------------------------------- /apps/backend/go.mod: -------------------------------------------------------------------------------- 1 | module pb-stack 2 | 3 | go 1.21.1 4 | 5 | require ( 6 | github.com/Telegram-Web-Apps/init-data-golang v1.1.1 7 | github.com/joho/godotenv v1.5.1 8 | github.com/pocketbase/pocketbase v0.17.3 9 | ) 10 | 11 | require ( 12 | github.com/AlecAivazis/survey/v2 v2.3.7 // indirect 13 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect 14 | github.com/aws/aws-sdk-go v1.44.313 // indirect 15 | github.com/aws/aws-sdk-go-v2 v1.20.0 // indirect 16 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.11 // indirect 17 | github.com/aws/aws-sdk-go-v2/config v1.18.31 // indirect 18 | github.com/aws/aws-sdk-go-v2/credentials v1.13.30 // indirect 19 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.7 // indirect 20 | github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.75 // indirect 21 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.37 // indirect 22 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.31 // indirect 23 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.38 // indirect 24 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.0 // indirect 25 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.12 // indirect 26 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.32 // indirect 27 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.31 // indirect 28 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.0 // indirect 29 | github.com/aws/aws-sdk-go-v2/service/s3 v1.38.0 // indirect 30 | github.com/aws/aws-sdk-go-v2/service/sso v1.13.0 // indirect 31 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.0 // indirect 32 | github.com/aws/aws-sdk-go-v2/service/sts v1.21.0 // indirect 33 | github.com/aws/smithy-go v1.14.0 // indirect 34 | github.com/disintegration/imaging v1.6.2 // indirect 35 | github.com/domodwyer/mailyak/v3 v3.6.1 // indirect 36 | github.com/dustin/go-humanize v1.0.1 // indirect 37 | github.com/fatih/color v1.15.0 // indirect 38 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 39 | github.com/ganigeorgiev/fexpr v0.3.0 // indirect 40 | github.com/go-chi/chi/v5 v5.0.10 41 | github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect 42 | github.com/golang-jwt/jwt/v4 v4.5.0 // indirect 43 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 44 | github.com/golang/protobuf v1.5.3 // indirect 45 | github.com/google/uuid v1.3.0 // indirect 46 | github.com/google/wire v0.5.0 // indirect 47 | github.com/googleapis/gax-go/v2 v2.12.0 // indirect 48 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 49 | github.com/jmespath/go-jmespath v0.4.0 // indirect 50 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect 51 | github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 52 | github.com/mattn/go-colorable v0.1.13 // indirect 53 | github.com/mattn/go-isatty v0.0.19 // indirect 54 | github.com/mattn/go-sqlite3 v1.14.17 // indirect 55 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect 56 | github.com/pocketbase/dbx v1.10.0 57 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect 58 | github.com/spf13/cast v1.5.1 // indirect 59 | github.com/spf13/cobra v1.7.0 // indirect 60 | github.com/spf13/pflag v1.0.5 // indirect 61 | github.com/valyala/bytebufferpool v1.0.0 // indirect 62 | github.com/valyala/fasttemplate v1.2.2 // indirect 63 | go.opencensus.io v0.24.0 // indirect 64 | gocloud.dev v0.32.0 // indirect 65 | golang.org/x/crypto v0.11.0 // indirect 66 | golang.org/x/image v0.9.0 // indirect 67 | golang.org/x/mod v0.12.0 // indirect 68 | golang.org/x/net v0.12.0 // indirect 69 | golang.org/x/oauth2 v0.10.0 // indirect 70 | golang.org/x/sync v0.3.0 // indirect 71 | golang.org/x/sys v0.10.0 // indirect 72 | golang.org/x/term v0.10.0 // indirect 73 | golang.org/x/text v0.11.0 // indirect 74 | golang.org/x/time v0.3.0 // indirect 75 | golang.org/x/tools v0.11.1 // indirect 76 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect 77 | google.golang.org/api v0.134.0 // indirect 78 | google.golang.org/appengine v1.6.7 // indirect 79 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf // indirect 80 | google.golang.org/grpc v1.57.0 // indirect 81 | google.golang.org/protobuf v1.31.0 // indirect 82 | lukechampine.com/uint128 v1.3.0 // indirect 83 | modernc.org/cc/v3 v3.41.0 // indirect 84 | modernc.org/ccgo/v3 v3.16.14 // indirect 85 | modernc.org/libc v1.24.1 // indirect 86 | modernc.org/mathutil v1.6.0 // indirect 87 | modernc.org/memory v1.6.0 // indirect 88 | modernc.org/opt v0.1.3 // indirect 89 | modernc.org/sqlite v1.24.0 // indirect 90 | modernc.org/strutil v1.1.3 // indirect 91 | modernc.org/token v1.1.0 // indirect 92 | ) 93 | -------------------------------------------------------------------------------- /apps/frontend/src/pages/dashboard/store/store-id/products.tsx: -------------------------------------------------------------------------------- 1 | import { Image } from '@kobalte/core' 2 | import { useNavigate, useParams } from '@solidjs/router' 3 | import { useSDK } from '@tma.js/sdk-solid' 4 | import PocketBase from 'pocketbase' 5 | import { createResource, For, onCleanup, onMount, Show } from 'solid-js' 6 | 7 | import { DashboardStoreLayout } from '@/components/dashboard-store-layout' 8 | import { usePocketBase } from '@/contexts/pocketbase' 9 | import { Collections, ProductsResponse } from '@/types/pb-types' 10 | 11 | export function StoreProductsPage() { 12 | const params = useParams() 13 | const pb = usePocketBase() 14 | const navigate = useNavigate() 15 | 16 | const [products, { refetch: refetchProducts }] = createResource( 17 | { storeId: params.storeId, _pb: pb }, 18 | fetchProducts 19 | ) 20 | 21 | const sdk = useSDK() 22 | 23 | onMount(() => { 24 | sdk.mainButton().on('click', handleAddProduct) 25 | sdk.backButton().on('click', handleBackClick) 26 | sdk.mainButton().setText('Add product') 27 | 28 | if (!sdk.mainButton().isVisible) sdk.mainButton().show() 29 | if (!sdk.backButton().isVisible) sdk.backButton().show() 30 | 31 | if (!sdk.mainButton().isEnabled) sdk.mainButton().enable() 32 | }) 33 | 34 | onCleanup(() => { 35 | sdk.mainButton().off('click', handleAddProduct) 36 | sdk.backButton().off('click', handleBackClick) 37 | }) 38 | 39 | function handleAddProduct() { 40 | sdk.mainButton().disable() 41 | 42 | navigate(`/dashboard/store/${params.storeId}/create-product`) 43 | } 44 | 45 | function handleBackClick() { 46 | navigate(`/dashboard/store/${params.storeId}`) 47 | } 48 | 49 | function fetchProducts({ 50 | storeId, 51 | _pb 52 | }: { 53 | storeId: string 54 | _pb: PocketBase 55 | }) { 56 | console.log('fetchProducts', storeId) 57 | try { 58 | const req = _pb 59 | .collection(Collections.Products) 60 | .getFullList({ 61 | filter: `store="${storeId}"` 62 | }) 63 | 64 | return req 65 | } catch (error) { 66 | console.log(error) 67 | throw new Error('Не удалось получить список товаров') 68 | } 69 | } 70 | 71 | async function handleProductClick(_product: ProductsResponse) { 72 | // popup to delete store 73 | const deleteConfirm = await sdk.popup().open({ 74 | message: 'Are you sure you want to delete this product?', 75 | title: _product.name, 76 | buttons: [ 77 | { 78 | type: 'destructive', 79 | text: 'Delete', 80 | id: 'delete' 81 | }, 82 | { 83 | type: 'cancel', 84 | id: 'cancel' 85 | } 86 | ] 87 | }) 88 | 89 | if (deleteConfirm === 'cancel') return 90 | 91 | try { 92 | console.log('delete product', _product.id) 93 | 94 | await pb.collection(Collections.Products).delete(_product.id) 95 | refetchProducts() 96 | } catch (error) { 97 | console.error(error) 98 | } 99 | } 100 | 101 | return ( 102 | <> 103 | 104 |

Current Product List

105 |
106 | 0} 108 | fallback={
There are no products yet
} 109 | > 110 | 111 | {(_product) => ( 112 |
handleProductClick(_product)} 114 | class="store__item flex items-center p-2 space-x-4 cursor-pointer" 115 | > 116 |
117 | 118 | 126 | 127 | {_product.name.charAt(0).toUpperCase()} 128 | 129 | 130 |
131 |
132 |

{_product.name}

133 |
134 |
135 |

{_product.price}$

136 |
137 |
138 | )} 139 |
140 |
141 |
142 |
143 | 144 | ) 145 | } 146 | -------------------------------------------------------------------------------- /apps/frontend/src/components/create-store/create-store.tsx: -------------------------------------------------------------------------------- 1 | import { Image, TextField } from '@kobalte/core' 2 | import { useNavigate } from '@solidjs/router' 3 | import { useSDK } from '@tma.js/sdk-solid' 4 | import { createEffect, createSignal, onCleanup, onMount } from 'solid-js' 5 | import type { 6 | StoresRecord, 7 | StoresResponse, 8 | UsersResponse 9 | } from '@/types/pb-types' 10 | 11 | import { usePocketBase } from '@/contexts/pocketbase' 12 | import { Collections } from '@/types/pb-types' 13 | 14 | import '@/styles/text-field.css' 15 | import '@/styles/image.css' 16 | import './create-store.css' 17 | 18 | export function CreateStore() { 19 | const [storeName, setStoreName] = createSignal('') 20 | const [storeDescription, setStoreDescription] = createSignal('') 21 | const [storeAvatar, setStoreAvatar] = createSignal('') 22 | 23 | const { mainButton, backButton } = useSDK() 24 | const pb = usePocketBase() 25 | const navigate = useNavigate() 26 | 27 | async function goToNext() { 28 | mainButton().showProgress().disable() 29 | 30 | try { 31 | if (storeName().length === 0) throw new Error('Store name is required') 32 | 33 | const data: StoresRecord = { 34 | name: storeName(), 35 | user: (pb.authStore.model as UsersResponse).id, 36 | description: storeDescription() 37 | } 38 | 39 | const response = await pb 40 | .collection(Collections.Stores) 41 | .create(data) 42 | .catch((err) => { 43 | throw err 44 | }) 45 | 46 | navigate(`/dashboard/store/${response.id}/create-product`) 47 | } catch (error) { 48 | console.error(error) 49 | } finally { 50 | mainButton().hideProgress().enable() 51 | } 52 | } 53 | 54 | function onBack() { 55 | navigate('/dashboard') 56 | } 57 | 58 | onMount(() => { 59 | mainButton().setText('Next') 60 | 61 | mainButton().on('click', goToNext) 62 | backButton().on('click', onBack) 63 | 64 | if (!backButton().isVisible) backButton().show() 65 | 66 | if (!mainButton().isEnabled) mainButton().enable() 67 | if (!mainButton().isProgressVisible) mainButton().hideProgress() 68 | }) 69 | 70 | createEffect(() => { 71 | if (storeName().length === 0) { 72 | mainButton().hide().disable() 73 | } else { 74 | mainButton().show().enable() 75 | } 76 | }) 77 | 78 | onCleanup(() => { 79 | mainButton().off('click', goToNext) 80 | backButton().off('click', onBack) 81 | }) 82 | 83 | function onImageClick() { 84 | const fileInput = document.createElement('input') 85 | fileInput.type = 'file' 86 | fileInput.accept = '.jpg, .jpeg, .png, .webp' 87 | fileInput.click() 88 | 89 | fileInput.addEventListener('change', () => { 90 | const file = fileInput.files![0] 91 | const reader = new FileReader() 92 | reader.readAsDataURL(file) 93 | 94 | reader.onload = () => { 95 | setStoreAvatar(reader.result as string) 96 | console.log(reader.result) 97 | } 98 | reader.onerror = () => console.log(reader.error) 99 | }) 100 | } 101 | 102 | return ( 103 | <> 104 |
105 | 109 | 113 | 114 | 115 | 121 | 125 | 126 | 127 | 128 | 129 | 130 |
131 |
132 | 138 | Name 139 | 143 | 144 | 145 | 151 | 152 | Description 153 | 154 | 158 | 159 |
160 | 161 | ) 162 | } 163 | -------------------------------------------------------------------------------- /apps/frontend/src/pages/market/product/product-id/index.tsx: -------------------------------------------------------------------------------- 1 | import { Image } from '@kobalte/core' 2 | import { useNavigate, useParams } from '@solidjs/router' 3 | import { createEffect, createResource, onCleanup, Show } from 'solid-js' 4 | 5 | import { usePocketBase } from '@/contexts/pocketbase' 6 | import { 7 | Collections, 8 | OrderItemsRecord, 9 | OrdersRecord, 10 | OrdersResponse, 11 | OrdersStatusOptions, 12 | ProductsRecord, 13 | UsersResponse 14 | } from '@/types/pb-types' 15 | import { useBackButton } from '@/utils/useBackButton' 16 | import { useMainButton } from '@/utils/useMainButton' 17 | 18 | import '@/styles/image.css' 19 | import './styles.css' 20 | 21 | export function MarketProductIdPage() { 22 | const navigate = useNavigate() 23 | const params = useParams() 24 | const pb = usePocketBase() 25 | const mb = useMainButton() 26 | useBackButton(() => navigate(-1)) 27 | 28 | async function fetcherProduct(productId: string) { 29 | return pb.collection(Collections.Products).getOne(productId) 30 | } 31 | 32 | async function fetcherOrder(productId: string) { 33 | return pb 34 | .collection(Collections.OrderItems) 35 | .getFirstListItem( 36 | `product.id = "${productId}" && order.status = "BuyerInProcess"` 37 | ) 38 | } 39 | 40 | const [product] = createResource(params.productId, fetcherProduct) 41 | const [order] = createResource(params.productId, fetcherOrder) 42 | 43 | const ADD_TO_CART = () => `Add to cart $${product()?.price}` 44 | const GO_TO_CART = '📦 In cart' 45 | 46 | async function addToCart() { 47 | mb.showProgress().disable() 48 | const userId = (pb.authStore.model as UsersResponse).id 49 | 50 | try { 51 | let getOrderResponse: OrdersResponse 52 | 53 | //Ищем корзину 54 | try { 55 | getOrderResponse = await pb 56 | .collection(Collections.Orders) 57 | .getFirstListItem( 58 | `user.id = "${userId}" && status = "BuyerInProcess"` 59 | ) 60 | } catch (err) { 61 | //Не нашли - создаём новую 62 | const newOrder: OrdersRecord = { 63 | user: userId, 64 | status: OrdersStatusOptions.BuyerInProcess 65 | } 66 | 67 | getOrderResponse = await pb 68 | .collection(Collections.Orders) 69 | .create(newOrder) 70 | .catch((err) => { 71 | throw err 72 | }) 73 | } 74 | 75 | const newOrderItem: OrderItemsRecord = { 76 | order: getOrderResponse.id, 77 | product: params.productId, 78 | quantity: 1 79 | } 80 | 81 | await pb 82 | .collection(Collections.OrderItems) 83 | .create(newOrderItem) 84 | .catch((err) => { 85 | throw err 86 | }) 87 | 88 | mb.off('click', addToCart) 89 | mb.setText(GO_TO_CART).on('click', goToCart) 90 | } catch (error) { 91 | console.error(error) 92 | } finally { 93 | mb.hideProgress().enable() 94 | } 95 | } 96 | 97 | function goToCart() { 98 | mb.off('click', goToCart) 99 | navigate('/market/cart') 100 | } 101 | 102 | createEffect(() => { 103 | mb.off('click', goToCart) 104 | mb.off('click', addToCart) 105 | if (order.state != 'ready') { 106 | mb.setText(ADD_TO_CART()).on('click', addToCart) 107 | } else { 108 | mb.setText(GO_TO_CART).on('click', goToCart) 109 | } 110 | }) 111 | 112 | onCleanup(() => { 113 | mb.off('click', goToCart) 114 | mb.off('click', addToCart) 115 | }) 116 | 117 | return ( 118 |
119 | Loading...} 122 | > 123 | {(p) => ( 124 | <> 125 | 126 | {p().images && p().images && ( 127 | 132 | )} 133 | 134 | 📦 135 | 136 | 137 |
138 |
139 |
140 |
Title
141 |
{p().name}
142 |
143 |
144 |
Price
145 |
{p().price}$
146 |
147 |
148 |
149 |
Description
150 |
{p().description}
151 |
152 | 153 | )} 154 |
155 |
156 | ) 157 | } 158 | -------------------------------------------------------------------------------- /apps/frontend/src/App.tsx: -------------------------------------------------------------------------------- 1 | import './App.css' 2 | 3 | import { Navigate, Route, Router, Routes } from '@solidjs/router' 4 | import { SDKProvider } from '@tma.js/sdk-solid' 5 | 6 | import { DisplayGate } from './components/twa-display-gate' 7 | import { PocketbaseProvider } from './contexts/pocketbase' 8 | import { DashboardPage } from './pages/dashboard' 9 | import { OrdersPage } from './pages/dashboard/order' 10 | import { OrderIdPage } from './pages/dashboard/order/order-id' 11 | import { ProductIdPage } from './pages/dashboard/product/product-id' 12 | import { StoresPage } from './pages/dashboard/store' 13 | import { CreateStorePage } from './pages/dashboard/store/create' 14 | import { StoreIdPage } from './pages/dashboard/store/store-id' 15 | import { CreateProductPage } from './pages/dashboard/store/store-id/create-product' 16 | import { StoreProductsPage } from './pages/dashboard/store/store-id/products' 17 | import { StoreSettingsPage } from './pages/dashboard/store/store-id/settings' 18 | import { MarketPage } from './pages/market' 19 | import { MarketCartPage } from './pages/market/cart' 20 | import { MarketOrdersPage } from './pages/market/order' 21 | import { MarketOrderIdPage } from './pages/market/order/order-id' 22 | import { MarketProductIdPage } from './pages/market/product/product-id' 23 | import { MarketStoreIdPage } from './pages/market/store/store-id' 24 | 25 | /* SITE MAP 26 | /market 27 | / 28 | /:storeId 29 | /:productId 30 | /order 31 | /:orderId 32 | /cart 33 | /dashboard 34 | / 35 | /store 36 | / 37 | /create 38 | /:storeId 39 | / 40 | /products 41 | /settings 42 | /orders 43 | 44 | / 45 | /:orderId 46 | /product 47 | /create 48 | /:productId 49 | */ 50 | 51 | function App() { 52 | return ( 53 | 60 | 61 | 62 | 63 | 64 | 65 | 69 | 73 | 77 | 78 | 82 | 86 | 87 | 91 | } 94 | /> 95 | 96 | 97 | 101 | 102 | 106 | 107 | 108 | 112 | 116 | 117 | 118 | 122 | 126 | 127 | 131 | 135 | 139 | 143 | 144 | 145 | } 148 | /> 149 | 150 | 151 | 152 | 153 | 154 | 155 | ) 156 | } 157 | 158 | export default App 159 | -------------------------------------------------------------------------------- /apps/frontend/src/pages/market/cart.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Image } from '@kobalte/core' 2 | import { useNavigate } from '@solidjs/router' 3 | import { BsBoxSeamFill, BsTrashFill } from 'solid-icons/bs' 4 | import { 5 | createEffect, 6 | createResource, 7 | createSignal, 8 | For, 9 | Show, 10 | Suspense 11 | } from 'solid-js' 12 | 13 | import { usePocketBase } from '@/contexts/pocketbase' 14 | import { 15 | Collections, 16 | OrderItemsResponse, 17 | OrdersStatusOptions, 18 | ProductsResponse, 19 | UsersResponse 20 | } from '@/types/pb-types' 21 | import { useBackButton } from '@/utils/useBackButton' 22 | 23 | import '@/styles/button.css' 24 | 25 | import { Success } from '@/components/success/success' 26 | import { useMainButton } from '@/utils/useMainButton' 27 | import cartStyles from './cart.module.css' 28 | 29 | type Texpand = { 30 | product: ProductsResponse 31 | } 32 | 33 | export function MarketCartPage() { 34 | const pb = usePocketBase() 35 | const [isBuyed, setIsBuyed] = createSignal(false) 36 | 37 | const fetchOrdersItems = async () => { 38 | return pb 39 | .collection(Collections.OrderItems) 40 | .getFullList>({ 41 | filter: `order.user.id = "${ 42 | (pb.authStore.model as UsersResponse).id 43 | }" && order.status = "BuyerInProcess"`, 44 | expand: 'product' 45 | }) 46 | } 47 | 48 | const [orderItems, { refetch }] = createResource(fetchOrdersItems) 49 | const quantity = () => 50 | orderItems()?.reduce((acc, item) => acc + item.quantity, 0) 51 | 52 | const navigate = useNavigate() 53 | useBackButton(() => navigate(-1)) 54 | const mb = useMainButton(buy) 55 | 56 | createEffect(() => { 57 | const price = orderItems()?.reduce( 58 | (acc, item) => acc + item.quantity * item.expand?.product?.price!, 59 | 0 60 | ) 61 | if (!price) { 62 | mb.hide().disable() 63 | return 64 | } 65 | mb.show().enable() 66 | mb.setText(`Checkout $${price}`) 67 | }) 68 | 69 | async function clearCart() { 70 | try { 71 | await pb.collection(Collections.Orders).delete(orderItems()![0].order) 72 | refetch() 73 | mb.hide().disable() 74 | } catch (e) { 75 | console.error(e) 76 | } 77 | } 78 | 79 | async function buy() { 80 | try { 81 | await pb.collection(Collections.Orders).update(orderItems()![0].order, { 82 | status: OrdersStatusOptions.WaitForVendor 83 | }) 84 | mb.off('click', buy) 85 | setIsBuyed(true) 86 | } catch (e) { 87 | console.error(e) 88 | } 89 | } 90 | 91 | async function removeProduct(id: string) { 92 | try { 93 | await pb.collection(Collections.OrderItems).delete(id) 94 | refetch() 95 | } catch (e) { 96 | console.error(e) 97 | } 98 | } 99 | 100 | return ( 101 | 109 | } 110 | > 111 |
112 |
113 | Cart 114 | {quantity() && ( 115 | 119 | Clear 120 | 121 | )} 122 |
123 | Загрузка...
}> 124 | 128 | Nothing here. Go back and find some products you like 🤓 129 | 130 | } 131 | > 132 | {(item) => ( 133 |
134 | 137 | 145 | 146 | {item.expand?.product.name.charAt(0).toUpperCase()} 147 | 148 | 149 |
150 | {item.expand?.product.name} 151 |
152 |
${item.expand?.product.price}
153 | removeProduct(item.id)} 155 | class={`${cartStyles.button} button row-start-1 row-end-3 col-start-3 w-10 h-10`} 156 | > 157 | 158 | 159 |
160 | )} 161 |
162 | 163 | 171 | 172 |
173 | ) 174 | } 175 | -------------------------------------------------------------------------------- /apps/backend/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "net/http/httputil" 8 | "net/url" 9 | "os" 10 | "strconv" 11 | "strings" 12 | 13 | "github.com/go-chi/chi/v5" 14 | "github.com/joho/godotenv" 15 | "github.com/pocketbase/pocketbase" 16 | "github.com/pocketbase/pocketbase/apis" 17 | "github.com/pocketbase/pocketbase/core" 18 | "github.com/pocketbase/pocketbase/models" 19 | "github.com/pocketbase/pocketbase/plugins/migratecmd" 20 | 21 | // uncomment once you have at least one .go migration file in the "migrations" directory 22 | _ "pb-stack/migrations" 23 | ) 24 | 25 | func main() { 26 | // loosely check if it was executed using "go run" 27 | exePath := os.Args[0] 28 | isDevMode := strings.HasPrefix(exePath, os.TempDir()) || strings.Contains(exePath, "debug") 29 | 30 | if isDevMode { 31 | err := godotenv.Load("../../.env") 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | } 36 | 37 | app := pocketbase.New() 38 | 39 | migratecmd.MustRegister(app, app.RootCmd, migratecmd.Config{ 40 | // enable auto creation of migration files when making collection changes in the Admin UI 41 | // (the isGoRun check is to enable it only during development) 42 | Automigrate: isDevMode, 43 | }) 44 | 45 | // serves static files from the provided public dir (if exists) 46 | app.OnBeforeServe().Add(func(e *core.ServeEvent) error { 47 | e.Router.GET("/*", apis.StaticDirectoryHandler(os.DirFS("./pb_public"), true)) 48 | 49 | //e.Router.Pre(telegramCheck(app)) 50 | 51 | return nil 52 | }) 53 | 54 | app.OnRecordBeforeAuthWithPasswordRequest().Add(func(e *core.RecordAuthWithPasswordEvent) error { 55 | 56 | initDataUser, err := initDataCheck(e.HttpContext, e.Identity, e.Password) 57 | if initDataUser == nil { 58 | log.Println("no init data") 59 | return nil 60 | } else if err != nil { 61 | log.Println("something wrong: " + err.Error()) 62 | return err 63 | } 64 | 65 | // authorizing regular users (the same could be done for admins) 66 | user, err := app.Dao().FindRecordById("users", strconv.FormatInt(initDataUser.ID, 10)) 67 | if err != nil { 68 | // TODO: Check if the error is a "not found" error 69 | log.Default().Println("user not found, creating new one...") 70 | 71 | collection, errFind := app.Dao().FindCollectionByNameOrId("users") 72 | if errFind != nil { 73 | return err 74 | } 75 | 76 | record := models.NewRecord(collection) 77 | 78 | // set individual fields 79 | // or bulk load with record.Load(map[string]any{...}) 80 | username := getUsername(initDataUser.Username, initDataUser.ID) 81 | record.Set("id", initDataUser.ID) 82 | record.Set("username", username) 83 | 84 | if errVerify := record.SetPassword("PASSWORDLESS"); errVerify != nil { 85 | return errVerify 86 | } 87 | if errVerify := record.SetVerified(true); errVerify != nil { 88 | return errVerify 89 | } 90 | 91 | if errSave := app.Dao().SaveRecord(record); errSave != nil { 92 | return errSave 93 | } 94 | 95 | user = record 96 | } else { 97 | log.Default().Println("user found: " + user.Id) 98 | } 99 | 100 | // "authenticating" the user 101 | // for admins it would be `c.Set(apis.ContextAdminKey, admin)` 102 | //e.HttpContext.Set(apis.ContextAuthRecordKey, user) 103 | 104 | if errAuthResp := apis.RecordAuthResponse(app, e.HttpContext, user, nil); errAuthResp != nil { 105 | return errAuthResp 106 | } 107 | return fmt.Errorf("skip") 108 | }) 109 | 110 | // fires for every auth collection 111 | app.OnRecordBeforeAuthRefreshRequest().Add(func(e *core.RecordAuthRefreshEvent) error { 112 | initDataUser, err := initDataCheck(e.HttpContext, "USERNAMELESS", "PASSWORDLESS") 113 | if initDataUser == nil { 114 | log.Println("no init data") 115 | return nil 116 | } else if err != nil { 117 | log.Println("something wrong: " + err.Error()) 118 | return err 119 | } 120 | 121 | return nil 122 | }) 123 | 124 | if isDevMode { 125 | go reverseProxy() 126 | } 127 | 128 | if err := app.Start(); err != nil { 129 | log.Fatal(err) 130 | } 131 | } 132 | 133 | func reverseProxy() { 134 | 135 | log.Println("starting reverse proxy...") 136 | 137 | remoteBackend, err := url.Parse("http://localhost:8090") 138 | if err != nil { 139 | panic(err) 140 | } 141 | 142 | remoteFrontend, err := url.Parse("http://localhost:4321") 143 | if err != nil { 144 | panic(err) 145 | } 146 | 147 | handlerBackend := func(p *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) { 148 | return func(w http.ResponseWriter, r *http.Request) { 149 | r.Host = remoteBackend.Host 150 | w.Header().Set("X-Ben", "Rad") 151 | p.ServeHTTP(w, r) 152 | } 153 | } 154 | 155 | handlerFrontend := func(p *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) { 156 | return func(w http.ResponseWriter, r *http.Request) { 157 | log.Println("GET page from " + r.URL.String()) 158 | r.Host = remoteFrontend.Host 159 | w.Header().Set("X-Ben", "Rad") 160 | p.ServeHTTP(w, r) 161 | } 162 | } 163 | 164 | proxyBackend := httputil.NewSingleHostReverseProxy(remoteBackend) 165 | proxyFrontend := httputil.NewSingleHostReverseProxy(remoteFrontend) 166 | 167 | r := chi.NewRouter() 168 | 169 | r.HandleFunc("/api/*", handlerBackend(proxyBackend)) 170 | r.HandleFunc("/_/*", handlerBackend(proxyBackend)) 171 | r.HandleFunc("/*", handlerFrontend(proxyFrontend)) 172 | 173 | err = http.ListenAndServe(":3000", r) 174 | if err != nil { 175 | panic(err) 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /apps/frontend/public/lottie/check_mark.json: -------------------------------------------------------------------------------- 1 | {"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":180,"w":512,"h":512,"nm":"✅","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Layer 1","parent":2,"sr":1,"ks":{"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":2,"s":[99.949,99.949,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":4,"s":[99.74,99.74,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":6,"s":[99.194,99.194,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":8,"s":[98.19,98.19,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":10,"s":[97.313,97.313,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":12,"s":[96.725,96.725,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":14,"s":[96.309,96.309,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":16,"s":[96,96,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":18,"s":[95.761,95.761,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":20,"s":[95.574,95.574,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":22,"s":[95.425,95.425,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":24,"s":[95.307,95.307,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":26,"s":[95.214,95.214,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":28,"s":[95.141,95.141,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":30,"s":[95.086,95.086,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":32,"s":[95.047,95.047,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":34,"s":[95.02,95.02,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":36,"s":[95.005,95.005,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":38,"s":[95,95,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":40,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":42,"s":[124.413,124.413,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":44,"s":[121.9,121.9,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":46,"s":[108.676,108.676,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":48,"s":[97.943,97.943,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":50,"s":[94.256,94.256,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":52,"s":[95.772,95.772,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":54,"s":[98.788,98.788,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":56,"s":[100.813,100.813,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":58,"s":[101.274,101.274,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":60,"s":[100.777,100.777,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":62,"s":[100.125,100.125,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":64,"s":[99.763,99.763,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":66,"s":[99.731,99.731,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":68,"s":[99.865,99.865,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":70,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":72,"s":[100.061,100.061,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":74,"s":[100.054,100.054,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":76,"s":[100.022,100.022,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":78,"s":[99.995,99.995,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":80,"s":[99.986,99.986,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":82,"s":[99.99,99.99,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":84,"s":[99.997,99.997,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":86,"s":[99.978,99.978,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":88,"s":[99.728,99.728,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":90,"s":[98.834,98.834,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":92,"s":[97.5,97.5,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":94,"s":[96.673,96.673,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":96,"s":[96.155,96.155,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":98,"s":[95.8,95.8,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":100,"s":[95.545,95.545,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":102,"s":[95.358,95.358,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":104,"s":[95.223,95.223,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":106,"s":[95.126,95.126,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":108,"s":[95.061,95.061,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":110,"s":[95.021,95.021,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":112,"s":[95.002,95.002,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":114,"s":[96.81,96.81,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":116,"s":[116.32,116.32,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":118,"s":[125.58,125.58,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":120,"s":[115.613,115.613,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":122,"s":[102.513,102.513,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":124,"s":[95.238,95.238,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":126,"s":[94.6,94.6,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":128,"s":[97.295,97.295,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":130,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":132,"s":[101.215,101.215,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":134,"s":[101.09,101.09,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":136,"s":[100.432,100.432,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":138,"s":[99.898,99.898,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":140,"s":[99.714,99.714,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":142,"s":[99.789,99.789,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":144,"s":[99.94,99.94,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":146,"s":[100.04,100.04,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":148,"s":[100.063,100.063,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":150,"s":[100.039,100.039,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":152,"s":[100.006,100.006,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":154,"s":[99.988,99.988,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":156,"s":[99.987,99.987,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":158,"s":[99.993,99.993,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":160,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":162,"s":[100.003,100.003,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":164,"s":[100.003,100.003,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":166,"s":[100.001,100.001,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":168,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":170,"s":[99.999,99.999,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":172,"s":[99.999,99.999,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":174,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":176,"s":[100,100,100]},{"t":178,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.204,-1.809],[0,0],[-1.898,3.161],[0,0],[2.116,0],[0,0],[0.957,-1.693],[0,0],[1.497,2.204],[0,0],[1.786,0],[0,0]],"o":[[0,0],[2.043,3.07],[0,0],[1.089,-1.814],[0,0],[-1.944,0],[0,0],[-1.311,2.32],[0,0],[-1.004,-1.478],[0,0],[-2.173,0]],"v":[[-144.046,-15.735],[-37.054,145.012],[-28.569,144.814],[138.111,-132.808],[135.777,-136.931],[86.354,-136.931],[81.659,-134.192],[-28.704,61.066],[-34.804,61.317],[-88.43,-17.603],[-92.89,-19.966],[-141.78,-19.966]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":10},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":109,"s":[0]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":118,"s":[60]},{"t":180,"s":[0]}]},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":10},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":109,"s":[0]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":118,"s":[50]},{"t":180,"s":[0]}]},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 2","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":10},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":109,"s":[0]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":118,"s":[40]},{"t":180,"s":[0]}]},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 3","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":10},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":109,"s":[0]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":118,"s":[30]},{"t":180,"s":[0]}]},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 4","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":10},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":109,"s":[0]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":118,"s":[20]},{"t":180,"s":[0]}]},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 5","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":10},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":109,"s":[0]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":118,"s":[10]},{"t":180,"s":[0]}]},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 6","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 3","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.204,-1.809],[0,0],[-1.898,3.161],[0,0],[2.116,0],[0,0],[0.957,-1.693],[0,0],[1.497,2.204],[0,0],[1.786,0],[0,0]],"o":[[0,0],[2.043,3.07],[0,0],[1.089,-1.814],[0,0],[-1.944,0],[0,0],[-1.311,2.32],[0,0],[-1.004,-1.478],[0,0],[-2.173,0]],"v":[[-144.046,-15.735],[-37.054,145.012],[-28.569,144.814],[138.111,-132.808],[135.777,-136.931],[86.354,-136.931],[81.659,-134.192],[-28.704,61.066],[-34.804,61.317],[-88.43,-17.603],[-92.89,-19.966],[-141.78,-19.966]],"c":true}},"nm":"Path 1","hd":false},{"ty":"gf","o":{"a":0,"k":100},"r":1,"bm":0,"g":{"p":3,"k":{"a":1,"k":[{"t":0,"s":[0,1,1,1,0.5,1,1,1,1,1,1,1],"h":1},{"t":10,"s":[0,0.016,0.51,0,0.5,0.047,0.576,0.016,1,0.078,0.643,0.031],"h":1},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":40,"s":[0,0.017,0.541,0,0.5,0.034,0.606,0,1,0.052,0.671,0]},{"t":85,"s":[0,0.016,0.51,0,0.5,0.047,0.576,0.016,1,0.078,0.643,0.031],"h":1},{"t":115,"s":[0,1,1,1,0.5,1,1,1,1,1,1,1],"h":1}]}},"s":{"a":0,"k":[-2.368,147.973]},"e":{"a":0,"k":[-1.805,-130.216]},"t":1,"nm":"Gradient Fill 4","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.204,-1.809],[0,0],[-1.898,3.161],[0,0],[2.116,0],[0,0],[0.957,-1.693],[0,0],[1.497,2.204],[0,0],[1.786,0],[0,0]],"o":[[0,0],[2.043,3.07],[0,0],[1.089,-1.814],[0,0],[-1.944,0],[0,0],[-1.311,2.32],[0,0],[-1.004,-1.478],[0,0],[-2.173,0]],"v":[[-144.046,-20.287],[-37.054,140.46],[-28.569,140.263],[138.111,-137.36],[135.777,-141.483],[86.354,-141.483],[81.659,-138.743],[-28.704,56.514],[-34.804,56.765],[-88.43,-22.155],[-92.89,-24.517],[-141.78,-24.517]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.51372551918,0.082352943718,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Layer 3","parent":3,"sr":1,"ks":{"s":{"a":1,"k":[{"t":0,"s":[100,100,100],"h":1},{"t":10,"s":[93,93,100],"h":1},{"t":40,"s":[100,100,100],"h":1},{"t":75,"s":[100,100,100],"h":1},{"t":85,"s":[93,93,100],"h":1},{"t":115,"s":[100,100,100],"h":1}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[40.956,0],[0,0],[0,40.956],[0,0],[-40.956,0],[0,0],[0,-40.956],[0,0]],"o":[[0,0],[-40.956,0],[0,0],[0,-40.956],[0,0],[40.956,0],[0,0],[0,40.956]],"v":[[121.065,195.221],[-121.065,195.221],[-195.221,121.065],[-195.221,-121.065],[-121.065,-195.221],[121.065,-195.221],[195.221,-121.065],[195.221,121.065]],"c":true}},"nm":"Path 1","hd":false},{"ty":"gf","o":{"a":0,"k":100},"r":1,"bm":0,"g":{"p":7,"k":{"a":0,"k":[0,0.749,1,0.812,0.102,0.375,0.924,0.406,0.204,0,0.847,0,0.352,0.057,0.825,0,0.5,0.114,0.804,0,0.75,0.116,0.698,0,1,0.118,0.592,0,0,1,0.102,0.75,0.204,0.5,0.352,0.5,0.5,0.5,0.75,0.5,1,0.5]}},"s":{"a":0,"k":[-1,-196]},"e":{"a":0,"k":[-1,185.887]},"t":1,"nm":"Gradient Fill 3","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 3","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Layer 2","sr":1,"ks":{"p":{"a":0,"k":[256,256,0]},"s":{"a":1,"k":[{"i":{"x":[0.94,0.94,1],"y":[-0.032,-0.032,1]},"o":{"x":[1,1,1],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.06,0.06,0],"y":[1.033,1.033,0]},"t":10,"s":[95,95,100]},{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[1,1,1],"y":[0,0,0]},"t":20,"s":[90,90,100]},{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":61,"s":[100,100,100]},{"i":{"x":[0.94,0.94,1],"y":[-0.032,-0.032,1]},"o":{"x":[1,1,1],"y":[0,0,0]},"t":75,"s":[100,100,100]},{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.06,0.06,0],"y":[1.033,1.033,0]},"t":85,"s":[95,95,100]},{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[1,1,1],"y":[0,0,0]},"t":95,"s":[90,90,100]},{"t":136,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[45.667,0],[0,0],[0,45.667],[0,0],[-45.667,0],[0,0],[0,-45.667],[0,0]],"o":[[0,0],[-45.667,0],[0,0],[0,-45.667],[0,0],[45.667,0],[0,0],[0,45.667]],"v":[[123.16,205.847],[-123.16,205.847],[-205.847,123.16],[-205.847,-123.16],[-123.16,-205.847],[123.16,-205.847],[205.847,-123.16],[205.847,123.16]],"c":true}},"nm":"Path 1","hd":false},{"ty":"gf","o":{"a":0,"k":100},"r":1,"bm":0,"g":{"p":5,"k":{"a":0,"k":[0,0.118,0.584,0,0.25,0.118,0.671,0,0.5,0.118,0.757,0,0.75,0.094,0.829,0,1,0.071,0.902,0,0,0.5,0.25,0.35,0.5,0.2,0.75,0.35,1,0.5]}},"s":{"a":0,"k":[-1,147]},"e":{"a":0,"k":[-1,-169.651]},"t":1,"nm":"Gradient Fill 2","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[48.422,0],[0,0],[0,48.422],[0,0],[-48.422,0],[0,0],[0,-48.422],[0,0]],"o":[[0,0],[-48.422,0],[0,0],[0,-48.422],[0,0],[48.422,0],[0,0],[0,48.422]],"v":[[126.842,214.517],[-126.842,214.517],[-214.517,126.842],[-214.517,-126.842],[-126.842,-214.517],[126.842,-214.517],[214.517,-126.842],[214.517,126.842]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.51372551918,0.082352943718,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 3","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 3","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0}]} 2 | -------------------------------------------------------------------------------- /apps/backend/go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q= 3 | cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= 4 | cloud.google.com/go/compute v1.22.0 h1:cB8R6FtUtT1TYGl5R3xuxnW6OUIc/DrT2aiR16TTG7Y= 5 | cloud.google.com/go/compute v1.22.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= 6 | cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= 7 | cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= 8 | cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= 9 | cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= 10 | cloud.google.com/go/storage v1.31.0 h1:+S3LjjEN2zZ+L5hOwj4+1OkGCsLVe0NzpXKQ1pSdTCI= 11 | cloud.google.com/go/storage v1.31.0/go.mod h1:81ams1PrhW16L4kF7qg+4mTq7SRs5HsbDTM0bWvrwJ0= 12 | github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= 13 | github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= 14 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 15 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= 16 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= 17 | github.com/Telegram-Web-Apps/init-data-golang v1.1.1 h1:odw+hBU7KqZ7bwxM8Gzyc8BXtZI2ZVCHNsru/OZVo64= 18 | github.com/Telegram-Web-Apps/init-data-golang v1.1.1/go.mod h1:jSZDetZQqKMVlFpPZO5KjXaKzlloULpF8Yo7Iklkguw= 19 | github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= 20 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= 21 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= 22 | github.com/aws/aws-sdk-go v1.44.313 h1:u6EuNQqgAmi09GEZ5g/XGHLF0XV31WcdU5rnHyIBHBc= 23 | github.com/aws/aws-sdk-go v1.44.313/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= 24 | github.com/aws/aws-sdk-go-v2 v1.20.0 h1:INUDpYLt4oiPOJl0XwZDK2OVAVf0Rzo+MGVTv9f+gy8= 25 | github.com/aws/aws-sdk-go-v2 v1.20.0/go.mod h1:uWOr0m0jDsiWw8nnXiqZ+YG6LdvAlGYDLLf2NmHZoy4= 26 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.11 h1:/MS8AzqYNAhhRNalOmxUvYs8VEbNGifTnzhPFdcRQkQ= 27 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.11/go.mod h1:va22++AdXht4ccO3kH2SHkHHYvZ2G9Utz+CXKmm2CaU= 28 | github.com/aws/aws-sdk-go-v2/config v1.18.31 h1:CcacHsJjsPtHpe1MaopwPddUErmLnl+X77+7n4G2KkY= 29 | github.com/aws/aws-sdk-go-v2/config v1.18.31/go.mod h1:pnSeuahFFvtScCHy0INXLxJ4N8H7KncD5u6A48bx3/8= 30 | github.com/aws/aws-sdk-go-v2/credentials v1.13.30 h1:4pt4sI4OwXrrWUGuGr5NEb2g+4IBUB/I2BVj0t2Ak7Q= 31 | github.com/aws/aws-sdk-go-v2/credentials v1.13.30/go.mod h1:Scpo/dGUdxAtRKsNCaXMXONnl3gvvugbXVldy5Fz2DQ= 32 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.7 h1:X3H6+SU21x+76LRglk21dFRgMTJMa5QcpW+SqUf5BBg= 33 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.7/go.mod h1:3we0V09SwcJBzNlnyovrR2wWJhWmVdqAsmVs4uronv8= 34 | github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.75 h1:dKQyBQCZiu47cQ47UhwsVEr5TYu4YqoyAnue6qgybh0= 35 | github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.75/go.mod h1:ZduPOCZCNBJ4anGPXD9oU+3tJgOmm9VdoBQuNHvYnFY= 36 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.37 h1:zr/gxAZkMcvP71ZhQOcvdm8ReLjFgIXnIn0fw5AM7mo= 37 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.37/go.mod h1:Pdn4j43v49Kk6+82spO3Tu5gSeQXRsxo56ePPQAvFiA= 38 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.31 h1:0HCMIkAkVY9KMgueD8tf4bRTUanzEYvhw7KkPXIMpO0= 39 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.31/go.mod h1:fTJDMe8LOFYtqiFFFeHA+SVMAwqLhoq0kcInYoLa9Js= 40 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.38 h1:+i1DOFrW3YZ3apE45tCal9+aDKK6kNEbW6Ib7e1nFxE= 41 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.38/go.mod h1:1/jLp0OgOaWIetycOmycW+vYTYgTZFPttJQRgsI1PoU= 42 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.0 h1:U5yySdwt2HPo/pnQec04DImLzWORbeWML1fJiLkKruI= 43 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.0/go.mod h1:EhC/83j8/hL/UB1WmExo3gkElaja/KlmZM/gl1rTfjM= 44 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.12 h1:uAiiHnWihGP2rVp64fHwzLDrswGjEjsPszwRYMiYQPU= 45 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.12/go.mod h1:fUTHpOXqRQpXvEpDPSa3zxCc2fnpW6YnBoba+eQr+Bg= 46 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.32 h1:kvN1jPHr9UffqqG3bSgZ8tx4+1zKVHz/Ktw/BwW6hX8= 47 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.32/go.mod h1:QmMEM7es84EUkbYWcpnkx8i5EW2uERPfrTFeOch128Y= 48 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.31 h1:auGDJ0aLZahF5SPvkJ6WcUuX7iQ7kyl2MamV7Tm8QBk= 49 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.31/go.mod h1:3+lloe3sZuBQw1aBc5MyndvodzQlyqCZ7x1QPDHaWP4= 50 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.0 h1:Wgjft9X4W5pMeuqgPCHIQtbZ87wsgom7S5F8obreg+c= 51 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.0/go.mod h1:FWNzS4+zcWAP05IF7TDYTY1ysZAzIvogxWaDT9p8fsA= 52 | github.com/aws/aws-sdk-go-v2/service/s3 v1.38.0 h1:Flb+Uw+ewvmbZiaXEl+aIs1HygnwztxZmhkCe1b+YQg= 53 | github.com/aws/aws-sdk-go-v2/service/s3 v1.38.0/go.mod h1:6SOWLiobcZZshbmECRTADIRYliPL0etqFSigauQEeT0= 54 | github.com/aws/aws-sdk-go-v2/service/sso v1.13.0 h1:agnjK56/1jtGPehxV8QZ/AYHV++pEfl7CpYbWjHjBDc= 55 | github.com/aws/aws-sdk-go-v2/service/sso v1.13.0/go.mod h1:TC9BubuFMVScIU+TLKamO6VZiYTkYoEHqlSQwAe2omw= 56 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.0 h1:g0Rr6COTBEaIG9TFQ0GmRkPWOGuDfySGSq2PlMcclrY= 57 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.0/go.mod h1:XO/VcyoQ8nKyKfFW/3DMsRQXsfh/052tHTWmg3xBXRg= 58 | github.com/aws/aws-sdk-go-v2/service/sts v1.21.0 h1:HI1YIL5Q9FtucxF5tcNpzCEyLnkeUcqg6xtOx8u09S4= 59 | github.com/aws/aws-sdk-go-v2/service/sts v1.21.0/go.mod h1:G8SbvL0rFk4WOJroU8tKBczhsbhj2p/YY7qeJezJ3CI= 60 | github.com/aws/smithy-go v1.14.0 h1:+X90sB94fizKjDmwb4vyl2cTTPXTE5E2G/1mjByb0io= 61 | github.com/aws/smithy-go v1.14.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= 62 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 63 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 64 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 65 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 66 | github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= 67 | github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 68 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 69 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 70 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 71 | github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= 72 | github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= 73 | github.com/domodwyer/mailyak/v3 v3.6.1 h1:o3vjinPjPUpw/2Ng5N91JrlFDkoIKu4HTHw3U/CNemE= 74 | github.com/domodwyer/mailyak/v3 v3.6.1/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c= 75 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 76 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 77 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 78 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 79 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 80 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 81 | github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= 82 | github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= 83 | github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= 84 | github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 85 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 86 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 87 | github.com/ganigeorgiev/fexpr v0.3.0 h1:RwSyJBME+g/XdzlUW0paH/4VXhLHPg+rErtLeC7K8Ew= 88 | github.com/ganigeorgiev/fexpr v0.3.0/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE= 89 | github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= 90 | github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= 91 | github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es= 92 | github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew= 93 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 94 | github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= 95 | github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= 96 | github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= 97 | github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 98 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 99 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 100 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 101 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 102 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 103 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 104 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 105 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 106 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 107 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 108 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 109 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 110 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 111 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 112 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 113 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 114 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 115 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 116 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 117 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 118 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 119 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 120 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 121 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 122 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 123 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 124 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 125 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 126 | github.com/google/go-replayers/grpcreplay v1.1.0 h1:S5+I3zYyZ+GQz68OfbURDdt/+cSMqCK1wrvNx7WBzTE= 127 | github.com/google/go-replayers/grpcreplay v1.1.0/go.mod h1:qzAvJ8/wi57zq7gWqaE6AwLM6miiXUQwP1S+I9icmhk= 128 | github.com/google/go-replayers/httpreplay v1.2.0 h1:VM1wEyyjaoU53BwrOnaf9VhAyQQEEioJvFYxYcLRKzk= 129 | github.com/google/go-replayers/httpreplay v1.2.0/go.mod h1:WahEFFZZ7a1P4VM1qEeHy+tME4bwyqPcwWbNlUI1Mcg= 130 | github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= 131 | github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= 132 | github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA= 133 | github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= 134 | github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= 135 | github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= 136 | github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= 137 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 138 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 139 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 140 | github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= 141 | github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= 142 | github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= 143 | github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= 144 | github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= 145 | github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= 146 | github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= 147 | github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= 148 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 149 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 150 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 151 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 152 | github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= 153 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 154 | github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= 155 | github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 156 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= 157 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 158 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 159 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 160 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 161 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 162 | github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 h1:FwuzbVh87iLiUQj1+uQUsuw9x5t9m5n5g7rG7o4svW4= 163 | github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61/go.mod h1:paQfF1YtHe+GrGg5fOgjsjoCX/UKDr9bc1DoWpZfns8= 164 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 165 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 166 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 167 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 168 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 169 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 170 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 171 | github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= 172 | github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= 173 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 174 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= 175 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 176 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 177 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 178 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 179 | github.com/pocketbase/dbx v1.10.0 h1:58VIT7r6T+BnVbYVosvGBsPjQEic3/VFRYGT823vWSQ= 180 | github.com/pocketbase/dbx v1.10.0/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs= 181 | github.com/pocketbase/pocketbase v0.17.3 h1:TscX1bx2Um3LQtzPUtmfzXvHpjDSQf8fOPqr2ovrQBs= 182 | github.com/pocketbase/pocketbase v0.17.3/go.mod h1:fEYQs9QijcvYU1/zqyGVsJgC6fcVlK12c9BHZEz2qvk= 183 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 184 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= 185 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 186 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 187 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 188 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 189 | github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= 190 | github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= 191 | github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= 192 | github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= 193 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 194 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 195 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 196 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 197 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 198 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 199 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 200 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 201 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 202 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 203 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 204 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 205 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 206 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 207 | github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= 208 | github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 209 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 210 | go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= 211 | go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 212 | gocloud.dev v0.32.0 h1:jHf8WSkByuAuXcvFt04OiiQH+N0zaRtxI6iEph8Bq8Y= 213 | gocloud.dev v0.32.0/go.mod h1:m/x/N9cRjDF5MD0i5TLFbKbqkGffl/qayXA9FcMT5Oc= 214 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 215 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 216 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 217 | golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= 218 | golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= 219 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 220 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 221 | golang.org/x/image v0.9.0 h1:QrzfX26snvCM20hIhBwuHI/ThTg18b/+kcKdXHvnR+g= 222 | golang.org/x/image v0.9.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0= 223 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 224 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 225 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 226 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 227 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 228 | golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= 229 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 230 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 231 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 232 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 233 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 234 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 235 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 236 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 237 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 238 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 239 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 240 | golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= 241 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 242 | golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= 243 | golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 244 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 245 | golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= 246 | golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= 247 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 248 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 249 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 250 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 251 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 252 | golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= 253 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 254 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 255 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 256 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 257 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 258 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 259 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 260 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 261 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 262 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 263 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 264 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 265 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 266 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 267 | golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= 268 | golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 269 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 270 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 271 | golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 272 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 273 | golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= 274 | golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= 275 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 276 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 277 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 278 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 279 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 280 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 281 | golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= 282 | golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 283 | golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= 284 | golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 285 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 286 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 287 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 288 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 289 | golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 290 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 291 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 292 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 293 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 294 | golang.org/x/tools v0.11.1 h1:ojD5zOW8+7dOGzdnNgersm8aPfcDjhMp12UfG93NIMc= 295 | golang.org/x/tools v0.11.1/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= 296 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 297 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 298 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= 299 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 300 | google.golang.org/api v0.134.0 h1:ktL4Goua+UBgoP1eL1/60LwZJqa1sIzkLmvoR3hR6Gw= 301 | google.golang.org/api v0.134.0/go.mod h1:sjRL3UnjTx5UqNQS9EWr9N8p7xbHpy1k0XGRLCf3Spk= 302 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 303 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 304 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 305 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 306 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 307 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 308 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 309 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 310 | google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e h1:xIXmWJ303kJCuogpj0bHq+dcjcZHU+XFyc1I0Yl9cRg= 311 | google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108= 312 | google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 h1:lCbbUxUDD+DiXx9Q6F/ttL0aAu7N2pz8XnmMm8ZW4NE= 313 | google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= 314 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf h1:guOdSPaeFgN+jEJwTo1dQ71hdBm+yKSCCKuTRkJzcVo= 315 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I= 316 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 317 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 318 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 319 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 320 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 321 | google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= 322 | google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= 323 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 324 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 325 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 326 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 327 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 328 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 329 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 330 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 331 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 332 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 333 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 334 | google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= 335 | google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 336 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 337 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 338 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 339 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 340 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 341 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 342 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 343 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 344 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 345 | lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= 346 | lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= 347 | modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q= 348 | modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= 349 | modernc.org/ccgo/v3 v3.16.14 h1:af6KNtFgsVmnDYrWk3PQCS9XT6BXe7o3ZFJKkIKvXNQ= 350 | modernc.org/ccgo/v3 v3.16.14/go.mod h1:mPDSujUIaTNWQSG4eqKw+atqLOEbma6Ncsa94WbC9zo= 351 | modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= 352 | modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= 353 | modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= 354 | modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= 355 | modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM= 356 | modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak= 357 | modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= 358 | modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= 359 | modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o= 360 | modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= 361 | modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= 362 | modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= 363 | modernc.org/sqlite v1.24.0 h1:EsClRIWHGhLTCX44p+Ri/JLD+vFGo0QGjasg2/F9TlI= 364 | modernc.org/sqlite v1.24.0/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk= 365 | modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= 366 | modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= 367 | modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= 368 | modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c= 369 | modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= 370 | modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= 371 | modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= 372 | modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= 373 | --------------------------------------------------------------------------------